/** * Parse the color string, and return the corresponding color-int. * Supported formats are: * #RRGGBB * #AARRGGBB */ int parseColor(string colorString) { if (colorString[0] == '#') { // Use a long to avoid rollovers on #ffXXXXXX char** end; long color = strtol(colorString.c_str() + 1, NULL, 16); if (colorString.size() == 7) { // Set the alpha value color |= 0x00000000ff000000; } else if (colorString.size() != 9) { osmand_log_print(LOG_ERROR, "Unknown color %s", colorString.c_str()); } return (int) color; } osmand_log_print(LOG_ERROR, "Unknown color %s", colorString.c_str()); return -1; }
RenderingRule::RenderingRule(map<string, string>& attrs, RenderingRulesStorage* storage) { properties.reserve(attrs.size()); intProperties.assign(attrs.size(), -1); map<string, string>::iterator it = attrs.begin(); int i = 0; for (; it != attrs.end(); it++) { RenderingRuleProperty* property = storage->PROPS.getProperty(it->first.c_str()); if (property == NULL) { osmand_log_print(LOG_ERROR, "Property %s was not found in registry", it->first.c_str()); return ; } properties.push_back(property); if (property->isString()) { intProperties[i] = storage->getDictionaryValue(it->second); } else if (property->isFloat()) { if (floatProperties.size() == 0) { // lazy creates floatProperties.assign(attrs.size(), - 1); } floatProperties[i] = property->parseFloatValue(it->second); } else { intProperties[i] = property->parseIntValue(it->second); } i++; } }
void RenderingRulesStorage::parseRulesFromXmlInputStream(const char* filename, RenderingRulesStorageResolver* resolver) { XML_Parser parser = XML_ParserCreate(NULL); RenderingRulesHandler* handler = new RenderingRulesHandler(resolver, this); XML_SetUserData(parser, handler); XML_SetElementHandler(parser, RenderingRulesHandler::startElementHandler, RenderingRulesHandler::endElementHandler); FILE *file = fopen(filename, "r"); if (file == NULL) { osmand_log_print(LOG_ERROR, "File can not be open %s", filename); return; } char buffer[512]; bool done = false; while (!done) { fgets(buffer, sizeof(buffer), file); int len = strlen(buffer); if (feof(file) != 0) { done = true; } if (XML_Parse(parser, buffer, len, done) == XML_STATUS_ERROR) { return; } } RenderingRulesStorage* depends = handler->getDependsStorage(); if (depends != NULL) { // merge results // dictionary and props are already merged map<std::string, RenderingRule*>::iterator it = depends->renderingAttributes.begin(); for(;it != depends->renderingAttributes.end(); it++) { map<std::string, RenderingRule*>::iterator o = renderingAttributes.find(it->first); if (o != renderingAttributes.end()) { std::vector<RenderingRule*>::iterator list = it->second->ifElseChildren.begin(); for (;list != it->second->ifElseChildren.end(); list++) { o->second->ifElseChildren.push_back(*list); } } else { renderingAttributes[it->first] = it->second; } } for (int i = 0; i < SIZE_STATES; i++) { if (depends->tagValueGlobalRules[i].empty()) { continue; } HMAP::hash_map<int, RenderingRule*>::iterator it = depends->tagValueGlobalRules[i].begin(); for (; it != depends->tagValueGlobalRules[i].end(); it++) { HMAP::hash_map<int, RenderingRule*>::iterator o = tagValueGlobalRules[i].find(it->first); RenderingRule* toInsert = it->second; if (o != tagValueGlobalRules[i].end()) { toInsert = createTagValueRootWrapperRule(it->first, o->second); toInsert->ifElseChildren.push_back(it->second); } tagValueGlobalRules[i][it->first] = toInsert; } } } }
void RenderingRulesStorage::registerGlobalRule(RenderingRule* rr, int state) { int tag = rr->getIntPropertyValue(this->PROPS.R_TAG->attrName); if (tag == -1) { osmand_log_print(LOG_ERROR, "Attribute tag should be specified for root filter "); } int value = rr->getIntPropertyValue(this->PROPS.R_VALUE->attrName); if (value == -1) { // attrsMap.toString() osmand_log_print(LOG_ERROR, "Attribute tag should be specified for root filter "); } int key = (tag << SHIFT_TAG_VAL) + value; RenderingRule* toInsert = rr; RenderingRule* previous = tagValueGlobalRules[state][key]; if (previous != NULL) { // all root rules should have at least tag/value toInsert = createTagValueRootWrapperRule(key, previous); toInsert->ifElseChildren.push_back(rr); } tagValueGlobalRules[state][key] = toInsert; }
int main(int argc, char **argv) { if (argc <= 1) { // 1. Test Rendering rule storage // testRenderingRuleStorage("/home/victor/projects/OsmAnd/git/DataExtractionOSM/src/net/osmand/render/", // "test_depends.render.xml" // "default.render.xml" // ); // 2. Test simple rendering printUsage(""); return 1; } const char* f = argv[1]; if (f[0] == '-') { // command if (f[1]=='v') { if (argc < 2) { printUsage("Missing file parameter"); } else { VerboseInfo* vinfo = new VerboseInfo(argc, argv); printFileInformation(argv[argc -1], vinfo); } } else if (f[1]=='r') { if (argc < 2) { printUsage("Missing file parameter"); } else { RenderingInfo* info = new RenderingInfo(argc, argv); char s[100]; for (int i = 1; i != argc; ++i) { if (sscanf(argv[i], "-renderingInputFile=%s", s)) { BinaryMapFile* mf = initBinaryMapFile(s); osmand_log_print(LOG_INFO, "Init %d (success) binary map file %s.", mf->version, mf->inputName.c_str()); } } runSimpleRendering(info->renderingFileName, info->imagesFileName, info); for (int i = 1; i != argc; ++i) { if (sscanf(argv[i], "-renderingInputFile=%s", s)) { closeBinaryMapFile(s); } } delete info; } } else { printUsage("Unknown command"); } } else { printFileInformation(f, NULL); } SkGraphics::PurgeFontCache(); purgeCachedBitmaps(); }
SkBitmap* RenderingContext::getCachedBitmap(const std::string& bitmapResource) { if (defaultIconsDir.size() > 0) { string fl = string(defaultIconsDir + "h_" + bitmapResource + ".png"); FILE* f = fopen(fl.c_str(), "r"); if (f == NULL) { fl = string(defaultIconsDir + "g_" + bitmapResource + ".png"); f = fopen(fl.c_str(), "r"); } if (f != NULL) { fclose(f); osmand_log_print(LOG_INFO, "Open file %s", fl.c_str()); SkBitmap* bmp = new SkBitmap(); if (!SkImageDecoder::DecodeFile(fl.c_str(), bmp)) { return NULL; } return bmp; } } return NULL; }
void runSimpleRendering( string renderingFileName, string resourceDir, RenderingInfo* info) { SkColor defaultMapColor = SK_ColorLTGRAY; if (info->width > 10000 || info->height > 10000) { osmand_log_print(LOG_ERROR, "We don't rendering images more than 10000x10000 "); return; } osmand_log_print(LOG_INFO, "Rendering info bounds(%d, %d, %d, %d) zoom(%d), width/height(%d/%d) tilewidth/tileheight(%d/%d) fileName(%s)", info->left, info->top, info->right, info->bottom, info->zoom, info->width, info->height, info->tileWX, info->tileHY, info->tileFileName.c_str()); RenderingRulesStorage* st = new RenderingRulesStorage(renderingFileName.c_str()); st->parseRulesFromXmlInputStream(renderingFileName.c_str(), NULL); RenderingRuleSearchRequest* searchRequest = new RenderingRuleSearchRequest(st); ResultPublisher* publisher = new ResultPublisher(); SearchQuery q(floor(info->left), floor(info->right), ceil(info->top), ceil(info->bottom), searchRequest, publisher); q.zoom = info->zoom; ResultPublisher* res = searchObjectsForRendering(&q, true, "Nothing found"); osmand_log_print(LOG_INFO, "Found %d objects", res->result.size()); SkBitmap* bitmap = new SkBitmap(); bitmap->setConfig(SkBitmap::kRGB_565_Config, info->width, info->height); size_t bitmapDataSize = bitmap->getSize(); void* bitmapData = malloc(bitmapDataSize); bitmap->setPixels(bitmapData); osmand_log_print(LOG_INFO, "Initializing rendering style and rendering context"); ElapsedTimer initObjects; initObjects.start(); RenderingContext rc; rc.setDefaultIconsDir(resourceDir); searchRequest->clearState(); searchRequest->setIntFilter(st->PROPS.R_MINZOOM, info->zoom); if (searchRequest->searchRenderingAttribute(A_DEFAULT_COLOR)) { defaultMapColor = searchRequest->getIntPropertyValue(searchRequest->props()->R_ATTR_COLOR_VALUE); } searchRequest->clearState(); searchRequest->setIntFilter(st->PROPS.R_MINZOOM, info->zoom); if (searchRequest->searchRenderingAttribute(A_SHADOW_RENDERING)) { rc.setShadowRenderingMode(searchRequest->getIntPropertyValue(searchRequest->props()->R_ATTR_INT_VALUE)); //rc.setShadowRenderingColor(searchRequest->getIntPropertyValue(searchRequest->props()->R_SHADOW_COLOR)); } rc.setLocation( ((double)info->left)/getPowZoom(31-info->zoom), ((double)info->top)/getPowZoom(31-info->zoom) ); rc.setDimension(info->width, info->height); rc.setZoom(info->zoom); rc.setRotate(0); rc.setDensityScale(1); osmand_log_print(LOG_INFO, "Rendering image"); initObjects.pause(); SkCanvas* canvas = new SkCanvas(*bitmap); canvas->drawColor(defaultMapColor); doRendering(res->result, canvas, searchRequest, &rc); osmand_log_print(LOG_INFO, "End Rendering image"); osmand_log_print(LOG_INFO, "Native ok (init %d, rendering %d) ", initObjects.getElapsedTime(), rc.nativeOperations.getElapsedTime()); SkImageEncoder* enc = SkImageEncoder::Create(SkImageEncoder::kPNG_Type); if (enc != NULL && !enc->encodeFile(info->tileFileName.c_str(), *bitmap, 100)) { osmand_log_print(LOG_ERROR, "FAIL to save tile to %s", info->tileFileName.c_str()); } else { osmand_log_print(LOG_INFO, "Tile successfully saved to %s", info->tileFileName.c_str()); } delete enc; delete publisher; delete searchRequest; delete st; delete canvas; delete bitmap; free(bitmapData); return; }
static void startElementHandler(void *data, const char *tag, const char **atts) { RenderingRulesHandler* t = (RenderingRulesHandler*) data; int len = strlen(tag); string name(tag); if (len == 6 && "filter" == name) { map<string, string> attrsMap; if (t->st.size() > 0 && t->st.top().isGroup()) { attrsMap.insert(t->st.top().groupAttributes.begin(), t->st.top().groupAttributes.end()); } parseAttributes(atts, attrsMap); RenderingRule* renderingRule = new RenderingRule(attrsMap,t->storage); if (t->st.size() > 0 && t->st.top().isGroup()) { t->st.top().children.push_back(renderingRule); } else if (t->st.size() > 0 && !t->st.top().isGroup()) { RenderingRule* parent = t->st.top().singleRule; t->st.top().singleRule->ifElseChildren.push_back(renderingRule); } else { t->storage->registerGlobalRule(renderingRule, t->state); } GroupRules gr(renderingRule); t->st.push(gr); } else if ("groupFilter" == name) { //$NON-NLS-1$ map<string, string> attrsMap; parseAttributes(atts, attrsMap); RenderingRule* renderingRule = new RenderingRule(attrsMap,t->storage); if (t->st.size() > 0 && t->st.top().isGroup()) { GroupRules parent = ((GroupRules) t->st.top()); t->st.top().addGroupFilter(renderingRule); } else if (t->st.size() > 0 && !t->st.top().isGroup()) { t->st.top().singleRule->ifChildren.push_back(renderingRule); } else { osmand_log_print(LOG_ERROR, "Group filter without parent"); } t->st.push(GroupRules(renderingRule)); } else if ("group" == name) { //$NON-NLS-1$ GroupRules groupRules; if (t->st.size() > 0 && t->st.top().isGroup()) { groupRules.groupAttributes.insert(t->st.top().groupAttributes.begin(), t->st.top().groupAttributes.end()); } parseAttributes(atts, groupRules.groupAttributes); t->st.push(groupRules); } else if ("order" == name) { //$NON-NLS-1$ t->state = RenderingRulesStorage::ORDER_RULES; } else if ("text" == name) { //$NON-NLS-1$ t->state = RenderingRulesStorage::TEXT_RULES; } else if ("point" == name) { //$NON-NLS-1$ t->state = RenderingRulesStorage::POINT_RULES; } else if ("line" == name) { //$NON-NLS-1$ t->state = RenderingRulesStorage::LINE_RULES; } else if ("polygon" == name) { //$NON-NLS-1$ t->state = RenderingRulesStorage::POLYGON_RULES; } else if ("renderingAttribute" == name) { //$NON-NLS-1$ map<string, string> attrsMap; parseAttributes(atts, attrsMap); string attr = attrsMap["name"]; map<string, string> empty; RenderingRule* root = new RenderingRule(empty,t->storage); t->storage->renderingAttributes[attr] = root; t->st.push(GroupRules(root)); } else if ("renderingProperty" == name) { map<string, string> attrsMap; parseAttributes(atts, attrsMap); string attr = attrsMap["attr"]; RenderingRuleProperty* prop; string type = attrsMap["type"]; if ("boolean" == type) { prop = RenderingRuleProperty::createInputBooleanProperty(attr); } else if ("string" == type) { prop = RenderingRuleProperty::createInputStringProperty(attr); } else { prop = RenderingRuleProperty::createInputIntProperty(attr); } prop->description = attrsMap["description"]; prop->name = attrsMap["name"]; string possible = attrsMap["possibleValues"]; if (possible != "") { int n; int p = 0; while ((n = possible.find(',', p)) != string::npos) { prop->possibleValues.push_back(possible.substr(p, n)); p = n + 1; } prop->possibleValues.push_back(possible.substr(p)); } t->storage->PROPS.registerRule(prop); } else if ("renderingStyle" == name) { map<string, string> attrsMap; parseAttributes(atts, attrsMap); string depends = attrsMap["depends"]; if (depends.size() > 0 && t->resolver != NULL) { t->dependsStorage = t->resolver->resolve(depends, t->resolver); } if (t->dependsStorage != NULL) { // copy dictionary t->storage->dictionary = t->dependsStorage->dictionary; t->storage->dictionaryMap = t->dependsStorage->dictionaryMap; t->storage->PROPS.merge(t->dependsStorage->PROPS); } else if (depends.size() > 0) { osmand_log_print(LOG_ERROR, "!Dependent rendering style was not resolved : %s", depends.c_str()); } //renderingName = attrsMap["name"]; } else { osmand_log_print(LOG_WARN, "Unknown tag : %s", name.c_str()); } }
// returns true if coastlines were added! bool processCoastlines(std::vector<MapDataObject*>& coastLines, int leftX, int rightX, int bottomY, int topY, int zoom, bool showIfThereIncompleted, bool addDebugIncompleted, std::vector<MapDataObject*>& res) { std::vector<coordinates> completedRings; std::vector<coordinates > uncompletedRings; std::vector<MapDataObject*>::iterator val = coastLines.begin(); long long dbId = 0; for (; val != coastLines.end(); val++) { MapDataObject* o = *val; int len = o->points.size(); if (len < 2) { continue; } dbId = o->id >> 1; coordinates* cs = new coordinates(); int px = o->points.at(0).first; int py = o->points.at(0).second; int x = px; int y = py; bool pinside = leftX <= x && x <= rightX && y >= topY && y <= bottomY; if (pinside) { cs->push_back(int_pair(x, y)); } for (int i = 1; i < len; i++) { x = o->points.at(i).first; y = o->points.at(i).second; bool inside = leftX <= x && x <= rightX && y >= topY && y <= bottomY; bool lineEnded = calculateLineCoordinates(inside, x, y, pinside, px, py, leftX, rightX, bottomY, topY, *cs); if (lineEnded) { combineMultipolygonLine(completedRings, uncompletedRings, *cs); // create new line if it goes outside cs = new coordinates(); } px = x; py = y; pinside = inside; } combineMultipolygonLine(completedRings, uncompletedRings, *cs); } if (completedRings.size() == 0 && uncompletedRings.size() == 0) { return false; } if (uncompletedRings.size() > 0) { unifyIncompletedRings(uncompletedRings, completedRings, leftX, rightX, bottomY, topY, dbId, zoom); } if (addDebugIncompleted) { // draw uncompleted for debug purpose for (int i = 0; i < uncompletedRings.size(); i++) { MapDataObject* o = new MapDataObject(); o->points = uncompletedRings[i]; o->types.push_back(tag_value("natural", "coastline_broken")); res.push_back(o); } } if (!showIfThereIncompleted && uncompletedRings.size() > 0) { return false; } bool clockwiseFound = false; for (int i = 0; i < completedRings.size(); i++) { bool clockwise = isClockwiseWay(completedRings[i]); clockwiseFound = clockwiseFound || clockwise; MapDataObject* o = new MapDataObject(); o->points = completedRings[i]; if (clockwise) { o->types.push_back(tag_value("natural", "coastline")); } else { o->types.push_back(tag_value("natural", "land")); } o->id = dbId; o->area = true; res.push_back(o); } if (!clockwiseFound && uncompletedRings.size() == 0) { // add complete water tile MapDataObject* o = new MapDataObject(); o->points.push_back(int_pair(leftX, topY)); o->points.push_back(int_pair(rightX, topY)); o->points.push_back(int_pair(rightX, bottomY)); o->points.push_back(int_pair(leftX, bottomY)); o->points.push_back(int_pair(leftX, topY)); o->id = dbId; o->types.push_back(tag_value("natural", "coastline")); osmand_log_print(LOG_ERROR, "!!! Isolated islands !!!"); res.push_back(o); } return true; }
void unifyIncompletedRings(std::vector<std::vector<int_pair> >& toProccess, std::vector<std::vector<int_pair> >& completedRings, int leftX, int rightX, int bottomY, int topY, long dbId, int zoom) { std::set<int> nonvisitedRings; std::vector<coordinates > incompletedRings(toProccess); toProccess.clear(); std::vector<coordinates >::iterator ir = incompletedRings.begin(); int j = 0; for (j = 0; ir != incompletedRings.end(); ir++,j++) { int x = ir->at(0).first; int y = ir->at(0).second; int sx = ir->at(ir->size() - 1).first; int sy = ir->at(ir->size() - 1).second; 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) { osmand_log_print(LOG_ERROR, "Error processing multipolygon"); toProccess.push_back(*ir); } else { nonvisitedRings.insert(j); } } ir = incompletedRings.begin(); for (j = 0; ir != incompletedRings.end(); ir++, 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<std::vector<int_pair> >::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<int_pair> 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); } }