diff --git a/OsmAnd/jni/osmand/binaryRead.cpp b/OsmAnd/jni/osmand/binaryRead.cpp index e2896c45a4..153074271d 100644 --- a/OsmAnd/jni/osmand/binaryRead.cpp +++ b/OsmAnd/jni/osmand/binaryRead.cpp @@ -14,6 +14,7 @@ #include "renderRules.h" #include "common.h" #include "mapObjects.h" +#include "multipolygons.h" #include "proto/osmand_odb.pb.h" char errorMsg[1024]; @@ -61,7 +62,10 @@ struct SearchQuery { int top; int bottom; int zoom; - SearchResult* res; + std::vector< MapDataObject*> result; + + jobject o; + jfieldID interruptedField; std::vector > cacheCoordinates; std::vector cacheTypes; @@ -71,17 +75,15 @@ struct SearchQuery { int numberOfAcceptedObjects; int numberOfReadSubtrees; int numberOfAcceptedSubtrees; - bool interrupted; - SearchQuery(int l, int r, int t, int b, RenderingRuleSearchRequest* req, SearchResult* res) : - req(req), left(l), right(r), top(t), bottom(b), res(res) { + SearchQuery(int l, int r, int t, int b, RenderingRuleSearchRequest* req, jobject o, jfieldID interruptedField) : + req(req), left(l), right(r), top(t), bottom(b), o(o), interruptedField(interruptedField) { numberOfAcceptedObjects = numberOfVisitedObjects = 0; numberOfAcceptedSubtrees = numberOfReadSubtrees = 0; - interrupted = false; } bool isCancelled(){ - return interrupted; + return globalEnv()->GetBooleanField(o, interruptedField); } }; @@ -110,7 +112,7 @@ struct MapIndex { uint32 length; int filePointer; std::string name; - std::hash_map > decodingRules; + std::hash_map decodingRules; vector levels; }; @@ -393,7 +395,7 @@ static const int SHIFT_COORDINATES = 5; static const int MASK_TO_READ = ~((1 << SHIFT_COORDINATES) - 1); static const int MASK_10 = (1 << 10) - 1; -BaseMapDataObject* readMapDataObject(io::CodedInputStream* input, int left, int right, int top, int bottom, SearchQuery* req, +MapDataObject* readMapDataObject(io::CodedInputStream* input, int left, int right, int top, int bottom, SearchQuery* req, MapIndex* root) { uint32 tag = input->ReadTag(); if (MapData::kCoordinatesFieldNumber != WireFormatLite::GetTagFieldNumber(tag)) { @@ -468,7 +470,7 @@ BaseMapDataObject* readMapDataObject(io::CodedInputStream* input, int left, int if (mask != RenderingRulesStorage::POINT_RULES) { type = type & MASK_10; } - std::pair pair = root->decodingRules[type]; + tag_value pair = root->decodingRules[type]; if (r != NULL && !accept) { if(mask == RenderingRulesStorage::MULTI_POLYGON_TYPE){ mask = RenderingRulesStorage::POLYGON_RULES; @@ -589,11 +591,11 @@ bool searchMapTreeBounds(io::CodedInputStream* input, int pleft, int pright, int input->ReadVarint32(&length); int oldLimit = input->PushLimit(length); if (lastIndexResult == -1) { - lastIndexResult = req->res->result.size(); + lastIndexResult = req->result.size(); } - BaseMapDataObject* mapObject = readMapDataObject(input, cleft, cright, ctop, cbottom, req, root); + MapDataObject* mapObject = readMapDataObject(input, cleft, cright, ctop, cbottom, req, root); if (mapObject != NULL) { - req->res->result.push_back(mapObject); + req->result.push_back(mapObject); } input->Skip(input->BytesUntilLimit()); input->PopLimit(oldLimit); @@ -618,10 +620,10 @@ bool searchMapTreeBounds(io::CodedInputStream* input, int pleft, int pright, int uint64 baseId; input->ReadVarint64(&baseId); if (lastIndexResult != -1) { - for (uint32 i = lastIndexResult; i < req->res->result.size(); i++) { - BaseMapDataObject* rs = req->res->result.at(i); + for (uint32 i = lastIndexResult; i < req->result.size(); i++) { + BaseMapDataObject* rs = req->result.at(i); rs->id += baseId; - // TODO restrictions are not supported + // restrictions are not supported // if (rs.restrictions != null) { // for (int j = 0; j < rs.restrictions.length; j++) { // rs.restrictions[j] += baseId; @@ -640,8 +642,8 @@ bool searchMapTreeBounds(io::CodedInputStream* input, int pleft, int pright, int readStringTable(input, stringTable); input->PopLimit(oldLimit); if (lastIndexResult != -1) { - for (uint32 i = lastIndexResult; i < req->res->result.size(); i++) { - BaseMapDataObject* rs = req->res->result.at(i); + for (uint32 i = lastIndexResult; i < req->result.size(); i++) { + BaseMapDataObject* rs = req->result.at(i); if (rs->stringId != BaseMapDataObject::UNDEFINED_STRING) { rs->name = stringTable.at(rs->stringId); } @@ -685,7 +687,8 @@ void searchMapData(io::CodedInputStream* input, MapRoot* root, MapIndex* ind, Se extern "C" JNIEXPORT jint JNICALL Java_net_osmand_plus_render_NativeOsmandLibrary_searchObjectsForRendering(JNIEnv* ienv, jobject obj, jint sleft, jint sright, jint stop, jint sbottom, jint zoom, jstring mapName, - jobject renderingRuleSearchRequest, bool skipDuplicates, jint searchResult) { + jobject renderingRuleSearchRequest, bool skipDuplicates, jint searchResult, jobject objInterrupted) { + // TODO skipDuplicates not supported setGlobalEnv(ienv); SearchResult* result = (SearchResult*) searchResult; if(result == NULL) { @@ -698,7 +701,10 @@ extern "C" JNIEXPORT jint JNICALL Java_net_osmand_plus_render_NativeOsmandLibrar } BinaryMapFile* file = i->second; RenderingRuleSearchRequest* req = initSearchRequest(renderingRuleSearchRequest); - SearchQuery q(sleft,sright, stop, sbottom, req, result); + jclass clObjInterrupted = globalEnv()->GetObjectClass(objInterrupted); + jfieldID interruptedField = getFid(clObjInterrupted, "interrupted", "Z"); + globalEnv()->DeleteLocalRef(clObjInterrupted); + SearchQuery q(sleft,sright, stop, sbottom, req, objInterrupted, interruptedField); fseek(file->f, 0, 0); io::FileInputStream input(fileno(file->f)); @@ -724,11 +730,25 @@ extern "C" JNIEXPORT jint JNICALL Java_net_osmand_plus_render_NativeOsmandLibrar } } } -// if(result->result.size() > 0) { + result->result.insert(result->result.end(), q.result.begin(), q.result.end()); + std::map > multyPolygons; + std::vector::iterator mdo = q.result.begin(); + for(;mdo!= q.result.end(); mdo++) { + for(size_t j = 0; j<(*mdo)->types.size(); j++) { + int type = (*mdo)->types.at(j); + if((type & 0x3) == RenderingRulesStorage::MULTI_POLYGON_TYPE) { + tagValueType tagValue((*mdo)->tagValues.at(j), type); + multyPolygons[tagValue].push_back(*mdo); + } + } + } + + proccessMultiPolygons(multyPolygons, q.left, q.right, q.bottom, q.top, q.zoom, result->result); + if(q.result.size() > 0) { sprintf(errorMsg, "Search : tree - read( %d), accept( %d), objs - visit( %d), accept(%d), in result(%d) ", q.numberOfReadSubtrees, q.numberOfAcceptedSubtrees, q.numberOfVisitedObjects, q.numberOfAcceptedObjects, result->result.size()); __android_log_print(ANDROID_LOG_INFO, "net.osmand", errorMsg); -// } + } delete req; return (jint)result; } diff --git a/OsmAnd/jni/osmand/common.cpp b/OsmAnd/jni/osmand/common.cpp index 2fcf0fd554..98d7305382 100644 --- a/OsmAnd/jni/osmand/common.cpp +++ b/OsmAnd/jni/osmand/common.cpp @@ -223,6 +223,7 @@ void copyRenderingContext(jobject orc, RenderingContext* rc) rc->shadowLevelMin = globalEnv()->GetIntField( orc, getFid( RenderingContextClass, "shadowLevelMin", "I" ) ); rc->shadowLevelMax = globalEnv()->GetIntField( orc, getFid( RenderingContextClass, "shadowLevelMax", "I" ) ); rc->androidContext = globalEnv()->GetObjectField(orc, getFid( RenderingContextClass, "ctx", "Landroid/content/Context;")); + rc->lastRenderedKey = 0; rc->originalRC = orc; @@ -236,6 +237,8 @@ void mergeRenderingContext(jobject orc, RenderingContext* rc) globalEnv()->SetIntField( orc, getFid(RenderingContextClass, "visible", "I" ) , rc->visible); globalEnv()->SetIntField( orc, getFid(RenderingContextClass, "allObjects", "I" ) , rc->allObjects); globalEnv()->SetIntField( orc, getFid(RenderingContextClass, "textRenderingTime", "I" ) , rc->textRendering.getElapsedTime()); + globalEnv()->SetIntField( orc, getFid(RenderingContextClass, "lastRenderedKey", "I" ) , rc->lastRenderedKey); + globalEnv()->DeleteLocalRef(rc->androidContext); } diff --git a/OsmAnd/jni/osmand/common.h b/OsmAnd/jni/osmand/common.h index eeb691364a..ec697cc767 100644 --- a/OsmAnd/jni/osmand/common.h +++ b/OsmAnd/jni/osmand/common.h @@ -128,9 +128,11 @@ struct RenderingContext { int pointInsideCount; int visible; int allObjects; + int lastRenderedKey; watcher textRendering; watcher nativeOperations; + // use to calculate points float calcX; float calcY; diff --git a/OsmAnd/jni/osmand/mapObjects.cpp b/OsmAnd/jni/osmand/mapObjects.cpp index 6254108f82..4ce42230a1 100644 --- a/OsmAnd/jni/osmand/mapObjects.cpp +++ b/OsmAnd/jni/osmand/mapObjects.cpp @@ -149,4 +149,14 @@ void unloadJniMapObjects() globalEnv()->DeleteGlobalRef( TagValuePairClass ); } +int getNegativeWayLayer(int type) { + int i = (3 & (type >> 12)); + if (i == 1) { + return -1; + } else if (i == 2) { + return 1; + } + return 0; +} + #endif /*_OSMAND_MAP_OBJECTS*/ diff --git a/OsmAnd/jni/osmand/mapObjects.h b/OsmAnd/jni/osmand/mapObjects.h index 30d141f894..4ede2fe336 100644 --- a/OsmAnd/jni/osmand/mapObjects.h +++ b/OsmAnd/jni/osmand/mapObjects.h @@ -7,6 +7,9 @@ #include "common.h" +typedef std::pair tag_value; +typedef std::pair int_pair; + class BaseMapDataObject { @@ -35,17 +38,18 @@ public: std::string value; std::vector< std::string > names; int layer; - std::vector< std::vector< std::pair > > points; + std::vector< std::vector< int_pair> > points; }; + class MapDataObject : public BaseMapDataObject { public: MapDataObject() : BaseMapDataObject(MAP_DATA_OBJECT) { } std::vector< int> types; - std::vector< std::pair > points; - std::vector< std::pair > tagValues; + std::vector< int_pair > points; + std::vector< tag_value > tagValues; int highwayAttributes; }; @@ -58,4 +62,7 @@ void loadJniMapObjects(); void unloadJniMapObjects(); +// 0 - normal, -1 - under, 1 - bridge,over +int getNegativeWayLayer(int type); + #endif /*_OSMAND_MAP_OBJECTS_H*/ diff --git a/OsmAnd/jni/osmand/multipolygons.h b/OsmAnd/jni/osmand/multipolygons.h new file mode 100644 index 0000000000..e6b7b7553a --- /dev/null +++ b/OsmAnd/jni/osmand/multipolygons.h @@ -0,0 +1,605 @@ + +#include +#include +#include +#include +#include + + +#include "renderRules.h" +#include "common.h" +#include "mapObjects.h" + +#define INT_MAX 0x7fffffff /* max value for an int */ +#define INT_MIN (-0x7fffffff-1) /* min value for an int */ + +char textMsg[1024] ; +struct tagValueType { + int type; + std::string tag; + std::string value; + + tagValueType(tag_value t, int type) : type(type) { + tag = t.first; + value = t.second; + } + +}; +bool operator==(const tagValueType& __x, const tagValueType& __y) { + return __x.type == __y.type; +} +bool operator<(const tagValueType& __x, const tagValueType& __y) { + return __x.type < __y.type; +} +/// !!! Fuly copied from MapRenderRepositories.java, should be carefully synchroinized +bool isClockwiseWay(std::vector& c) ; +bool calculateLineCoordinates(bool inside, int x, int y, bool pinside, int px, int py, int leftX, int rightX, + int bottomY, int topY, std::vector& coordinates); + +void processMultipolygonLine(std::vector >& completedRings, std::vector >& incompletedRings, + std::vector &completedRingsNames, std::vector &incompletedRingsNames, std::vector & coordinates, std::string name); + +void unifyIncompletedRings(std::vector >& incompletedRings, std::vector >& completedRings, std::vector &completedRingNames, + std::vector &incompletedRingNames, int leftX, int rightX, int bottomY, int topY, long dbId, int zoom); + +MultiPolygonObject* processMultiPolygon(int leftX, int rightX, int bottomY, int topY, + std::vector >& completedRings, std::vector >& incompletedRings, + std::vector& completedRingNames, std::vector& incompletedRingNames, + const tagValueType& type, std::vector & directList, std::vector& inverselist, + int zoom) { + MultiPolygonObject* pl = new MultiPolygonObject(); + // delete direction last bit (to not show point) + pl->tag = type.tag; + pl->value = type.value; + pl->layer = getNegativeWayLayer(type.type); + long long dbId = 0; + for (int km = 0; km < 2; km++) { + std::vector::iterator o = (km == 0 ? directList.begin() : inverselist.begin()); + std::vector::iterator oEnd = (km == 0 ? directList.end() : inverselist.end()); + for (; o != oEnd; o++) { + int len = (*o)->points.size(); + if (len < 2) { + continue; + } + dbId = (*o)->id >> 1; + std::vector coordinates; + int_pair p = (*o)->points.at(km == 0 ? 0 : len - 1); + int px = p.first; + int py = p.second; + int x = p.first; + int y = p.second; + bool pinside = leftX <= x && x <= rightX && y >= topY && y <= bottomY; + if (pinside) { + coordinates.push_back(int_pair(x, y)); + } + for (int i = 1; i < len; i++) { + int_pair cp = (*o)->points.at(km == 0 ? i : len - i - 1); + x = cp.first; + y = cp.second; + bool inside = leftX <= x && x <= rightX && y >= topY && y <= bottomY; + bool lineEnded = calculateLineCoordinates(inside, x, y, pinside, px, py, leftX, rightX, bottomY, topY, + coordinates); + if (lineEnded) { + processMultipolygonLine(completedRings, incompletedRings, completedRingNames, incompletedRingNames, + coordinates, (*o)->name); + // create new line if it goes outside + coordinates.clear(); + } + px = x; + py = y; + pinside = inside; + } + processMultipolygonLine(completedRings, incompletedRings, completedRingNames, incompletedRingNames, + coordinates, (*o)->name); + } + } + if (completedRings.size() == 0 && incompletedRings.size() == 0) { + return NULL; + } + if (incompletedRings.size() > 0) { + unifyIncompletedRings(incompletedRings, completedRings, completedRingNames, incompletedRingNames, leftX, rightX, + bottomY, topY, dbId, zoom); + } else { + // due to self intersection small objects (for low zooms check only coastline) + if (zoom >= 13 || ("natural" == type.tag && "coastline" == type.value)) { + bool clockwiseFound = false; + std::vector > ::iterator c = completedRings.begin(); + for (; c != completedRings.end(); c++) { + if (isClockwiseWay(*c)) { + clockwiseFound = true; + break; + } + } + if (!clockwiseFound) { + // add whole bound + std::vector whole; + whole.push_back(int_pair(leftX, topY)); + whole.push_back(int_pair(rightX, topY)); + whole.push_back(int_pair(leftX, bottomY)); + whole.push_back(int_pair(rightX, bottomY)); + completedRings.push_back(whole); + __android_log_print(ANDROID_LOG_INFO, "net.osmand", "!!! Isolated island !!!"); + } + + } + } + + pl->names = completedRingNames; + pl->points = completedRings; + return pl; +} + +static std::vector EMPTY_LIST; +void proccessMultiPolygons(std::map >& multyPolygons, int leftX, + int rightX, int bottomY, int topY, int zoom, std::vector& listPolygons) { + std::vector > completedRings; + std::vector > incompletedRings; + std::vector completedRingNames; + std::vector incompletedRingNames; + std::map >::iterator val = multyPolygons.begin(); + for (; val != multyPolygons.end(); val++) { + std::vector* directList; + std::vector* inverselist; + if (((val->first.type >> 15) & 1) == 1) { + tagValueType directType = val->first; + directType.type = val->first.type & ((1 << 15) - 1); + if (multyPolygons.find(directType) == multyPolygons.end()) { + inverselist = &val->second; + directList = &EMPTY_LIST; + } else { + // continue on inner boundaries + continue; + } + } else { + tagValueType inverseType = val->first; + inverseType.type = val->first.type | (1 << 15); + directList = &val->second; + inverselist = &multyPolygons[inverseType]; + } + completedRings.clear(); + incompletedRings.clear(); + completedRingNames.clear(); + incompletedRingNames.clear(); + + sprintf(textMsg, "Process multipolygon %s %s direct list %d rev %d", val->first.tag.c_str(), val->first.value.c_str(), directList->size(), inverselist->size()); + __android_log_print(ANDROID_LOG_INFO, "net.osmand", textMsg); + MultiPolygonObject* pl = processMultiPolygon(leftX, rightX, bottomY, topY, completedRings, incompletedRings, + completedRingNames, incompletedRingNames, val->first, *directList, *inverselist, zoom); + if (pl != NULL) { + listPolygons.push_back(pl); + } else { + __android_log_print(ANDROID_LOG_INFO, "net.osmand", "Multipolygon skipped"); + } + } +} + +// Copied from MapAlgorithms +int ray_intersect_x(int prevX, int prevY, int x, int y, int middleY) { + // prev node above line + // x,y node below line + if (prevY > y) { + int tx = prevX; + int ty = prevY; + x = prevX; + y = prevY; + prevX = tx; + prevY = ty; + } + if (y == middleY || prevY == middleY) { + middleY -= 1; + } + if (prevY > middleY || y < middleY) { + return INT_MIN; + } else { + if (y == prevY) { + // the node on the boundary !!! + return x; + } + // that tested on all cases (left/right) + double rx = x + ((double) middleY - y) * ((double) x - prevX) / (((double) y - prevY)); + return (int) rx; + } +} + +// Copied from MapAlgorithms +bool isClockwiseWay(std::vector& c) { + if (c.size() == 0) { + return true; + } + + // calculate middle Y + long middleY = 0; + for (size_t i = 0; i < c.size(); i++) { + middleY += c.at(i).second; + } + middleY /= (long) c.size(); + + double clockwiseSum = 0; + + bool firstDirectionUp = false; + int previousX = INT_MIN; + int firstX = INT_MIN; + + int prevX = c.at(0).first; + int prevY = c.at(0).second; + + for (size_t i = 1; i < c.size(); i++) { + int x = c.at(i).first; + int y = c.at(i).second; + int rX = ray_intersect_x(prevX, prevY, x, y, (int) middleY); + if (rX != INT_MIN) { + bool skipSameSide = (y <= middleY) == (prevY <= middleY); + if (skipSameSide) { + continue; + } + bool directionUp = prevY >= middleY; + if (firstX == INT_MIN) { + firstDirectionUp = directionUp; + firstX = rX; + } else { + bool clockwise = (!directionUp) == (previousX < rX); + if (clockwise) { + clockwiseSum += abs(previousX - rX); + } else { + clockwiseSum -= abs(previousX - rX); + } + } + previousX = rX; + prevX = x; + prevY = y; + } + } + + if (firstX != INT_MIN) { + bool clockwise = (!firstDirectionUp) == (previousX < firstX); + if (clockwise) { + clockwiseSum += abs(previousX - firstX); + } else { + clockwiseSum -= abs(previousX - firstX); + } + } + + return clockwiseSum >= 0; +} + + + +void processMultipolygonLine(std::vector >& completedRings, std::vector >& incompletedRings, + std::vector &completedRingsNames, + std::vector &incompletedRingsNames, std::vector & coordinates, std::string name) { + if (coordinates.size() > 0) { + if (coordinates.at(0) == coordinates.at(coordinates.size() - 1)) { + completedRings.push_back(coordinates); + completedRingsNames.push_back(name); + } else { + bool add = true; + for (size_t k = 0; k < incompletedRings.size();) { + bool remove = false; + std::vector i = incompletedRings.at(k); + std::string oldName = incompletedRingsNames.at(k); + if (coordinates.at(0) == i.at(i.size() - 1)) { + std::vector::iterator tit = coordinates.begin(); + i.insert(i.end(), ++tit, coordinates.end()); + remove = true; + coordinates = i; + } else if (coordinates.at(coordinates.size() - 1) == i.at(0)) { + std::vector::iterator tit = i.begin(); + coordinates.insert(coordinates.end(), ++tit, i.end()); + remove = true; + } + if (remove) { + std::vector >::iterator ti = incompletedRings.begin(); + ti += k; + incompletedRings.erase(ti); + std::vector :: iterator tis = incompletedRingsNames.begin(); + tis += k; + incompletedRingsNames.erase(tis); + } else { + k++; + } + if (coordinates.at(0) == coordinates.at(coordinates.size() - 1)) { + completedRings.push_back(coordinates); + if (oldName.length() > 0) { + completedRingsNames.push_back(oldName); + } else { + completedRingsNames.push_back(name); + } + add = false; + break; + } + } + if (add) { + incompletedRings.push_back(coordinates); + incompletedRingsNames.push_back(name); + } + } + } +} + +int safelyAddDelta(int number, int delta) { + int res = number + delta; + if (delta > 0 && res < number) { + return INT_MAX; + } else if (delta < 0 && res > number) { + return INT_MIN; + } + return res; +} + +void unifyIncompletedRings(std::vector >& incompletedRings, std::vector >& completedRings, + std::vector &completedRingNames, std::vector & incompletedRingsNames, + int leftX, int rightX, int bottomY, int topY, long dbId, int zoom) { + std::set nonvisitedRings; + std::vector >::iterator ir = incompletedRings.begin(); + std::vector::iterator irs = incompletedRingsNames.begin(); + int j = 0; + for (j = 0; ir != incompletedRings.end(); ir++, irs++, j++) { + int x = ir->at(0).first; + int y = ir->at(0).second; + int sx = x; + int sy = y; + bool st = y == topY || x == rightX || y == bottomY || x == leftX; + bool end = sy == topY || sx == rightX || sy == bottomY || sx == leftX; + // something goes wrong + // These exceptions are used to check logic about processing multipolygons + // However this situation could happen because of broken multipolygons (so it should data causes app error) + // that's why these exceptions could be replaced with return; statement. + if (!end || !st) { + // TODO message +// float dx = (float) MapUtils.get31LongitudeX(x); +// float dsx = (float) MapUtils.get31LongitudeX(sx); +// float dy = (float) MapUtils.get31LatitudeY(y); +// float dsy = (float) MapUtils.get31LatitudeY(sy); +// String str; +// if (!end) { +// str = " Start point (to close) not found : end_x = {0}, end_y = {1}, start_x = {2}, start_y = {3} : bounds {4} {5} - {6} {7}"; //$NON-NLS-1$ +// System.err +// .println(MessageFormat.format(dbId + str, dx, dy, dsx, dsy, leftX + "", topY + "", rightX + "", bottomY + "")); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$//$NON-NLS-4$ +// } +// if (!st) { +// str = " End not found : end_x = {0}, end_y = {1}, start_x = {2}, start_y = {3} : bounds {4} {5} - {6} {7}"; //$NON-NLS-1$ +// System.err +// .println(MessageFormat.format(dbId + str, dx, dy, dsx, dsy, leftX + "", topY + "", rightX + "", bottomY + "")); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$//$NON-NLS-4$ +// } + __android_log_print(ANDROID_LOG_INFO, "net.osmand", "Error processing multipolygon"); + } else { + nonvisitedRings.insert(j); + } + } + ir = incompletedRings.begin(); + irs = incompletedRingsNames.begin(); + for (j = 0; ir != incompletedRings.end(); ir++, irs++, j++) { + if (nonvisitedRings.find(j) == nonvisitedRings.end()) { + continue; + } + int x = ir->at(ir->size() - 1).first; + int y = ir->at(ir->size() - 1).second; + // 31 - (zoom + 8) + const int EVAL_DELTA = 6 << (23 - zoom); + const int UNDEFINED_MIN_DIFF = -1 - EVAL_DELTA; + while (true) { + int st = 0; // st already checked to be one of the four + if (y == topY) { + st = 0; + } else if (x == rightX) { + st = 1; + } else if (y == bottomY) { + st = 2; + } else if (x == leftX) { + st = 3; + } + int nextRingIndex = -1; + // BEGIN go clockwise around rectangle + for (int h = st; h < st + 4; h++) { + + // BEGIN find closest nonvisited start (including current) + int mindiff = UNDEFINED_MIN_DIFF; + std::vector >::iterator cni = incompletedRings.begin(); + int cnik = 0; + for (;cni != incompletedRings.end(); cni++, cnik ++) { + if (nonvisitedRings.find(cnik) == nonvisitedRings.end()) { + continue; + } + int csx = cni->at(0).first; + int csy = cni->at(0).second; + if (h % 4 == 0) { + // top + if (csy == topY && csx >= safelyAddDelta(x, -EVAL_DELTA)) { + if (mindiff == UNDEFINED_MIN_DIFF || (csx - x) <= mindiff) { + mindiff = (csx - x); + nextRingIndex = cnik; + } + } + } else if (h % 4 == 1) { + // right + if (csx == rightX && csy >= safelyAddDelta(y, -EVAL_DELTA)) { + if (mindiff == UNDEFINED_MIN_DIFF || (csy - y) <= mindiff) { + mindiff = (csy - y); + nextRingIndex = cnik; + } + } + } else if (h % 4 == 2) { + // bottom + if (csy == bottomY && csx <= safelyAddDelta(x, EVAL_DELTA)) { + if (mindiff == UNDEFINED_MIN_DIFF || (x - csx) <= mindiff) { + mindiff = (x - csx); + nextRingIndex = cnik; + } + } + } else if (h % 4 == 3) { + // left + if (csx == leftX && csy <= safelyAddDelta(y, EVAL_DELTA)) { + if (mindiff == UNDEFINED_MIN_DIFF || (y - csy) <= mindiff) { + mindiff = (y - csy); + nextRingIndex = cnik; + } + } + } + } // END find closest start (including current) + + // we found start point + if (mindiff != UNDEFINED_MIN_DIFF) { + break; + } else { + if (h % 4 == 0) { + // top + y = topY; + x = rightX; + } else if (h % 4 == 1) { + // right + y = bottomY; + x = rightX; + } else if (h % 4 == 2) { + // bottom + y = bottomY; + x = leftX; + } else if (h % 4 == 3) { + y = topY; + x = leftX; + } + ir->push_back(int_pair(x, y)); + } + + } // END go clockwise around rectangle + if (nextRingIndex == -1) { + // it is impossible (current start should always be found) + } else if (nextRingIndex == j) { + ir->push_back(ir->at(0)); + nonvisitedRings.erase(j); + break; + } else { + std::vector p = incompletedRings.at(nextRingIndex); + ir->insert(ir->end(), p.begin(), p.end()); + nonvisitedRings.erase(nextRingIndex); + // get last point and start again going clockwise + x = ir->at(ir->size() - 1).first; + y = ir->at(ir->size() - 1).second; + } + } + + completedRings.push_back(*ir); + completedRingNames.push_back(*irs); + } + +} + + + + /** + * @return -1 if there is no instersection or x<<32 | y + */ +bool calculateIntersection(int x, int y, int px, int py, int leftX, int rightX, int bottomY, int topY, int_pair& b) { + // firstly try to search if the line goes in + if (py < topY && y >= topY) { + int tx = (int) (px + ((double) (x - px) * (topY - py)) / (y - py)); + if (leftX <= tx && tx <= rightX) { + b.first = tx; + b.second = topY; + return true; + } + } + if (py > bottomY && y <= bottomY) { + int tx = (int) (px + ((double) (x - px) * (py - bottomY)) / (py - y)); + if (leftX <= tx && tx <= rightX) { + b.first = tx; + b.second = bottomY; + return true; + } + } + if (px < leftX && x >= leftX) { + int ty = (int) (py + ((double) (y - py) * (leftX - px)) / (x - px)); + if (ty >= topY && ty <= bottomY) { + b.first = leftX; + b.second = ty; + return true; + } + + } + if (px > rightX && x <= rightX) { + int ty = (int) (py + ((double) (y - py) * (px - rightX)) / (px - x)); + if (ty >= topY && ty <= bottomY) { + b.first = rightX; + b.second = ty; + return true; + } + + } + + // try to search if point goes out + if (py > topY && y <= topY) { + int tx = (int) (px + ((double) (x - px) * (topY - py)) / (y - py)); + if (leftX <= tx && tx <= rightX) { + b.first = tx; + b.second = topY; + return true; + } + } + if (py < bottomY && y >= bottomY) { + int tx = (int) (px + ((double) (x - px) * (py - bottomY)) / (py - y)); + if (leftX <= tx && tx <= rightX) { + b.first = tx; + b.second = bottomY; + return true; + } + } + if (px > leftX && x <= leftX) { + int ty = (int) (py + ((double) (y - py) * (leftX - px)) / (x - px)); + if (ty >= topY && ty <= bottomY) { + b.first = leftX; + b.second = ty; + return true; + } + + } + if (px < rightX && x >= rightX) { + int ty = (int) (py + ((double) (y - py) * (px - rightX)) / (px - x)); + if (ty >= topY && ty <= bottomY) { + b.first = rightX; + b.second = ty; + return true; + } + + } + + if (px == rightX || px == leftX || py == topY || py == bottomY) { + b.first = px; + b.second = py; +// return true; + // Is it right? to not return anything? + } + return false; +} + +bool calculateLineCoordinates(bool inside, int x, int y, bool pinside, int px, int py, int leftX, int rightX, + int bottomY, int topY, std::vector& coordinates) { + bool lineEnded = false; + int_pair b(x, y); + if (pinside) { + if (!inside) { + bool is = calculateIntersection(x, y, px, py, leftX, rightX, bottomY, topY, b); + if (!is) { + b.first = px; + b.second = py; + } + coordinates.push_back(b); + lineEnded = true; + } else { + coordinates.push_back(b); + } + } else { + bool is = calculateIntersection(x, y, px, py, leftX, rightX, bottomY, topY, b); + if (inside) { + // assert is != -1; + coordinates.push_back(b); + int_pair n(x, y); + coordinates.push_back(n); + } else if (is) { + coordinates.push_back(b); + calculateIntersection(x, y, b.first, b.second, leftX, rightX, bottomY, topY, b); + coordinates.push_back(b); + lineEnded = true; + } + } + + return lineEnded; +} diff --git a/OsmAnd/jni/osmand/renderRules.cpp b/OsmAnd/jni/osmand/renderRules.cpp index 6591a56c83..6e04ac75f1 100644 --- a/OsmAnd/jni/osmand/renderRules.cpp +++ b/OsmAnd/jni/osmand/renderRules.cpp @@ -293,6 +293,14 @@ RenderingRuleSearchRequest::RenderingRuleSearchRequest(jobject rrs) : clearState(); } +RenderingRuleSearchRequest::~RenderingRuleSearchRequest() { + delete PROPS; + delete[] fvalues; + delete[] values; + delete[] savedFvalues; + delete[] savedValues; +} + int RenderingRuleSearchRequest::getIntPropertyValue(RenderingRuleProperty* prop) { if (prop == NULL) { return 0; diff --git a/OsmAnd/jni/osmand/renderRules.h b/OsmAnd/jni/osmand/renderRules.h index f178c0d3f8..8ee297eca0 100644 --- a/OsmAnd/jni/osmand/renderRules.h +++ b/OsmAnd/jni/osmand/renderRules.h @@ -204,6 +204,8 @@ private : public: RenderingRuleSearchRequest(jobject rrs); + ~RenderingRuleSearchRequest(); + int getIntPropertyValue(RenderingRuleProperty* prop); int getIntPropertyValue(RenderingRuleProperty* prop, int def); diff --git a/OsmAnd/jni/osmand/rendering.cpp b/OsmAnd/jni/osmand/rendering.cpp index cebf6ab04b..289a58dd73 100644 --- a/OsmAnd/jni/osmand/rendering.cpp +++ b/OsmAnd/jni/osmand/rendering.cpp @@ -407,8 +407,11 @@ void drawMultiPolygon(MultiPolygonObject* mapObject,RenderingRuleSearchRequest* NAT_COUNT(rc, cv->drawPath(path, *paint)); // for test purpose - // render.strokeWidth = 1.5f; - // render.color = Color.BLACK; +// paint->setStyle(SkPaint::kStroke_Style); +// paint->setStrokeWidth(2); +// paint->setPathEffect(NULL); +// paint->setColor(BLACK_COLOR); +// NAT_COUNT(rc, cv->drawPath(path, *paint)); if (updatePaint(req, paint, 1, 0, rc)) { NAT_COUNT(rc, cv->drawPath(path, *paint)); } @@ -504,17 +507,6 @@ void drawPoint(MapDataObject* mObj, RenderingRuleSearchRequest* req, SkCanvas* c } -// 0 - normal, -1 - under, 1 - bridge,over -int getNegativeWayLayer(int type) { - int i = (3 & (type >> 12)); - if (i == 1) { - return -1; - } else if (i == 2) { - return 1; - } - return 0; -} - void drawObject(RenderingContext* rc, BaseMapDataObject* mapObject, SkCanvas* cv, RenderingRuleSearchRequest* req, SkPaint* paint, int l, int renderText, int drawOnlyShadow) { rc->allObjects++; @@ -679,8 +671,8 @@ void doRendering(std::vector mapDataObjects, SkCanvas* can BaseMapDataObject* mapObject = mapDataObjects.at(ind); // show text only for main type drawObject(rc, mapObject, canvas, req, paint, l, l == 0, false); - } + rc->lastRenderedKey = *ks; if (rc->interrupted()) { return; } diff --git a/OsmAnd/jni/osmand/textdraw.cpp b/OsmAnd/jni/osmand/textdraw.cpp index c41c2c8ea2..9544fbe09d 100644 --- a/OsmAnd/jni/osmand/textdraw.cpp +++ b/OsmAnd/jni/osmand/textdraw.cpp @@ -410,9 +410,7 @@ bool intersect(SkRect tRect, float tRot, TextDrawInfo* s) } bool intersect(TextDrawInfo* t, TextDrawInfo* s) { - // TODO - return t->bounds.intersect(s->bounds); -// return intersect(t->bounds, t->pathRotate, s); + return intersect(t->bounds, t->pathRotate, s); } std::vector search; bool findTextIntersection(SkCanvas* cv, RenderingContext* rc, quad_tree& boundIntersections, TextDrawInfo* text, @@ -435,7 +433,7 @@ bool findTextIntersection(SkCanvas* cv, RenderingContext* rc, quad_treebounds, text->pathRotate, paintIcon, text->text, NULL/*paintText*/); + drawTestBox(cv, &text->bounds, text->pathRotate, paintIcon, text->text, NULL/*paintText*/); boundIntersections.query_in_box(text->bounds, search); for (uint i = 0; i < search.size(); i++) { TextDrawInfo* t = search.at(i); diff --git a/OsmAnd/libs/armeabi/libosmand.so b/OsmAnd/libs/armeabi/libosmand.so index ee51ce5b6f..9edb5cd3e2 100755 Binary files a/OsmAnd/libs/armeabi/libosmand.so and b/OsmAnd/libs/armeabi/libosmand.so differ diff --git a/OsmAnd/src/net/osmand/plus/render/MapRenderRepositories.java b/OsmAnd/src/net/osmand/plus/render/MapRenderRepositories.java index efc5aa4000..6a208a76b8 100644 --- a/OsmAnd/src/net/osmand/plus/render/MapRenderRepositories.java +++ b/OsmAnd/src/net/osmand/plus/render/MapRenderRepositories.java @@ -83,7 +83,7 @@ public class MapRenderRepositories { private RotatedTileBox bmpLocation = null; // already rendered bitmap private Bitmap bmp; - + // Field used in C++ private boolean interrupted = false; private RenderingContext currentRenderingContext; private SearchRequest searchRequest; @@ -260,7 +260,7 @@ public class MapRenderRepositories { log.debug("Native resource " + mapName + " initialized"); //$NON-NLS-1$ //$NON-NLS-2$ } resultHandler = NativeOsmandLibrary.searchObjectsForRendering(leftX, rightX, topY, bottomY, zoom, mapName,renderingReq, - PerformanceFlags.checkForDuplicateObjectIds, resultHandler); + PerformanceFlags.checkForDuplicateObjectIds, resultHandler, this); if (checkWhetherInterrupted()) { NativeOsmandLibrary.deleteSearchResult(resultHandler); return false; @@ -517,9 +517,12 @@ public class MapRenderRepositories { String renderingDebugInfo = currentRenderingContext.renderingDebugInfo; currentRenderingContext.ended = true; if (checkWhetherInterrupted()) { - // revert if it was interrupted - this.bmp = this.prevBmp; - this.bmpLocation = this.prevBmpLocation; + // revert if it was interrupted + // (be smart a bit do not revert if road already drawn) + if(currentRenderingContext.lastRenderedKey < 35) { + this.bmp = this.prevBmp; + this.bmpLocation = this.prevBmpLocation; + } currentRenderingContext = null; return; } @@ -749,7 +752,7 @@ public class MapRenderRepositories { continue; } boolean directionUp = prevY >= middleY; - if (firstX == -Integer.MIN_VALUE) { + if (firstX == Integer.MIN_VALUE) { firstDirectionUp = directionUp; firstX = rX; } else { @@ -765,8 +768,7 @@ public class MapRenderRepositories { prevY = y; } } - - if (firstX != -360) { + if (firstX != Integer.MIN_VALUE) { boolean clockwise = (!firstDirectionUp) == (previousX < firstX); if (clockwise) { clockwiseSum += Math.abs(previousX - firstX); @@ -806,38 +808,6 @@ public class MapRenderRepositories { } } - // NOT WORKING GOOD ! - private boolean isClockwiseWayOld(TLongList c) { - double angle = 0; - double prevAng = 0; - int px = 0; - int py = 0; - int mask = 0xffffffff; - for (int i = 0; i < c.size(); i++) { - int x = (int) (c.get(i) >> 32); - int y = (int) (c.get(i) & mask); - if (i >= 1) { - double ang = Math.atan2(py - y, x - px); - if (i > 1) { - double delta = (ang - prevAng); - if (delta < -Math.PI) { - delta += 2 * Math.PI; - } else if (delta > Math.PI) { - delta -= 2 * Math.PI; - } - angle += delta; - prevAng = ang; - } else { - prevAng = ang; - } - } - px = x; - py = y; - - } - return angle < 0; - } - private void processMultipolygonLine(List completedRings, List incompletedRings, List completedRingsNames, List incompletedRingsNames, TLongList coordinates, String name) { if (coordinates.size() > 0) { diff --git a/OsmAnd/src/net/osmand/plus/render/NativeOsmandLibrary.java b/OsmAnd/src/net/osmand/plus/render/NativeOsmandLibrary.java index 2bfc9ac79e..6949b5decc 100644 --- a/OsmAnd/src/net/osmand/plus/render/NativeOsmandLibrary.java +++ b/OsmAnd/src/net/osmand/plus/render/NativeOsmandLibrary.java @@ -26,13 +26,16 @@ public class NativeOsmandLibrary { * @param searchResultHandle * - must be null if there is no need to append to previous results returns native handle to results */ - public static NativeSearchResult searchObjectsForRendering(int sleft, int sright, int stop, int sbottom, int zoom, - String mapName, RenderingRuleSearchRequest request, boolean skipDuplicates, NativeSearchResult searchResultHandler) { + public static NativeSearchResult searchObjectsForRendering(int sleft, int sright, int stop, int sbottom, int zoom, String mapName, + RenderingRuleSearchRequest request, boolean skipDuplicates, NativeSearchResult searchResultHandler, + Object objectWithInterruptedField) { if (searchResultHandler == null) { - return new NativeSearchResult(searchObjectsForRendering(sleft, sright, stop, sbottom, zoom, mapName, request, skipDuplicates, 0)); + return new NativeSearchResult(searchObjectsForRendering(sleft, sright, stop, sbottom, zoom, mapName, request, skipDuplicates, + 0, objectWithInterruptedField)); } else { - int res = searchObjectsForRendering(sleft, sright, stop, sbottom, zoom, mapName, request, skipDuplicates, searchResultHandler.nativeHandler); - if(res == searchResultHandler.nativeHandler){ + int res = searchObjectsForRendering(sleft, sright, stop, sbottom, zoom, mapName, request, skipDuplicates, + searchResultHandler.nativeHandler, objectWithInterruptedField); + if (res == searchResultHandler.nativeHandler) { return searchResultHandler; } return new NativeSearchResult(res); @@ -70,6 +73,6 @@ public class NativeOsmandLibrary { private static native String generateRendering(RenderingContext rc, int searchResultHandler, Bitmap bmp, boolean useEnglishNames, RenderingRuleSearchRequest render, int defaultColor); - private static native int searchObjectsForRendering(int sleft, int sright, int stop, int sbottom, int zoom, - String mapnaem, RenderingRuleSearchRequest request, boolean skipDuplicates, int searchResultHandler); + private static native int searchObjectsForRendering(int sleft, int sright, int stop, int sbottom, int zoom, String mapnaem, + RenderingRuleSearchRequest request, boolean skipDuplicates, int searchResultHandler, Object objectWithInterruptedField); } diff --git a/OsmAnd/src/net/osmand/plus/render/OsmandRenderer.java b/OsmAnd/src/net/osmand/plus/render/OsmandRenderer.java index dd10a07720..f130aa4da9 100644 --- a/OsmAnd/src/net/osmand/plus/render/OsmandRenderer.java +++ b/OsmAnd/src/net/osmand/plus/render/OsmandRenderer.java @@ -148,6 +148,7 @@ public class OsmandRenderer { int visible = 0; int allObjects = 0; int textRenderingTime = 0; + int lastRenderedKey = 0; // use to calculate points PointF tempPoint = new PointF(); @@ -228,7 +229,10 @@ public class OsmandRenderer { } - public Bitmap generateNewBitmapNative(RenderingContext rc, NativeSearchResult searchResultHandler, Bitmap bmp, boolean useEnglishNames, + /** + * @return if map could be replaced + */ + public void generateNewBitmapNative(RenderingContext rc, NativeSearchResult searchResultHandler, Bitmap bmp, boolean useEnglishNames, RenderingRuleSearchRequest render, final List notifyList, int defaultColor) { long now = System.currentTimeMillis(); if (rc.width > 0 && rc.height > 0 && searchResultHandler != null) { @@ -253,11 +257,9 @@ public class OsmandRenderer { e.printStackTrace(); } } - return bmp; - } - public Bitmap generateNewBitmap(RenderingContext rc, List objects, Bitmap bmp, boolean useEnglishNames, + public void generateNewBitmap(RenderingContext rc, List objects, Bitmap bmp, boolean useEnglishNames, RenderingRuleSearchRequest render, final List notifyList, int defaultColor) { long now = System.currentTimeMillis(); @@ -305,7 +307,7 @@ public class OsmandRenderer { shadowDrawn = true; } if (rc.interrupted) { - return null; + return; } TIntArrayList list = orderMap.get(keys[k]); @@ -319,6 +321,7 @@ public class OsmandRenderer { drawObj(obj, render, cv, rc, l, l == 0, false); objCount++; } + rc.lastRenderedKey = keys[k]; if (objCount > 25) { notifyListeners(notifyList); objCount = 0; @@ -341,7 +344,7 @@ public class OsmandRenderer { } - return bmp; + return; } private void notifyListenersWithDelay(final RenderingContext rc, final List notifyList, final Handler h) { @@ -441,7 +444,7 @@ public class OsmandRenderer { } if (rc.interrupted) { - return null; + return orderMap; } } } @@ -782,9 +785,13 @@ public class OsmandRenderer { } } canvas.drawPath(path, paint); - // for test purpose -// render.strokeWidth = 1.5f; -// render.color = Color.BLACK; + // for test purpose +// paint.setStyle(Style.STROKE); +// paint.setStrokeWidth(1.5f); +// paint.setColor(Color.BLACK); +// paint.setPathEffect(null); +// canvas.drawPath(path, paint); + if (updatePaint(render, paint, 1, false, rc)) { canvas.drawPath(path, paint); }