bool OsmAnd::BinaryMapDataProvider_P::obtainData( const TileId tileId, const ZoomLevel zoom, std::shared_ptr<MapTiledData>& outTiledData, const IQueryController* const queryController) { std::shared_ptr<TileEntry> tileEntry; for (;;) { // Try to obtain previous instance of tile _tileReferences.obtainOrAllocateEntry(tileEntry, tileId, zoom, [] (const TiledEntriesCollection<TileEntry>& collection, const TileId tileId, const ZoomLevel zoom) -> TileEntry* { return new TileEntry(collection, tileId, zoom); }); // If state is "Undefined", change it to "Loading" and proceed with loading if (tileEntry->setStateIf(TileState::Undefined, TileState::Loading)) break; // In case tile entry is being loaded, wait until it will finish loading if (tileEntry->getState() == TileState::Loading) { QReadLocker scopedLcoker(&tileEntry->_loadedConditionLock); // If tile is in 'Loading' state, wait until it will become 'Loaded' while (tileEntry->getState() != TileState::Loaded) REPEAT_UNTIL(tileEntry->_loadedCondition.wait(&tileEntry->_loadedConditionLock)); } // Try to lock tile reference outTiledData = tileEntry->_tile.lock(); // If successfully locked, just return it if (outTiledData) return true; // Otherwise consider this tile entry as expired, remove it from collection (it's safe to do that right now) // This will enable creation of new entry on next loop cycle _tileReferences.removeEntry(tileId, zoom); tileEntry.reset(); } #if OSMAND_PERFORMANCE_METRICS const auto total_Begin = std::chrono::high_resolution_clock::now(); #endif // OSMAND_PERFORMANCE_METRICS // Obtain OBF data interface #if OSMAND_PERFORMANCE_METRICS const auto obtainDataInterface_Begin = std::chrono::high_resolution_clock::now(); #endif // OSMAND_PERFORMANCE_METRICS const auto& dataInterface = owner->obfsCollection->obtainDataInterface(); #if OSMAND_PERFORMANCE_METRICS const auto obtainDataInterface_End = std::chrono::high_resolution_clock::now(); const std::chrono::duration<float> obtainDataInterface_Elapsed = obtainDataInterface_End - obtainDataInterface_Begin; #endif // OSMAND_PERFORMANCE_METRICS // Get bounding box that covers this tile const auto tileBBox31 = Utilities::tileBoundingBox31(tileId, zoom); // Perform read-out QList< std::shared_ptr<const Model::BinaryMapObject> > referencedMapObjects; QList< proper::shared_future< std::shared_ptr<const Model::BinaryMapObject> > > futureReferencedMapObjects; QList< std::shared_ptr<const Model::BinaryMapObject> > loadedMapObjects; QSet< uint64_t > loadedSharedMapObjects; MapFoundationType tileFoundation; #if OSMAND_PERFORMANCE_METRICS float dataFilter = 0.0f; const auto dataRead_Begin = std::chrono::high_resolution_clock::now(); #endif // OSMAND_PERFORMANCE_METRICS #if OSMAND_PERFORMANCE_METRICS > 1 ObfMapSectionReader_Metrics::Metric_loadMapObjects dataRead_Metric; #endif // OSMAND_PERFORMANCE_METRICS > 1 dataInterface->loadMapObjects(&loadedMapObjects, &tileFoundation, tileBBox31, zoom, nullptr, [this, zoom, &referencedMapObjects, &futureReferencedMapObjects, &loadedSharedMapObjects, tileBBox31 #if OSMAND_PERFORMANCE_METRICS , &dataFilter #endif // OSMAND_PERFORMANCE_METRICS ] (const std::shared_ptr<const ObfMapSectionInfo>& section, const uint64_t id, const AreaI& bbox, const ZoomLevel firstZoomLevel, const ZoomLevel lastZoomLevel) -> bool { #if OSMAND_PERFORMANCE_METRICS const auto dataFilter_Begin = std::chrono::high_resolution_clock::now(); #endif // OSMAND_PERFORMANCE_METRICS // This map object may be shared only in case it crosses bounds of a tile const auto canNotBeShared = tileBBox31.contains(bbox); // If map object can not be shared, just read it if (canNotBeShared) { #if OSMAND_PERFORMANCE_METRICS const auto dataFilter_End = std::chrono::high_resolution_clock::now(); const std::chrono::duration<float> dataRead_Elapsed = dataFilter_End - dataFilter_Begin; dataFilter += dataRead_Elapsed.count(); #endif // OSMAND_PERFORMANCE_METRICS return true; } // Otherwise, this map object can be shared, so it should be checked for // being present in shared mapObjects storage, or be reserved there std::shared_ptr<const Model::BinaryMapObject> sharedMapObjectReference; proper::shared_future< std::shared_ptr<const Model::BinaryMapObject> > futureSharedMapObjectReference; if (_sharedMapObjects.obtainReferenceOrFutureReferenceOrMakePromise(id, zoom, Utilities::enumerateZoomLevels(firstZoomLevel, lastZoomLevel), sharedMapObjectReference, futureSharedMapObjectReference)) { if (sharedMapObjectReference) { // If map object is already in shared objects cache and is available, use that one referencedMapObjects.push_back(qMove(sharedMapObjectReference)); } else { futureReferencedMapObjects.push_back(qMove(futureSharedMapObjectReference)); } #if OSMAND_PERFORMANCE_METRICS const auto dataFilter_End = std::chrono::high_resolution_clock::now(); const std::chrono::duration<float> dataRead_Elapsed = dataFilter_End - dataFilter_Begin; dataFilter += dataRead_Elapsed.count(); #endif // OSMAND_PERFORMANCE_METRICS return false; } // This map object was reserved, and is going to be shared, but needs to be loaded loadedSharedMapObjects.insert(id); return true; }, #if OSMAND_PERFORMANCE_METRICS > 1 & dataRead_Metric #else nullptr #endif // OSMAND_PERFORMANCE_METRICS > 1 ); #if OSMAND_PERFORMANCE_METRICS const auto dataRead_End = std::chrono::high_resolution_clock::now(); const std::chrono::duration<float> dataRead_Elapsed = dataRead_End - dataRead_Begin; const auto dataIdsProcess_Begin = std::chrono::high_resolution_clock::now(); #endif // OSMAND_PERFORMANCE_METRICS // Process loaded-and-shared map objects for (auto& mapObject : loadedMapObjects) { // Check if this map object is shared if (!loadedSharedMapObjects.contains(mapObject->id)) continue; // Add unique map object under lock to all zoom levels, for which this map object is valid assert(mapObject->level); _sharedMapObjects.fulfilPromiseAndReference( mapObject->id, Utilities::enumerateZoomLevels(mapObject->level->minZoom, mapObject->level->maxZoom), mapObject); } for (auto& futureMapObject : futureReferencedMapObjects) { auto mapObject = futureMapObject.get(); referencedMapObjects.push_back(qMove(mapObject)); } #if OSMAND_PERFORMANCE_METRICS const auto dataIdsProcess_End = std::chrono::high_resolution_clock::now(); const std::chrono::duration<float> dataIdsProcess_Elapsed = dataIdsProcess_End - dataIdsProcess_Begin; const auto dataProcess_Begin = std::chrono::high_resolution_clock::now(); #endif // OSMAND_PERFORMANCE_METRICS #if OSMAND_PERFORMANCE_METRICS > 1 Rasterizer_Metrics::Metric_prepareContext dataProcess_metric; #endif // OSMAND_PERFORMANCE_METRICS > 1 // Prepare data for the tile const auto sharedMapObjectsCount = referencedMapObjects.size() + loadedSharedMapObjects.size(); const auto allMapObjects = loadedMapObjects + referencedMapObjects; // Allocate and prepare rasterizer context bool nothingToRasterize = false; std::shared_ptr<RasterizerContext> rasterizerContext(new RasterizerContext(owner->rasterizerEnvironment, owner->rasterizerSharedContext)); Rasterizer::prepareContext(*rasterizerContext, tileBBox31, zoom, tileFoundation, allMapObjects, ¬hingToRasterize, nullptr, #if OSMAND_PERFORMANCE_METRICS > 1 & dataProcess_metric #else nullptr #endif // OSMAND_PERFORMANCE_METRICS > 1 ); #if OSMAND_PERFORMANCE_METRICS const auto dataProcess_End = std::chrono::high_resolution_clock::now(); const std::chrono::duration<float> dataProcess_Elapsed = dataProcess_End - dataProcess_Begin; #endif // OSMAND_PERFORMANCE_METRICS // Create tile const std::shared_ptr<BinaryMapDataTile> newTile(new BinaryMapDataTile( tileFoundation, allMapObjects, rasterizerContext, nothingToRasterize, tileId, zoom)); newTile->_p->_weakLink = _link->getWeak(); newTile->_p->_refEntry = tileEntry; // Publish new tile outTiledData = newTile; // Store weak reference to new tile and mark it as 'Loaded' tileEntry->_tile = newTile; tileEntry->setState(TileState::Loaded); // Notify that tile has been loaded { QWriteLocker scopedLcoker(&tileEntry->_loadedConditionLock); tileEntry->_loadedCondition.wakeAll(); } #if OSMAND_PERFORMANCE_METRICS const auto total_End = std::chrono::high_resolution_clock::now(); const std::chrono::duration<float> total_Elapsed = total_End - total_Begin; #if OSMAND_PERFORMANCE_METRICS <= 1 LogPrintf(LogSeverityLevel::Info, "%d map objects (%d unique, %d shared) from %dx%d@%d in %fs", allMapObjects.size(), allMapObjects.size() - sharedMapObjectsCount, sharedMapObjectsCount, tileId.x, tileId.y, zoom, total_Elapsed.count()); #else LogPrintf(LogSeverityLevel::Info, "%d map objects (%d unique, %d shared) from %dx%d@%d in %fs:\n" "\topen %fs\n" "\tread %fs (filter-by-id %fs):\n" "\t - visitedLevels = %d\n" "\t - acceptedLevels = %d\n" "\t - visitedNodes = %d\n" "\t - acceptedNodes = %d\n" "\t - elapsedTimeForNodes = %fs\n" "\t - mapObjectsBlocksRead = %d\n" "\t - visitedMapObjects = %d\n" "\t - acceptedMapObjects = %d\n" "\t - elapsedTimeForMapObjectsBlocks = %fs\n" "\t - elapsedTimeForOnlyVisitedMapObjects = %fs\n" "\t - elapsedTimeForOnlyAcceptedMapObjects = %fs\n" "\t - average time per 1K only-visited map objects = %fms\n" "\t - average time per 1K only-accepted map objects = %fms\n" "\tprocess-ids %fs\n" "\tprocess-content %fs:\n" "\t - elapsedTimeForSortingObjects = %fs\n" "\t - elapsedTimeForPolygonizingCoastlines = %fs\n" "\t - polygonizedCoastlines = %d\n" "\t - elapsedTimeForObtainingPrimitives = %fs\n" "\t - elapsedTimeForOrderEvaluation = %fs\n" "\t - orderEvaluations = %d\n" "\t - average time per 1K order evaluations = %fms\n" "\t - elapsedTimeForPolygonEvaluation = %fs\n" "\t - polygonEvaluations = %d\n" "\t - average time per 1K polygon evaluations = %fms\n" "\t - polygonPrimitives = %d\n" "\t - elapsedTimeForPolylineEvaluation = %fs\n" "\t - polylineEvaluations = %d\n" "\t - average time per 1K polyline evaluations = %fms\n" "\t - polylinePrimitives = %d\n" "\t - elapsedTimeForPointEvaluation = %fs\n" "\t - pointEvaluations = %d\n" "\t - average time per 1K point evaluations = %fms\n" "\t - pointPrimitives = %d\n" "\t - elapsedTimeForObtainingPrimitivesSymbols = %fs", allMapObjects.size(), allMapObjects.size() - sharedMapObjectsCount, sharedMapObjectsCount, tileId.x, tileId.y, zoom, total_Elapsed.count(), obtainDataInterface_Elapsed.count(), dataRead_Elapsed.count(), dataFilter, dataRead_Metric.visitedLevels, dataRead_Metric.acceptedLevels, dataRead_Metric.visitedNodes, dataRead_Metric.acceptedNodes, dataRead_Metric.elapsedTimeForNodes, dataRead_Metric.mapObjectsBlocksRead, dataRead_Metric.visitedMapObjects, dataRead_Metric.acceptedMapObjects, dataRead_Metric.elapsedTimeForMapObjectsBlocks, dataRead_Metric.elapsedTimeForOnlyVisitedMapObjects, dataRead_Metric.elapsedTimeForOnlyAcceptedMapObjects, (dataRead_Metric.elapsedTimeForOnlyVisitedMapObjects * 1000.0f) / (static_cast<float>(dataRead_Metric.visitedMapObjects - dataRead_Metric.acceptedMapObjects) / 1000.0f), (dataRead_Metric.elapsedTimeForOnlyAcceptedMapObjects * 1000.0f) / (static_cast<float>(dataRead_Metric.acceptedMapObjects) / 1000.0f), dataIdsProcess_Elapsed.count(), dataProcess_Elapsed.count(), dataProcess_metric.elapsedTimeForSortingObjects, dataProcess_metric.elapsedTimeForPolygonizingCoastlines, dataProcess_metric.polygonizedCoastlines, dataProcess_metric.elapsedTimeForObtainingPrimitives, dataProcess_metric.elapsedTimeForOrderEvaluation, dataProcess_metric.orderEvaluations, (dataProcess_metric.elapsedTimeForOrderEvaluation * 1000.0f / static_cast<float>(dataProcess_metric.orderEvaluations)) * 1000.0f, dataProcess_metric.elapsedTimeForPolygonEvaluation, dataProcess_metric.polygonEvaluations, (dataProcess_metric.elapsedTimeForPolygonEvaluation * 1000.0f / static_cast<float>(dataProcess_metric.polygonEvaluations)) * 1000.0f, dataProcess_metric.polygonPrimitives, dataProcess_metric.elapsedTimeForPolylineEvaluation, dataProcess_metric.polylineEvaluations, (dataProcess_metric.elapsedTimeForPolylineEvaluation * 1000.0f / static_cast<float>(dataProcess_metric.polylineEvaluations)) * 1000.0f, dataProcess_metric.polylinePrimitives, dataProcess_metric.elapsedTimeForPointEvaluation, dataProcess_metric.pointEvaluations, (dataProcess_metric.elapsedTimeForPointEvaluation * 1000.0f / static_cast<float>(dataProcess_metric.pointEvaluations)) * 1000.0f, dataProcess_metric.pointPrimitives, dataProcess_metric.elapsedTimeForObtainingPrimitivesSymbols); #endif // OSMAND_PERFORMANCE_METRICS <= 1 #endif // OSMAND_PERFORMANCE_METRICS return true; }
void rasterize(std::ostream &output, const OsmAnd::EyePiece::Configuration& cfg) #endif { // Obtain and configure rasterization style context OsmAnd::MapStyles stylesCollection; for(auto itStyleFile = cfg.styleFiles.cbegin(); itStyleFile != cfg.styleFiles.cend(); ++itStyleFile) { const auto& styleFile = *itStyleFile; if(!stylesCollection.registerStyle(styleFile.absoluteFilePath())) output << xT("Failed to parse metadata of '") << QStringToStlString(styleFile.fileName()) << xT("' or duplicate style") << std::endl; } std::shared_ptr<const OsmAnd::MapStyle> style; if(!stylesCollection.obtainStyle(cfg.styleName, style)) { output << xT("Failed to resolve style '") << QStringToStlString(cfg.styleName) << xT("'") << std::endl; return; } if(cfg.dumpRules) style->dump(); OsmAnd::ObfsCollection obfsCollection; obfsCollection.watchDirectory(cfg.obfsDir); // Collect all map objects (this should be replaced by something like RasterizerViewport/RasterizerContext) QList< std::shared_ptr<const OsmAnd::Model::MapObject> > mapObjects; OsmAnd::AreaI bbox31( OsmAnd::Utilities::get31TileNumberY(cfg.bbox.top), OsmAnd::Utilities::get31TileNumberX(cfg.bbox.left), OsmAnd::Utilities::get31TileNumberY(cfg.bbox.bottom), OsmAnd::Utilities::get31TileNumberX(cfg.bbox.right) ); const auto& obfDI = obfsCollection.obtainDataInterface(); OsmAnd::MapFoundationType mapFoundation; obfDI->obtainMapObjects(&mapObjects, &mapFoundation, bbox31, cfg.zoom, nullptr); bool basemapAvailable; obfDI->obtainBasemapPresenceFlag(basemapAvailable); // Calculate output size in pixels const auto tileWidth = OsmAnd::Utilities::getTileNumberX(cfg.zoom, cfg.bbox.right) - OsmAnd::Utilities::getTileNumberX(cfg.zoom, cfg.bbox.left); const auto tileHeight = OsmAnd::Utilities::getTileNumberY(cfg.zoom, cfg.bbox.bottom) - OsmAnd::Utilities::getTileNumberY(cfg.zoom, cfg.bbox.top); const auto pixelWidth = qCeil(tileWidth * cfg.tileSide); const auto pixelHeight = qCeil(tileHeight * cfg.tileSide); output << xT("Will rasterize ") << mapObjects.count() << xT(" objects onto ") << pixelWidth << xT("x") << pixelHeight << xT(" bitmap") << std::endl; // Allocate render target SkBitmap renderSurface; renderSurface.setConfig(cfg.is32bit ? SkBitmap::kARGB_8888_Config : SkBitmap::kRGB_565_Config, pixelWidth, pixelHeight); if(!renderSurface.allocPixels()) { output << xT("Failed to allocated render target ") << pixelWidth << xT("x") << pixelHeight; return; } SkBitmapDevice renderTarget(renderSurface); // Create render canvas SkCanvas canvas(&renderTarget); // Perform actual rendering std::shared_ptr<OsmAnd::RasterizerEnvironment> rasterizerEnv(new OsmAnd::RasterizerEnvironment(style, cfg.densityFactor)); std::shared_ptr<OsmAnd::RasterizerContext> rasterizerContext(new OsmAnd::RasterizerContext(rasterizerEnv)); OsmAnd::Rasterizer::prepareContext(*rasterizerContext, bbox31, cfg.zoom, mapFoundation, mapObjects); OsmAnd::Rasterizer rasterizer(rasterizerContext); if(cfg.drawMap) { rasterizer.rasterizeMap(canvas); } /*if(cfg.drawText) OsmAnd::Rasterizer::rasterizeText(rasterizerContext, !cfg.drawMap, canvas, nullptr);*/ // Save rendered area if(!cfg.output.isEmpty()) { std::unique_ptr<SkImageEncoder> encoder(CreatePNGImageEncoder()); encoder->encodeFile(cfg.output.toLocal8Bit(), renderSurface, 100); } return; }