std::shared_ptr<OsmAnd::MapMarker> OsmAnd::MapMarkerBuilder_P::buildAndAddToCollection( const std::shared_ptr<MapMarkersCollection>& collection) { QReadLocker scopedLocker(&_lock); // Construct map symbols group for this marker const std::shared_ptr<MapMarker> marker(new MapMarker( _markerId, _baseOrder, _pinIcon, _pinIconVerticalAlignment, _pinIconHorisontalAlignment, detachedOf(_onMapSurfaceIcons), _isAccuracyCircleSupported, _accuracyCircleBaseColor)); marker->setIsHidden(_isHidden); if (_isAccuracyCircleSupported) { marker->setIsAccuracyCircleVisible(_isAccuracyCircleVisible); marker->setAccuracyCircleRadius(_accuracyCircleRadius); } marker->setPosition(_position); marker->setPinIconModulationColor(_pinIconModulationColor); marker->applyChanges(); // Add marker to collection and return it if adding was successful if (!collection->_p->addMarker(marker)) return nullptr; return marker; }
bool OsmAnd::MapRendererTiledResourcesCollection::updateCollectionSnapshot() const { const auto invalidatesDiscarded = _collectionSnapshotInvalidatesCount.fetchAndAddOrdered(0); if (invalidatesDiscarded == 0) return true; // Copy from original storage to temp storage Storage storageCopy; { if (!_collectionLock.tryLockForRead()) return false; for (int zoomLevel = MinZoomLevel; zoomLevel <= MaxZoomLevel; zoomLevel++) { const auto& sourceStorageLevel = constOf(_storage)[zoomLevel]; auto& targetStorageLevel = storageCopy[zoomLevel]; targetStorageLevel = detachedOf(sourceStorageLevel); } _collectionLock.unlock(); } _collectionSnapshotInvalidatesCount.fetchAndAddOrdered(-invalidatesDiscarded); // Copy from temp storage to snapshot { QWriteLocker scopedLocker(&_snapshot->_lock); _snapshot->_storage = qMove(storageCopy); } return true; }
QHash< OsmAnd::MapMarker::OnSurfaceIconKey, std::shared_ptr<const SkBitmap> > OsmAnd::MapMarkerBuilder_P::getOnMapSurfaceIcons() const { QReadLocker scopedLocker(&_lock); return detachedOf(_onMapSurfaceIcons); }
QHash< OsmAnd::ResolvedMapStyle::ValueDefinitionId, OsmAnd::MapStyleConstantValue > OsmAnd::MapPresentationEnvironment_P::getSettings() const { QMutexLocker scopedLocker(&_settingsChangeMutex); return detachedOf(_settings); }
QList< QList<OsmAnd::MapObjectsSymbolsProvider_P::ComputedPinPoint> > OsmAnd::MapObjectsSymbolsProvider_P::computePinPoints( const QVector<PointI>& path31, const float globalLeftPaddingInPixels, const float globalRightPaddingInPixels, const float globalSpacingBetweenBlocksInPixels, const QList<SymbolForPinPointsComputation>& symbolsForPinPointsComputation, const ZoomLevel minZoom, const ZoomLevel maxZoom, const ZoomLevel neededZoom) const { QList< QList<ComputedPinPoint> > computedPinPointsByLayer; // Compute pin-points placement starting from minZoom to maxZoom. How this works: // // Example of block instance placement assuming it fits exactly 4 times on minZoom ('bi' is block instance): // minZoom+0: bibibibi // Since each next zoom is 2x bigger, thus here's how minZoom+1 will look like without additional instances ('-' is widthOfBlockInPixels/2) // minZoom+1: -bi--bi--bi--bi- // After placing 3 additional instances it will look like // minZoom+1: -bibibibibibibi- // On next zoom without additional instances it will look like // minZoom+2: ---bi--bi--bi--bi--bi--bi--bi--- // After placing additional 8 instances it will look like // minZoom+2: -bibibibibibibibibibibibibibibi- // On next zoom without additional 16 instances it will look like // minZoom+3: ---bi--bi--bi--bi--bi--bi--bi--bi--bi--bi--bi--bi--bi--bi--bi--- // On next zoom without additional 32 instances it will look like // minZoom+4: ---bi--bi--bi--bi--bi--bi--bi--bi--bi--bi--bi--bi--bi--bi--bi--bi--bi--bi--bi--bi--bi--bi--bi--bi--bi--bi--bi--bi--bi--bi--bi--- // // Another example of block instance placement assuming only 3.5 block instances fit ('.' is widthOfBlockInPixels/4) // minZoom+0: .bibibi. // On next zoom without 4 additional instances // minZoom+1: --bi--bi--bi-- // After placement additional 4 instances // minZoom+1: bibibibibibibi // On next zoom without additional 6 instances // minZoom+2: -bi--bi--bi--bi--bi--bi--bi- // minZoom+2: -bibibibibibibibibibibibibi- // On next zoom without additional 14 instances // minZoom+3: ---bi--bi--bi--bi--bi--bi--bi--bi--bi--bi--bi--bi--bi--- // // Step 0. Initial checks if (symbolsForPinPointsComputation.isEmpty()) return computedPinPointsByLayer; // Step 1. Get scale factor from 31 to pixels for minZoom. // Length on path in pixels depends on tile size in pixels, and density const auto tileSize31 = (1u << (ZoomLevel::MaxZoomLevel - minZoom)); const auto from31toPixelsScale = static_cast<double>(owner->referenceTileSizeOnScreenInPixels) / tileSize31; // Step 2. Compute path length, path segments length (in 31 and in pixels) const auto pathSize = path31.size(); const auto pathSegmentsCount = pathSize - 1; float pathLengthInPixels = 0.0f; QVector<float> pathSegmentsLengthInPixelsOnBaseZoom(pathSegmentsCount); auto pPathSegmentLengthInPixelsOnBaseZoom = pathSegmentsLengthInPixelsOnBaseZoom.data(); double pathLength31 = 0.0; QVector<double> pathSegmentsLength31(pathSegmentsCount); auto pPathSegmentLength31 = pathSegmentsLength31.data(); auto pPoint31 = path31.constData(); auto pPrevPoint31 = pPoint31++; auto basePathPointIndex = 0; auto globalPaddingFromBasePathPoint = 0.0f; bool capturedBasePathPointIndex = false; for (auto segmentIdx = 0; segmentIdx < pathSegmentsCount; segmentIdx++) { const auto segmentLength31 = qSqrt((*(pPoint31++) - *(pPrevPoint31++)).squareNorm()); *(pPathSegmentLength31++) = segmentLength31; pathLength31 += segmentLength31; const auto segmentLengthInPixels = segmentLength31 * from31toPixelsScale; *(pPathSegmentLengthInPixelsOnBaseZoom++) = segmentLengthInPixels; pathLengthInPixels += segmentLengthInPixels; if (pathLength31 > globalLeftPaddingInPixels && !capturedBasePathPointIndex) { basePathPointIndex = segmentIdx; if (!qFuzzyIsNull(globalLeftPaddingInPixels)) globalPaddingFromBasePathPoint = globalLeftPaddingInPixels - (pathLengthInPixels - segmentLengthInPixels); capturedBasePathPointIndex = true; } } const auto usablePathLengthInPixels = pathLengthInPixels - globalLeftPaddingInPixels - globalRightPaddingInPixels; if (usablePathLengthInPixels <= 0.0f) return computedPinPointsByLayer; // Step 3. Compute total width of all symbols requested. This will be the block width. const auto symbolsCount = symbolsForPinPointsComputation.size(); QVector<float> symbolsFullSizesInPixels(symbolsCount); auto pSymbolFullSizeInPixels = symbolsFullSizesInPixels.data(); float blockWidthWithoutSpacing = 0.0f; for (const auto& symbolForPinPointsComputation : constOf(symbolsForPinPointsComputation)) { auto symbolWidth = 0.0f; symbolWidth += symbolForPinPointsComputation.leftPaddingInPixels; symbolWidth += symbolForPinPointsComputation.widthInPixels; symbolWidth += symbolForPinPointsComputation.rightPaddingInPixels; *(pSymbolFullSizeInPixels++) = symbolWidth; blockWidthWithoutSpacing += symbolWidth; } if (symbolsForPinPointsComputation.isEmpty() || qFuzzyIsNull(blockWidthWithoutSpacing)) return computedPinPointsByLayer; const auto blockWidthWithSpacing = blockWidthWithoutSpacing + globalSpacingBetweenBlocksInPixels; // Step 4. Process values for base zoom level const auto lengthOfPathInPixelsOnBaseZoom = usablePathLengthInPixels; // Step 5. Process by zoom levels auto lengthOfPathInPixelsOnCurrentZoom = lengthOfPathInPixelsOnBaseZoom; auto pathSegmentsLengthInPixelsOnCurrentZoom = detachedOf(pathSegmentsLengthInPixelsOnBaseZoom); auto totalNumberOfCompleteBlocks = 0; auto remainingPathLengthOnPrevZoom = 0.0f; auto kOffsetToFirstBlockOnPrevZoom = 0.0f; auto globalPaddingInPixelsFromBasePathPointOnCurrentZoom = globalPaddingFromBasePathPoint; for (int currentZoomLevel = minZoom; currentZoomLevel <= maxZoom; currentZoomLevel++) { // Compute how many new blocks will fit, where and how to place them auto blocksToInstantiate = 0; auto kOffsetToFirstNewBlockOnCurrentZoom = 0.0f; auto kOffsetToFirstPresentBlockOnCurrentZoom = 0.0f; auto fullSizeOfSymbolsThatFit = 0.0f; // This is used only in case even 1 block doesn't fit auto numberOfSymbolsThatFit = 0; // This is used only in case even 1 block doesn't fit if (totalNumberOfCompleteBlocks == 0) { if (lengthOfPathInPixelsOnCurrentZoom >= blockWidthWithoutSpacing) { const auto numberOfBlocksThatFit = lengthOfPathInPixelsOnCurrentZoom / blockWidthWithoutSpacing; blocksToInstantiate = qMax(qFloor(numberOfBlocksThatFit), 1); kOffsetToFirstNewBlockOnCurrentZoom = (numberOfBlocksThatFit - static_cast<int>(numberOfBlocksThatFit)) / 2.0f; kOffsetToFirstPresentBlockOnCurrentZoom = -1.0f; //if (lengthOfPathInPixelsOnCurrentZoom >= blockWidth + globalSpacingBetweenBlocksInPixels + blockWidth) //{ // // If more than 2 blocks + spacing between them // const auto numberOfBlocksThatFit = lengthOfPathInPixelsOnCurrentZoom / blockWidthWithSpacing; // blocksToInstantiate = qMax(qFloor(numberOfBlocksThatFit), 1); // kOffsetToFirstNewBlockOnCurrentZoom = (numberOfBlocksThatFit - static_cast<int>(numberOfBlocksThatFit)) / 2.0f; //} //else //{ // // If only 1 block (with or without spacing) // blocksToInstantiate = 1; // kOffsetToFirstNewBlockOnCurrentZoom = (numberOfBlocksThatFit - static_cast<int>(numberOfBlocksThatFit)) / 2.0f; //} //kOffsetToFirstPresentBlockOnCurrentZoom = -1.0f; } else { blocksToInstantiate = 0; for (auto symbolIdx = 0; symbolIdx < symbolsCount; symbolIdx++) { const auto& symbolFullSize = symbolsFullSizesInPixels[symbolIdx]; if (fullSizeOfSymbolsThatFit + symbolFullSize > lengthOfPathInPixelsOnCurrentZoom) break; fullSizeOfSymbolsThatFit += symbolFullSize; numberOfSymbolsThatFit++; } // Actually offset to incomplete block kOffsetToFirstNewBlockOnCurrentZoom = ((lengthOfPathInPixelsOnCurrentZoom - fullSizeOfSymbolsThatFit) / blockWidthWithoutSpacing) / 2.0f; kOffsetToFirstPresentBlockOnCurrentZoom = -1.0f; } } else { kOffsetToFirstPresentBlockOnCurrentZoom = kOffsetToFirstBlockOnPrevZoom * 2.0f + 0.5f; // 0.5f represents shift of present instance due to x2 scale-up blocksToInstantiate = qRound((lengthOfPathInPixelsOnCurrentZoom / blockWidthWithoutSpacing) - 2.0f * kOffsetToFirstPresentBlockOnCurrentZoom) - totalNumberOfCompleteBlocks; assert(blocksToInstantiate >= 0); if (kOffsetToFirstPresentBlockOnCurrentZoom > 1.0f) { // Insert new block before present. This will add 2 extra blocks on both sides kOffsetToFirstNewBlockOnCurrentZoom = kOffsetToFirstPresentBlockOnCurrentZoom - 1.0f; blocksToInstantiate += 2; } else { // Insert new block after present kOffsetToFirstNewBlockOnCurrentZoom = kOffsetToFirstPresentBlockOnCurrentZoom + 1.0f; } if (blocksToInstantiate == 0) kOffsetToFirstNewBlockOnCurrentZoom = -1.0f; } const auto remainingPathLengthOnCurrentZoom = lengthOfPathInPixelsOnCurrentZoom - (totalNumberOfCompleteBlocks + blocksToInstantiate) * blockWidthWithoutSpacing; const auto offsetToFirstNewBlockInPixels = kOffsetToFirstNewBlockOnCurrentZoom * blockWidthWithoutSpacing; const auto eachNewBlockAfterFirstOffsetInPixels = (totalNumberOfCompleteBlocks > 0 ? 2.0f : 1.0f) * blockWidthWithoutSpacing; #if OSMAND_LOG_SYMBOLS_PIN_POINTS_COMPUTATION LogPrintf(LogSeverityLevel::Debug, "%d->%f/(%d of [%d;%d]):" "\n\ttotalNumberOfCompleteBlocks=%d" "\n\tremainingPathLengthOnPrevZoom=%f" "\n\tkOffsetToFirstBlockOnPrevZoom=%f" "\n\tblocksToInstantiate=%d" "\n\tkOffsetToFirstNewBlockOnCurrentZoom=%f" "\n\tkOffsetToFirstPresentBlockOnCurrentZoom=%f" "\n\tfullSizeOfSymbolsThatFit=%f" "\n\tnumberOfSymbolsThatFit=%d" "\n\tlengthOfPathInPixelsOnCurrentZoom=%f" "\n\tblockWidth=%f" /*"\n\t=%f"*/, symbolsForPinPointsComputation.size(), pathLength31, currentZoomLevel, minZoom, maxZoom, totalNumberOfCompleteBlocks, remainingPathLengthOnPrevZoom, kOffsetToFirstBlockOnPrevZoom, blocksToInstantiate, kOffsetToFirstNewBlockOnCurrentZoom, kOffsetToFirstPresentBlockOnCurrentZoom, fullSizeOfSymbolsThatFit, numberOfSymbolsThatFit, lengthOfPathInPixelsOnCurrentZoom, blockWidthWithoutSpacing); #endif // OSMAND_LOG_SYMBOLS_PIN_POINTS_COMPUTATION // Compute actual pin-points only for zoom levels less detained that needed, including needed if (currentZoomLevel <= neededZoom) { // In case at least 1 block fits, only complete blocks are being used. // Otherwise, plot only part of symbols (virtually, smaller block) if (blocksToInstantiate > 0) { QList<ComputedPinPoint> computedPinPoints; unsigned int scanOriginPathPointIndex = basePathPointIndex; float scanOriginPathPointOffsetInPixels = globalPaddingInPixelsFromBasePathPointOnCurrentZoom; for (auto blockIdx = 0; blockIdx < blocksToInstantiate; blockIdx++) { // Compute base pin-point of block. Actually blocks get pinned, // symbols inside block just receive offset from base pin-point ComputedPinPoint computedBlockPinPoint; unsigned int nextScanOriginPathPointIndex; float nextScanOriginPathPointOffsetInPixels; bool fits = computeBlockPinPoint( pathSegmentsLengthInPixelsOnCurrentZoom, lengthOfPathInPixelsOnCurrentZoom, pathSegmentsLength31, path31, blockWidthWithoutSpacing, offsetToFirstNewBlockInPixels + blockIdx * eachNewBlockAfterFirstOffsetInPixels, scanOriginPathPointIndex, scanOriginPathPointOffsetInPixels, nextScanOriginPathPointIndex, nextScanOriginPathPointOffsetInPixels, computedBlockPinPoint); if (!fits) { assert(false); break; } scanOriginPathPointIndex = nextScanOriginPathPointIndex; scanOriginPathPointOffsetInPixels = nextScanOriginPathPointOffsetInPixels; float symbolPinPointOffset = -blockWidthWithoutSpacing / 2.0f; for (auto symbolIdx = 0u; symbolIdx < symbolsCount; symbolIdx++) { const auto& symbol = symbolsForPinPointsComputation[symbolIdx]; const auto offsetFromBlockPinPointInPixelOnCurrentZoom = symbolPinPointOffset + symbol.leftPaddingInPixels + symbol.widthInPixels / 2.0f; const auto zoomShiftScaleFactor = qPow(2.0f, neededZoom - currentZoomLevel); const auto offsetFromBlockPinPointInPixelOnNeededZoom = offsetFromBlockPinPointInPixelOnCurrentZoom * zoomShiftScaleFactor; symbolPinPointOffset += symbolsFullSizesInPixels[symbolIdx]; ComputedPinPoint computedSymbolPinPoint; fits = computeSymbolPinPoint( pathSegmentsLengthInPixelsOnCurrentZoom, lengthOfPathInPixelsOnCurrentZoom, pathSegmentsLength31, path31, computedBlockPinPoint, zoomShiftScaleFactor, offsetFromBlockPinPointInPixelOnNeededZoom, computedSymbolPinPoint); if (!fits) { assert(false); continue; } computedPinPoints.push_back(qMove(computedSymbolPinPoint)); } } computedPinPointsByLayer.push_front(qMove(computedPinPoints)); } else if (numberOfSymbolsThatFit > 0) { QList<ComputedPinPoint> computedPinPoints; unsigned int scanOriginPathPointIndex = basePathPointIndex; float scanOriginPathPointOffsetInPixels = globalPaddingInPixelsFromBasePathPointOnCurrentZoom; // Compute base pin-point of this virtual block. Actually blocks get pinned, // symbols inside block just receive offset from base pin-point ComputedPinPoint computedBlockPinPoint; unsigned int nextScanOriginPathPointIndex; float nextScanOriginPathPointOffsetInPixels; bool fits = computeBlockPinPoint( pathSegmentsLengthInPixelsOnCurrentZoom, lengthOfPathInPixelsOnCurrentZoom, pathSegmentsLength31, path31, fullSizeOfSymbolsThatFit, offsetToFirstNewBlockInPixels, scanOriginPathPointIndex, scanOriginPathPointOffsetInPixels, nextScanOriginPathPointIndex, nextScanOriginPathPointOffsetInPixels, computedBlockPinPoint); if (!fits) { assert(false); break; } scanOriginPathPointIndex = nextScanOriginPathPointIndex; scanOriginPathPointOffsetInPixels = nextScanOriginPathPointOffsetInPixels; float symbolPinPointOffset = -fullSizeOfSymbolsThatFit / 2.0f; for (auto symbolIdx = 0u; symbolIdx < numberOfSymbolsThatFit; symbolIdx++) { const auto& symbol = symbolsForPinPointsComputation[symbolIdx]; const auto offsetFromBlockPinPointInPixelOnCurrentZoom = symbolPinPointOffset + symbol.leftPaddingInPixels + symbol.widthInPixels / 2.0f; const auto zoomShiftScaleFactor = qPow(2.0f, neededZoom - currentZoomLevel); const auto offsetFromBlockPinPointInPixelOnNeededZoom = offsetFromBlockPinPointInPixelOnCurrentZoom * zoomShiftScaleFactor; symbolPinPointOffset += symbolsFullSizesInPixels[symbolIdx]; ComputedPinPoint computedSymbolPinPoint; fits = computeSymbolPinPoint( pathSegmentsLengthInPixelsOnCurrentZoom, lengthOfPathInPixelsOnCurrentZoom, pathSegmentsLength31, path31, computedBlockPinPoint, zoomShiftScaleFactor, offsetFromBlockPinPointInPixelOnNeededZoom, computedSymbolPinPoint); if (!fits) { assert(false); continue; } computedPinPoints.push_back(qMove(computedSymbolPinPoint)); } computedPinPointsByLayer.push_back(qMove(computedPinPoints)); } } // Move to next zoom level lengthOfPathInPixelsOnCurrentZoom *= 2.0f; for (auto& pathSegmentLengthInPixelsOnCurrentZoom : pathSegmentsLengthInPixelsOnCurrentZoom) pathSegmentLengthInPixelsOnCurrentZoom *= 2.0f; globalPaddingInPixelsFromBasePathPointOnCurrentZoom *= 2.0f; remainingPathLengthOnPrevZoom = remainingPathLengthOnCurrentZoom; if (blocksToInstantiate > 0 || totalNumberOfCompleteBlocks > 0) { if (blocksToInstantiate > 0 && totalNumberOfCompleteBlocks > 0) { // In case new blocks added and there was previous blocks, use block offset closest to start kOffsetToFirstBlockOnPrevZoom = qMin(kOffsetToFirstNewBlockOnCurrentZoom, kOffsetToFirstPresentBlockOnCurrentZoom); } else if (blocksToInstantiate > 0 && totalNumberOfCompleteBlocks == 0) { // In case blocks were added and they are first ones, use first new block offset kOffsetToFirstBlockOnPrevZoom = kOffsetToFirstNewBlockOnCurrentZoom; } else if (blocksToInstantiate == 0 && totalNumberOfCompleteBlocks > 0) { // In case no blocks were added, but there was previously blocks, use offset to first present block kOffsetToFirstBlockOnPrevZoom = kOffsetToFirstPresentBlockOnCurrentZoom; } } totalNumberOfCompleteBlocks += blocksToInstantiate; } return computedPinPointsByLayer; }