diff --git a/CHANGELOG.md b/CHANGELOG.md index 62bfc2009..a18fadae7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,18 @@ # Changelog for Open Mobile Maps -## Version 2.0.0 (16.02.2023) +## Version 2.0.1 (07.03.2024) +- Fixed WGS84 bounding box parsing for WMTS layers +- Fix deadlock in renderToImage on iOS +- Fix crash for geojsons without type +- Fix renderToImage for vector layers +- Reference djinni by version on iOS +- Fix crash on certain polygon patters on iOS +- Fix constant loading for some geojsons +- Fix crash of symbol objects +- Fix crash from race condition in geojson reload +- Fix deallocation on OffscreenMapRenderer on Android + +## Version 2.0.0 (16.02.2024) - Major improvements in vector map performance, stability, and rendering, alongside expanded options for style specifications and GeoJSON support. - Enhanced stability and performance for raster, icon, polygon, and line layers. - Many additional improvements and bug fixes. diff --git a/Package.swift b/Package.swift index 81dae954e..dfd9b3c20 100644 --- a/Package.swift +++ b/Package.swift @@ -23,7 +23,7 @@ let package = Package( ), ], dependencies: [ - .package(url: "https://github.com/UbiqueInnovation/djinni.git", branch: "master"), + .package(url: "https://github.com/UbiqueInnovation/djinni.git", .upToNextMajor(from: "1.0.6")), ], targets: [ .target( diff --git a/android/gradle.properties b/android/gradle.properties index d2f43f957..a1d13adfa 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -31,8 +31,8 @@ SNAPSHOT_REPOSITORY_URL=https://oss.sonatype.org/content/repositories/snapshots/ GROUP=io.openmobilemaps POM_ARTIFACT_ID=mapscore -VERSION_NAME=2.0.0 -VERSION_CODE=2000000 +VERSION_NAME=2.0.1 +VERSION_CODE=2000100 POM_NAME=mapscore POM_PACKAGING=aar diff --git a/android/src/main/java/io/openmobilemaps/mapscore/map/util/OffscreenMapRenderer.kt b/android/src/main/java/io/openmobilemaps/mapscore/map/util/OffscreenMapRenderer.kt index bd581f4ab..3e66caa2b 100644 --- a/android/src/main/java/io/openmobilemaps/mapscore/map/util/OffscreenMapRenderer.kt +++ b/android/src/main/java/io/openmobilemaps/mapscore/map/util/OffscreenMapRenderer.kt @@ -2,7 +2,6 @@ package io.openmobilemaps.mapscore.map.util import android.opengl.GLSurfaceView import io.openmobilemaps.mapscore.graphics.GLThread -import io.openmobilemaps.mapscore.map.scheduling.AndroidScheduler import io.openmobilemaps.mapscore.map.scheduling.AndroidSchedulerCallback import io.openmobilemaps.mapscore.shared.graphics.common.Color import io.openmobilemaps.mapscore.shared.graphics.common.Vec2I @@ -56,6 +55,7 @@ open class OffscreenMapRenderer(val sizePx: Vec2I, val density: Float = 72f) : G protected open fun onGlThreadFinishing() { glThread.renderer = null + mapInterface?.destroy() mapInterface = null } diff --git a/ios/graphics/Model/Polygon/PolygonPatternGroup2d.swift b/ios/graphics/Model/Polygon/PolygonPatternGroup2d.swift index 9dc7b5a64..0aabfbece 100644 --- a/ios/graphics/Model/Polygon/PolygonPatternGroup2d.swift +++ b/ios/graphics/Model/Polygon/PolygonPatternGroup2d.swift @@ -109,6 +109,7 @@ final class PolygonPatternGroup2d: BaseGraphicsObject { } else { var scaleFactors = customScreenPixelFactor.x != 0 ? customScreenPixelFactor : SIMD2([pixelFactor, pixelFactor]) encoder.setVertexBytes(&scaleFactors, length: MemoryLayout>.stride, index: 2) + encoder.setVertexBytes(&posOffset, length: MemoryLayout>.stride, index: 3) } // texture diff --git a/ios/graphics/Shader/Metal/PolygonGroupShader.metal b/ios/graphics/Shader/Metal/PolygonGroupShader.metal index cabf600f7..c1e039baa 100644 --- a/ios/graphics/Shader/Metal/PolygonGroupShader.metal +++ b/ios/graphics/Shader/Metal/PolygonGroupShader.metal @@ -150,7 +150,7 @@ polygonPatternGroupFadeInFragmentShader(PolygonPatternGroupVertexOut in [[stage_ const float scalingFactorFactor = (scalingFactor.x / screenPixelAsRealMeterFactor) - 1.0; const float2 spacing = pixelSize * scalingFactorFactor; const float2 totalSize = pixelSize + spacing; - const float2 adjustedPixelPosition = in.pixelPosition + totalSize * 0.5; // in other project pixelSize + const float2 adjustedPixelPosition = in.pixelPosition + pixelSize * 0.5; // in other project pixelSize float2 uvTot = fmod(adjustedPixelPosition, totalSize); const int yIndex = int(adjustedPixelPosition.y / totalSize.y) % 2; diff --git a/ios/maps/MCMapView.swift b/ios/maps/MCMapView.swift index ca27929a1..8559b65ff 100644 --- a/ios/maps/MCMapView.swift +++ b/ios/maps/MCMapView.swift @@ -190,15 +190,18 @@ extension MCMapView: MTKViewDelegate { } } - public func renderToImage(size: CGSize, timeout: Float, bounds: MCRectCoord, callback: @escaping (UIImage?, MCLayerReadyState) -> Void) { + public func renderToImage(size: CGSize, timeout: Float, bounds: MCRectCoord, callbackQueue: DispatchQueue = .main, callback: @escaping (UIImage?, MCLayerReadyState) -> Void) { renderToImageQueue.async { - self.frame = CGRect(origin: .zero, size: .init(width: size.width / UIScreen.main.scale, height: size.height / UIScreen.main.scale)) - self.setNeedsLayout() - self.layoutIfNeeded() + DispatchQueue.main.sync { + self.frame = CGRect(origin: .zero, size: .init(width: size.width / UIScreen.main.scale, height: size.height / UIScreen.main.scale)) + self.setNeedsLayout() + self.layoutIfNeeded() + } let mapReadyCallbacks = MCMapViewMapReadyCallbacks() mapReadyCallbacks.delegate = self mapReadyCallbacks.callback = callback + mapReadyCallbacks.callbackQueue = callbackQueue self.mapInterface.drawReadyFrame(bounds, timeout: timeout, callbacks: mapReadyCallbacks) } @@ -319,13 +322,17 @@ public extension MCMapView { private class MCMapViewMapReadyCallbacks: MCMapReadyCallbackInterface { public weak var delegate: MCMapView? public var callback: ((UIImage?, MCLayerReadyState) -> Void)? + public var callbackQueue: DispatchQueue? + public let semaphore = DispatchSemaphore(value: 1) func stateDidUpdate(_ state: MCLayerReadyState) { guard let delegate = self.delegate else { return } + semaphore.wait() + delegate.draw(in: delegate) - DispatchQueue.main.async { + callbackQueue?.async { switch state { case .NOT_READY: break @@ -336,6 +343,7 @@ private class MCMapViewMapReadyCallbacks: MCMapReadyCallbackInterface { @unknown default: break } + self.semaphore.signal() } } } diff --git a/shared/public/GeoJsonTypes.h b/shared/public/GeoJsonTypes.h index 2fa8050dc..aebf4f4f4 100644 --- a/shared/public/GeoJsonTypes.h +++ b/shared/public/GeoJsonTypes.h @@ -67,6 +67,7 @@ class GeoJSONTileInterface { class GeoJSONTileDelegate { public: virtual void didLoad(uint8_t maxZoom) = 0; + virtual void failedToLoad() = 0; }; class GeoJson { diff --git a/shared/public/Tiled2dMapRasterSource.h b/shared/public/Tiled2dMapRasterSource.h index ea0676e37..f1030fa8c 100644 --- a/shared/public/Tiled2dMapRasterSource.h +++ b/shared/public/Tiled2dMapRasterSource.h @@ -40,8 +40,8 @@ class Tiled2dMapRasterSource virtual bool hasExpensivePostLoadingTask() override; - virtual std::shared_ptr<::TextureHolderInterface> postLoadingTask(const std::shared_ptr &loadedData, - const Tiled2dMapTileInfo &tile) override; + virtual std::shared_ptr<::TextureHolderInterface> postLoadingTask(std::shared_ptr loadedData, + Tiled2dMapTileInfo tile) override; private: diff --git a/shared/public/Tiled2dMapSource.h b/shared/public/Tiled2dMapSource.h index b5ef4f085..6b37b8614 100644 --- a/shared/public/Tiled2dMapSource.h +++ b/shared/public/Tiled2dMapSource.h @@ -125,7 +125,7 @@ class Tiled2dMapSource : protected: virtual bool hasExpensivePostLoadingTask() = 0; - virtual R postLoadingTask(const L &loadedData, const Tiled2dMapTileInfo &tile) = 0; + virtual R postLoadingTask(L loadedData, Tiled2dMapTileInfo tile) = 0; MapConfig mapConfig; std::shared_ptr layerConfig; diff --git a/shared/public/Tiled2dMapSourceImpl.h b/shared/public/Tiled2dMapSourceImpl.h index 37c2972bd..ab7c09e49 100644 --- a/shared/public/Tiled2dMapSourceImpl.h +++ b/shared/public/Tiled2dMapSourceImpl.h @@ -126,9 +126,7 @@ void Tiled2dMapSource::onVisibleBoundsChanged(const ::RectCoord &visibl const double visibleBottom = visibleBoundsLayer.bottomRight.y - signHeight * viewboundsPadding; visibleHeight = std::abs(visibleHeight) + 2 * viewboundsPadding; - - - size_t visibleTileHash = 0; + size_t visibleTileHash = targetZoomLevelIdentifier; for (int i = startZoomLayer; i <= endZoomLevel; i++) { const Tiled2dMapZoomLevelInfo &zoomLevelInfo = zoomLevelInfos.at(i); @@ -396,10 +394,10 @@ void Tiled2dMapSource::performLoadingTask(Tiled2dMapTileInfo tile, size auto weakActor = WeakActor(mailbox, std::static_pointer_cast(shared_from_this())); currentlyLoading.insert({tile, loaderIndex}); + std::string layerName = layerConfig->getLayerName(); readyTiles.erase(tile); - loadDataAsync(tile, loaderIndex).then([weakActor, loaderIndex, tile, weakSelfPtr](::djinni::Future result) { - + loadDataAsync(tile, loaderIndex).then([weakActor, loaderIndex, tile, weakSelfPtr, layerName](::djinni::Future result) { auto strongSelf = weakSelfPtr.lock(); if (strongSelf) { auto res = result.get(); @@ -430,7 +428,7 @@ void Tiled2dMapSource::performLoadingTask(Tiled2dMapTileInfo tile, size template void Tiled2dMapSource::didLoad(Tiled2dMapTileInfo tile, size_t loaderIndex, const R &result) { currentlyLoading.erase(tile); - + std::string layerName = layerConfig->getLayerName(); const bool isVisible = currentVisibleTiles.count(tile); if (!isVisible) { errorTiles[loaderIndex].erase(tile); diff --git a/shared/public/Tiled2dMapVectorSource.h b/shared/public/Tiled2dMapVectorSource.h index 3dccd3eea..0493f08dc 100644 --- a/shared/public/Tiled2dMapVectorSource.h +++ b/shared/public/Tiled2dMapVectorSource.h @@ -52,7 +52,7 @@ class Tiled2dMapVectorSource : public Tiled2dMapSource &loadedData, const Tiled2dMapTileInfo &tile) override; + virtual Tiled2dMapVectorTileInfo::FeatureMap postLoadingTask(std::shared_ptr loadedData, Tiled2dMapTileInfo tile) override; const std::vector> loaders; const std::unordered_set layersToDecode; diff --git a/shared/public/Tiled2dMapVectorStyleParser.h b/shared/public/Tiled2dMapVectorStyleParser.h index 012285edf..44355a65a 100644 --- a/shared/public/Tiled2dMapVectorStyleParser.h +++ b/shared/public/Tiled2dMapVectorStyleParser.h @@ -377,9 +377,6 @@ class Tiled2dMapVectorStyleParser { }; ValueVariant getVariant(const nlohmann::json &json) { - if (json == "–") { - LogDebug <<= "break"; - } if (json.is_number_float()) { return json.get(); } else if (json.is_number_integer()) { diff --git a/shared/public/VectorTilePolygonHandler.h b/shared/public/VectorTilePolygonHandler.h index 284938c0e..501c32ab1 100644 --- a/shared/public/VectorTilePolygonHandler.h +++ b/shared/public/VectorTilePolygonHandler.h @@ -23,9 +23,6 @@ class VectorTilePolygonHandler { void ring_begin(uint32_t count) { currentRing = std::vector<::Coord>(); currentRing.reserve(count); - /*LogDebug <<= " new Polygon with TopLeft: (" + tileCoords.topLeft.systemIdentifier + ": " + std::to_string(tileCoords.topLeft.x) + ", " + std::to_string(tileCoords.topLeft.y) + ")"; - LogDebug <<= " BottomRight: (" + tileCoords.bottomRight.systemIdentifier + ": " + std::to_string(tileCoords.bottomRight.x) + ", " + std::to_string(tileCoords.bottomRight.y) + ")"; - LogDebug <<= " and width/height: (" + std::to_string(tileWidth) + ", " + std::to_string(tileHeight) + ")";*/ } void ring_point(vtzero::point point) noexcept { @@ -33,8 +30,6 @@ class VectorTilePolygonHandler { double y = tileCoords.topLeft.y + tileHeight * (point.y / extent); Coord newCoord = Coord(tileCoords.topLeft.systemIdentifier, x, y, 0.0); currentRing.push_back(newCoord); - /*LogDebug <<= " -> new point: (" + std::to_string(point.x) + ", " + std::to_string(point.y) + ") in extent " + std::to_string(extent); - LogDebug <<= " -> coord (" + newCoord.systemIdentifier + ": " + std::to_string(newCoord.x) + ", " + std::to_string(newCoord.y) + ")";*/ } void ring_end(vtzero::ring_type ringType) noexcept { diff --git a/shared/src/MapsCoreSharedModule.cpp b/shared/src/MapsCoreSharedModule.cpp index de532cd72..c900b4b1f 100644 --- a/shared/src/MapsCoreSharedModule.cpp +++ b/shared/src/MapsCoreSharedModule.cpp @@ -10,4 +10,4 @@ #include "MapsCoreSharedModule.h" -std::string MapsCoreSharedModule::version() { return "2.0.0"; } +std::string MapsCoreSharedModule::version() { return "2.0.1"; } diff --git a/shared/src/map/camera/MapCamera2d.cpp b/shared/src/map/camera/MapCamera2d.cpp index 6b1d29061..a2ce198e8 100644 --- a/shared/src/map/camera/MapCamera2d.cpp +++ b/shared/src/map/camera/MapCamera2d.cpp @@ -216,7 +216,10 @@ void MapCamera2d::setZoom(double zoom, bool animated) { zoomAnimation->start(); mapInterface->invalidate(); } else { - this->zoom = targetZoom; + const auto [adjPosition, adjZoom] = + getBoundsCorrectedCoords(adjustCoordForPadding(centerPosition, targetZoom), targetZoom); + centerPosition = adjPosition; + zoom = adjZoom; notifyListeners(ListenerType::BOUNDS); mapInterface->invalidate(); } @@ -251,8 +254,10 @@ void MapCamera2d::setRotation(float angle, bool animated) { Coord realCenter = getCenterPosition(); Vec2D rotatedDiff = Vec2DHelper::rotate(Vec2D(centerScreen.x - realCenter.x, centerScreen.y - realCenter.y), Vec2D(0.0, 0.0), angleDiff); - centerPosition.x = realCenter.x + rotatedDiff.x; - centerPosition.y = realCenter.y + rotatedDiff.y; + const auto [adjPosition, adjZoom] = + getBoundsCorrectedCoords(adjustCoordForPadding(Coord(mapCoordinateSystem.identifier, realCenter.x + rotatedDiff.x, realCenter.y + rotatedDiff.y, centerPosition.z), zoom), zoom); + centerPosition = adjPosition; + zoom = adjZoom; this->angle = newAngle; notifyListeners(ListenerType::ROTATION | ListenerType::BOUNDS); @@ -666,9 +671,9 @@ bool MapCamera2d::onTwoFingerMove(const std::vector<::Vec2F> &posScreenOld, cons double scaleFactor = Vec2FHelper::distance(posScreenNew[0], posScreenNew[1]) / Vec2FHelper::distance(posScreenOld[0], posScreenOld[1]); - zoom /= scaleFactor; + double newZoom = zoom / scaleFactor; - zoom = std::clamp(zoom, zoomMax, zoomMin); + newZoom = std::clamp(newZoom, zoomMax, zoomMin); if (zoom > startZoom * ROTATION_LOCKING_FACTOR || zoom < startZoom / ROTATION_LOCKING_FACTOR) { rotationPossible = false; @@ -691,8 +696,10 @@ bool MapCamera2d::onTwoFingerMove(const std::vector<::Vec2F> &posScreenOld, cons double diffCenterX = -leftDiff * zoom * screenPixelAsRealMeterFactor; double diffCenterY = topDiff * zoom * screenPixelAsRealMeterFactor; - centerPosition.x += diffCenterX; - centerPosition.y += diffCenterY; + Coord newPos = Coord(centerPosition.systemIdentifier, + centerPosition.x + diffCenterX, + centerPosition.y + diffCenterY, + centerPosition.z); if (config.rotationEnabled) { float olda = atan2(posScreenOld[0].x - posScreenOld[1].x, posScreenOld[0].y - posScreenOld[1].y); @@ -707,8 +714,8 @@ bool MapCamera2d::onTwoFingerMove(const std::vector<::Vec2F> &posScreenOld, cons (centerScreen.x - midpoint.x) * sin(newa - olda) + midpoint.y - centerScreen.y; double rotDiffX = (cosAngle * centerXDiff - sinAngle * centerYDiff); double rotDiffY = (cosAngle * centerYDiff + sinAngle * centerXDiff); - centerPosition.x += rotDiffX * zoom * screenPixelAsRealMeterFactor; - centerPosition.y += rotDiffY * zoom * screenPixelAsRealMeterFactor; + newPos.x += rotDiffX * zoom * screenPixelAsRealMeterFactor; + newPos.y += rotDiffY * zoom * screenPixelAsRealMeterFactor; listenerType |= ListenerType::ROTATION; } else { @@ -720,9 +727,9 @@ bool MapCamera2d::onTwoFingerMove(const std::vector<::Vec2F> &posScreenOld, cons } } - auto mapConfig = mapInterface->getMapConfig(); - - clampCenterToPaddingCorrectedBounds(); + const auto [adjPosition, adjZoom] = getBoundsCorrectedCoords(newPos, newZoom); + centerPosition = adjPosition; + zoom = adjZoom; notifyListeners(listenerType); mapInterface->invalidate(); @@ -851,19 +858,27 @@ bool MapCamera2d::isInBounds(const Coord &coords) { std::tuple MapCamera2d::getBoundsCorrectedCoords(const Coord &position, double zoom) { auto const &paddingCorrectedBounds = getPaddingCorrectedBounds(); + auto const clampedPosition = [&paddingCorrectedBounds](const Coord& position) -> Coord { + return Coord(position.systemIdentifier, + std::clamp(position.x, + std::min(paddingCorrectedBounds.topLeft.x, paddingCorrectedBounds.bottomRight.x), + std::max(paddingCorrectedBounds.topLeft.x, paddingCorrectedBounds.bottomRight.x)), + std::clamp(position.y, + std::min(paddingCorrectedBounds.topLeft.y, paddingCorrectedBounds.bottomRight.y), + std::max(paddingCorrectedBounds.topLeft.y, paddingCorrectedBounds.bottomRight.y)), + position.z); + }; + if (!config.boundsRestrictWholeVisibleRect) { - Coord newPosition = Coord(position.systemIdentifier, - std::clamp(position.x, - std::min(paddingCorrectedBounds.topLeft.x, paddingCorrectedBounds.bottomRight.x), - std::max(paddingCorrectedBounds.topLeft.x, paddingCorrectedBounds.bottomRight.x)), - std::clamp(position.y, - std::min(paddingCorrectedBounds.topLeft.y, paddingCorrectedBounds.bottomRight.y), - std::max(paddingCorrectedBounds.topLeft.y, paddingCorrectedBounds.bottomRight.y)), - position.z); - return {newPosition, zoom}; + return {clampedPosition(position), zoom}; } else { Vec2I sizeViewport = mapInterface->getRenderingContext()->getViewportSize(); + // fallback if the viewport size is not set yet + if (sizeViewport.x == 0 && sizeViewport.y == 0) { + return {clampedPosition(position), zoom}; + } + double zoomFactor = screenPixelAsRealMeterFactor * zoom; double halfWidth = sizeViewport.x * 0.5 * zoomFactor; @@ -872,28 +887,59 @@ std::tuple MapCamera2d::getBoundsCorrectedCoords(const Coord &pos double sinAngle = sin(angle * M_PI / 180.0); double cosAngle = cos(angle * M_PI / 180.0); - double deltaX = std::abs(halfWidth * cosAngle) + std::abs(halfHeight * sinAngle); - double deltaY = std::abs(halfWidth * sinAngle) + std::abs(halfHeight * cosAngle); - - double diffLeft = deltaX - (position.x - paddingCorrectedBounds.topLeft.x); - double diffRight = deltaX - (paddingCorrectedBounds.bottomRight.x - position.x); - double targetScaleDiffFactorX = std::min((paddingCorrectedBounds.bottomRight.x - paddingCorrectedBounds.topLeft.x) / (2.0 * deltaX), 1.0); - - double diffTop = deltaY - (paddingCorrectedBounds.topLeft.y - position.y); - double diffBottom = deltaY - (position.y - paddingCorrectedBounds.bottomRight.y); - double targetScaleDiffFactorY = std::min((paddingCorrectedBounds.topLeft.y - paddingCorrectedBounds.bottomRight.y) / (2.0 * deltaY), 1.0); + double negSinAngle = sin(-angle * M_PI / 180.0); + double negCosAngle = cos(-angle * M_PI / 180.0); + + double boundsHalfWidth = std::abs(paddingCorrectedBounds.bottomRight.x - paddingCorrectedBounds.topLeft.x) * 0.5; + double boundsHalfHeight = std::abs(paddingCorrectedBounds.topLeft.y - paddingCorrectedBounds.bottomRight.y) * 0.5; + double boundsDeltaXVp = std::abs(boundsHalfWidth * negCosAngle) + std::abs(boundsHalfHeight * negSinAngle); + double boundsDeltaYVp = std::abs(boundsHalfWidth * negSinAngle) + std::abs(boundsHalfHeight * negCosAngle); + + double targetScaleDiffFactorX = boundsDeltaXVp / halfWidth; + double targetScaleDiffFactorY = boundsDeltaYVp / halfHeight; + double targetScaleDiffFactor = std::min(std::max(targetScaleDiffFactorX, targetScaleDiffFactorY), 1.0); + + double centerBBoxX = (paddingCorrectedBounds.topLeft.x + (paddingCorrectedBounds.bottomRight.x - paddingCorrectedBounds.topLeft.x) * 0.5); + double centerBBoxY = (paddingCorrectedBounds.topLeft.y + (paddingCorrectedBounds.bottomRight.y - paddingCorrectedBounds.topLeft.y) * 0.5); + double centerDiffsX = centerBBoxX - position.x; + double centerDiffsY = centerBBoxY - position.y; + double dotHor = centerDiffsX * cosAngle + centerDiffsY * sinAngle; + double centerDiffHorX = dotHor * cosAngle; + double centerDiffHorY = dotHor * sinAngle; + double dotVert = centerDiffsX * -sinAngle + centerDiffsY * cosAngle; + double centerDiffVertX = -(dotVert * sinAngle); + double centerDiffVertY = dotVert * cosAngle; + + double positionVpX = negCosAngle * position.x - negSinAngle * position.y; + double positionVpY = negSinAngle * position.x + negCosAngle * position.y; + double centerBBoxVpX = negCosAngle * centerBBoxX - negSinAngle * centerBBoxY; + double centerBBoxVpY = negSinAngle * centerBBoxX + negCosAngle * centerBBoxY; + double diffLeftVp = (centerBBoxVpX - boundsDeltaXVp) - (positionVpX - halfWidth); + double diffRightVp = (positionVpX + halfWidth) - (centerBBoxVpX + boundsDeltaXVp); + double diffTopVp = (positionVpY + halfHeight) - (centerBBoxVpY + boundsDeltaYVp); + double diffBottomVp = (centerBBoxVpY - boundsDeltaYVp) - (positionVpY - halfHeight); + double shiftRightVp = std::max(0.0, diffLeftVp) - std::max(0.0, diffRightVp); + double shiftTopVp = std::max(0.0, diffBottomVp) - std::max(0.0, diffTopVp); + + double shiftRightX = cosAngle * shiftRightVp; + double shiftRightY = sinAngle * shiftRightVp; + double shiftTopX = -(sinAngle * shiftTopVp); + double shiftTopY = cosAngle * shiftTopVp; - double targetScaleDiffFactor = std::min(targetScaleDiffFactorX, targetScaleDiffFactorY); Coord newPosition = position; - if (targetScaleDiffFactorX < 1.0) { - newPosition.x = paddingCorrectedBounds.topLeft.x + (paddingCorrectedBounds.bottomRight.x - paddingCorrectedBounds.topLeft.x) / 2.0; + if (targetScaleDiffFactorX <= 1.0) { + newPosition.x += centerDiffHorX; + newPosition.y += centerDiffHorY; } else { - newPosition.x += (std::max(0.0, diffLeft) - std::max(0.0, diffRight)) * targetScaleDiffFactor; + newPosition.x += shiftRightX * targetScaleDiffFactor; + newPosition.y += shiftRightY * targetScaleDiffFactor; } - if (targetScaleDiffFactorY < 1.0) { - newPosition.y = paddingCorrectedBounds.bottomRight.y + (paddingCorrectedBounds.topLeft.y - paddingCorrectedBounds.bottomRight.y) / 2.0; + if (targetScaleDiffFactorY <= 1.0) { + newPosition.x += centerDiffVertX; + newPosition.y += centerDiffVertY; } else { - newPosition.y += (std::max(0.0, diffBottom) - std::max(0.0, diffTop)) * targetScaleDiffFactor; + newPosition.x += shiftTopX * targetScaleDiffFactor; + newPosition.y += shiftTopY * targetScaleDiffFactor; } double newZoom = zoom * targetScaleDiffFactor; diff --git a/shared/src/map/layers/tiled/raster/Tiled2dMapRasterSource.cpp b/shared/src/map/layers/tiled/raster/Tiled2dMapRasterSource.cpp index 066e83861..a96fae1d6 100644 --- a/shared/src/map/layers/tiled/raster/Tiled2dMapRasterSource.cpp +++ b/shared/src/map/layers/tiled/raster/Tiled2dMapRasterSource.cpp @@ -45,8 +45,8 @@ bool Tiled2dMapRasterSource::hasExpensivePostLoadingTask() { return false; } -std::shared_ptr<::TextureHolderInterface> Tiled2dMapRasterSource::postLoadingTask(const std::shared_ptr &loadedData, - const Tiled2dMapTileInfo &tile) { +std::shared_ptr<::TextureHolderInterface> Tiled2dMapRasterSource::postLoadingTask(std::shared_ptr loadedData, + Tiled2dMapTileInfo tile) { return loadedData->data; } diff --git a/shared/src/map/layers/tiled/vector/Tiled2dMapVectorLayer.cpp b/shared/src/map/layers/tiled/vector/Tiled2dMapVectorLayer.cpp index 068e3b6f9..e7c0d60ab 100644 --- a/shared/src/map/layers/tiled/vector/Tiled2dMapVectorLayer.cpp +++ b/shared/src/map/layers/tiled/vector/Tiled2dMapVectorLayer.cpp @@ -340,6 +340,12 @@ void Tiled2dMapVectorLayer::initializeVectorLayer() { auto layerConfig = layerConfigs[source]; + + if (!layerConfig) { + LogError << "Missing layer config for " << source <<= ", layer will be ignored."; + continue; + } + auto sourceMailbox = std::make_shared(mapInterface->getScheduler()); Actor vectorSource; @@ -482,15 +488,16 @@ void Tiled2dMapVectorLayer::initializeVectorLayer() { } void Tiled2dMapVectorLayer::reloadDataSource(const std::string &sourceName) { - if (const auto &geoSource = mapDescription->geoJsonSources[sourceName]) { - geoSource->reload(loaders); - auto promise = std::make_shared<::djinni::Promise>>(); - geoSource->waitIfNotLoaded(promise); - promise->getFuture().wait(); - } + auto &source = vectorTileSources[sourceName]; + const auto &geoSource = mapDescription->geoJsonSources[sourceName]; + if (source && geoSource) { + source.syncAccess([&,geoSource](const auto &source) { + + geoSource->reload(loaders); + auto promise = std::make_shared<::djinni::Promise>>(); + geoSource->waitIfNotLoaded(promise); + promise->getFuture().wait(); - if (auto &source = vectorTileSources[sourceName]) { - source.syncAccess([](const auto &source) { source->reloadTiles(); }); } @@ -632,10 +639,11 @@ void Tiled2dMapVectorLayer::pregenerateRenderPasses() { if (description->renderObjects.empty()) { continue; } - if (description->maskingObject != lastMask && !renderObjects.empty()) { - newPasses.emplace_back(std::make_shared(RenderPassConfig(description->renderPassIndex, false), renderObjects, lastMask)); + if ((description->renderPassIndex != lastRenderPassIndex || description->maskingObject != lastMask) && !renderObjects.empty()) { + newPasses.emplace_back(std::make_shared(RenderPassConfig(lastRenderPassIndex, false), renderObjects, lastMask)); renderObjects.clear(); lastMask = nullptr; + lastRenderPassIndex = 0; } if (description->isModifyingMask || description->selfMasked) { diff --git a/shared/src/map/layers/tiled/vector/Tiled2dMapVectorSource.cpp b/shared/src/map/layers/tiled/vector/Tiled2dMapVectorSource.cpp index 9ab3972c0..50a9c200d 100644 --- a/shared/src/map/layers/tiled/vector/Tiled2dMapVectorSource.cpp +++ b/shared/src/map/layers/tiled/vector/Tiled2dMapVectorSource.cpp @@ -45,8 +45,15 @@ bool Tiled2dMapVectorSource::hasExpensivePostLoadingTask() { return true; } -Tiled2dMapVectorTileInfo::FeatureMap Tiled2dMapVectorSource::postLoadingTask(const std::shared_ptr &loadedData, const Tiled2dMapTileInfo &tile) { +Tiled2dMapVectorTileInfo::FeatureMap Tiled2dMapVectorSource::postLoadingTask(std::shared_ptr loadedData, Tiled2dMapTileInfo tile) { auto layerFeatureMap = std::make_shared>>>(); + + if (!loadedData->data.has_value()) { + LogError <<= "postLoadingTask, but data has no value for " + layerConfig->getLayerName() + ": " + std::to_string(tile.zoomIdentifier) + "/" + + std::to_string(tile.x) + "/" + std::to_string(tile.y); + return layerFeatureMap; + } + try { vtzero::vector_tile tileData((char*)loadedData->data->buf(), loadedData->data->len()); diff --git a/shared/src/map/layers/tiled/vector/geojson/GeoJsonParser.h b/shared/src/map/layers/tiled/vector/geojson/GeoJsonParser.h index 51c0b8def..fc7ff87ef 100644 --- a/shared/src/map/layers/tiled/vector/geojson/GeoJsonParser.h +++ b/shared/src/map/layers/tiled/vector/geojson/GeoJsonParser.h @@ -35,11 +35,10 @@ class GeoJsonParser { public: static std::shared_ptr getGeoJson(const nlohmann::json &geojson) { // preconditions - if (!geojson["type"].is_string() || + if (!geojson.contains("type") || (!geojson["type"].is_string() || geojson["type"] != "FeatureCollection" || - !geojson["features"].is_array()) { + !geojson["features"].is_array())) { LogError <<= "Geojson is not valid"; - assert(false); return nullptr; } diff --git a/shared/src/map/layers/tiled/vector/geojson/Tiled2dVectorGeoJsonSource.h b/shared/src/map/layers/tiled/vector/geojson/Tiled2dVectorGeoJsonSource.h index f8526cd2b..431ac0489 100644 --- a/shared/src/map/layers/tiled/vector/geojson/Tiled2dVectorGeoJsonSource.h +++ b/shared/src/map/layers/tiled/vector/geojson/Tiled2dVectorGeoJsonSource.h @@ -70,7 +70,14 @@ class Tiled2dVectorGeoJsonSource : public Tiled2dMapVectorSource, public GeoJSON } } + void failedToLoad() override { + loadFailed = true; + } + virtual ::LayerReadyState isReadyToRenderOffscreen() override { + if (loadFailed) { + return LayerReadyState::ERROR; + } if (geoJson->isLoaded()) { return Tiled2dMapVectorSource::isReadyToRenderOffscreen(); } @@ -89,7 +96,7 @@ class Tiled2dVectorGeoJsonSource : public Tiled2dMapVectorSource, public GeoJSON virtual bool hasExpensivePostLoadingTask() override { return false; }; - virtual Tiled2dMapVectorTileInfo::FeatureMap postLoadingTask(const std::shared_ptr &loadedData, const Tiled2dMapTileInfo &tile) override { + virtual Tiled2dMapVectorTileInfo::FeatureMap postLoadingTask(std::shared_ptr loadedData, Tiled2dMapTileInfo tile) override { const auto &geoJsonTile = geoJson->getTile(tile.zoomIdentifier, tile.x, tile.y); Tiled2dMapVectorTileInfo::FeatureMap featureMap = std::make_shared>>>(); std::shared_ptr> features = std::make_shared>(); @@ -105,4 +112,6 @@ class Tiled2dVectorGeoJsonSource : public Tiled2dMapVectorSource, public GeoJSON private: const std::shared_ptr geoJson; const std::weak_ptr<::MapCamera2dInterface> camera; + + bool loadFailed = false; }; diff --git a/shared/src/map/layers/tiled/vector/geojson/geojsonvt/geojsonvt.hpp b/shared/src/map/layers/tiled/vector/geojson/geojsonvt/geojsonvt.hpp index 86d7feca2..a655c402a 100644 --- a/shared/src/map/layers/tiled/vector/geojson/geojsonvt/geojsonvt.hpp +++ b/shared/src/map/layers/tiled/vector/geojson/geojsonvt/geojsonvt.hpp @@ -106,6 +106,9 @@ class GeoJSONVT: public GeoJSONVTInterface, public std::enable_shared_from_this< if (fromLocal) { self->load(false); } + else { + self->delegate.message(&GeoJSONTileDelegate::failedToLoad); + } } else { auto string = std::string((char*)result.data->buf(), result.data->len()); nlohmann::json json; @@ -275,7 +278,7 @@ class GeoJSONVT: public GeoJSONVTInterface, public std::enable_shared_from_this< auto& tile = it->second; if (geometries.empty()) { - tiles.erase(it); + // We need to keep empty tiles, otherwise getTile will throw an error return; } @@ -324,8 +327,8 @@ class GeoJSONVT: public GeoJSONVTInterface, public std::enable_shared_from_this< tile.source_features.clear(); if (z < options.minZoom) { - // if z smaller than min zoom, no need to keep tile - tiles.erase(it); + // if z smaller than min zoom, no need to keep tile, but we keep it + // otherwise some loading states are never resolved } } diff --git a/shared/src/map/layers/tiled/vector/symbol/Tiled2dMapVectorSymbolGroup.cpp b/shared/src/map/layers/tiled/vector/symbol/Tiled2dMapVectorSymbolGroup.cpp index bf6633e20..5b403c836 100644 --- a/shared/src/map/layers/tiled/vector/symbol/Tiled2dMapVectorSymbolGroup.cpp +++ b/shared/src/map/layers/tiled/vector/symbol/Tiled2dMapVectorSymbolGroup.cpp @@ -357,7 +357,7 @@ void Tiled2dMapVectorSymbolGroup::initialize(std::weak_ptrstyle.getBlendMode(EvaluationContext(0.0, dpFactor, std::make_shared(), featureStateManager))); iconInstancedObject = strongMapInterface->getGraphicsObjectFactory()->createQuadInstanced(alphaInstancedShader); #if DEBUG - iconInstancedObject->asGraphicsObject()->setDebugLabel(layerDescription->identifier); + iconInstancedObject->asGraphicsObject()->setDebugLabel(layerDescription->identifier + "_" + tileInfo.tileInfo.to_string_short()); #endif iconInstancedObject->setInstanceCount(instanceCounts.icons); @@ -376,7 +376,7 @@ void Tiled2dMapVectorSymbolGroup::initialize(std::weak_ptrstyle.getBlendMode(EvaluationContext(0.0, dpFactor, std::make_shared(), featureStateManager))); stretchedInstancedObject = strongMapInterface->getGraphicsObjectFactory()->createQuadStretchedInstanced(shader); #if DEBUG - stretchedInstancedObject->asGraphicsObject()->setDebugLabel(layerDescription->identifier); + stretchedInstancedObject->asGraphicsObject()->setDebugLabel(layerDescription->identifier + "_" + tileInfo.tileInfo.to_string_short()); #endif stretchedInstancedObject->setInstanceCount(instanceCounts.stretchedIcons); @@ -395,7 +395,7 @@ void Tiled2dMapVectorSymbolGroup::initialize(std::weak_ptrstyle.getBlendMode(EvaluationContext(0.0, dpFactor, std::make_shared(), featureStateManager))); textInstancedObject = strongMapInterface->getGraphicsObjectFactory()->createTextInstanced(shader); #if DEBUG - textInstancedObject->asGraphicsObject()->setDebugLabel(layerDescription->identifier); + textInstancedObject->asGraphicsObject()->setDebugLabel(layerDescription->identifier + "_" + tileInfo.tileInfo.to_string_short()); #endif textInstancedObject->setInstanceCount(instanceCounts.textCharacters); diff --git a/shared/src/map/layers/tiled/vector/symbol/Tiled2dMapVectorSymbolLabelObject.cpp b/shared/src/map/layers/tiled/vector/symbol/Tiled2dMapVectorSymbolLabelObject.cpp index 84ec97092..eff46dd29 100644 --- a/shared/src/map/layers/tiled/vector/symbol/Tiled2dMapVectorSymbolLabelObject.cpp +++ b/shared/src/map/layers/tiled/vector/symbol/Tiled2dMapVectorSymbolLabelObject.cpp @@ -305,7 +305,7 @@ void Tiled2dMapVectorSymbolLabelObject::updatePropertiesPoint(std::vector centerPositions.push_back(zero); } - static std::vector lineEndIndices; + std::vector lineEndIndices; lineEndIndices.clear(); auto pen = zero; @@ -333,6 +333,7 @@ void Tiled2dMapVectorSymbolLabelObject::updatePropertiesPoint(std::vector for(const auto &i : splittedTextInfo) { if(i.glyphIndex >= 0) { + assert(i.glyphIndex < fontResult->fontData->glyphs.size()); auto &d = fontResult->fontData->glyphs[i.glyphIndex]; auto scale = fontSize * i.scale; @@ -375,12 +376,16 @@ void Tiled2dMapVectorSymbolLabelObject::updatePropertiesPoint(std::vector } else if(i.glyphIndex == -1) { if (numberOfCharacters > 0) { lineEndIndices.push_back(numberOfCharacters - 1); + assert((countOffset + numberOfCharacters-1)*2 < scales.size()); } pen.x = 0.0; pen.y += fontSize * lineHeight; baseLineStartIndex = numberOfCharacters; } + else { + assert(false); + } } // Use the median base line of the last line for size calculations @@ -398,6 +403,7 @@ void Tiled2dMapVectorSymbolLabelObject::updatePropertiesPoint(std::vector if (numberOfCharacters > 0) { lineEndIndices.push_back(numberOfCharacters - 1); + assert((countOffset + numberOfCharacters-1)*2 < scales.size()); } const Vec2D size((boxMax.x - boxMin.x), (medianLastBaseLine - boxMin.y)); @@ -411,10 +417,18 @@ void Tiled2dMapVectorSymbolLabelObject::updatePropertiesPoint(std::vector case TextJustify::RIGHT: case TextJustify::CENTER: { size_t lineStart = 0; + size_t centerPositionsSize = centerPositions.size(); for (auto const lineEndIndex: lineEndIndices) { + if (lineStart >= centerPositionsSize || lineEndIndex >= centerPositionsSize) { + continue; + } auto factor = textJustify == TextJustify::CENTER ? 2.0 : 1.0; - double startFirst = centerPositions[lineStart].x - scales[2 * (countOffset + lineStart)] * 0.5; - double endLast = centerPositions[lineEndIndex].x + scales[2 * (countOffset + lineEndIndex)] * 0.5; + const auto idx = 2 * (countOffset + lineEndIndex); + assert(idx >= 0 && idx < scales.size()); + assert(lineStart < centerPositions.size()); + assert(lineEndIndex < centerPositions.size()); + double startFirst = centerPositions[lineStart].x - scales[idx] * 0.5; + double endLast = centerPositions[lineEndIndex].x + scales[idx] * 0.5; double lineWidth = endLast - startFirst; double delta = (size.x - lineWidth) / factor; diff --git a/shared/src/map/layers/tiled/vector/tiles/line/Tiled2dMapVectorLineTile.cpp b/shared/src/map/layers/tiled/vector/tiles/line/Tiled2dMapVectorLineTile.cpp index dbfbceada..0610d5d68 100644 --- a/shared/src/map/layers/tiled/vector/tiles/line/Tiled2dMapVectorLineTile.cpp +++ b/shared/src/map/layers/tiled/vector/tiles/line/Tiled2dMapVectorLineTile.cpp @@ -348,7 +348,7 @@ void Tiled2dMapVectorLineTile::addLines(const std::vectorcreateLineGroup(shader->asShaderProgramInterface()); #if DEBUG - lineGroupGraphicsObject->asGraphicsObject()->setDebugLabel(description->identifier); + lineGroupGraphicsObject->asGraphicsObject()->setDebugLabel(description->identifier + "_" + tileInfo.tileInfo.to_string_short()); #endif auto lineGroupObject = std::make_shared(coordinateConverterHelper, lineGroupGraphicsObject, diff --git a/shared/src/map/layers/tiled/vector/tiles/polygon/Tiled2dMapVectorPolygonPatternTile.cpp b/shared/src/map/layers/tiled/vector/tiles/polygon/Tiled2dMapVectorPolygonPatternTile.cpp index 923f083b3..2637736ae 100644 --- a/shared/src/map/layers/tiled/vector/tiles/polygon/Tiled2dMapVectorPolygonPatternTile.cpp +++ b/shared/src/map/layers/tiled/vector/tiles/polygon/Tiled2dMapVectorPolygonPatternTile.cpp @@ -295,7 +295,7 @@ void Tiled2dMapVectorPolygonPatternTile::addPolygons(const std::vectorcreatePolygonPatternGroup(shader->asShaderProgramInterface()); #if DEBUG - polygonObject->asGraphicsObject()->setDebugLabel(description->identifier); + polygonObject->asGraphicsObject()->setDebugLabel(description->identifier + "_" + tileInfo.tileInfo.to_string_short()); #endif auto layerObject = std::make_shared(converter, polygonObject, shader); diff --git a/shared/src/map/layers/tiled/vector/tiles/polygon/Tiled2dMapVectorPolygonTile.cpp b/shared/src/map/layers/tiled/vector/tiles/polygon/Tiled2dMapVectorPolygonTile.cpp index 9da3501f9..57ce00574 100644 --- a/shared/src/map/layers/tiled/vector/tiles/polygon/Tiled2dMapVectorPolygonTile.cpp +++ b/shared/src/map/layers/tiled/vector/tiles/polygon/Tiled2dMapVectorPolygonTile.cpp @@ -275,7 +275,7 @@ void Tiled2dMapVectorPolygonTile::addPolygons(const std::vectorcreatePolygonGroup(shader->asShaderProgramInterface()); #if DEBUG - polygonObject->asGraphicsObject()->setDebugLabel(description->identifier); + polygonObject->asGraphicsObject()->setDebugLabel(description->identifier + "_" + tileInfo.tileInfo.to_string_short()); #endif auto layerObject = std::make_shared(converter, polygonObject, shader); diff --git a/shared/src/map/layers/tiled/vector/tiles/raster/Tiled2dMapVectorRasterTile.cpp b/shared/src/map/layers/tiled/vector/tiles/raster/Tiled2dMapVectorRasterTile.cpp index 4d7a5ebc1..68b80cbb9 100644 --- a/shared/src/map/layers/tiled/vector/tiles/raster/Tiled2dMapVectorRasterTile.cpp +++ b/shared/src/map/layers/tiled/vector/tiles/raster/Tiled2dMapVectorRasterTile.cpp @@ -31,7 +31,7 @@ Tiled2dMapVectorRasterTile::Tiled2dMapVectorRasterTile(const std::weak_ptrasShaderProgramInterface()->setBlendMode(description->style.getBlendMode(EvaluationContext(0.0, dpFactor, std::make_shared(), featureStateManager))); auto quad = pMapInterface->getGraphicsObjectFactory()->createQuad(shader->asShaderProgramInterface()); #if DEBUG - quad->asGraphicsObject()->setDebugLabel(description->identifier); + quad->asGraphicsObject()->setDebugLabel(description->identifier + "_" + tileInfo.tileInfo.to_string_short()); #endif tileObject = std::make_shared(quad, shader, pMapInterface); tileObject->setRectCoord(tileInfo.tileInfo.bounds); diff --git a/shared/src/map/layers/tiled/wmts/WmtsCapabilitiesResource.cpp b/shared/src/map/layers/tiled/wmts/WmtsCapabilitiesResource.cpp index 103bc0ba9..9ff081749 100644 --- a/shared/src/map/layers/tiled/wmts/WmtsCapabilitiesResource.cpp +++ b/shared/src/map/layers/tiled/wmts/WmtsCapabilitiesResource.cpp @@ -169,21 +169,27 @@ int32_t numZ) override { double c2 = 0; std::string lowerCorner = boundingBox.child_value("ows:LowerCorner"); if (lowerCorner.size() > 0) { - c1 = std::stod(lowerCorner.substr(0, lowerCorner.find(" "))); - c2 = std::stod(lowerCorner.substr(lowerCorner.find(" "))); + auto delimiterIndex = lowerCorner.find(" "); + if (delimiterIndex != std::string::npos) { + c1 = std::stod(lowerCorner.substr(0, delimiterIndex)); + c2 = std::stod(lowerCorner.substr(delimiterIndex + 1)); + } } double c3 = 0; double c4 = 0; std::string upperCorner = boundingBox.child_value("ows:UpperCorner"); if (upperCorner.size() > 0) { - c3 = std::stod(upperCorner.substr(0, lowerCorner.find(" "))); - c4 = std::stod(upperCorner.substr(lowerCorner.find(" "))); + auto delimiterIndex = upperCorner.find(" "); + if (delimiterIndex != std::string::npos) { + c3 = std::stod(upperCorner.substr(0, delimiterIndex)); + c4 = std::stod(upperCorner.substr(delimiterIndex + 1)); + } } std::optional bounds; if (c1 != 0 && c2 != 0 && c3 != 0 && c4 != 0) { - bounds = RectCoord(Coord(CoordinateSystemIdentifiers::EPSG4326(), c1, c2, 0), - Coord(CoordinateSystemIdentifiers::EPSG4326(), c3, c4, 0)); + bounds = RectCoord(Coord(CoordinateSystemIdentifiers::EPSG4326(), c1, c4, 0), + Coord(CoordinateSystemIdentifiers::EPSG4326(), c3, c2, 0)); } std::string tileMatrixSetLink = layer.child("TileMatrixSetLink").child_value("TileMatrixSet"); std::string resourceTemplate = layer.child("ResourceURL").attribute("template").as_string();