From bfe19cf0bceced8650fd91f0089a8988805e30ec Mon Sep 17 00:00:00 2001 From: Masumori Date: Sun, 29 Mar 2026 03:06:32 +0900 Subject: [PATCH] add: api-docs (experimental) --- .../mapconductor/core/BitmapIconCache.kt.md | 166 ++++ .../mapconductor/core/ChildCollector.kt.md | 269 ++++++ .../java/com/mapconductor/core/MathExt.kt.md | 97 +++ .../mapconductor/core/OverlayProvider.kt.md | 147 ++++ .../mapconductor/core/ResourceProvider.kt.md | 318 +++++++ .../mapconductor/core/StateFlowDelegate.kt.md | 119 +++ .../java/com/mapconductor/core/Utils.kt.md | 108 +++ .../com/mapconductor/core/UtilsDomain.kt.md | 120 +++ .../AbstractCircleOverlayRenderer.kt.md | 209 +++++ .../com/mapconductor/core/circle/Circle.kt.md | 159 ++++ .../core/circle/CircleCapableInterface.kt.md | 154 ++++ .../core/circle/CircleCompose.kt.md | 133 +++ .../core/circle/CircleController.kt.md | 200 +++++ .../core/circle/CircleEntity.kt.md | 105 +++ .../core/circle/CircleManager.kt.md | 246 ++++++ .../core/circle/CircleOverlay.kt.md | 97 +++ .../CircleOverlayRendererInterface.kt.md | 196 +++++ .../controller/BaseMapViewController.kt.md | 179 ++++ .../MapViewControllerInterface.kt.md | 251 ++++++ .../OverlayControllerInterface.kt.md | 154 ++++ .../controller/OverlayRendererInterface.kt.md | 178 ++++ .../mapconductor/core/features/GeoPoint.kt.md | 238 ++++++ .../core/features/GeoRectBounds.kt.md | 305 +++++++ .../core/geocell/HexCellRegistry.kt.md | 523 ++++++++++++ .../core/geocell/HexGeocellImpl.kt.md | 406 +++++++++ .../core/geocell/HexGeocellInterface.kt.md | 285 +++++++ .../mapconductor/core/geocell/KDTree.kt.md | 279 +++++++ .../AbstractGroundImageOverlayRenderer.kt.md | 209 +++++ .../core/groundimage/GroundImage.kt.md | 149 ++++ .../GroundImageCapableInterface.kt.md | 167 ++++ .../groundimage/GroundImageComponent.kt.md | 156 ++++ .../groundimage/GroundImageController.kt.md | 230 ++++++ .../core/groundimage/GroundImageEntity.kt.md | 90 ++ .../core/groundimage/GroundImageManager.kt.md | 211 +++++ .../core/groundimage/GroundImageOverlay.kt.md | 89 ++ .../GroundImageOverlayRendererInterface.kt.md | 187 +++++ .../groundimage/GroundImageTileProvider.kt.md | 139 ++++ .../core/info/DrawInfoBubble.kt.md | 69 ++ .../core/info/InfoBubbleCompose.kt.md | 141 ++++ .../core/info/InfoWindowOverlay.kt.md | 113 +++ .../core/map/BaseMapViewSaver.kt.md | 196 +++++ .../core/map/CollectAndUpdateEach.kt.md | 115 +++ .../core/map/LocalMapOverlayRegistry.kt.md | 131 +++ .../core/map/MapCameraPositionBase.kt.md | 156 ++++ .../core/map/MapDesignTypeInterface.kt.md | 85 ++ .../core/map/MapPaddingsImpl.kt.md | 156 ++++ .../core/map/MapServiceRegistry.kt.md | 189 +++++ .../mapconductor/core/map/MapViewBase.kt.md | 141 ++++ .../core/map/MapViewHolderInterface.kt.md | 209 +++++ .../core/map/MapViewHolderStore.kt.md | 238 ++++++ .../mapconductor/core/map/MapViewState.kt.md | 276 +++++++ .../marker/AbstractMarkerController.kt.md | 201 +++++ .../AbstractMarkerOverlayRenderer.kt.md | 190 +++++ .../AbstractMarkerRenderingStrategy.kt.md | 153 ++++ .../marker/AbstractViewportStrategy.kt.md | 152 ++++ .../core/marker/DefaultMarkerIcon.kt.md | 289 +++++++ .../mapconductor/core/marker/ImageIcon.kt.md | 79 ++ .../com/mapconductor/core/marker/Marker.kt.md | 204 +++++ .../core/marker/MarkerAnimation.kt.md | 38 + .../core/marker/MarkerCapableInterface.kt.md | 240 ++++++ .../core/marker/MarkerCollector.kt.md | 68 ++ .../core/marker/MarkerCompose.kt.md | 172 ++++ .../core/marker/MarkerEntity.kt.md | 93 +++ .../MarkerEventControllerInterface.kt.md | 52 ++ .../mapconductor/core/marker/MarkerIcon.kt.md | 128 +++ .../core/marker/MarkerIngestionEngine.kt.md | 119 +++ .../core/marker/MarkerManager.kt.md | 299 +++++++ .../core/marker/MarkerOverlay.kt.md | 88 ++ .../MarkerOverlayRendererInterface.kt.md | 193 +++++ .../MarkerRenderingStrategyInterface.kt.md | 112 +++ .../core/marker/MarkerRenderingSupport.kt.md | 136 +++ .../MarkerTileRasterLayerCallback.kt.md | 103 +++ .../core/marker/MarkerTileRenderer.kt.md | 126 +++ .../core/marker/MarkerTilingOptions.kt.md | 87 ++ .../marker/StrategyMarkerController.kt.md | 243 ++++++ .../AbstractPolygonOverlayRenderer.kt.md | 267 ++++++ .../mapconductor/core/polygon/Polygon.kt.md | 202 +++++ .../polygon/PolygonCapableInterface.kt.md | 161 ++++ .../core/polygon/PolygonComponent.kt.md | 210 +++++ .../core/polygon/PolygonController.kt.md | 211 +++++ .../core/polygon/PolygonEntity.kt.md | 101 +++ .../core/polygon/PolygonManager.kt.md | 227 +++++ .../core/polygon/PolygonOverlay.kt.md | 93 +++ .../PolygonOverlayRendererInterface.kt.md | 205 +++++ .../polygon/PolygonRasterTileRenderer.kt.md | 122 +++ .../core/polygon/PolygonUnion.kt.md | 151 ++++ .../AbstractPolylineOverlayRenderer.kt.md | 227 +++++ .../mapconductor/core/polyline/Polyline.kt.md | 216 +++++ .../polyline/PolylineCapableInterface.kt.md | 127 +++ .../core/polyline/PolylineComponent.kt.md | 188 +++++ .../core/polyline/PolylineController.kt.md | 218 +++++ .../core/polyline/PolylineEntity.kt.md | 143 ++++ .../core/polyline/PolylineManager.kt.md | 249 ++++++ .../core/polyline/PolylineOverlay.kt.md | 103 +++ .../PolylineOverlayRendererInterface.kt.md | 195 +++++ .../mapconductor/core/projection/Earth.kt.md | 40 + .../core/projection/ProjectionInterface.kt.md | 114 +++ .../mapconductor/core/projection/WGS84.kt.md | 85 ++ .../core/projection/WebMercator.kt.md | 97 +++ .../core/raster/RasterLayer.kt.md | 212 +++++ .../raster/RasterLayerCapableInterface.kt.md | 202 +++++ .../core/raster/RasterLayerComponent.kt.md | 141 ++++ .../core/raster/RasterLayerController.kt.md | 210 +++++ .../core/raster/RasterLayerEntity.kt.md | 95 +++ .../core/raster/RasterLayerManager.kt.md | 202 +++++ .../core/raster/RasterLayerOverlay.kt.md | 104 +++ .../RasterLayerOverlayRendererInterface.kt.md | 214 +++++ .../core/raster/RasterLayerSource.kt.md | 146 ++++ .../spherical/CalculateMetersPerPixel.kt.md | 102 +++ .../CalculatePositionAtDistance.kt.md | 60 ++ .../spherical/ClosestPointOnSegment.kt.md | 79 ++ .../spherical/CreateInterpolatePoints.kt.md | 87 ++ .../CreateLinearInterpolatePoints.kt.md | 56 ++ .../CreateOppositeMeridianPoint.kt.md | 71 ++ .../core/spherical/ExpandBounds.kt.md | 62 ++ .../core/spherical/GeoNearest.kt.md | 85 ++ .../spherical/GeographicLibCalculator.kt.md | 115 +++ .../InterpolateAtMeridianGeodesic.kt.md | 76 ++ .../InterpolateAtMeridianLinear.kt.md | 60 ++ .../core/spherical/IsPointOnLinearLine.kt.md | 89 ++ .../spherical/IsPointOnTheGeodesicLine.kt.md | 92 +++ .../core/spherical/LineSegmentUtils.kt.md | 129 +++ .../PointOnGeodesicSegmentOrNull.kt.md | 98 +++ .../core/spherical/Spherical.kt.md | 428 ++++++++++ .../core/spherical/SplitByMeridian.kt.md | 92 +++ .../core/spherical/WGS84Geodesic.kt.md | 159 ++++ .../core/tileserver/LocalTileServer.kt.md | 256 ++++++ .../tileserver/TileProviderInterface.kt.md | 83 ++ .../core/tileserver/TileRequest.kt.md | 45 + .../core/tileserver/TileServerRegistry.kt.md | 112 +++ .../zoom/AbstractZoomAltitudeConverter.kt.md | 158 ++++ .../com/mapconductor/settings/Settings.kt.md | 141 ++++ .../mapconductor/arcgis/ArcGISExtension.kt.md | 52 ++ .../arcgis/ArcGISMapDesignType.kt.md | 196 +++++ .../mapconductor/arcgis/ArcGISMapView.kt.md | 145 ++++ .../arcgis/ArcGISMapViewController.kt.md | 415 ++++++++++ .../ArcGISMapViewControllerInterface.kt.md | 121 +++ .../arcgis/ArcGISMapViewHolderImpl.kt.md | 254 ++++++ .../arcgis/ArcGISMapViewInitOptions.kt.md | 39 + .../arcgis/ArcGISMapViewScope.kt.md | 64 ++ .../arcgis/ArcGISMapViewStateImpl.kt.md | 140 ++++ .../mapconductor/arcgis/ArcGISTypeAlias.kt.md | 65 ++ .../arcgis/ArcGISViewControllerStore.kt.md | 91 ++ .../com/mapconductor/arcgis/GeoPoint.kt.md | 175 ++++ .../arcgis/MapCameraPosition.kt.md | 421 ++++++++++ .../ZoomAltitudeConverterDeprecated.kt.md | 297 +++++++ .../ArcGISAuthenticationHelpers.kt.md | 221 +++++ .../ArcGISCircleOverlayController.kt.md | 69 ++ .../circle/ArcGISCircleOverlayRenderer.kt.md | 166 ++++ .../ArcGISGroundImageController.kt.md | 71 ++ .../groundimage/ArcGISGroundImageHandle.kt.md | 55 ++ .../ArcGISGroundImageOverlayRenderer.kt.md | 211 +++++ .../marker/ArcGISMarkerController.kt.md | 164 ++++ .../marker/ArcGISMarkerEventController.kt.md | 331 ++++++++ .../arcgis/marker/ArcGISMarkerRenderer.kt.md | 182 ++++ .../ArcGISPolygonOverlayController.kt.md | 59 ++ .../ArcGISPolygonOverlayRenderer.kt.md | 226 +++++ .../ArcGISPolylineOverlayController.kt.md | 52 ++ .../ArcGISPolylineOverlayRenderer.kt.md | 151 ++++ .../raster/ArcGISRasterLayerController.kt.md | 62 ++ .../ArcGISRasterLayerOverlayRenderer.kt.md | 183 ++++ .../arcgis/zoom/ZoomAltitudeConverter.kt.md | 432 ++++++++++ .../googlemaps/AdaptiveInterpolation.kt.md | 241 ++++++ .../mapconductor/googlemaps/GeoPoint.kt.md | 129 +++ .../googlemaps/GeoRectBounds.kt.md | 100 +++ .../googlemaps/GoogleMapDesign.kt.md | 167 ++++ .../googlemaps/GoogleMapTypeAlias.kt.md | 20 + .../googlemaps/GoogleMapView.kt.md | 146 ++++ .../googlemaps/GoogleMapViewController.kt.md | 368 +++++++++ .../GoogleMapViewControllerInterface.kt.md | 92 +++ .../GoogleMapViewControllerStore.kt.md | 102 +++ .../googlemaps/GoogleMapViewHolder.kt.md | 122 +++ .../googlemaps/GoogleMapViewScope.kt.md | 38 + .../googlemaps/GoogleMapViewStateImpl.kt.md | 141 ++++ .../googlemaps/MapCameraPosition.kt.md | 157 ++++ .../circle/CirclePolygonHelper.kt.md | 82 ++ .../circle/GoogleMapCircleController.kt.md | 77 ++ .../GoogleMapCircleOverlayRenderer.kt.md | 171 ++++ .../GoogleMapGroundImageController.kt.md | 67 ++ .../GoogleMapGroundImageOverlayRenderer.kt.md | 146 ++++ .../marker/BitmapDescriptorCache.kt.md | 82 ++ .../marker/GoogleMapMarkerController.kt.md | 341 ++++++++ .../GoogleMapMarkerEventController.kt.md | 275 ++++++ .../marker/GoogleMapMarkerRenderer.kt.md | 151 ++++ .../polygon/GoogleMapPolygonController.kt.md | 58 ++ .../GoogleMapPolygonOverlayRenderer.kt.md | 161 ++++ .../GoogleMapPolylineController.kt.md | 52 ++ .../GoogleMapPolylineOverlayRenderer.kt.md | 163 ++++ .../GoogleMapRasterLayerController.kt.md | 80 ++ .../GoogleMapRasterLayerOverlayRenderer.kt.md | 201 +++++ .../zoom/ZoomAltitudeConverter.kt.md | 133 +++ .../com/mapconductor/here/BitmapIcon.kt.md | 86 ++ .../java/com/mapconductor/here/GeoPoint.kt.md | 162 ++++ .../com/mapconductor/here/GeoRectBounds.kt.md | 96 +++ .../com/mapconductor/here/HereMapDesign.kt.md | 158 ++++ .../com/mapconductor/here/HereMapView.kt.md | 174 ++++ .../here/HereMapViewController.kt.md | 781 ++++++++++++++++++ .../here/HereMapViewControllerInterface.kt.md | 99 +++ .../com/mapconductor/here/HereTypeAlias.kt.md | 85 ++ .../here/HereViewControllerStore.kt.md | 90 ++ .../mapconductor/here/HereViewHolder.kt.md | 146 ++++ .../here/HereViewInitOptions.kt.md | 48 ++ .../com/mapconductor/here/HereViewScope.kt.md | 37 + .../mapconductor/here/HereViewStateImpl.kt.md | 136 +++ .../mapconductor/here/MapCameraPosition.kt.md | 151 ++++ .../here/circle/HereCircleController.kt.md | 61 ++ .../circle/HereCircleOverlayRenderer.kt.md | 160 ++++ .../HereGroundImageController.kt.md | 69 ++ .../groundimage/HereGroundImageHandle.kt.md | 56 ++ .../HereGroundImageOverlayRenderer.kt.md | 191 +++++ .../here/marker/HereMarkerController.kt.md | 283 +++++++ .../marker/HereMarkerEventController.kt.md | 264 ++++++ .../here/marker/HereMarkerRenderer.kt.md | 185 +++++ .../here/polygon/HerePolygonController.kt.md | 54 ++ .../polygon/HerePolygonOverlayRenderer.kt.md | 218 +++++ .../polyline/HerePolylineController.kt.md | 62 ++ .../HerePolylineOverlayRenderer.kt.md | 191 +++++ .../raster/HereRasterLayerController.kt.md | 47 ++ .../HereRasterLayerOverlayRenderer.kt.md | 200 +++++ .../here/zoom/ZoomAltitudeConverter.kt.md | 216 +++++ .../com/mapconductor/mapbox/GeoPoint.kt.md | 135 +++ .../mapconductor/mapbox/GeoRectBounds.kt.md | 101 +++ .../mapbox/MapCameraPosition.kt.md | 214 +++++ .../mapconductor/mapbox/MapboxExtension.kt.md | 69 ++ .../mapconductor/mapbox/MapboxInitSDK.kt.md | 86 ++ .../mapconductor/mapbox/MapboxMapDesign.kt.md | 156 ++++ .../mapconductor/mapbox/MapboxMapView.kt.md | 148 ++++ .../mapbox/MapboxMapViewControllerImpl.kt.md | 291 +++++++ .../MapboxMapViewControllerInterface.kt.md | 76 ++ .../mapbox/MapboxMapViewHolderImpl.kt.md | 193 +++++ .../mapbox/MapboxMapViewScope.kt.md | 33 + .../mapconductor/mapbox/MapboxPaddings.kt.md | 128 +++ .../mapconductor/mapbox/MapboxPolyUtils.kt.md | 146 ++++ .../mapconductor/mapbox/MapboxTypeAlias.kt.md | 85 ++ .../mapbox/MapboxViewStateImpl.kt.md | 126 +++ .../circle/MapboxCircleController.kt.md | 57 ++ .../mapbox/circle/MapboxCircleLayer.kt.md | 162 ++++ .../circle/MapboxCircleOverlayRenderer.kt.md | 160 ++++ .../MapboxGroundImageController.kt.md | 62 ++ .../groundimage/MapboxGroundImageHandle.kt.md | 71 ++ .../MapboxGroundImageOverlayRenderer.kt.md | 194 +++++ .../marker/MapboxMarkerController.kt.md | 203 +++++ .../marker/MapboxMarkerEventController.kt.md | 271 ++++++ .../marker/MapboxMarkerOverlayRenderer.kt.md | 154 ++++ .../mapbox/marker/MarkerDragLayer.kt.md | 110 +++ .../mapbox/marker/MarkerLayer.kt.md | 105 +++ .../polygon/MapboxPolygonConductor.kt.md | 207 +++++ .../mapbox/polygon/MapboxPolygonLayer.kt.md | 171 ++++ .../MapboxPolygonOverlayRenderer.kt.md | 207 +++++ .../polyline/MapboxPolylineController.kt.md | 65 ++ .../mapbox/polyline/MapboxPolylineLayer.kt.md | 171 ++++ .../MapboxPolylineOverlayRenderer.kt.md | 167 ++++ .../raster/MapboxRasterLayerController.kt.md | 80 ++ .../MapboxRasterLayerOverlayRenderer.kt.md | 218 +++++ .../mapbox/zoom/ZoomAltitudeConverter.kt.md | 205 +++++ .../com/mapconductor/maplibre/GeoPoint.kt.md | 170 ++++ .../maplibre/MapCameraPosition.kt.md | 154 ++++ .../maplibre/MapLibreDesign.kt.md | 120 +++ .../maplibre/MapLibreMapView.kt.md | 155 ++++ .../maplibre/MapLibreMapViewHolderImpl.kt.md | 167 ++++ .../maplibre/MapLibreMapViewScope.kt.md | 107 +++ .../maplibre/MapLibrePolyUtils.kt.md | 197 +++++ .../maplibre/MapLibreTypeAlias.kt.md | 95 +++ .../maplibre/MapLibreViewControllerImpl.kt.md | 303 +++++++ .../MapLibreViewControllerInterface.kt.md | 87 ++ .../maplibre/MapLibreViewStateImpl.kt.md | 147 ++++ .../circle/MapLibreCircleController.kt.md | 62 ++ .../maplibre/circle/MapLibreCircleLayer.kt.md | 164 ++++ .../MapLibreCircleOverlayRenderer.kt.md | 152 ++++ .../MapLibreGroundImageController.kt.md | 74 ++ .../MapLibreGroundImageHandle.kt.md | 56 ++ .../MapLibreGroundImageOverlayRenderer.kt.md | 178 ++++ .../marker/MapLibreMarkerController.kt.md | 189 +++++ .../MapLibreMarkerEventController.kt.md | 353 ++++++++ .../MapLibreMarkerOverlayRenderer.kt.md | 236 ++++++ .../maplibre/marker/MarkerDragLayer.kt.md | 113 +++ .../maplibre/marker/MarkerLayer.kt.md | 103 +++ .../polygon/MapLibrePolygonConductor.kt.md | 232 ++++++ .../polygon/MapLibrePolygonLayer.kt.md | 162 ++++ .../MapLibrePolygonOverlayRenderer.kt.md | 129 +++ .../polyline/MapLibrePolylineController.kt.md | 66 ++ .../polyline/MapLibrePolylineLayer.kt.md | 188 +++++ .../MapLibrePolylineOverlayRenderer.kt.md | 182 ++++ .../MapLibreRasterLayerController.kt.md | 74 ++ .../MapLibreRasterLayerOverlayRenderer.kt.md | 162 ++++ .../maplibre/zoom/ZoomAltitudeConverter.kt.md | 189 +++++ .../heatmap/HeatmapCameraController.kt.md | 92 +++ .../heatmap/HeatmapGradient.kt.md | 156 ++++ .../mapconductor/heatmap/HeatmapOverlay.kt.md | 152 ++++ .../heatmap/HeatmapOverlayState.kt.md | 110 +++ .../mapconductor/heatmap/HeatmapPoint.kt.md | 69 ++ .../heatmap/HeatmapPointCompose.kt.md | 137 +++ .../heatmap/HeatmapPointState.kt.md | 148 ++++ .../heatmap/HeatmapTileRenderer.kt.md | 204 +++++ .../com/mapconductor/icons/CircleIcon.kt.md | 144 ++++ .../com/mapconductor/icons/FlagIcon.kt.md | 150 ++++ .../icons/RightTailInfoBubbleIcon.kt.md | 153 ++++ .../icons/RoundInfoBubbleIcon.kt.md | 160 ++++ 298 files changed, 46408 insertions(+) create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/BitmapIconCache.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/ChildCollector.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/MathExt.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/OverlayProvider.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/ResourceProvider.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/StateFlowDelegate.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/Utils.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/UtilsDomain.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/circle/AbstractCircleOverlayRenderer.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/circle/Circle.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/circle/CircleCapableInterface.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/circle/CircleCompose.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/circle/CircleController.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/circle/CircleEntity.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/circle/CircleManager.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/circle/CircleOverlay.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/circle/CircleOverlayRendererInterface.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/controller/BaseMapViewController.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/controller/MapViewControllerInterface.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/controller/OverlayControllerInterface.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/controller/OverlayRendererInterface.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/features/GeoPoint.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/features/GeoRectBounds.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/geocell/HexCellRegistry.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/geocell/HexGeocellImpl.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/geocell/HexGeocellInterface.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/geocell/KDTree.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/groundimage/AbstractGroundImageOverlayRenderer.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/groundimage/GroundImage.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/groundimage/GroundImageCapableInterface.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/groundimage/GroundImageComponent.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/groundimage/GroundImageController.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/groundimage/GroundImageEntity.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/groundimage/GroundImageManager.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/groundimage/GroundImageOverlay.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/groundimage/GroundImageOverlayRendererInterface.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/groundimage/GroundImageTileProvider.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/info/DrawInfoBubble.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/info/InfoBubbleCompose.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/info/InfoWindowOverlay.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/map/BaseMapViewSaver.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/map/CollectAndUpdateEach.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/map/LocalMapOverlayRegistry.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/map/MapCameraPositionBase.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/map/MapDesignTypeInterface.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/map/MapPaddingsImpl.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/map/MapServiceRegistry.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/map/MapViewBase.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/map/MapViewHolderInterface.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/map/MapViewHolderStore.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/map/MapViewState.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/AbstractMarkerController.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/AbstractMarkerOverlayRenderer.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/AbstractMarkerRenderingStrategy.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/AbstractViewportStrategy.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/DefaultMarkerIcon.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/ImageIcon.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/Marker.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/MarkerAnimation.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/MarkerCapableInterface.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/MarkerCollector.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/MarkerCompose.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/MarkerEntity.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/MarkerEventControllerInterface.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/MarkerIcon.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/MarkerIngestionEngine.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/MarkerManager.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/MarkerOverlay.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/MarkerOverlayRendererInterface.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/MarkerRenderingStrategyInterface.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/MarkerRenderingSupport.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/MarkerTileRasterLayerCallback.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/MarkerTileRenderer.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/MarkerTilingOptions.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/StrategyMarkerController.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polygon/AbstractPolygonOverlayRenderer.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polygon/Polygon.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polygon/PolygonCapableInterface.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polygon/PolygonComponent.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polygon/PolygonController.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polygon/PolygonEntity.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polygon/PolygonManager.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polygon/PolygonOverlay.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polygon/PolygonOverlayRendererInterface.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polygon/PolygonRasterTileRenderer.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polygon/PolygonUnion.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polyline/AbstractPolylineOverlayRenderer.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polyline/Polyline.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polyline/PolylineCapableInterface.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polyline/PolylineComponent.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polyline/PolylineController.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polyline/PolylineEntity.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polyline/PolylineManager.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polyline/PolylineOverlay.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polyline/PolylineOverlayRendererInterface.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/projection/Earth.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/projection/ProjectionInterface.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/projection/WGS84.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/projection/WebMercator.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/raster/RasterLayer.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/raster/RasterLayerCapableInterface.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/raster/RasterLayerComponent.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/raster/RasterLayerController.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/raster/RasterLayerEntity.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/raster/RasterLayerManager.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/raster/RasterLayerOverlay.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/raster/RasterLayerOverlayRendererInterface.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/raster/RasterLayerSource.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/spherical/CalculateMetersPerPixel.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/spherical/CalculatePositionAtDistance.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/spherical/ClosestPointOnSegment.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/spherical/CreateInterpolatePoints.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/spherical/CreateLinearInterpolatePoints.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/spherical/CreateOppositeMeridianPoint.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/spherical/ExpandBounds.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/spherical/GeoNearest.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/spherical/GeographicLibCalculator.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/spherical/InterpolateAtMeridianGeodesic.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/spherical/InterpolateAtMeridianLinear.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/spherical/IsPointOnLinearLine.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/spherical/IsPointOnTheGeodesicLine.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/spherical/LineSegmentUtils.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/spherical/PointOnGeodesicSegmentOrNull.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/spherical/Spherical.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/spherical/SplitByMeridian.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/spherical/WGS84Geodesic.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/tileserver/LocalTileServer.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/tileserver/TileProviderInterface.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/tileserver/TileRequest.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/tileserver/TileServerRegistry.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/zoom/AbstractZoomAltitudeConverter.kt.md create mode 100644 experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/settings/Settings.kt.md create mode 100644 experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/ArcGISExtension.kt.md create mode 100644 experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/ArcGISMapDesignType.kt.md create mode 100644 experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/ArcGISMapView.kt.md create mode 100644 experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/ArcGISMapViewController.kt.md create mode 100644 experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/ArcGISMapViewControllerInterface.kt.md create mode 100644 experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/ArcGISMapViewHolderImpl.kt.md create mode 100644 experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/ArcGISMapViewInitOptions.kt.md create mode 100644 experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/ArcGISMapViewScope.kt.md create mode 100644 experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/ArcGISMapViewStateImpl.kt.md create mode 100644 experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/ArcGISTypeAlias.kt.md create mode 100644 experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/ArcGISViewControllerStore.kt.md create mode 100644 experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/GeoPoint.kt.md create mode 100644 experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/MapCameraPosition.kt.md create mode 100644 experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/ZoomAltitudeConverterDeprecated.kt.md create mode 100644 experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/authentication/ArcGISAuthenticationHelpers.kt.md create mode 100644 experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/circle/ArcGISCircleOverlayController.kt.md create mode 100644 experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/circle/ArcGISCircleOverlayRenderer.kt.md create mode 100644 experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/groundimage/ArcGISGroundImageController.kt.md create mode 100644 experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/groundimage/ArcGISGroundImageHandle.kt.md create mode 100644 experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/groundimage/ArcGISGroundImageOverlayRenderer.kt.md create mode 100644 experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/marker/ArcGISMarkerController.kt.md create mode 100644 experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/marker/ArcGISMarkerEventController.kt.md create mode 100644 experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/marker/ArcGISMarkerRenderer.kt.md create mode 100644 experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/polygon/ArcGISPolygonOverlayController.kt.md create mode 100644 experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/polygon/ArcGISPolygonOverlayRenderer.kt.md create mode 100644 experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/polyline/ArcGISPolylineOverlayController.kt.md create mode 100644 experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/polyline/ArcGISPolylineOverlayRenderer.kt.md create mode 100644 experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/raster/ArcGISRasterLayerController.kt.md create mode 100644 experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/raster/ArcGISRasterLayerOverlayRenderer.kt.md create mode 100644 experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/zoom/ZoomAltitudeConverter.kt.md create mode 100644 experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/AdaptiveInterpolation.kt.md create mode 100644 experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/GeoPoint.kt.md create mode 100644 experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/GeoRectBounds.kt.md create mode 100644 experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/GoogleMapDesign.kt.md create mode 100644 experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/GoogleMapTypeAlias.kt.md create mode 100644 experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/GoogleMapView.kt.md create mode 100644 experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/GoogleMapViewController.kt.md create mode 100644 experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/GoogleMapViewControllerInterface.kt.md create mode 100644 experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/GoogleMapViewControllerStore.kt.md create mode 100644 experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/GoogleMapViewHolder.kt.md create mode 100644 experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/GoogleMapViewScope.kt.md create mode 100644 experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/GoogleMapViewStateImpl.kt.md create mode 100644 experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/MapCameraPosition.kt.md create mode 100644 experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/circle/CirclePolygonHelper.kt.md create mode 100644 experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/circle/GoogleMapCircleController.kt.md create mode 100644 experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/circle/GoogleMapCircleOverlayRenderer.kt.md create mode 100644 experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/groundimage/GoogleMapGroundImageController.kt.md create mode 100644 experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/groundimage/GoogleMapGroundImageOverlayRenderer.kt.md create mode 100644 experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/marker/BitmapDescriptorCache.kt.md create mode 100644 experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/marker/GoogleMapMarkerController.kt.md create mode 100644 experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/marker/GoogleMapMarkerEventController.kt.md create mode 100644 experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/marker/GoogleMapMarkerRenderer.kt.md create mode 100644 experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/polygon/GoogleMapPolygonController.kt.md create mode 100644 experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/polygon/GoogleMapPolygonOverlayRenderer.kt.md create mode 100644 experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/polyline/GoogleMapPolylineController.kt.md create mode 100644 experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/polyline/GoogleMapPolylineOverlayRenderer.kt.md create mode 100644 experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/raster/GoogleMapRasterLayerController.kt.md create mode 100644 experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/raster/GoogleMapRasterLayerOverlayRenderer.kt.md create mode 100644 experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/zoom/ZoomAltitudeConverter.kt.md create mode 100644 experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/BitmapIcon.kt.md create mode 100644 experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/GeoPoint.kt.md create mode 100644 experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/GeoRectBounds.kt.md create mode 100644 experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/HereMapDesign.kt.md create mode 100644 experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/HereMapView.kt.md create mode 100644 experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/HereMapViewController.kt.md create mode 100644 experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/HereMapViewControllerInterface.kt.md create mode 100644 experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/HereTypeAlias.kt.md create mode 100644 experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/HereViewControllerStore.kt.md create mode 100644 experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/HereViewHolder.kt.md create mode 100644 experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/HereViewInitOptions.kt.md create mode 100644 experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/HereViewScope.kt.md create mode 100644 experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/HereViewStateImpl.kt.md create mode 100644 experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/MapCameraPosition.kt.md create mode 100644 experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/circle/HereCircleController.kt.md create mode 100644 experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/circle/HereCircleOverlayRenderer.kt.md create mode 100644 experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/groundimage/HereGroundImageController.kt.md create mode 100644 experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/groundimage/HereGroundImageHandle.kt.md create mode 100644 experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/groundimage/HereGroundImageOverlayRenderer.kt.md create mode 100644 experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/marker/HereMarkerController.kt.md create mode 100644 experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/marker/HereMarkerEventController.kt.md create mode 100644 experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/marker/HereMarkerRenderer.kt.md create mode 100644 experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/polygon/HerePolygonController.kt.md create mode 100644 experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/polygon/HerePolygonOverlayRenderer.kt.md create mode 100644 experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/polyline/HerePolylineController.kt.md create mode 100644 experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/polyline/HerePolylineOverlayRenderer.kt.md create mode 100644 experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/raster/HereRasterLayerController.kt.md create mode 100644 experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/raster/HereRasterLayerOverlayRenderer.kt.md create mode 100644 experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/zoom/ZoomAltitudeConverter.kt.md create mode 100644 experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/GeoPoint.kt.md create mode 100644 experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/GeoRectBounds.kt.md create mode 100644 experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/MapCameraPosition.kt.md create mode 100644 experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/MapboxExtension.kt.md create mode 100644 experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/MapboxInitSDK.kt.md create mode 100644 experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/MapboxMapDesign.kt.md create mode 100644 experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/MapboxMapView.kt.md create mode 100644 experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/MapboxMapViewControllerImpl.kt.md create mode 100644 experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/MapboxMapViewControllerInterface.kt.md create mode 100644 experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/MapboxMapViewHolderImpl.kt.md create mode 100644 experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/MapboxMapViewScope.kt.md create mode 100644 experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/MapboxPaddings.kt.md create mode 100644 experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/MapboxPolyUtils.kt.md create mode 100644 experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/MapboxTypeAlias.kt.md create mode 100644 experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/MapboxViewStateImpl.kt.md create mode 100644 experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/circle/MapboxCircleController.kt.md create mode 100644 experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/circle/MapboxCircleLayer.kt.md create mode 100644 experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/circle/MapboxCircleOverlayRenderer.kt.md create mode 100644 experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/groundimage/MapboxGroundImageController.kt.md create mode 100644 experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/groundimage/MapboxGroundImageHandle.kt.md create mode 100644 experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/groundimage/MapboxGroundImageOverlayRenderer.kt.md create mode 100644 experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/marker/MapboxMarkerController.kt.md create mode 100644 experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/marker/MapboxMarkerEventController.kt.md create mode 100644 experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/marker/MapboxMarkerOverlayRenderer.kt.md create mode 100644 experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/marker/MarkerDragLayer.kt.md create mode 100644 experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/marker/MarkerLayer.kt.md create mode 100644 experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/polygon/MapboxPolygonConductor.kt.md create mode 100644 experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/polygon/MapboxPolygonLayer.kt.md create mode 100644 experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/polygon/MapboxPolygonOverlayRenderer.kt.md create mode 100644 experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/polyline/MapboxPolylineController.kt.md create mode 100644 experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/polyline/MapboxPolylineLayer.kt.md create mode 100644 experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/polyline/MapboxPolylineOverlayRenderer.kt.md create mode 100644 experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/raster/MapboxRasterLayerController.kt.md create mode 100644 experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/raster/MapboxRasterLayerOverlayRenderer.kt.md create mode 100644 experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/zoom/ZoomAltitudeConverter.kt.md create mode 100644 experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/GeoPoint.kt.md create mode 100644 experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/MapCameraPosition.kt.md create mode 100644 experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/MapLibreDesign.kt.md create mode 100644 experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/MapLibreMapView.kt.md create mode 100644 experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/MapLibreMapViewHolderImpl.kt.md create mode 100644 experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/MapLibreMapViewScope.kt.md create mode 100644 experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/MapLibrePolyUtils.kt.md create mode 100644 experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/MapLibreTypeAlias.kt.md create mode 100644 experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/MapLibreViewControllerImpl.kt.md create mode 100644 experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/MapLibreViewControllerInterface.kt.md create mode 100644 experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/MapLibreViewStateImpl.kt.md create mode 100644 experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/circle/MapLibreCircleController.kt.md create mode 100644 experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/circle/MapLibreCircleLayer.kt.md create mode 100644 experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/circle/MapLibreCircleOverlayRenderer.kt.md create mode 100644 experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/groundimage/MapLibreGroundImageController.kt.md create mode 100644 experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/groundimage/MapLibreGroundImageHandle.kt.md create mode 100644 experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/groundimage/MapLibreGroundImageOverlayRenderer.kt.md create mode 100644 experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/marker/MapLibreMarkerController.kt.md create mode 100644 experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/marker/MapLibreMarkerEventController.kt.md create mode 100644 experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/marker/MapLibreMarkerOverlayRenderer.kt.md create mode 100644 experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/marker/MarkerDragLayer.kt.md create mode 100644 experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/marker/MarkerLayer.kt.md create mode 100644 experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/polygon/MapLibrePolygonConductor.kt.md create mode 100644 experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/polygon/MapLibrePolygonLayer.kt.md create mode 100644 experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/polygon/MapLibrePolygonOverlayRenderer.kt.md create mode 100644 experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/polyline/MapLibrePolylineController.kt.md create mode 100644 experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/polyline/MapLibrePolylineLayer.kt.md create mode 100644 experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/polyline/MapLibrePolylineOverlayRenderer.kt.md create mode 100644 experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/raster/MapLibreRasterLayerController.kt.md create mode 100644 experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/raster/MapLibreRasterLayerOverlayRenderer.kt.md create mode 100644 experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/zoom/ZoomAltitudeConverter.kt.md create mode 100644 experimental/api-docs/mapconductor-heatmap/src/main/java/com/mapconductor/heatmap/HeatmapCameraController.kt.md create mode 100644 experimental/api-docs/mapconductor-heatmap/src/main/java/com/mapconductor/heatmap/HeatmapGradient.kt.md create mode 100644 experimental/api-docs/mapconductor-heatmap/src/main/java/com/mapconductor/heatmap/HeatmapOverlay.kt.md create mode 100644 experimental/api-docs/mapconductor-heatmap/src/main/java/com/mapconductor/heatmap/HeatmapOverlayState.kt.md create mode 100644 experimental/api-docs/mapconductor-heatmap/src/main/java/com/mapconductor/heatmap/HeatmapPoint.kt.md create mode 100644 experimental/api-docs/mapconductor-heatmap/src/main/java/com/mapconductor/heatmap/HeatmapPointCompose.kt.md create mode 100644 experimental/api-docs/mapconductor-heatmap/src/main/java/com/mapconductor/heatmap/HeatmapPointState.kt.md create mode 100644 experimental/api-docs/mapconductor-heatmap/src/main/java/com/mapconductor/heatmap/HeatmapTileRenderer.kt.md create mode 100644 experimental/api-docs/mapconductor-icons/src/main/java/com/mapconductor/icons/CircleIcon.kt.md create mode 100644 experimental/api-docs/mapconductor-icons/src/main/java/com/mapconductor/icons/FlagIcon.kt.md create mode 100644 experimental/api-docs/mapconductor-icons/src/main/java/com/mapconductor/icons/RightTailInfoBubbleIcon.kt.md create mode 100644 experimental/api-docs/mapconductor-icons/src/main/java/com/mapconductor/icons/RoundInfoBubbleIcon.kt.md diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/BitmapIconCache.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/BitmapIconCache.kt.md new file mode 100644 index 00000000..ee7194ac --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/BitmapIconCache.kt.md @@ -0,0 +1,166 @@ +Excellent. Here is the high-quality SDK documentation for the provided `BitmapIconCache` code snippet. + +# BitmapIconCache + +The `BitmapIconCache` is a singleton object that provides an in-memory cache for `BitmapIcon` objects. It is designed to optimize memory usage and improve performance by reusing bitmap icons instead of recreating them. + +The cache employs a two-level strategy: +1. **LruCache**: An underlying `LruCache` (Least Recently Used) stores the `BitmapIcon` objects. It automatically evicts the least recently used items when the cache exceeds its designated memory limit (1/8th of the application's maximum memory). +2. **Reference Counting**: A reference counting mechanism tracks how many components are actively using a specific icon. An icon is only added to the `LruCache` on its first `put` call and is only removed from the cache when its reference count drops to zero. This prevents actively used icons from being evicted from memory. + +This object is thread-safe for the operations it exposes, but the underlying `LruCache` has its own synchronization behavior. + +--- + +## `put` + +Adds a `BitmapIcon` to the cache or increments its reference count if it already exists. + +If the icon with the specified `id` is not already in the cache, it will be added to the `LruCache` and its reference count will be initialized to 1. If the icon already exists, this method simply increments its reference count without re-adding it to the cache. + +### Signature + +```kotlin +fun put(id: Int, bitmapIcon: BitmapIcon) +``` + +### Parameters + +| Parameter | Type | Description | +| :----------- | :----------- | :----------------------------------------- | +| `id` | `Int` | The unique identifier for the bitmap icon. | +| `bitmapIcon` | `BitmapIcon` | The `BitmapIcon` object to cache. | + +### Example + +```kotlin +// Assume createBitmapIcon() is a function that returns a BitmapIcon instance +val newIcon = createBitmapIcon(R.drawable.my_icon) +val iconId = 101 + +// Add the icon to the cache for the first time +BitmapIconCache.put(iconId, newIcon) +``` + +--- + +## `get` + +Retrieves a `BitmapIcon` from the cache by its identifier. + +### Signature + +```kotlin +fun get(id: Int): BitmapIcon? +``` + +### Parameters + +| Parameter | Type | Description | +| :-------- | :---- | :------------------------------------------- | +| `id` | `Int` | The unique identifier of the icon to retrieve. | + +### Returns + +**`BitmapIcon?`** + +The cached `BitmapIcon` object if it exists, or `null` if no icon with the specified `id` is found in the cache. + +### Example + +```kotlin +val iconId = 101 +val cachedIcon = BitmapIconCache.get(iconId) + +if (cachedIcon != null) { + // Use the cached icon + myMarker.setIcon(cachedIcon) +} else { + // Icon not in cache, create it and put it in the cache + val newIcon = createBitmapIcon(R.drawable.my_icon) + BitmapIconCache.put(iconId, newIcon) + myMarker.setIcon(newIcon) +} +``` + +--- + +## `refCountUp` + +Manually increments the reference count for a cached icon. + +This is useful when a new component begins using an icon that is already in the cache, ensuring it won't be removed prematurely. + +### Signature + +```kotlin +fun refCountUp(id: Int) +``` + +### Parameters + +| Parameter | Type | Description | +| :-------- | :---- | :----------------------------------------------------------------- | +| `id` | `Int` | The unique identifier of the icon to increment the reference count for. | + +### Example + +```kotlin +// Another part of the app needs to use the same icon +val iconId = 101 +BitmapIconCache.refCountUp(iconId) +``` + +--- + +## `refCountDown` + +Decrements the reference count for a cached icon. If the reference count drops to zero, the icon is removed from the cache. + +This method should be called when a component is finished using an icon, allowing the cache to free up memory if the icon is no longer needed by any other component. + +### Signature + +```kotlin +fun refCountDown(id: Int) +``` + +### Parameters + +| Parameter | Type | Description | +| :-------- | :---- | :----------------------------------------------------------------- | +| `id` | `Int` | The unique identifier of the icon to decrement the reference count for. | + +### Example + +```kotlin +// A component that was using an icon is being destroyed +val iconId = 101 +BitmapIconCache.refCountDown(iconId) +``` + +--- + +## `clear` + +Removes all icons from the cache and resets all reference counts. + +This is a destructive operation that completely empties the cache. It can be useful in low-memory situations or when a significant part of the application's UI is being torn down. + +### Signature + +```kotlin +@Keep +fun clear() +``` + +### Example + +```kotlin +// For example, in your Activity's onDestroy or onLowMemory method +override fun onLowMemory() { + super.onLowMemory() + // Clear the entire icon cache to free up memory + BitmapIconCache.clear() +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/ChildCollector.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/ChildCollector.kt.md new file mode 100644 index 00000000..dddd9732 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/ChildCollector.kt.md @@ -0,0 +1,269 @@ +Of course! Here is the high-quality SDK documentation for the provided Kotlin code snippet. + +*** + +## ChildCollector API Reference + +The `ChildCollector` API provides a robust and efficient mechanism for managing a collection of stateful components. It is designed for reactive systems where you need to observe and react to changes in a dynamic set of objects. + +The core of the API is the `ChildCollectorImpl` class, which handles the addition, removal, and observation of individual component states. + +### `ChildCollectorImpl` + +A generic, thread-safe collector that manages a dynamic collection of objects conforming to the `ComponentState` interface. It efficiently batches add/remove operations and provides a debounced mechanism to handle updates to individual states. + +#### Signature + +```kotlin +class ChildCollectorImpl( + private val asFlow: (T) -> Flow, + private val updateDebounce: Duration, + scope: CoroutineScope = CoroutineScope(Dispatchers.Main.immediate), +) : ChildCollector +``` + +#### Description + +`ChildCollectorImpl` maintains an in-memory map of component states, identified by their unique `id`. It exposes this collection as a `StateFlow`, allowing observers to react to any changes in the set of components. + +A key feature is its ability to monitor individual states for changes. This is achieved via the `asFlow` function provided during initialization, which defines what constitutes an "update" for a state object. When a change is detected, a configurable `updateHandler` is invoked after a specified `updateDebounce` period. This prevents system overload from rapid, successive updates. + +All asynchronous operations are managed within the provided `CoroutineScope`. + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `asFlow` | `(T) -> Flow` | A function that takes a state object `T` and returns a `Flow`. The collector subscribes to this flow to detect changes. The `FingerPrint` is a generic type representing the data whose changes should trigger an update. For example, this could be a flow of a specific property, a tuple of multiple properties, or the object itself. | +| `updateDebounce` | `Duration` | The time duration to wait for new changes before invoking the `updateHandler`. This helps to coalesce multiple rapid updates into a single callback. | +| `scope` | `CoroutineScope` | The coroutine scope in which all background jobs for collecting, debouncing, and updating will be launched. Defaults to `CoroutineScope(Dispatchers.Main.immediate)`. | + +--- + +### Properties + +#### `flow` + +A `StateFlow` that emits the current map of managed states. + +**Signature** +```kotlin +override val flow: MutableStateFlow> +``` + +**Description** +Collectors can subscribe to this flow to receive an updated map (`Map`) whenever a state is added, removed, or the entire collection is replaced. The map's keys are the state `id`s. + +--- + +### Functions + +#### `add` + +Asynchronously adds a new state to the collection or updates an existing one with the same `id`. + +**Signature** +```kotlin +override suspend fun add(state: T) +``` + +**Description** +This function submits the state to an internal queue. Additions are batched and debounced for performance, so the state will not be reflected in the main `flow` immediately. If a state with the same `id` already exists, it will be replaced. + +**Parameters** +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `state` | `T` | The component state object to add or update. | + +#### `remove` + +Removes a state from the collection by its ID. + +**Signature** +```kotlin +override fun remove(id: String) +``` + +**Description** +This is a non-suspending, fire-and-forget operation. Like `add`, removals are batched and debounced for efficiency. Any active update-monitoring job for the corresponding state will be cancelled. + +**Parameters** +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `id` | `String` | The unique identifier of the state to remove. | + +#### `setUpdateHandler` + +Sets or clears the callback that is invoked when a managed state has an update. + +**Signature** +```kotlin +override fun setUpdateHandler(handler: (suspend (T) -> Unit)?) +``` + +**Description** +When a non-null `handler` is provided, the collector begins monitoring all current and future states for changes (as defined by the `asFlow` function). When a change is detected and the `updateDebounce` period passes, the handler is called with the updated state. + +If the handler is set to `null`, all active update-monitoring jobs are cancelled and no further update callbacks will be triggered. + +**Parameters** +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `handler` | `(suspend (T) -> Unit)?` | The suspendable lambda to execute on a state update, or `null` to clear the handler. | + +#### `replaceAll` + +Atomically replaces the entire collection of states with a new list. + +**Signature** +```kotlin +override fun replaceAll(states: List) +``` + +**Description** +This function provides an efficient way to perform a bulk update. It calculates the difference between the old and new sets of states, cancels monitoring jobs for removed states, and starts new jobs for added states. The main `flow` is updated with the new map in a single emission. This is more performant than clearing the collection and adding items individually. + +**Parameters** +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `states` | `List` | The new list of states to manage. | + +--- + +### Example + +```kotlin +import com.mapconductor.core.ChildCollectorImpl +import com.mapconductor.core.ComponentState +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.MutableStateFlow +import kotlin.time.Duration.Companion.seconds + +// 1. Define a state object that implements ComponentState +data class WidgetState( + override val id: String, + val nameFlow: MutableStateFlow, + var lastUpdated: Long = 0 +) : ComponentState + +suspend fun main() { + val scope = CoroutineScope(Dispatchers.Default + SupervisorJob()) + + // 2. Instantiate the ChildCollectorImpl + val widgetCollector = ChildCollectorImpl( + // The "fingerprint" is the widget's name. Updates trigger when the name changes. + asFlow = { widgetState -> widgetState.nameFlow }, + updateDebounce = 1.seconds, + scope = scope + ) + + // 3. Set an update handler to react to individual widget changes + widgetCollector.setUpdateHandler { widget -> + println("UPDATE HANDLER: Widget '${widget.id}' was updated. New name: ${widget.nameFlow.value}") + widget.lastUpdated = System.currentTimeMillis() + } + + // 4. Collect the main flow to observe the entire collection + scope.launch { + widgetCollector.flow.collect { widgets -> + println("COLLECTION CHANGED: ${widgets.size} widgets total.") + println("Current widgets: ${widgets.keys}") + println("---") + } + } + + delay(100) // Allow collector to start + + // 5. Add widgets + println("Adding widget-1 and widget-2...") + val widget1 = WidgetState("widget-1", MutableStateFlow("First Widget")) + val widget2 = WidgetState("widget-2", MutableStateFlow("Second Widget")) + widgetCollector.add(widget1) + widgetCollector.add(widget2) + + delay(1000) + + // 6. Trigger an update on a widget + println("Updating widget-1's name...") + widget1.nameFlow.value = "Updated First Widget" // This will trigger the updateHandler after 1 second + + delay(2000) + + // 7. Remove a widget + println("Removing widget-2...") + widgetCollector.remove("widget-2") + + delay(1000) + + // 8. Replace all widgets + println("Replacing all widgets...") + val widget3 = WidgetState("widget-3", MutableStateFlow("Third Widget")) + widgetCollector.replaceAll(listOf(widget1, widget3)) + + delay(1000) + + scope.cancel() // Clean up +} + +/* +Expected Output: + +COLLECTION CHANGED: 0 widgets total. +Current widgets: [] +--- +Adding widget-1 and widget-2... +COLLECTION CHANGED: 2 widgets total. +Current widgets: [widget-1, widget-2] +--- +Updating widget-1's name... +UPDATE HANDLER: Widget 'widget-1' was updated. New name: Updated First Widget +Removing widget-2... +COLLECTION CHANGED: 1 widgets total. +Current widgets: [widget-1] +--- +Replacing all widgets... +COLLECTION CHANGED: 2 widgets total. +Current widgets: [widget-1, widget-3] +--- +*/ +``` + +--- + +## Supporting Interfaces + +### `ChildCollector` + +Defines the public contract for a collector of `ComponentState` objects. + +#### Signature + +```kotlin +interface ChildCollector +``` + +#### Description + +This interface abstracts the implementation details of the collector, providing a clear and stable API for managing a collection of states. It includes methods for adding, removing, and replacing states, as well as mechanisms for observing the collection as a whole and handling updates to individual items. + +--- + +### `ComponentState` + +A contract for state objects that can be managed by a `ChildCollector`. + +#### Signature + +```kotlin +interface ComponentState +``` + +#### Description + +Any class representing a state that will be managed by `ChildCollector` must implement this interface. + +#### Properties + +| Property | Type | Description | +| :--- | :--- | :--- | +| `id` | `String` | A unique identifier for the state object. This is used as the key in the collector's internal map. | \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/MathExt.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/MathExt.kt.md new file mode 100644 index 00000000..bf1edca8 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/MathExt.kt.md @@ -0,0 +1,97 @@ +Of course! Here is the high-quality SDK documentation for the provided Kotlin code snippet. + +*** + +## toFixed + +These extension functions format a `Double` or `Float` to a string with a specified number of decimal places. The formatting is done by truncating the number, not by rounding. + +--- + +### Double.toFixed() + +An extension function that formats a `Double` into a string representation with a fixed number of decimal places. + +#### Signature +```kotlin +fun Double.toFixed(decimals: Int = 0): String +``` + +#### Description +This function converts a `Double` to a string with a specified number of digits after the decimal point. It uses `RoundingMode.DOWN`, which means it always truncates the number and never rounds up. The resulting string will not use scientific notation. + +#### Parameters +| Parameter | Type | Description | Default | +|------------|-------|------------------------------------------------------|---------| +| `decimals` | `Int` | The number of decimal places to preserve in the output. | `0` | + +#### Returns +`String` - The formatted string representation of the `Double`. + +#### Example +```kotlin +import com.mapconductor.core.toFixed + +fun main() { + val pi = 3.14159265359 + + // Format to 4 decimal places + val formattedPi = pi.toFixed(4) + println(formattedPi) // Output: "3.1415" + + // Format to 2 decimal places + val price = 19.999 + val formattedPrice = price.toFixed(2) + println(formattedPrice) // Output: "19.99" + + // Use the default value (0 decimals) + val integerPart = 123.456 + val formattedInteger = integerPart.toFixed() + println(formattedInteger) // Output: "123" +} +``` + +--- + +### Float.toFixed() + +An extension function that formats a `Float` into a string representation with a fixed number of decimal places. + +#### Signature +```kotlin +fun Float.toFixed(decimals: Int = 0): String +``` + +#### Description +This function converts a `Float` to a string with a specified number of digits after the decimal point. It uses `RoundingMode.DOWN`, which means it always truncates the number and never rounds up. The resulting string will not use scientific notation. + +#### Parameters +| Parameter | Type | Description | Default | +|------------|-------|------------------------------------------------------|---------| +| `decimals` | `Int` | The number of decimal places to preserve in the output. | `0` | + +#### Returns +`String` - The formatted string representation of the `Float`. + +#### Example +```kotlin +import com.mapconductor.core.toFixed + +fun main() { + val value = 123.4567f + + // Format to 3 decimal places + val formattedValue = value.toFixed(3) + println(formattedValue) // Output: "123.456" + + // Format to 1 decimal place + val temperature = 98.76f + val formattedTemp = temperature.toFixed(1) + println(formattedTemp) // Output: "98.7" + + // Use the default value (0 decimals) + val anotherValue = 45.9f + val formattedAnother = anotherValue.toFixed() + println(formattedAnother) // Output: "45" +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/OverlayProvider.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/OverlayProvider.kt.md new file mode 100644 index 00000000..005010e9 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/OverlayProvider.kt.md @@ -0,0 +1,147 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet, incorporating the feedback to add a comprehensive example and maintain a professional tone. + +*** + +### `MapViewScope` + +A scope that provides a declarative, composable context for defining map overlays. + +`MapViewScope` acts as a receiver for a content lambda within a map composable. It is responsible for collecting the state of all declared child overlays (such as markers, polylines, and polygons) and making them available for rendering. + +#### Methods + +##### `buildRegistry()` + +Creates and configures a `MapOverlayRegistry` containing all the overlay states collected within this scope. This registry is the central collection of all declared overlays and is essential for rendering them on the map. + +**Signature** +```kotlin +fun buildRegistry(): MapOverlayRegistry +``` + +**Returns** +| Type | Description | +| :--- | :--- | +| `MapOverlayRegistry` | An object containing flows for each type of map overlay, ready to be consumed by `CollectAndRenderOverlays`. | + +*** + +### `CollectAndRenderOverlays` + +A composable function that bridges the declarative overlay definitions with the imperative map controller. + +This function observes the state of all overlays registered in the `MapOverlayRegistry`. When an overlay's state changes, it uses the provided `MapViewControllerInterface` to execute the necessary rendering commands (add, update, or remove) on the underlying native map view. + +It should be placed within the same composable that hosts the native map view and manages the `MapViewControllerInterface`. + +**Signature** +```kotlin +@Composable +fun CollectAndRenderOverlays( + registry: MapOverlayRegistry, + controller: MapViewControllerInterface, +) +``` + +**Parameters** + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `registry` | `MapOverlayRegistry` | The registry of all declared overlays, typically obtained by calling `MapViewScope.buildRegistry()`. | +| `controller` | `MapViewControllerInterface` | The controller that interacts with the underlying native map view to perform rendering operations. | + +*** + +### Example + +The following example demonstrates how to use `MapViewScope` and `CollectAndRenderOverlays` together to create a declarative map component. + +First, we define a wrapper composable, `MapView`, which sets up the scope and handles rendering. Users of this component can then declaratively add overlays like `Marker` and `Polyline` inside its content lambda. + +```kotlin +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.viewinterop.AndroidView +import com.mapconductor.core.MapViewScope +import com.mapconductor.core.CollectAndRenderOverlays +import com.mapconductor.core.controller.MapViewControllerInterface +import com.mapconductor.core.marker.Marker // Assumed composable +import com.mapconductor.core.polyline.Polyline // Assumed composable +import com.google.android.gms.maps.model.LatLng // Example dependency + +/** + * A custom MapView composable that provides a declarative API for adding overlays. + * + * @param content A lambda with `MapViewScope` as its receiver, allowing for the + * declarative definition of map overlays like Markers, Polylines, etc. + */ +@Composable +fun MapView( + // Other map properties like camera position can be added here + content: @Composable MapViewScope.() -> Unit +) { + // 1. Create a scope for collecting overlay states. + val scope = remember { MapViewScope() } + + // 2. Initialize the controller for the imperative map view. + // This controller would be implemented to interact with a specific map SDK + // like Google Maps or Mapbox. + val mapController = remember { /* ... initialize your MapViewController ... */ } + + // 3. Build the registry from the overlays declared in the `content` lambda. + val registry = scope.buildRegistry() + + // 4. Instantiate the underlying native map view. + AndroidView( + factory = { context -> + // Create and configure the native MapView from the map SDK + val nativeMapView = com.google.android.gms.maps.MapView(context) + + // Attach the controller to the native view + // mapController.attach(nativeMapView) + + nativeMapView + }, + update = { /* Handle view updates if necessary */ } + ) + + // 5. Execute the content lambda to collect the declared overlays into the scope. + scope.content() + + // 6. Render the collected overlays by bridging their state to the map controller. + CollectAndRenderOverlays( + registry = registry, + controller = mapController + ) +} + +// --- Usage Example --- + +/** + * An example screen demonstrating the declarative usage of the MapView. + */ +@Composable +fun MyMapScreen() { + val sanFrancisco = LatLng(37.7749, -122.4194) + val losAngeles = LatLng(34.0522, -118.2437) + + MapView { // The receiver is MapViewScope + // Declaratively add a marker to the map. + // The `Marker` composable would internally use the scope's `markerCollector`. + Marker( + id = "marker-sf", + position = sanFrancisco, + title = "San Francisco" + ) + + // Declaratively add a polyline between two points. + // The `Polyline` composable would use the scope's `polylineCollector`. + Polyline( + id = "polyline-ca", + points = listOf(sanFrancisco, losAngeles), + color = 0xFF0000FF.toInt(), // Blue + width = 10f + ) + } +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/ResourceProvider.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/ResourceProvider.kt.md new file mode 100644 index 00000000..bd13d6f5 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/ResourceProvider.kt.md @@ -0,0 +1,318 @@ +Excellent. Here is the high-quality SDK documentation for the provided code snippet. + +*** + +# MapConductor Core SDK + +This document provides detailed information about the `ResourceProvider` object and the `IconResource` data class from the MapConductor Core library. + +## `IconResource` + +A data class that represents a drawable resource for an icon, containing its dimensions, anchor points, and a reference to the resource. + +### Signature + +```kotlin +data class IconResource( + val name: String, + val width: Double, + val height: Double, + val anchorX: Double, + val anchorY: Double +) +``` + +### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `name` | `String` | The unique identifier for the icon. | +| `width` | `Double` | The width of the icon in device-independent pixels (dp). | +| `height` | `Double` | The height of the icon in device-independent pixels (dp). | +| `anchorX` | `Double` | The horizontal anchor point as a fraction of the width (from 0.0 for the left edge to 1.0 for the right edge). | +| `anchorY` | `Double` | The vertical anchor point as a fraction of the height (from 0.0 for the top edge to 1.0 for the bottom edge). | + +--- + +## `ResourceProvider` + +A singleton object that provides utility functions for accessing and converting Android resources, such as display metrics, densities, and dimension units (dp, sp, px). + +**Important:** You must initialize the `ResourceProvider` by calling the `init()` method before using any other functions in this object. + +### `init` + +Initializes the `ResourceProvider` with the application context. This method **must** be called once, typically in your `Application` class's `onCreate` method, before any other methods of this object are used. + +#### Signature + +```kotlin +fun init(context: Context) +``` + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `context` | `Context` | The application context. Using `context.applicationContext` is recommended to avoid memory leaks. | + +#### Example + +```kotlin +// In your custom Application class +import android.app.Application +import com.mapconductor.core.ResourceProvider + +class MainApplication : Application() { + override fun onCreate() { + super.onCreate() + ResourceProvider.init(this) + } +} +``` + +--- + +### `getDisplayMetrics` + +Retrieves the system's current display metrics. + +#### Signature + +```kotlin +fun getDisplayMetrics(): DisplayMetrics +``` + +#### Returns + +| Type | Description | +| :--- | :--- | +| `DisplayMetrics` | An object containing information about the display, such as its size, density, and font scaling. | + +--- + +### `getSystemConfiguration` + +Retrieves the system's current configuration. + +#### Signature + +```kotlin +fun getSystemConfiguration(): Configuration +``` + +#### Returns + +| Type | Description | +| :--- | :--- | +| `Configuration` | An object describing all device configuration information, such as screen orientation, font scale, etc. | + +--- + +### `getDensity` + +Gets the logical density of the display. This is a scaling factor used to convert between dp units and pixel units. + +#### Signature + +```kotlin +fun getDensity(): Float +``` + +#### Returns + +| Type | Description | +| :--- | :--- | +| `Float` | The screen's logical density factor. | + +--- + +### `setBitmapDensity` + +Sets a custom density to be used for bitmap creation. This is particularly useful for map providers that automatically scale bitmaps based on their `density` property. If `null` is provided, the system's default density will be used. + +#### Signature + +```kotlin +fun setBitmapDensity(density: Float?) +``` + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `density` | `Float?` | The density to use for bitmap creation, or `null` to reset to the system density. | + +--- + +### `getBitmapDensity` + +Gets the density that should be used for bitmap creation. It returns the custom override density if one has been set via `setBitmapDensity()`, otherwise, it falls back to the system's display density. + +#### Signature + +```kotlin +fun getBitmapDensity(): Float +``` + +#### Returns + +| Type | Description | +| :--- | :--- | +| `Float` | The density to use for bitmaps. | + +--- + +### `dpToPx` + +Converts a value from device-independent pixels (dp) to physical pixels (px) based on the screen's density. Overloads are available for `Float`, `Dp`, and `Double` types. + +#### Signature + +```kotlin +fun dpToPx(dp: Double): Double +fun dpToPx(dp: Float): Double +fun dpToPx(dp: Dp): Double +``` + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `dp` | `Double` \| `Float` \| `Dp` | The value in device-independent pixels (dp). | + +#### Returns + +| Type | Description | +| :--- | :--- | +| `Double` | The equivalent value in physical pixels (px). | + +--- + +### `dpToPxForBitmap` + +Converts a dp value to pixels specifically for creating bitmaps. This method calculates the pixel size using the device's actual screen density, which is suitable for creating the raw bitmap data. The `bitmapDensityOverride` (set via `setBitmapDensity`) is intended to be set on the `Bitmap.density` property *after* creation, not for calculating the pixel dimensions. Overloads are available for `Float` and `Dp` types. + +#### Signature + +```kotlin +fun dpToPxForBitmap(dp: Double): Double +fun dpToPxForBitmap(dp: Float): Double +fun dpToPxForBitmap(dp: Dp): Double +``` + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `dp` | `Double` \| `Float` \| `Dp` | The value in device-independent pixels (dp). | + +#### Returns + +| Type | Description | +| :--- | :--- | +| `Double` | The equivalent value in physical pixels (px) for bitmap dimensions. | + +--- + +### `pxToSp` + +Converts a value from physical pixels (px) to scale-independent pixels (sp). This calculation accounts for both the screen density and the user's font size preference. + +#### Signature + +```kotlin +fun pxToSp(px: Double): Double +``` + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `px` | `Double` | The value in physical pixels (px). | + +#### Returns + +| Type | Description | +| :--- | :--- | +| `Double` | The equivalent value in scale-independent pixels (sp). | + +--- + +### `spToPx` + +Converts a value from scale-independent pixels (sp) to physical pixels (px). This conversion considers the user's font size preference. Overloads are available for `Float`, `TextUnit`, and `Double` types. + +#### Signature + +```kotlin +fun spToPx(sp: Double): Double +fun spToPx(sp: Float): Double +fun spToPx(sp: TextUnit): Double +``` + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `sp` | `Double` \| `Float` \| `TextUnit` | The value in scale-independent pixels (sp). | + +#### Returns + +| Type | Description | +| :--- | :--- | +| `Double` | The equivalent value in physical pixels (px). | + +--- + +### `getFontScale` + +Gets the scaling factor for fonts, based on the user's font size preference in the system settings. + +#### Signature + +```kotlin +fun getFontScale(): Float +``` + +#### Returns + +| Type | Description | +| :--- | :--- | +| `Float` | The font scaling factor. | + +--- + +### `getEffectiveScaledDensity` + +Calculates the effective scaled density. This method provides a consistent way to get the scaled density across different Android versions. On Android 14 (API 34) and higher, it is calculated as `density * fontScale`. On older versions, it returns the value of `DisplayMetrics.scaledDensity`. + +#### Signature + +```kotlin +fun getEffectiveScaledDensity(): Float +``` + +#### Returns + +| Type | Description | +| :--- | :--- | +| `Float` | The effective scaled density. | + +--- + +### `getOptimalTileSize` + +Determines the optimal tile size for components like map tiles based on the device's screen density. It returns 512 for high-density screens (density >= 2.0) and 256 for lower-density screens. + +#### Signature + +```kotlin +fun getOptimalTileSize(): Int +``` + +#### Returns + +| Type | Description | +| :--- | :--- | +| `Int` | The optimal tile size in pixels (either 256 or 512). | \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/StateFlowDelegate.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/StateFlowDelegate.kt.md new file mode 100644 index 00000000..5381ef4e --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/StateFlowDelegate.kt.md @@ -0,0 +1,119 @@ +# StateFlowDelegate + +A property delegate that backs a `var` property with a `kotlinx.coroutines.flow.MutableStateFlow`, allowing property changes to be observed reactively. + +## Signature + +```kotlin +class StateFlowDelegate( + initialValue: T, +) : ReadWriteProperty +``` + +## Description + +`StateFlowDelegate` is a property delegate that seamlessly integrates a standard mutable property (`var`) with the power of Kotlin's `StateFlow`. When you delegate a property to `StateFlowDelegate`, any assignment to that property automatically updates the value of an internal `MutableStateFlow` and emits the new value to all its collectors. + +This class implements the `ReadWriteProperty` interface, allowing it to be used with the `by` keyword. The `getValue` and `setValue` methods are invoked by the Kotlin compiler upon property access and assignment. + +This delegate is ideal for state management in application architectures like MVVM or MVI, where you need to expose observable state from a ViewModel or a repository to the UI layer or other consumers. + +## Constructor + +### `StateFlowDelegate(initialValue: T)` + +Creates a new instance of the delegate, initializing the internal `StateFlow` with the provided value. + +#### Parameters + +| Parameter | Type | Description | +|----------------|------|------------------------------------| +| `initialValue` | `T` | The initial value of the property. | + +## Methods + +### `asStateFlow()` + +Exposes the underlying `MutableStateFlow` that backs the property. This allows consumers to observe changes to the property's value by collecting from the returned flow. + +While the method returns a `MutableStateFlow`, it is a common and recommended practice for consumers to treat it as a read-only `StateFlow` to maintain unidirectional data flow and prevent external modifications. + +#### Signature + +```kotlin +fun asStateFlow(): MutableStateFlow +``` + +#### Returns + +| Type | Description | +|-----------------------|-------------------------------------------| +| `MutableStateFlow` | The backing `MutableStateFlow` instance. | + +## Example + +The following example demonstrates how to use `StateFlowDelegate` in a `UserViewModel` to manage and observe a user's status. + +```kotlin +import com.mapconductor.core.StateFlowDelegate +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* + +class UserViewModel { + // 1. Create a private instance of the delegate with an initial value. + private val statusDelegate = StateFlowDelegate("Offline") + + // 2. Delegate the public 'status' property to the instance. + // Reads and writes to 'status' will now go through the delegate. + var status: String by statusDelegate + + // 3. Expose the underlying StateFlow for observers. + // This allows other parts of the app to react to status changes. + val statusFlow: StateFlow = statusDelegate.asStateFlow() +} + +fun main() = runBlocking { + val viewModel = UserViewModel() + + println("Initial status: ${viewModel.status}") + + // Launch a coroutine to collect updates from statusFlow. + // The initial value is collected immediately. + val collectorJob = launch { + viewModel.statusFlow.collect { newStatus -> + println("Status updated via flow: $newStatus") + } + } + + // Wait a bit, then change the property's value. + // This will trigger the collector in the coroutine. + delay(100) + println("\n> Setting status to 'Connecting...'") + viewModel.status = "Connecting..." + + delay(100) + println("\n> Setting status to 'Online'") + viewModel.status = "Online" + + // The property can be read directly at any time. + println("Current status property: ${viewModel.status}") + + // Clean up the coroutine + delay(50) + collectorJob.cancel() +} +``` + +### Expected Output + +``` +Initial status: Offline +Status updated via flow: Offline + +> Setting status to 'Connecting...' +Status updated via flow: Connecting... + +> Setting status to 'Online' +Status updated via flow: Online +Current status property: Online +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/Utils.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/Utils.kt.md new file mode 100644 index 00000000..654baf8d --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/Utils.kt.md @@ -0,0 +1,108 @@ +Excellent! Here is the high-quality SDK documentation for the provided Kotlin code snippet. + +--- + +### `debounceBatch` + +**Signature** +```kotlin +@OptIn(FlowPreview::class) +fun Flow.debounceBatch( + window: Duration, + maxSize: Int +): Flow> +``` + +**Description** + +Collects items from the source `Flow` and emits them as batches (lists). This operator is an extension function on `Flow`. + +A batch is emitted when either of these two conditions is met: +1. A time window of `window` duration passes without any new items being received. +2. The number of collected items in the current batch reaches `maxSize`. + +This is particularly useful for improving efficiency by grouping a burst of frequent emissions into a single, larger chunk before performing an operation like a network request or a database write. + +If the source flow completes, any remaining buffered items are emitted as a final batch before the downstream flow completes. + +**Parameters** + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `window` | `Duration` | The duration of inactivity after which the current batch of items is emitted. | +| `maxSize` | `Int` | The maximum number of items to collect in a batch. When the batch size reaches this limit, it is emitted immediately. Must be greater than 0. | + +**Returns** + +A `Flow>` that emits lists of items, where each list represents a batch collected from the source flow. + +**Example** + +The following example demonstrates how `debounceBatch` groups items based on both the time `window` and the `maxSize`. + +```kotlin +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* +import kotlin.time.Duration +import kotlin.time.Duration.Companion.milliseconds + +// The debounceBatch function is assumed to be defined in this scope. +// Note: The original function is internal, but we use it here for demonstration. + +@OptIn(FlowPreview::class) +fun main() = runBlocking { + val sourceFlow = flow { + // --- Batch 1: Triggered by time window --- + println("-> Emitting 1") + emit(1) + delay(50) + println("-> Emitting 2") + emit(2) + // Wait for 500ms, which is longer than the 300ms window. + // This inactivity triggers the emission of the first batch. + delay(500) + + // --- Batch 2: Triggered by maxSize --- + println("-> Emitting 3") + emit(3) + delay(50) + println("-> Emitting 4") + emit(4) + delay(50) + // The third emission in this burst fills the batch to maxSize. + // This triggers an immediate emission of the second batch. + println("-> Emitting 5") + emit(5) + + // --- Batch 3: Triggered by flow completion --- + println("-> Emitting 6") + emit(6) + // A short delay before the flow completes. + delay(50) + } + + println("Collecting batches with window=300ms, maxSize=3...") + sourceFlow + .debounceBatch(window = 300.milliseconds, maxSize = 3) + .collect { batch -> + println("<- Collected Batch: $batch") + } + println("Collection complete.") +} + +/* +Expected Output: + +Collecting batches with window=300ms, maxSize=3... +-> Emitting 1 +-> Emitting 2 +<- Collected Batch: [1, 2] +-> Emitting 3 +-> Emitting 4 +-> Emitting 5 +<- Collected Batch: [3, 4, 5] +-> Emitting 6 +<- Collected Batch: [6] +Collection complete. +*/ +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/UtilsDomain.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/UtilsDomain.kt.md new file mode 100644 index 00000000..2ef64dbe --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/UtilsDomain.kt.md @@ -0,0 +1,120 @@ +Of course! Here is the high-quality SDK documentation for the provided Kotlin code snippet. + +*** + +## Package `com.mapconductor.core` + +This document provides an overview of utility functions available in the `com.mapconductor.core` package. + +### `printPoints` + +#### Signature +```kotlin +fun printPoints( + tag: String, + points: List +) +``` + +#### Description +Logs a list of geographic points to the Android Logcat for debugging purposes. It first logs a separator line, then iterates through the provided list. Each point is converted to its URL-safe string representation using `GeoPoint.from(point).toUrlValue()` before being logged with a `DEBUG` level. + +#### Parameters +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `tag` | `String` | The tag to use for the log messages in Logcat. | +| `points` | `List` | A list of geographic point objects to be printed. | + +#### Returns +This function does not return a value. + +#### Example +```kotlin +// Assuming GeoPoint is a data class implementing GeoPointInterface +val point1 = GeoPoint(latitude = 35.681236, longitude = 139.767125) // Tokyo Station +val point2 = GeoPoint(latitude = 34.652500, longitude = 135.506302) // Osaka + +val pointsToLog = listOf(point1, point2) + +// Log the points with the tag "MapDebug" +printPoints("MapDebug", pointsToLog) + +/* +Expected Logcat output: +D/MapDebug: ----------- +D/MapDebug: 35.681236,139.767125 +D/MapDebug: 34.6525,135.506302 +*/ +``` + +### `calculateZIndex` + +#### Signature +```kotlin +fun calculateZIndex(geoPointBase: GeoPointInterface): Int +``` + +#### Description +Calculates a Z-index integer value for a geographic point. The calculation is designed to create a visual sense of depth on a 2D map, where points further south appear "in front" (higher Z-index) and points further north appear "in the back" (lower Z-index). For points at the same latitude, points further west are given priority (a higher Z-index) to appear in front. + +The formula used is: `(-latitude * 1,000,000 - longitude).roundToInt()` + +#### Parameters +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `geoPointBase` | `GeoPointInterface` | The geographic point for which to calculate the Z-index. | + +#### Returns +| Type | Description | +| :--- | :--- | +| `Int` | The calculated Z-index value. | + +#### Example +```kotlin +// Assuming GeoPoint is a data class implementing GeoPointInterface +val point = GeoPoint(latitude = 35.681236, longitude = 139.767125) + +val zIndex = calculateZIndex(point) + +// zIndex will be -35821003 +println(zIndex) +``` + +### `normalizeLng` + +#### Signature +```kotlin +fun normalizeLng(lng: Double): Double +``` + +#### Description +Normalizes a given longitude value to ensure it falls within the standard geographical range of `[-180.0, 180.0]`. This is useful for correcting longitude values that may have wrapped around during map panning or other calculations. + +#### Parameters +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `lng` | `Double` | The longitude value to be normalized. | + +#### Returns +| Type | Description | +| :--- | :--- | +| `Double` | The normalized longitude value, guaranteed to be within the range `[-180.0, 180.0]`. | + +#### Example +```kotlin +// A longitude value that is out of the standard range +val longitude1 = 190.0 +val normalizedLng1 = normalizeLng(longitude1) // Result: -170.0 + +// A negative longitude value that is out of range +val longitude2 = -200.0 +val normalizedLng2 = normalizeLng(longitude2) // Result: 160.0 + +// A longitude value already within the range +val longitude3 = 150.5 +val normalizedLng3 = normalizeLng(longitude3) // Result: 150.5 + +println("190.0 becomes $normalizedLng1") +println("-200.0 becomes $normalizedLng2") +println("150.5 becomes $normalizedLng3") +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/circle/AbstractCircleOverlayRenderer.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/circle/AbstractCircleOverlayRenderer.kt.md new file mode 100644 index 00000000..76aece95 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/circle/AbstractCircleOverlayRenderer.kt.md @@ -0,0 +1,209 @@ +# AbstractCircleOverlayRenderer + +An abstract base class for rendering circle overlays on a map. This class provides the core logic for processing add, change, and remove operations for circles, delegating the platform-specific rendering details to subclasses. + +Developers should extend this class to create a concrete renderer for a specific map provider (e.g., Google Maps, Mapbox). Subclasses are required to implement the abstract methods for creating, updating, and removing the actual map circle objects. + +The generic type `` represents the platform-specific circle object (e.g., `com.google.android.gms.maps.model.Circle`). + +## Properties + +| Name | Type | Description | +| :--- | :--- | :--- | +| `holder` | `MapViewHolderInterface<*, *>` | **[Abstract]** The map view holder that provides access to the map instance. | +| `coroutine` | `CoroutineScope` | **[Abstract]** The coroutine scope used to launch and manage suspend functions. | + +## Methods + +### onPostProcess +A lifecycle hook called after all add, change, and remove operations for a given update cycle are complete. Subclasses can override this to perform any final processing, such as triggering a screen redraw. The default implementation is empty. + +**Signature** +```kotlin +suspend fun onPostProcess() +``` + +--- + +### removeCircle +**[Abstract]** Subclasses must implement this method to remove a platform-specific circle from the map. + +**Signature** +```kotlin +abstract suspend fun removeCircle(entity: CircleEntityInterface) +``` + +**Parameters** + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `entity` | `CircleEntityInterface` | The circle entity containing the platform-specific circle object to be removed from the map. | + +--- + +### createCircle +**[Abstract]** Subclasses must implement this method to create a new platform-specific circle on the map based on the provided state. + +**Signature** +```kotlin +abstract suspend fun createCircle(state: CircleState): ActualCircle? +``` + +**Parameters** + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `state` | `CircleState` | An object containing the initial properties (e.g., center, radius, color) for the new circle. | + +**Returns** + +| Type | Description | +| :--- | :--- | +| `ActualCircle?` | The newly created platform-specific circle object, or `null` if creation failed. | + +--- + +### updateCircleProperties +**[Abstract]** Subclasses must implement this method to update the properties of an existing platform-specific circle on the map. + +**Signature** +```kotlin +abstract suspend fun updateCircleProperties( + circle: ActualCircle, + current: CircleEntityInterface, + prev: CircleEntityInterface, +): ActualCircle? +``` + +**Parameters** + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `circle` | `ActualCircle` | The existing platform-specific circle object to update. | +| `current` | `CircleEntityInterface` | The entity representing the new, updated state of the circle. | +| `prev` | `CircleEntityInterface` | The entity representing the previous state of the circle, for comparison. | + +**Returns** + +| Type | Description | +| :--- | :--- | +| `ActualCircle?` | The updated circle object. This can be the same instance or a new one. Returns `null` if the update resulted in the circle being removed or replaced. | + +--- + +### onAdd +Handles the addition of new circles to the map. This method iterates through a list of circle data and calls the abstract `createCircle` method for each one. + +**Signature** +```kotlin +override suspend fun onAdd( + data: List +): List +``` + +**Parameters** + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `data` | `List` | A list of parameters, where each element contains the state for a new circle to be added. | + +**Returns** + +| Type | Description | +| :--- | :--- | +| `List` | A list of the newly created platform-specific circle objects. An element will be `null` if the corresponding circle could not be created. | + +--- + +### onChange +Handles property changes for existing circles. This method iterates through a list of change data and calls the abstract `updateCircleProperties` method for each circle that has been modified. + +**Signature** +```kotlin +override suspend fun onChange( + data: List> +): List +``` + +**Parameters** + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `data` | `List>` | A list of parameters, where each element contains the previous and current state for a circle to be updated. | + +**Returns** + +| Type | Description | +| :--- | :--- | +| `List` | A list of the updated platform-specific circle objects. | + +--- + +### onRemove +Handles the removal of circles from the map. This method iterates through a list of circle entities and calls the abstract `removeCircle` method for each one. + +**Signature** +```kotlin +override suspend fun onRemove(data: List>) +``` + +**Parameters** + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `data` | `List>` | A list of circle entities to be removed from the map. | + +## Example + +Here is a conceptual example of how to subclass `AbstractCircleOverlayRenderer` for Google Maps. + +```kotlin +// Assuming GoogleMap.Circle is the ActualCircle type +// and GoogleMapViewHolder implements MapViewHolderInterface +class GoogleCircleOverlayRenderer( + override val holder: GoogleMapViewHolder, + override val coroutine: CoroutineScope +) : AbstractCircleOverlayRenderer() { + + private val googleMap: GoogleMap? + get() = holder.getMap() + + override suspend fun createCircle(state: CircleState): Circle? { + val map = googleMap ?: return null + + val circleOptions = CircleOptions() + .center(state.center.toLatLng()) + .radius(state.radius) + .strokeColor(state.strokeColor) + .fillColor(state.fillColor) + .zIndex(state.zIndex) + + return map.addCircle(circleOptions) + } + + override suspend fun updateCircleProperties( + circle: Circle, + current: CircleEntityInterface, + prev: CircleEntityInterface + ): Circle? { + // Update properties that have changed + if (current.state.center != prev.state.center) { + circle.center = current.state.center.toLatLng() + } + if (current.state.radius != prev.state.radius) { + circle.radius = current.state.radius + } + if (current.state.fillColor != prev.state.fillColor) { + circle.fillColor = current.state.fillColor + } + // ... update other properties as needed + + return circle + } + + override suspend fun removeCircle(entity: CircleEntityInterface) { + // The platform-specific circle object is stored in the entity + entity.circle.remove() + } +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/circle/Circle.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/circle/Circle.kt.md new file mode 100644 index 00000000..e53944d1 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/circle/Circle.kt.md @@ -0,0 +1,159 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet, formatted in Markdown. + +*** + +# CircleState SDK Documentation + +This document provides detailed information about the `CircleState` class and its related components, which are used to define and manage a circle on a map. + +## `CircleState` + +### Signature +```kotlin +class CircleState( + center: GeoPointInterface, + radiusMeters: Double, + geodesic: Boolean = true, + clickable: Boolean = true, + strokeColor: Color = Color.Red, + strokeWidth: Dp = 1.dp, + fillColor: Color, + id: String? = null, + zIndex: Int? = null, + extra: Serializable? = null, + onClick: OnCircleEventHandler? = null, +) : ComponentState +``` + +### Description +The `CircleState` class is a state holder that defines the properties and behavior of a circle drawn on a map. It is designed to be used within a Jetpack Compose environment, where changes to its properties will automatically trigger UI updates. + +An instance of `CircleState` represents a single circle. It manages properties such as position, size, appearance, and interactivity. If an `id` is not provided, a unique one is generated based on the circle's properties. + +### Parameters +| Parameter | Type | Description | Default | +|---|---|---|---| +| `center` | `GeoPointInterface` | The geographic coordinates for the center of the circle. | (required) | +| `radiusMeters` | `Double` | The radius of the circle in meters. | (required) | +| `geodesic` | `Boolean` | Specifies if the circle should be drawn as a geodesic shape. A geodesic circle correctly represents the shape on the Earth's curved surface. | `true` | +| `clickable` | `Boolean` | Determines if the circle can receive click events. If `true`, the `onClick` handler will be invoked on user clicks. | `true` | +| `strokeColor` | `Color` | The color of the circle's outline. | `Color.Red` | +| `strokeWidth` | `Dp` | The width of the circle's outline. | `1.dp` | +| `fillColor` | `Color` | The color used to fill the interior of the circle. | semi-transparent white | +| `id` | `String?` | An optional unique identifier for the circle. If `null`, an ID will be generated automatically. | `null` | +| `zIndex` | `Int?` | The z-index of the circle, which determines its stacking order relative to other map components. Higher values are drawn on top. | `null` | +| `extra` | `Serializable?` | Optional, serializable data that can be associated with the circle. | `null` | +| `onClick` | `OnCircleEventHandler?` | A callback function that is invoked when the circle is clicked. This requires `clickable` to be `true`. | `null` | + +--- + +## Member Functions + +### `copy()` + +#### Signature +```kotlin +fun copy( + center: GeoPointInterface = this.center, + radiusMeters: Double = this.radiusMeters, + // ... other parameters +): CircleState +``` + +#### Description +Creates a new `CircleState` instance with the same properties as the original, allowing for specific properties to be overridden. This is useful for creating a modified version of a state without changing the original object. + +#### Returns +| Type | Description | +|---|---| +| `CircleState` | A new `CircleState` instance with the updated properties. | + +### `asFlow()` + +#### Signature +```kotlin +fun asFlow(): Flow +``` + +#### Description +Returns a `Flow` that emits a new `CircleFingerPrint` whenever a property of the `CircleState` changes. This is useful for observing state changes in a reactive way. The flow is configured to emit only when the state has genuinely changed. + +#### Returns +| Type | Description | +|---|---| +| `Flow` | A flow that emits a fingerprint of the circle's state upon any change. | + +--- + +## Related Components + +### `CircleEvent` + +#### Signature +```kotlin +data class CircleEvent( + val state: CircleState, + val clicked: GeoPointInterface, +) +``` + +#### Description +A data class that encapsulates information about a click event on a circle. An object of this type is passed to the `OnCircleEventHandler` when a user clicks a circle. + +#### Properties +| Property | Type | Description | +|---|---|---| +| `state` | `CircleState` | The `CircleState` of the circle that was clicked. | +| `clicked` | `GeoPointInterface` | The exact geographical coordinate where the click occurred. | + +### `OnCircleEventHandler` + +#### Signature +```kotlin +typealias OnCircleEventHandler = (CircleEvent) -> Unit +``` + +#### Description +A type alias for the function that handles circle click events. It defines a function that takes a `CircleEvent` as a parameter and returns `Unit`. + +--- + +## Example + +The following example demonstrates how to create a `CircleState`, define a click handler, and use the `copy()` method to create a modified version. + +```kotlin +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp +import com.mapconductor.core.features.GeoPoint // Assuming GeoPoint implements GeoPointInterface + +// 1. Define a click handler +val handleCircleClick: OnCircleEventHandler = { circleEvent -> + println("Circle clicked: ${circleEvent.state.id}") + println("Click position: ${circleEvent.clicked.latitude}, ${circleEvent.clicked.longitude}") +} + +// 2. Define the center point for the circle +val circleCenter = GeoPoint(latitude = 34.0522, longitude = -118.2437) + +// 3. Create an instance of CircleState +val myCircle = CircleState( + center = circleCenter, + radiusMeters = 1000.0, + strokeColor = Color.Blue, + strokeWidth = 2.dp, + fillColor = Color.Blue.copy(alpha = 0.3f), + clickable = true, + onClick = handleCircleClick, + extra = "MyCircleData" +) + +// 4. Use the copy() method to create a new circle with a larger radius +val largerCircle = myCircle.copy( + radiusMeters = 2000.0, + id = "larger-circle" // Assign a new ID +) + +// Now `myCircle` and `largerCircle` can be added to a map component. +// When `myCircle` is clicked, the `handleCircleClick` lambda will be executed. +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/circle/CircleCapableInterface.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/circle/CircleCapableInterface.kt.md new file mode 100644 index 00000000..884b999e --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/circle/CircleCapableInterface.kt.md @@ -0,0 +1,154 @@ +# CircleCapableInterface + +An interface for components that can manage and display circles, such as a map view. It provides functionalities to add, update, and interact with circles on the component. + +--- + +## Methods + +### compositionCircles + +Adds or updates a collection of circles. This method is designed to manage the full set of circles, replacing any previously displayed circles with the new list provided. + +**Signature** +```kotlin +suspend fun compositionCircles(data: List) +``` + +**Description** +This is a suspend function that recomposes the view with a new list of circles. It's an efficient way to display a complete set of circles, as it handles adding, updating, and removing circles to match the provided `data` list. + +**Parameters** +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `data` | `List` | A list of `CircleState` objects, each defining the properties of a circle to be displayed. | + +**Returns** +This is a `suspend` function and does not return a value. + +**Example** +```kotlin +// Requires a CoroutineScope to launch the suspend function +coroutineScope.launch { + val circle1 = CircleState(id = "c1", center = LatLng(34.0, -118.2), radius = 1000.0) + val circle2 = CircleState(id = "c2", center = LatLng(36.1, -115.1), radius = 1500.0) + + mapView.compositionCircles(listOf(circle1, circle2)) +} +``` + +--- + +### updateCircle + +Updates a single existing circle with a new state. + +**Signature** +```kotlin +suspend fun updateCircle(state: CircleState) +``` + +**Description** +This suspend function updates the properties of a single circle that is already on the map. The circle to be updated is identified by the `id` within the provided `CircleState` object. + +**Parameters** +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `state` | `CircleState` | The new state for the circle. The `id` in the state must match an existing circle. | + +**Returns** +This is a `suspend` function and does not return a value. + +**Example** +```kotlin +// Assume a circle with id "c1" already exists on the map +coroutineScope.launch { + val updatedCircleState = CircleState( + id = "c1", + center = LatLng(34.0, -118.2), + radius = 2500.0, // Update the radius + fillColor = Color.BLUE // Update the color + ) + + mapView.updateCircle(updatedCircleState) +} +``` + +--- + +### setOnCircleClickListener + +Sets a listener to handle click events on any circle. + +> **Deprecated:** This method is deprecated. Use the `onClick` lambda property within `CircleState` for handling click events on a per-circle basis. + +**Signature** +```kotlin +@Deprecated("Use CircleState.onClick instead.") +fun setOnCircleClickListener(listener: OnCircleEventHandler?) +``` + +**Description** +Registers a callback function that will be invoked when any circle managed by this interface is clicked. Setting the listener to `null` removes any previously set listener. + +**Parameters** +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `listener` | `OnCircleEventHandler?` | The listener to be invoked on a circle click, or `null` to remove the current listener. | + +**Returns** +This function does not return a value. + +**Example** +```kotlin +// Old, deprecated way +val circleClickListener = OnCircleEventHandler { circleState -> + Log.d("Map", "Circle with ID ${circleState.id} was clicked.") +} +mapView.setOnCircleClickListener(circleClickListener) + +// Recommended modern approach: Define the click handler directly in the state +val circleWithHandler = CircleState( + id = "c1", + center = LatLng(34.0, -118.2), + radius = 1000.0, + onClick = { state -> + Log.d("Map", "Circle ${state.id} clicked!") + } +) +``` + +--- + +### hasCircle + +Checks if a specific circle is currently managed by the component. + +**Signature** +```kotlin +fun hasCircle(state: CircleState): Boolean +``` + +**Description** +Determines whether a circle, identified by the `id` in the provided `CircleState`, exists on the map. + +**Parameters** +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `state` | `CircleState` | The `CircleState` object representing the circle to check. Only the `id` is typically used for the lookup. | + +**Returns** +| Type | Description | +| :--- | :--- | +| `Boolean` | Returns `true` if a circle with the same ID exists, `false` otherwise. | + +**Example** +```kotlin +val circleToCheck = CircleState(id = "c1") + +if (mapView.hasCircle(circleToCheck)) { + println("Circle 'c1' is currently on the map.") +} else { + println("Circle 'c1' is not on the map.") +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/circle/CircleCompose.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/circle/CircleCompose.kt.md new file mode 100644 index 00000000..62c1b42f --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/circle/CircleCompose.kt.md @@ -0,0 +1,133 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +--- + +### `Circle` + +The `Circle` composable adds a circular overlay to the map. It is a convenient way to define and display a geographic area with a specific radius around a central point. + +This function must be called from within the scope of a `MapView` composable. + +### Signature + +```kotlin +@Composable +fun MapViewScope.Circle( + center: GeoPointInterface, + radiusMeters: Double, + id: String? = null, + strokeColor: Color = Color.Red, + strokeWidth: Dp = 2.dp, + fillColor: Color = Color.White.copy(alpha = 0.5f), + zIndex: Int? = null, + extra: Serializable? = null, + onClick: OnCircleEventHandler? = null, +) +``` + +### Description + +This composable creates and displays a circle on the map. You can customize its appearance, such as stroke color, width, and fill color, as well as its behavior, like handling click events. + +### Parameters + +| Parameter | Type | Description | Default Value | +| :------------- | :-------------------- | :------------------------------------------------------------------------------------------------------ | :----------------------------- | +| `center` | `GeoPointInterface` | **Required.** The geographical coordinate for the center of the circle. | - | +| `radiusMeters` | `Double` | **Required.** The radius of the circle in meters. | - | +| `id` | `String?` | An optional unique identifier for the circle. Useful for later retrieval or management. | `null` | +| `strokeColor` | `Color` | The color of the circle's outline. | `Color.Red` | +| `strokeWidth` | `Dp` | The width of the circle's outline in density-independent pixels (Dp). | `2.dp` | +| `fillColor` | `Color` | The color used to fill the circle's area. | `Color.White.copy(alpha=0.5f)` | +| `zIndex` | `Int?` | The z-index of the circle, which controls its stacking order relative to other overlays on the map. | `null` | +| `extra` | `Serializable?` | Optional serializable data to associate with the circle, which can be retrieved in event handlers. | `null` | +| `onClick` | `OnCircleEventHandler?` | A lambda function that is invoked when the user clicks on the circle. The handler receives the circle's state. | `null` | + +### Returns + +This composable does not return any value. + +### Example + +Here's how to add a simple circle to a map centered on a specific location. + +```kotlin +import com.mapconductor.core.MapView +import com.mapconductor.core.circle.Circle +import com.mapconductor.core.features.GeoPoint + +// ... inside a Composable function + +MapView( + // ... other MapView parameters +) { + // Add a circle with a 500-meter radius + Circle( + center = GeoPoint(40.7128, -74.0060), // New York City + radiusMeters = 500.0, + strokeColor = Color.Blue, + strokeWidth = 3.dp, + fillColor = Color.Blue.copy(alpha = 0.3f), + onClick = { circleState -> + println("Circle clicked! ID: ${circleState.id}") + } + ) +} +``` + +--- + +### `Circle (State-based)` + +This is an alternative version of the `Circle` composable that accepts a `CircleState` object. This is useful for managing the state of circles declaratively, for instance, from a ViewModel. + +### Signature + +```kotlin +@Composable +fun MapViewScope.Circle(state: CircleState) +``` + +### Description + +This composable adds a circle to the map based on the properties defined in the provided `CircleState` object. It handles the addition and removal of the circle from the map as the composable enters or leaves the composition. + +### Parameters + +| Parameter | Type | Description | +| :-------- | :------------ | :----------------------------------------------------------------------- | +| `state` | `CircleState` | A state holder object that encapsulates all properties of the circle. | + +### Returns + +This composable does not return any value. + +### Example + +This example shows how to manage a `CircleState` and pass it to the composable. + +```kotlin +import com.mapconductor.core.MapView +import com.mapconductor.core.circle.Circle +import com.mapconductor.core.circle.CircleState +import com.mapconductor.core.features.GeoPoint + +// ... inside a Composable function + +// Create and remember the CircleState +val circleState = remember { + CircleState( + center = GeoPoint(34.0522, -118.2437), // Los Angeles + radiusMeters = 1000.0, + strokeColor = Color.Green, + fillColor = Color.Green.copy(alpha = 0.2f) + ) +} + +MapView( + // ... other MapView parameters +) { + // Add the circle using its state + Circle(state = circleState) +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/circle/CircleController.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/circle/CircleController.kt.md new file mode 100644 index 00000000..7c8272f9 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/circle/CircleController.kt.md @@ -0,0 +1,200 @@ +Of course! Here is the high-quality SDK documentation for the provided `CircleController` code snippet. + +--- + +# CircleController + +## Signature + +```kotlin +abstract class CircleController( + val circleManager: CircleManagerInterface, + open val renderer: CircleOverlayRendererInterface, + override var clickListener: OnCircleEventHandler? = null, +) : OverlayControllerInterface< + CircleState, + CircleEntityInterface, + CircleEvent, + > +``` + +## Description + +The `CircleController` is an abstract base class responsible for managing and rendering circle overlays on a map. It acts as a bridge between abstract `CircleState` data and the platform-specific circle objects (`ActualCircle`) displayed on the map. + +This controller handles the lifecycle of circles, including adding, updating, and removing them in a batch or individually. It uses a `CircleManagerInterface` to manage the state of circle entities and a `CircleOverlayRendererInterface` to handle the actual platform-specific drawing operations. + +All modification operations (`add`, `update`, `clear`) are thread-safe, ensured by an internal semaphore that guarantees atomic execution. + +## Generic Parameters + +| Parameter | Description | +| :------------- | :----------------------------------------------------------------------------------- | +| `ActualCircle` | The platform-specific circle object type (e.g., `com.google.android.gms.maps.model.Circle`). | + +## Constructor + +Creates a new instance of `CircleController`. + +| Parameter | Type | Description | +| :-------------- | :----------------------------------------------- | :------------------------------------------------------------------------------------------------------ | +| `circleManager` | `CircleManagerInterface` | An instance that manages the state and entities of the circles. | +| `renderer` | `CircleOverlayRendererInterface` | An instance responsible for the platform-specific rendering of circles on the map. | +| `clickListener` | `OnCircleEventHandler?` | An optional global listener that is invoked when any circle managed by this controller is clicked. Defaults to `null`. | + +## Properties + +| Property | Type | Description | +| :-------------- | :--------------------------------------------- | :------------------------------------------------------------------------------------------------------ | +| `circleManager` | `CircleManagerInterface` | The manager for circle state and entities. | +| `renderer` | `CircleOverlayRendererInterface` | The renderer for drawing circles on the map. | +| `clickListener` | `OnCircleEventHandler?` | The global click listener for all circles. Can be set or updated after initialization. | +| `zIndex` | `Int` | The z-index for the circle layer, determining its drawing order relative to other overlays. Defaults to `3`. | +| `semaphore` | `Semaphore` | A semaphore ensuring that `add`, `update`, and `clear` operations are executed atomically and are thread-safe. | + +## Methods + +### dispatchClick + +Dispatches a click event. This method invokes the specific `onClick` handler defined in the circle's `CircleState` and also calls the controller's global `clickListener`, if one is set. + +**Signature** +```kotlin +fun dispatchClick(event: CircleEvent) +``` + +**Parameters** + +| Parameter | Type | Description | +| :-------- | :------------ | :---------------------------------------- | +| `event` | `CircleEvent` | The circle event object containing click details. | + +### add + +Synchronizes the circles on the map with the provided list of `CircleState` objects. The method calculates the difference between the current state and the new data, then performs the necessary add, update, and remove operations via the `renderer`. The entire operation is performed atomically. + +**Signature** +```kotlin +override suspend fun add(data: List) +``` + +**Parameters** + +| Parameter | Type | Description | +| :-------- | :------------------ | :---------------------------------------- | +| `data` | `List` | The complete list of circle states to be displayed on the map. | + +### update + +Updates a single circle on the map based on the provided `CircleState`. To optimize performance, the update is only performed if the new state's "fingerprint" is different from the existing one. This is an atomic operation. + +**Signature** +```kotlin +override suspend fun update(state: CircleState) +``` + +**Parameters** + +| Parameter | Type | Description | +| :-------- | :------------ | :------------------------------------------- | +| `state` | `CircleState` | The new state for the circle to be updated. | + +### clear + +Removes all circles managed by this controller from the map. This is an atomic operation. + +**Signature** +```kotlin +override suspend fun clear() +``` + +### find + +Finds the topmost circle entity at a given geographic coordinate. This is useful for implementing features like tap-to-select. + +**Signature** +```kotlin +override fun find(position: GeoPointInterface): CircleEntityInterface? +``` + +**Parameters** + +| Parameter | Type | Description | +| :--------- | :------------------ | :---------------------------------------- | +| `position` | `GeoPointInterface` | The geographic coordinate to search for a circle. | + +**Returns** + +`CircleEntityInterface?` — The `CircleEntityInterface` at the specified position, or `null` if no circle is found. + +### onCameraChanged + +A lifecycle callback invoked when the map camera position changes. The base implementation is empty and can be overridden by subclasses to add custom logic, such as level-of-detail rendering. + +**Signature** +```kotlin +override suspend fun onCameraChanged(mapCameraPosition: MapCameraPosition) +``` + +**Parameters** + +| Parameter | Type | Description | +| :------------------ | :------------------ | :---------------------------------- | +| `mapCameraPosition` | `MapCameraPosition` | The new position of the map camera. | + +### destroy + +Cleans up resources used by the controller. The base implementation is empty as there are no specific native resources to release at this abstract level. Subclasses should override this method to release any platform-specific resources. + +**Signature** +```kotlin +override fun destroy() +``` + +## Example + +Since `CircleController` is an abstract class, you must create a concrete implementation for your specific map platform. The following example demonstrates how you might use a hypothetical concrete implementation, `MapCircleController`. + +```kotlin +import kotlinx.coroutines.runBlocking + +// Assume MapCircleController is a concrete implementation of CircleController +// and other necessary classes (MapCircleManager, MapCircleRenderer) are defined. +// val mapCircleController: MapCircleController = ... + +fun manageMapCircles() = runBlocking { + // 1. Define the states for the circles you want to display + val circleOneState = CircleState( + id = "circle-1", + center = GeoPoint(40.7128, -74.0060), // New York City + radius = 1000.0, // in meters + fillColor = Color.BLUE, + onClick = { event -> println("Circle ${event.state.id} clicked!") } + ) + + val circleTwoState = CircleState( + id = "circle-2", + center = GeoPoint(34.0522, -118.2437), // Los Angeles + radius = 1500.0, + fillColor = Color.RED + ) + + // 2. Add the circles to the map + println("Adding two circles...") + mapCircleController.add(listOf(circleOneState, circleTwoState)) + + // ... user interacts with the map ... + + // 3. Update a single circle + println("Updating circle one...") + val updatedCircleOneState = circleOneState.copy( + fillColor = Color.GREEN, + radius = 1200.0 + ) + mapCircleController.update(updatedCircleOneState) + + // 4. Remove all circles from the map + println("Clearing all circles...") + mapCircleController.clear() +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/circle/CircleEntity.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/circle/CircleEntity.kt.md new file mode 100644 index 00000000..57e77107 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/circle/CircleEntity.kt.md @@ -0,0 +1,105 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +*** + +# CircleEntity API Reference + +This document provides detailed documentation for the `CircleEntityInterface` and its concrete implementation, `CircleEntity`. These components are used to represent and manage circle objects and their associated state within the system. + +## `CircleEntityInterface` + +### Signature + +```kotlin +interface CircleEntityInterface +``` + +### Description + +The `CircleEntityInterface` defines the essential contract for any entity that represents a circle. It provides a standardized way to access the underlying circle object, its current state, and a unique fingerprint representing that state. The use of a generic type `ActualCircle` allows this interface to be implemented with any custom circle representation. + +### Properties + +| Property | Type | Description | +|---------------|---------------------|---------------------------------------------------------------------------------------------------------| +| `circle` | `ActualCircle` | A mutable reference to the underlying circle object. This can be any custom class representing a circle. | +| `state` | `CircleState` | An immutable property representing the current state of the circle entity. | +| `fingerPrint` | `CircleFingerPrint` | A unique identifier derived from the circle's `state`, used for efficient state comparison and tracking. | + +--- + +## `CircleEntity` + +### Signature + +```kotlin +class CircleEntity( + override var circle: ActualCircle, + override val state: CircleState, +) : CircleEntityInterface +``` + +### Description + +`CircleEntity` is the primary, concrete implementation of the `CircleEntityInterface`. It acts as a wrapper that encapsulates a generic circle object (`ActualCircle`) and its associated `CircleState`. + +Upon instantiation, it automatically generates a `fingerPrint` based on the provided `state`, making it easy to track and compare different circle entities. + +### Constructor + +Creates a new instance of `CircleEntity`. + +### Parameters + +| Parameter | Type | Description | +|-----------|----------------|--------------------------------------------------------| +| `circle` | `ActualCircle` | The underlying circle object to be managed by this entity. | +| `state` | `CircleState` | The initial state of the circle. | + +### Properties + +| Property | Type | Description | +|---------------|---------------------|------------------------------------------------------------------------------------------------------------| +| `circle` | `ActualCircle` | The underlying circle object provided in the constructor. This property is mutable. | +| `state` | `CircleState` | The state of the circle entity, as provided in the constructor. This property is immutable. | +| `fingerPrint` | `CircleFingerPrint` | A unique identifier automatically generated by calling the `fingerPrint()` method on the provided `state`. | + +### Example + +The following example demonstrates how to create and use a `CircleEntity` instance. + +```kotlin +// Assume the existence of these data classes for the example. +data class MyMapCircle(var radius: Double, var center: Pair) +data class CircleFingerPrint(val hash: String) +data class CircleState(val id: String, val isVisible: Boolean) { + fun fingerPrint(): CircleFingerPrint = CircleFingerPrint("hash_${id}_$isVisible") +} + +// 1. Define the state for our circle +val initialState = CircleState(id = "circle-main-1", isVisible = true) + +// 2. Define the actual circle object that will be displayed on a map +val mapCircle = MyMapCircle(radius = 100.0, center = 40.7128 to -74.0060) + +// 3. Create a CircleEntity instance to wrap the object and its state +val circleEntity = CircleEntity( + circle = mapCircle, + state = initialState +) + +// 4. Access its properties +println("Underlying circle object: ${circleEntity.circle}") +// > Underlying circle object: MyMapCircle(radius=100.0, center=(40.7128, -74.006)) + +println("Circle state: ${circleEntity.state}") +// > Circle state: CircleState(id=circle-main-1, isVisible=true) + +println("State fingerprint: ${circleEntity.fingerPrint}") +// > State fingerprint: CircleFingerPrint(hash=hash_circle-main-1_true) + +// 5. You can modify the underlying circle object directly +circleEntity.circle.radius = 150.0 +println("Updated circle object: ${circleEntity.circle}") +// > Updated circle object: MyMapCircle(radius=150.0, center=(40.7128, -74.006)) +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/circle/CircleManager.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/circle/CircleManager.kt.md new file mode 100644 index 00000000..2224a398 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/circle/CircleManager.kt.md @@ -0,0 +1,246 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +# Class: `CircleManager` + +A thread-safe manager for handling the lifecycle of circle entities on a map. It provides methods to add, remove, retrieve, and query circle objects. This class is responsible for maintaining a collection of `CircleEntityInterface` objects and uses a `ConcurrentHashMap` for its internal storage, ensuring safe concurrent access. + +The manager uses a generic type `ActualCircle` to represent the platform-specific circle implementation (e.g., a Google Maps `Circle` or a Mapbox circle annotation). + +## Methods + +### `registerEntity` + +Adds a new circle entity to the manager. If an entity with the same ID already exists, it will be replaced. + +**Signature** +```kotlin +fun registerEntity(entity: CircleEntityInterface) +``` + +**Description** +This method registers a given `CircleEntityInterface` with the manager, using the entity's ID as the unique key for storage. + +**Parameters** +| Parameter | Type | Description | +|-----------|------|-------------| +| `entity` | `CircleEntityInterface` | The circle entity to register. | + +**Example** +```kotlin +// Assuming circleManager is an instance of CircleManager +// and newCircle is an object implementing CircleEntityInterface +circleManager.registerEntity(newCircle) +``` + +--- + +### `updateEntity` + +Updates an existing circle entity or adds it if it doesn't exist. This method functions as an alias for `registerEntity`. + +**Signature** +```kotlin +fun updateEntity(entity: CircleEntityInterface) +``` + +**Description** +This method adds or updates a circle entity in the manager. It is functionally identical to `registerEntity`. + +**Parameters** +| Parameter | Type | Description | +|-----------|------|-------------| +| `entity` | `CircleEntityInterface` | The circle entity to add or update. | + +**Example** +```kotlin +// Get an existing entity and modify its state +val existingCircle = circleManager.getEntity("circle-id-1") +existingCircle?.state?.radiusMeters = 500.0 + +// Update the entity in the manager +if (existingCircle != null) { + circleManager.updateEntity(existingCircle) +} +``` + +--- + +### `removeEntity` + +Removes a circle entity from the manager based on its unique ID. + +**Signature** +```kotlin +fun removeEntity(id: String): CircleEntityInterface? +``` + +**Description** +This method finds and removes the circle entity associated with the specified `id`. + +**Parameters** +| Parameter | Type | Description | +|-----------|------|-------------| +| `id` | `String` | The unique identifier of the circle entity to remove. | + +**Returns** +| Type | Description | +|------|-------------| +| `CircleEntityInterface?` | The removed circle entity if it was found, or `null` if no entity with the specified ID exists. | + +**Example** +```kotlin +val removedEntity = circleManager.removeEntity("circle-id-to-delete") +if (removedEntity != null) { + println("Circle ${removedEntity.state.id} was successfully removed.") +} else { + println("Circle not found.") +} +``` + +--- + +### `getEntity` + +Retrieves a circle entity from the manager by its unique ID. + +**Signature** +```kotlin +fun getEntity(id: String): CircleEntityInterface? +``` + +**Description** +This method provides read-only access to a circle entity without removing it from the manager. + +**Parameters** +| Parameter | Type | Description | +|-----------|------|-------------| +| `id` | `String` | The unique identifier of the circle entity to retrieve. | + +**Returns** +| Type | Description | +|------|-------------| +| `CircleEntityInterface?` | The `CircleEntityInterface` corresponding to the given ID, or `null` if not found. | + +**Example** +```kotlin +val circle = circleManager.getEntity("circle-id-123") +circle?.let { + // Use the retrieved circle entity + println("Found circle with radius: ${it.state.radiusMeters}") +} +``` + +--- + +### `hasEntity` + +Checks if a circle entity with the specified ID is currently being managed. + +**Signature** +```kotlin +fun hasEntity(id: String): Boolean +``` + +**Parameters** +| Parameter | Type | Description | +|-----------|------|-------------| +| `id` | `String` | The unique identifier to check for. | + +**Returns** +| Type | Description | +|--------|-------------| +| `Boolean` | Returns `true` if an entity with the given ID exists, `false` otherwise. | + +**Example** +```kotlin +if (circleManager.hasEntity("circle-id-456")) { + println("The manager contains the circle.") +} else { + println("The circle is not in the manager.") +} +``` + +--- + +### `allEntities` + +Returns a list of all circle entities currently managed. + +**Signature** +```kotlin +fun allEntities(): List> +``` + +**Returns** +| Type | Description | +|------|-------------| +| `List>` | A `List` containing all `CircleEntityInterface` objects. The list will be empty if no entities are managed. | + +**Example** +```kotlin +val allCircles = circleManager.allEntities() +println("Total circles being managed: ${allCircles.size}") +for (circle in allCircles) { + // Process each circle +} +``` + +--- + +### `clear` + +Removes all circle entities from the manager, leaving it empty. + +**Signature** +```kotlin +fun clear() +``` + +**Description** +This method is useful for resetting the manager's state and clearing all circles from the map view. + +**Example** +```kotlin +// Remove all circles from the manager +circleManager.clear() +assert(circleManager.allEntities().isEmpty()) +``` + +--- + +### `find` + +Finds the top-most, clickable circle entity that contains a given geographic coordinate. + +**Signature** +```kotlin +fun find(position: GeoPointInterface): CircleEntityInterface? +``` + +**Description** +This method implements a "hit test" for circles. The search logic is as follows: +1. Filters all managed circles to find those whose radius contains the specified `position`. +2. From the filtered list, it only considers circles that are marked as clickable (`entity.state.clickable` is `true`). +3. If multiple clickable circles are found at the position, it returns the one with the highest `zIndex`. The `zIndex` is determined by `entity.state.zIndex` if set; otherwise, it's calculated based on the circle's center coordinate. + +**Parameters** +| Parameter | Type | Description | +|-----------|------|-------------| +| `position` | `GeoPointInterface` | The geographic coordinate (e.g., from a map tap event) to search at. | + +**Returns** +| Type | Description | +|------|-------------| +| `CircleEntityInterface?` | The matching `CircleEntityInterface` with the highest `zIndex`, or `null` if no clickable circle is found at the given position. | + +**Example** +```kotlin +// A GeoPoint representing a user's tap on the map +val tapPosition = GeoPoint(40.7128, -74.0060) + +val tappedCircle = circleManager.find(tapPosition) + +tappedCircle?.let { + println("User tapped on circle with ID: ${it.state.id}") +} ?: println("No clickable circle was found at this location.") +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/circle/CircleOverlay.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/circle/CircleOverlay.kt.md new file mode 100644 index 00000000..68954212 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/circle/CircleOverlay.kt.md @@ -0,0 +1,97 @@ +Excellent. Here is the high-quality SDK documentation for the provided code snippet. + +*** + +### `LocalCircleCollector` + +**Signature** + +```kotlin +val LocalCircleCollector: ProvidableCompositionLocal> +``` + +**Description** + +A `CompositionLocal` that provides access to a `ChildCollector` for `CircleState` objects. This collector is used internally by circle-related composables (e.g., ``) to register their state with the parent `` component. + +Attempting to use a composable that relies on this collector outside of a `` component will result in a runtime error, as the `MapView` is responsible for providing the collector instance. + +### `CircleOverlay` + +**Signature** + +```kotlin +class CircleOverlay( + override val flow: StateFlow> +) : MapOverlayInterface +``` + +**Description** + +`CircleOverlay` is an internal class that manages the rendering of all circle objects on the map. It implements the `MapOverlayInterface` and is responsible for observing a stream of circle states and passing them to the map controller for rendering. + +This class is typically instantiated and managed by the `` component and is not intended for direct use by developers. It serves as a bridge between the declarative `Circle` composables and the underlying map rendering engine. + +#### Constructor Parameters + +| Parameter | Type | Description | +|-----------|------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------| +| `flow` | `StateFlow>` | A state flow that emits the current map of circle states. The key is a unique identifier for the circle, and the value is its `CircleState`. | + +--- + +### Methods + +#### `render` + +**Signature** + +```kotlin +override suspend fun render( + data: MutableMap, + controller: MapViewControllerInterface +) +``` + +**Description** + +This function is called by the map's rendering system to draw the circles on the map. It delegates the rendering task to the provided `controller` by casting it to a `CircleCapableInterface` and invoking its `compositionCircles` method with the latest circle data. + +**Note:** This is part of the `MapOverlayInterface` implementation and should not be called directly from application code. + +**Parameters** + +| Parameter | Type | Description | +|--------------|------------------------------------|---------------------------------------------------------------------------------------------------------| +| `data` | `MutableMap` | A map containing the state of all circles to be rendered. | +| `controller` | `MapViewControllerInterface` | The map view controller responsible for executing rendering commands. It must implement `CircleCapableInterface`. | + +--- + +### Example + +While `CircleOverlay` and `LocalCircleCollector` are used internally, the following example illustrates how a developer would typically add a circle to a map. This interaction relies on these components behind the scenes. + +```kotlin +import androidx.compose.runtime.Composable +import com.mapconductor.core.MapView +import com.mapconductor.core.circle.Circle +import com.mapconductor.geo.LatLng +import androidx.compose.ui.graphics.Color + +@Composable +fun MyMapWithCircle() { + // The MapView component provides the LocalCircleCollector + // and uses CircleOverlay to render the collected circles. + MapView { + // The Circle composable uses LocalCircleCollector to register its state + // with the parent MapView. + Circle( + center = LatLng(34.0522, -118.2437), // Los Angeles + radius = 1000.0, // in meters + strokeColor = Color.Blue, + fillColor = Color.Blue.copy(alpha = 0.3f) + ) + } +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/circle/CircleOverlayRendererInterface.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/circle/CircleOverlayRendererInterface.kt.md new file mode 100644 index 00000000..c58fc52f --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/circle/CircleOverlayRendererInterface.kt.md @@ -0,0 +1,196 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +--- + +# CircleOverlayRendererInterface + +## Signature + +```kotlin +interface CircleOverlayRendererInterface +``` + +## Description + +The `CircleOverlayRendererInterface` defines a contract for rendering and managing the lifecycle of circle overlays on a map. Implement this interface to create a custom renderer that handles the addition, modification, and removal of circles based on state changes. + +The interface is generic, allowing it to work with different map SDKs by specifying the platform-specific circle object type for `ActualCircle` (e.g., `com.google.android.gms.maps.model.Circle` for Google Maps). + +## Type Parameters + +| Name | Description | +| :------------- | :------------------------------------------------------------------------------------------------------ | +| `ActualCircle` | The platform-specific class representing a circle object on the map (e.g., `GoogleMap.Circle`). | + +--- + +## Nested Interfaces + +### AddParamsInterface + +**Signature** +```kotlin +interface AddParamsInterface +``` + +**Description** +A data structure that holds the required information to add a new circle to the map. + +**Properties** + +| Name | Type | Description | +| :------ | :------------ | :---------------------------------------- | +| `state` | `CircleState` | The state object containing all the properties for the new circle (e.g., center, radius, color). | + +### ChangeParamsInterface + +**Signature** +```kotlin +interface ChangeParamsInterface +``` + +**Description** +A data structure that holds the information needed to update an existing circle. It provides both the previous and the new state of the circle entity. + +**Type Parameters** + +| Name | Description | +| :------------- | :------------------------------------------------------------------------------------------------------ | +| `ActualCircle` | The platform-specific class representing a circle object on the map. | + +**Properties** + +| Name | Type | Description | +| :-------- | :--------------------------------------- | :----------------------------------------------------------------------- | +| `current` | `CircleEntityInterface` | The circle entity containing the **new** state and properties to be applied. | +| `prev` | `CircleEntityInterface` | The circle entity containing the **previous** state and properties. | + +--- + +## Methods + +### onAdd + +**Signature** +```kotlin +suspend fun onAdd(data: List): List +``` + +**Description** +Asynchronously adds a batch of new circles to the map. This method is called when new circle states are introduced. + +**Parameters** + +| Name | Type | Description | +| :----- | :----------------------------- | :----------------------------------------------------------------------- | +| `data` | `List` | A list of `AddParamsInterface` objects, each describing a circle to be added. | + +**Returns** + +`List`: A list of the newly created, platform-specific `ActualCircle` objects. An element can be `null` if the creation of a specific circle failed. The order of the returned list must correspond to the order of the input `data` list. + +### onChange + +**Signature** +```kotlin +suspend fun onChange(data: List>): List +``` + +**Description** +Asynchronously updates a batch of existing circles on the map. This method is called when the properties of existing circles have changed. + +**Parameters** + +| Name | Type | Description | +| :----- | :------------------------------------------- | :----------------------------------------------------------------------- | +| `data` | `List>` | A list of `ChangeParamsInterface` objects, each describing the changes for a circle. | + +**Returns** + +`List`: A list of the updated, platform-specific `ActualCircle` objects. An element can be `null` if the update failed. The order of the returned list must correspond to the order of the input `data` list. + +### onRemove + +**Signature** +```kotlin +suspend fun onRemove(data: List>) +``` + +**Description** +Asynchronously removes a batch of circles from the map. + +**Parameters** + +| Name | Type | Description | +| :----- | :---------------------------------------- | :----------------------------------------------------------------------- | +| `data` | `List>` | A list of `CircleEntityInterface` objects representing the circles to be removed. | + +### onPostProcess + +**Signature** +```kotlin +suspend fun onPostProcess() +``` + +**Description** +A lifecycle callback that is executed after a batch of `onAdd`, `onChange`, and `onRemove` operations has been completed. This method can be used for cleanup, final rendering adjustments, or other post-processing tasks. + +--- + +## Example + +Here is a conceptual example of how to implement `CircleOverlayRendererInterface` for a hypothetical map SDK. + +```kotlin +// Assume these are defined elsewhere in the map SDK +// class MapView +// class MapCircle +// data class CircleOptions(val center: LatLng, val radius: Double, val color: Int) +// interface CircleEntityInterface { val actual: MapCircle?, val state: CircleState } +// data class CircleState(val center: LatLng, val radius: Double, val color: Int) + +class MyCircleRenderer(private val mapView: MapView) : CircleOverlayRendererInterface { + + override suspend fun onAdd(data: List): List { + return data.map { addParams -> + // Create circle options from the state + val options = CircleOptions( + center = addParams.state.center, + radius = addParams.state.radius, + color = addParams.state.color + ) + // Add the circle to the map and return the native circle object + mapView.addCircle(options) + } + } + + override suspend fun onChange(data: List>): List { + return data.map { changeParams -> + val circleToUpdate = changeParams.prev.actual + val newState = changeParams.current.state + + // Update the properties of the existing circle + circleToUpdate?.apply { + this.center = newState.center + this.radius = newState.radius + this.color = newState.color + } + // Return the updated circle + circleToUpdate + } + } + + override suspend fun onRemove(data: List>) { + data.forEach { circleEntity -> + // Remove the circle from the map + circleEntity.actual?.removeFromMap() + } + } + + override suspend fun onPostProcess() { + // For example, force a redraw of the map if needed + mapView.invalidate() + println("Circle batch processing complete.") + } +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/controller/BaseMapViewController.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/controller/BaseMapViewController.kt.md new file mode 100644 index 00000000..f8848217 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/controller/BaseMapViewController.kt.md @@ -0,0 +1,179 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +# BaseMapViewController + +`BaseMapViewController` is an abstract base class that provides core functionality for managing a map view. It is designed to be extended by concrete implementations for specific map providers (e.g., Google Maps, Mapbox). + +This class handles the registration of event listeners for common map interactions, such as camera movements, clicks, and long clicks. It also manages a collection of `OverlayControllerInterface` instances, automatically notifying them of camera changes. + +--- + +## Methods + +### setCameraMoveStartListener +Sets a listener that is invoked when the map camera starts moving. This is typically triggered when the user begins a pan or zoom gesture. + +**Signature** +```kotlin +fun setCameraMoveStartListener(listener: OnCameraMoveHandler?) +``` + +**Description** +Registers a callback to be executed when the camera movement on the map begins. To remove the listener, pass `null`. + +**Parameters** +| Name | Type | Description | +| :--- | :--- | :--- | +| `listener` | `OnCameraMoveHandler?` | The callback to invoke when the camera starts moving. It receives the `MapCameraPosition` at the start of the movement. | + +**Example** +```kotlin +mapViewController.setCameraMoveStartListener { mapCameraPosition -> + println("Camera move started at position: ${mapCameraPosition.target}") +} + +// To remove the listener +mapViewController.setCameraMoveStartListener(null) +``` + +--- + +### setCameraMoveListener +Sets a listener that is invoked repeatedly while the map camera is in motion. + +**Signature** +```kotlin +fun setCameraMoveListener(listener: OnCameraMoveHandler?) +``` + +**Description** +Registers a callback that is executed continuously as the camera position changes. This is useful for real-time UI updates that depend on the camera's viewport. To remove the listener, pass `null`. + +**Parameters** +| Name | Type | Description | +| :--- | :--- | :--- | +| `listener` | `OnCameraMoveHandler?` | The callback to invoke during camera movement. It receives the current `MapCameraPosition`. | + +**Example** +```kotlin +mapViewController.setCameraMoveListener { mapCameraPosition -> + // Update UI with the new camera position in real-time + updateCameraInfo(mapCameraPosition) +} + +// To remove the listener +mapViewController.setCameraMoveListener(null) +``` + +--- + +### setCameraMoveEndListener +Sets a listener that is invoked when the map camera movement has finished. + +**Signature** +```kotlin +fun setCameraMoveEndListener(listener: OnCameraMoveHandler?) +``` + +**Description** +Registers a callback to be executed once the camera has stopped moving. This is ideal for performing actions that should only happen after the user has settled on a new map view, such as fetching data for the new viewport. To remove the listener, pass `null`. + +**Parameters** +| Name | Type | Description | +| :--- | :--- | :--- | +| `listener` | `OnCameraMoveHandler?` | The callback to invoke when the camera movement ends. It receives the final `MapCameraPosition`. | + +**Example** +```kotlin +mapViewController.setCameraMoveEndListener { mapCameraPosition -> + println("Camera move ended at position: ${mapCameraPosition.target}") + // Fetch data for the new viewport + fetchDataForVisibleRegion(mapCameraPosition) +} + +// To remove the listener +mapViewController.setCameraMoveEndListener(null) +``` + +--- + +### setMapClickListener +Sets a listener that is invoked when the user clicks or taps on the map. + +**Signature** +```kotlin +fun setMapClickListener(listener: OnMapEventHandler?) +``` + +**Description** +Registers a callback to be executed when a click event occurs on the map. The callback receives the geographical coordinates (`LatLng`) of the click location. To remove the listener, pass `null`. + +**Parameters** +| Name | Type | Description | +| :--- | :--- | :--- | +| `listener` | `OnMapEventHandler?` | The callback to invoke on a map click. It receives the `LatLng` of the click location. | + +**Example** +```kotlin +mapViewController.setMapClickListener { latLng -> + println("Map clicked at coordinates: $latLng") +} + +// To remove the listener +mapViewController.setMapClickListener(null) +``` + +--- + +### setMapLongClickListener +Sets a listener that is invoked when the user long-presses on the map. + +**Signature** +```kotlin +fun setMapLongClickListener(listener: OnMapEventHandler?) +``` + +**Description** +Registers a callback to be executed when a long-click event occurs on the map. The callback receives the geographical coordinates (`LatLng`) of the long-press location. To remove the listener, pass `null`. + +**Parameters** +| Name | Type | Description | +| :--- | :--- | :--- | +| `listener` | `OnMapEventHandler?` | The callback to invoke on a map long-click. It receives the `LatLng` of the long-click location. | + +**Example** +```kotlin +mapViewController.setMapLongClickListener { latLng -> + println("Map long-clicked at coordinates: $latLng") + // For example, add a marker at the long-press location + addMarkerAt(latLng) +} + +// To remove the listener +mapViewController.setMapLongClickListener(null) +``` + +--- + +### registerOverlayController +Registers an `OverlayControllerInterface` with the map view controller. + +**Signature** +```kotlin +fun registerOverlayController(controller: OverlayControllerInterface<*, *, *>) +``` + +**Description** +Adds an overlay controller (e.g., for markers, polygons, or other visual elements) to be managed by this `BaseMapViewController`. Registered controllers will be automatically notified of map events, such as camera changes, allowing them to update their state accordingly. A controller will only be registered once, even if this method is called multiple times with the same controller instance. + +**Parameters** +| Name | Type | Description | +| :--- | :--- | :--- | +| `controller` | `OverlayControllerInterface<*, *, *>` | The overlay controller to register. | + +**Example** +```kotlin +// Assuming you have a custom MarkerController that implements OverlayControllerInterface +val markerController = MarkerController(map) +mapViewController.registerOverlayController(markerController) +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/controller/MapViewControllerInterface.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/controller/MapViewControllerInterface.kt.md new file mode 100644 index 00000000..85d91b9f --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/controller/MapViewControllerInterface.kt.md @@ -0,0 +1,251 @@ +Of course! Here is the high-quality SDK documentation for the provided Kotlin code snippet. + +--- + +# Interface `MapViewControllerInterface` + +## Description + +The `MapViewControllerInterface` defines the primary contract for interacting with and controlling a map view. It provides a high-level, platform-agnostic API for managing the map's camera, handling user interaction events like clicks and camera movements, and managing map overlays. Implementations of this interface act as the central controller for all map-related operations. + +## Properties + +### holder +Provides access to the underlying `MapViewHolderInterface`, which encapsulates the platform-specific map view instance. + +**Signature** +```kotlin +val holder: MapViewHolderInterface<*, *> +``` + +### coroutine +A `CoroutineScope` tied to the lifecycle of the map view. This scope should be used for launching any long-running or asynchronous operations that need to be automatically cancelled when the map view is destroyed, preventing memory leaks and unnecessary work. + +**Signature** +```kotlin +val coroutine: CoroutineScope +``` + +--- + +## Functions + +### clearOverlays +Asynchronously removes all overlays (e.g., markers, polylines, polygons) from the map. As a `suspend` function, it must be called from within a coroutine or another suspend function. + +**Signature** +```kotlin +suspend fun clearOverlays() +``` + +**Example** +```kotlin +// Assuming 'mapViewController' is an instance of MapViewControllerInterface +mapViewController.coroutine.launch { + // Remove all existing overlays from the map + mapViewController.clearOverlays() + println("All overlays have been cleared.") +} +``` + +--- + +### setCameraMoveStartListener +Sets a listener that is invoked exactly once when the map camera begins to move. This can be triggered by user gestures (panning, zooming) or programmatic camera updates. + +**Signature** +```kotlin +fun setCameraMoveStartListener(listener: OnCameraMoveHandler?) +``` + +**Parameters** +| Parameter | Type | Description | +|-----------|------|-------------| +| `listener` | `OnCameraMoveHandler?` | The callback to be invoked when camera movement starts. Pass `null` to remove the existing listener. | + +**Example** +```kotlin +mapViewController.setCameraMoveStartListener { + println("Camera movement has started.") +} + +// To remove the listener +mapViewController.setCameraMoveStartListener(null) +``` + +--- + +### setCameraMoveListener +Sets a listener that is invoked repeatedly as the camera position changes during movement. This is useful for tracking the camera's state in real-time while it is in motion. + +**Signature** +```kotlin +fun setCameraMoveListener(listener: OnCameraMoveHandler?) +``` + +**Parameters** +| Parameter | Type | Description | +|-----------|------|-------------| +| `listener` | `OnCameraMoveHandler?` | The callback to be invoked continuously during camera movement. Pass `null` to remove the existing listener. | + +**Example** +```kotlin +mapViewController.setCameraMoveListener { cameraPosition -> + println("Camera is moving. Current zoom: ${cameraPosition.zoom}") +} +``` + +--- + +### setCameraMoveEndListener +Sets a listener that is invoked exactly once when the map camera has finished moving and has settled in its new position. + +**Signature** +```kotlin +fun setCameraMoveEndListener(listener: OnCameraMoveHandler?) +``` + +**Parameters** +| Parameter | Type | Description | +|-----------|------|-------------| +| `listener` | `OnCameraMoveHandler?` | The callback to be invoked when camera movement ends. Pass `null` to remove the existing listener. | + +**Example** +```kotlin +mapViewController.setCameraMoveEndListener { finalPosition -> + println("Camera movement ended at position: ${finalPosition.target}") +} +``` + +--- + +### setMapClickListener +Sets a listener that is invoked when the user performs a single tap (click) on the map. The listener receives the geographic coordinates (`MapCoordinate`) of the tapped point. + +**Signature** +```kotlin +fun setMapClickListener(listener: OnMapEventHandler?) +``` + +**Parameters** +| Parameter | Type | Description | +|-----------|------|-------------| +| `listener` | `OnMapEventHandler?` | The callback to be invoked on a map click. It receives the `MapCoordinate` of the click location. Pass `null` to remove the listener. | + +**Example** +```kotlin +mapViewController.setMapClickListener { coordinate -> + println("Map clicked at Latitude: ${coordinate.latitude}, Longitude: ${coordinate.longitude}") +} +``` + +--- + +### setMapLongClickListener +Sets a listener that is invoked when the user performs a long press on the map. The listener receives the geographic coordinates (`MapCoordinate`) of the point that was long-pressed. + +**Signature** +```kotlin +fun setMapLongClickListener(listener: OnMapEventHandler?) +``` + +**Parameters** +| Parameter | Type | Description | +|-----------|------|-------------| +| `listener` | `OnMapEventHandler?` | The callback to be invoked on a map long click. It receives the `MapCoordinate` of the location. Pass `null` to remove the listener. | + +**Example** +```kotlin +mapViewController.setMapLongClickListener { coordinate -> + println("Map long-pressed at Latitude: ${coordinate.latitude}, Longitude: ${coordinate.longitude}") +} +``` + +--- + +### moveCamera +Instantly repositions the map's camera to a specified `MapCameraPosition`. This change is immediate and does not involve any animation. + +**Signature** +```kotlin +fun moveCamera(position: MapCameraPosition) +``` + +**Parameters** +| Parameter | Type | Description | +|-----------|------|-------------| +| `position` | `MapCameraPosition` | The target camera position, which includes properties like target coordinates, zoom level, tilt, and bearing. | + +**Example** +```kotlin +// Define a target location and camera properties +val newYorkCity = MapCoordinate(40.7128, -74.0060) +val cameraPosition = MapCameraPosition( + target = newYorkCity, + zoom = 12.0 +) + +// Instantly move the camera to the new position +mapViewController.moveCamera(cameraPosition) +``` + +--- + +### animateCamera +Smoothly animates the map's camera from its current position to a new specified `MapCameraPosition` over a given duration. + +**Signature** +```kotlin +fun animateCamera( + position: MapCameraPosition, + duration: Long, +) +``` + +**Parameters** +| Parameter | Type | Description | +|-----------|------|-------------| +| `position` | `MapCameraPosition` | The destination camera position for the animation. | +| `duration` | `Long` | The duration of the animation in milliseconds. | + +**Example** +```kotlin +// Define a target location +val sanFrancisco = MapCoordinate(37.7749, -122.4194) +val cameraPosition = MapCameraPosition( + target = sanFrancisco, + zoom = 14.0, + tilt = 30.0 +) + +// Animate the camera to the new position over 2 seconds +mapViewController.animateCamera( + position = cameraPosition, + duration = 2000L +) +``` + +--- + +### registerOverlayController +Registers an `OverlayControllerInterface` with the map view controller. This is used to link controllers for specific types of overlays (e.g., markers, polygons) to the main map controller, allowing for modular management of map elements. The default implementation is an empty function. + +**Signature** +```kotlin +fun registerOverlayController(controller: OverlayControllerInterface<*, *, *>) +``` + +**Parameters** +| Parameter | Type | Description | +|-----------|------|-------------| +| `controller` | `OverlayControllerInterface<*, *, *>` | The overlay controller instance to register. | + +**Example** +```kotlin +// Assuming 'markerController' is an implementation of OverlayControllerInterface +// for managing map markers. +val markerController = MyMarkerController() + +// Register the marker controller with the main map view controller +mapViewController.registerOverlayController(markerController) +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/controller/OverlayControllerInterface.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/controller/OverlayControllerInterface.kt.md new file mode 100644 index 00000000..344a9c92 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/controller/OverlayControllerInterface.kt.md @@ -0,0 +1,154 @@ +# Interface `OverlayControllerInterface` + +## Description + +The `OverlayControllerInterface` defines a standardized contract for managing a layer of visual elements (overlays) on a map. It provides a generic, abstract way to add, update, find, and clear entities, regardless of the underlying map provider. + +This interface is designed to be implemented for specific types of map objects, such as markers, polylines, or polygons. + +- **`StateType`**: Represents the data model or state of an individual entity. This is the provider-agnostic data you work with. +- **`EntityType`**: Represents the actual, provider-specific object rendered on the map (e.g., a Google Maps `Marker` or a Mapbox `Symbol`). +- **`EventType`**: Represents the data object passed to the click listener when an entity is clicked. This often contains identifying information from the original `StateType`. + +--- + +## Properties + +### zIndex + +Controls the vertical stacking order of the entire overlay layer. Overlays with a higher `zIndex` are drawn on top of those with a lower `zIndex`. + +**Signature** + +```kotlin +val zIndex: Int +``` + +--- + +### clickListener + +A callback lambda that is invoked when a user clicks on an entity managed by this controller. Set this property to handle user interactions with the overlay items. + +**Signature** + +```kotlin +var clickListener: ((EventType) -> Unit)? +``` + +**Example** + +```kotlin +// Assuming 'markerController' is an implementation of OverlayControllerInterface +markerController.clickListener = { clickedMarkerEvent -> + // clickedMarkerEvent is the EventType object + println("Marker clicked with ID: ${clickedMarkerEvent.id}") + // Show an info window, navigate to a new screen, etc. +} +``` + +--- + +## Functions + +### add + +Asynchronously adds a list of new entities to the map overlay. Each entity is defined by its corresponding state object. + +**Signature** + +```kotlin +suspend fun add(data: List) +``` + +**Parameters** + +| Parameter | Type | Description | +| :-------- | :-------------------- | :--------------------------------------------------- | +| `data` | `List` | A list of state objects representing the items to add. | + +--- + +### update + +Asynchronously updates an existing entity on the map. The controller identifies the entity to update based on the provided state object (usually via a unique ID within the state). + +**Signature** + +```kotlin +suspend fun update(state: StateType) +``` + +**Parameters** + +| Parameter | Type | Description | +| :-------- | :------------ | :--------------------------------------------------------- | +| `state` | `StateType` | The new state object for the entity that needs to be updated. | + +--- + +### clear + +Asynchronously removes all entities from this overlay layer. + +**Signature** + +```kotlin +suspend fun clear() +``` + +--- + +### find + +Finds the topmost entity at a specific geographical position on the map. This is useful for hit-testing, such as determining which entity was tapped by the user. + +**Signature** + +```kotlin +fun find(position: GeoPointInterface): EntityType? +``` + +**Parameters** + +| Parameter | Type | Description | +| :--------- | :------------------ | :---------------------------------------- | +| `position` | `GeoPointInterface` | The geographical coordinate to search at. | + +**Returns** + +| Type | Description | +| :----------- | :----------------------------------------------------------------------- | +| `EntityType?` | The provider-specific entity (`EntityType`) found at the position, or `null` if no entity exists there. | + +--- + +### onCameraChanged + +A lifecycle method called by the map framework whenever the map's camera view changes (e.g., pan, zoom, tilt). Implementations can use this to optimize performance, such as by clustering markers or adjusting the level of detail. + +**Signature** + +```kotlin +suspend fun onCameraChanged(mapCameraPosition: MapCameraPosition) +``` + +**Parameters** + +| Parameter | Type | Description | +| :------------------ | :------------------ | :-------------------------------------------------- | +| `mapCameraPosition` | `MapCameraPosition` | An object containing the new state of the map camera. | + +--- + +### destroy + +Cleans up and releases all resources used by the controller. This includes removing entities from the map, detaching listeners, and freeing up memory. + +> **IMPORTANT**: This method must be called when the controller is no longer needed, such as when switching between map providers or when the map view is being destroyed. Failure to do so can lead to memory leaks. + +**Signature** + +```kotlin +fun destroy() +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/controller/OverlayRendererInterface.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/controller/OverlayRendererInterface.kt.md new file mode 100644 index 00000000..1dac6439 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/controller/OverlayRendererInterface.kt.md @@ -0,0 +1,178 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +--- + +# Interface `OverlayRendererInterface` + +## Description + +The `OverlayRendererInterface` defines a contract for rendering and managing a collection of overlay entities on a map. It provides a set of lifecycle methods that are invoked by a controller to handle the addition, modification, and removal of visual objects. This interface is designed to be implemented by a class that translates abstract data states into actual, platform-specific map objects (e.g., markers, polylines, polygons). + +The generic types are used as follows: +- `ActualType`: The concrete, platform-specific object rendered on the map (e.g., `GoogleMap.Marker`, `Mapbox.Annotation`). +- `StateType`: The initial, minimal state required to create a new entity. This is typically used for the `onAdd` operation. +- `EntityType`: The full, managed representation of an entity, containing all its properties. This is used for change detection and removal. + +All methods are `suspend` functions, indicating they are designed for asynchronous execution within a coroutine context, allowing for non-blocking I/O or computation. + +--- + +## Nested Interface `ChangeParamsInterface` + +Represents the data required to process a change in an entity. It encapsulates the entity's state before and after the modification. + +### Signature +```kotlin +interface ChangeParamsInterface +``` + +### Properties + +| Property | Type | Description | +| :-------- | :----------- | :------------------------------------------- | +| `current` | `EntityType` | The new, updated state of the entity. | +| `prev` | `EntityType` | The previous state of the entity before the change. | + +--- + +## Methods + +### onAdd + +Adds a new batch of entities to the map. This method takes a list of initial states and is responsible for creating the corresponding visual objects. + +#### Signature +```kotlin +suspend fun onAdd(data: List): List +``` + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :------------------ | :------------------------------------------------------------------------------------------------------ | +| `data` | `List` | A list of initial state objects for the new entities to be created. | + +#### Returns +`List` - A list of the newly created `ActualType` objects. The list should correspond in order to the input `data` list. An element can be `null` if the creation of a specific object failed. + +--- + +### onChange + +Updates a batch of existing entities on the map. This method is called when properties of one or more entities have changed. + +#### Signature +```kotlin +suspend fun onChange(data: List>): List +``` + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :----------------------------------------- | :------------------------------------------------------------------------------------------------------ | +| `data` | `List>` | A list of `ChangeParamsInterface` objects, each containing the previous and current state of an entity. | + +#### Returns +`List` - A list of the updated `ActualType` objects. The list should correspond in order to the input `data` list. An element can be `null` if the update failed. + +--- + +### onRemove + +Removes a batch of entities from the map. + +#### Signature +```kotlin +suspend fun onRemove(data: List) +``` + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :------------------ | :----------------------------------------------- | +| `data` | `List` | A list of the entity objects to be removed. | + +--- + +### onPostProcess + +A lifecycle callback that is invoked after all `onAdd`, `onChange`, and `onRemove` operations for a given update cycle have been completed. This can be used for cleanup, final adjustments, or triggering a map redraw. + +#### Signature +```kotlin +suspend fun onPostProcess() +``` + +--- + +## Example + +Here is an example of a `MarkerRenderer` that implements the `OverlayRendererInterface` to manage custom markers on a map. + +```kotlin +// Define the data models for the generic types +data class MarkerState(val id: String, val position: LatLng) // StateType +data class MarkerEntity(val id: String, val position: LatLng, val title: String) // EntityType +class MapMarker { /* Platform-specific marker object */ } // ActualType + +// Define the ChangeParams implementation +data class MarkerChangeParams( + override val current: MarkerEntity, + override val prev: MarkerEntity +) : OverlayRendererInterface.ChangeParamsInterface + +// Implement the renderer +class MarkerRenderer(private val map: MapSDK) : // Assuming a hypothetical MapSDK + OverlayRendererInterface { + + // A map to track rendered markers by their ID + private val renderedMarkers = mutableMapOf() + + override suspend fun onAdd(data: List): List { + val newMarkers = mutableListOf() + for (state in data) { + // Create a new platform-specific marker + val newMarker = map.addMarker(position = state.position) + renderedMarkers[state.id] = newMarker + newMarkers.add(newMarker) + println("Added marker: ${state.id}") + } + return newMarkers + } + + override suspend fun onChange(data: List>): List { + val updatedMarkers = mutableListOf() + for (change in data) { + val marker = renderedMarkers[change.current.id] + if (marker != null) { + // Update marker properties if they have changed + if (change.current.position != change.prev.position) { + marker.position = change.current.position + } + if (change.current.title != change.prev.title) { + marker.title = change.current.title + } + println("Changed marker: ${change.current.id}") + } + updatedMarkers.add(marker) + } + return updatedMarkers + } + + override suspend fun onRemove(data: List) { + for (entity in data) { + renderedMarkers[entity.id]?.let { marker -> + map.removeMarker(marker) + renderedMarkers.remove(entity.id) + println("Removed marker: ${entity.id}") + } + } + } + + override suspend fun onPostProcess() { + // For example, force the map to redraw or refresh its camera view + println("Post-processing all marker changes.") + map.invalidate() + } +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/features/GeoPoint.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/features/GeoPoint.kt.md new file mode 100644 index 00000000..33ea3940 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/features/GeoPoint.kt.md @@ -0,0 +1,238 @@ +This document provides a detailed reference for the `GeoPoint` class and its related utility functions. + +# `GeoPoint` + +A data class representing a geographical point defined by latitude, longitude, and an optional altitude. It provides methods for coordinate manipulation, validation, and formatting. + +Note that `GeoPoint` instances are compared for equality with a small tolerance (`1e-7`) to account for floating-point inaccuracies. This behavior also affects the `hashCode` calculation. + +## Constructor + +### Signature + +```kotlin +data class GeoPoint( + override val latitude: Double, + override val longitude: Double, + override val altitude: Double = 0.0, +) : GeoPointInterface +``` + +### Description + +Creates a new `GeoPoint` instance. + +### Parameters + +| Parameter | Type | Description | +| :---------- | :------- | :--------------------------------------------- | +| `latitude` | `Double` | The latitude of the point in decimal degrees. | +| `longitude` | `Double` | The longitude of the point in decimal degrees. | +| `altitude` | `Double` | The altitude of the point in meters. Defaults to `0.0`. | + +## Properties + +| Property | Type | Description | +| :---------- | :------- | :--------------------------------------------- | +| `latitude` | `Double` | The latitude of the point in decimal degrees. | +| `longitude` | `Double` | The longitude of the point in decimal degrees. | +| `altitude` | `Double` | The altitude of the point in meters. | + +## Methods + +### `toUrlValue` + +Formats the geographical point into a URL-friendly string. + +#### Signature + +```kotlin +fun toUrlValue(precision: Int = 6): String +``` + +#### Description + +Converts the `latitude` and `longitude` of the point into a single string with the format `","`. + +#### Parameters + +| Parameter | Type | Description | +| :---------- | :---- | :----------------------------------------------------------------------- | +| `precision` | `Int` | The number of decimal places to use for formatting the coordinates. Defaults to `6`. | + +#### Returns + +A `String` representation of the coordinates, suitable for use in URLs. + +#### Example + +```kotlin +val point = GeoPoint(40.7128, -74.0060) +val urlValue = point.toUrlValue() // "40.712800,-74.006000" + +val urlValuePrecise = point.toUrlValue(precision = 8) // "40.71280000,-74.00600000" +``` + +### `wrap` + +Creates a new `GeoPoint` by wrapping the coordinates to handle values outside standard geographical ranges. + +#### Signature + +```kotlin +override fun wrap(): GeoPointInterface +``` + +#### Description + +This method normalizes coordinates that are out of the standard `[-90, 90]` latitude and `[-180, 180]` longitude bounds. + +- **Latitude**: If the latitude is beyond the poles (e.g., > 90 or < -90), it wraps around. When wrapping over a pole, the longitude is flipped by 180 degrees. +- **Longitude**: The longitude is normalized to fit within the `[-180, 180]` range. + +#### Returns + +A new `GeoPointInterface` instance with wrapped coordinates. + +#### Example + +```kotlin +// Latitude wraps from North Pole to South Pole, longitude is flipped +val point1 = GeoPoint(latitude = 95.0, longitude = 10.0) +val wrappedPoint1 = point1.wrap() // GeoPoint(latitude = -85.0, longitude = -170.0) + +// Longitude wraps around the 180th meridian +val point2 = GeoPoint(latitude = 50.0, longitude = 200.0) +val wrappedPoint2 = point2.wrap() // GeoPoint(latitude = 50.0, longitude = -160.0) +``` + +## Companion Object + +### `GeoPoint.fromLatLong` + +Creates a `GeoPoint` instance from latitude and longitude values. + +#### Signature + +```kotlin +fun fromLatLong(latitude: Double, longitude: Double): GeoPoint +``` + +#### Parameters + +| Parameter | Type | Description | +| :---------- | :------- | :-------------------------------------------- | +| `latitude` | `Double` | The latitude of the point in decimal degrees. | +| `longitude` | `Double` | The longitude of the point in decimal degrees.| + +#### Returns + +A new `GeoPoint` instance. + +### `GeoPoint.fromLongLat` + +Creates a `GeoPoint` instance from longitude and latitude values. + +#### Signature + +```kotlin +fun fromLongLat(longitude: Double, latitude: Double): GeoPoint +``` + +#### Parameters + +| Parameter | Type | Description | +| :---------- | :------- | :-------------------------------------------- | +| `longitude` | `Double` | The longitude of the point in decimal degrees.| +| `latitude` | `Double` | The latitude of the point in decimal degrees. | + +#### Returns + +A new `GeoPoint` instance. + +### `GeoPoint.from` + +Converts any object implementing `GeoPointInterface` into a `GeoPoint` instance. + +#### Signature + +```kotlin +fun from(position: GeoPointInterface): GeoPoint +``` + +#### Description + +If the provided `position` is already a `GeoPoint`, it is returned directly. Otherwise, a new `GeoPoint` is created from the `GeoPointInterface`'s properties. + +#### Parameters + +| Parameter | Type | Description | +| :--------- | :------------------ | :------------------------------------------- | +| `position` | `GeoPointInterface` | An object that implements `GeoPointInterface`. | + +#### Returns + +A `GeoPoint` instance. + +--- + +# Extension Functions + +These functions extend the `GeoPointInterface` and can be called on any `GeoPoint` object. + +## `normalize` + +Creates a new `GeoPoint` by clamping coordinates to valid geographical ranges. + +### Signature + +```kotlin +fun GeoPointInterface.normalize(): GeoPoint +``` + +### Description + +This function ensures that the coordinates of a geographical point are within standard bounds. +- **Latitude**: Clamped to the `[-90.0, 90.0]` range. +- **Longitude**: Normalized to the `[-180.0, 180.0]` range. + +Unlike `wrap()`, `normalize()` does not wrap coordinates around the globe but forces them to the nearest valid boundary. + +### Returns + +A new `GeoPoint` instance with normalized coordinates. + +### Example + +```kotlin +val point = GeoPoint(latitude = 95.0, longitude = 200.0) +val normalizedPoint = point.normalize() // GeoPoint(latitude = 90.0, longitude = -160.0) +``` + +## `isValid` + +Checks if the coordinates of a `GeoPointInterface` are within valid geographical ranges. + +### Signature + +```kotlin +fun GeoPointInterface.isValid(): Boolean +``` + +### Description + +Validates that the latitude is within `[-90.0, 90.0]` and the longitude is within `[-180.0, 180.0]`. + +### Returns + +`true` if the coordinates are valid, `false` otherwise. + +### Example + +```kotlin +val validPoint = GeoPoint(45.0, 90.0) +println(validPoint.isValid()) // true + +val invalidPoint = GeoPoint(95.0, 90.0) +println(invalidPoint.isValid()) // false +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/features/GeoRectBounds.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/features/GeoRectBounds.kt.md new file mode 100644 index 00000000..5a68a917 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/features/GeoRectBounds.kt.md @@ -0,0 +1,305 @@ +Of course! Here is a high-quality SDK document for the provided `GeoRectBounds` class. + +--- + +# GeoRectBounds + +## Class: GeoRectBounds + +### Description + +Represents a rectangular geographical area defined by a southwest and a northeast `GeoPoint`. This class is designed to correctly handle bounding boxes that cross the antimeridian (180th meridian). + +An empty `GeoRectBounds` can be created by providing no arguments to the constructor. The bounds can then be defined by adding points using the `extend` method. + +### Signature + +```kotlin +class GeoRectBounds( + southWest: GeoPoint? = null, + northEast: GeoPoint? = null +) +``` + +### Parameters + +| Parameter | Type | Description | +| :---------- | :--------- | :------------------------------------------------- | +| `southWest` | `GeoPoint?` | The southwest corner of the bounding box. Optional. | +| `northEast` | `GeoPoint?` | The northeast corner of the bounding box. Optional. | + +--- + +## Properties + +### isEmpty + +#### Signature + +```kotlin +val isEmpty: Boolean +``` + +#### Description + +Returns `true` if the bounds have not been initialized with at least one point, `false` otherwise. + +--- + +### southWest + +#### Signature + +```kotlin +val southWest: GeoPoint? +``` + +#### Description + +The southwest corner `GeoPoint` of the bounding box. Returns `null` if the bounds are empty. + +--- + +### northEast + +#### Signature + +```kotlin +val northEast: GeoPoint? +``` + +#### Description + +The northeast corner `GeoPoint` of the bounding box. Returns `null` if the bounds are empty. + +--- + +### center + +#### Signature + +```kotlin +val center: GeoPoint? +``` + +#### Description + +Calculates the geographical center of the bounding box. Correctly handles bounds that cross the antimeridian. Returns `null` if the bounds are empty. + +--- + +## Methods + +### extend + +#### Signature + +```kotlin +fun extend(point: GeoPointInterface) +``` + +#### Description + +Expands the bounding box to include the given geographical point. If the bounds are empty, they will be initialized to the location of this point. This method modifies the current `GeoRectBounds` instance. + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :------------------ | :---------------------------------------- | +| `point` | `GeoPointInterface` | The geographical point to include in the bounds. | + +--- + +### contains + +#### Signature + +```kotlin +fun contains(point: GeoPointInterface): Boolean +``` + +#### Description + +Checks if the given geographical point is within this bounding box (inclusive). This check correctly handles bounds that cross the antimeridian. + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :------------------ | :------------------------------------------- | +| `point` | `GeoPointInterface` | The point to check for containment. | + +#### Returns + +`Boolean` - `true` if the point is inside the bounds, `false` otherwise. Returns `false` if the bounds are empty. + +--- + +### union + +#### Signature + +```kotlin +fun union(other: GeoRectBounds): GeoRectBounds +``` + +#### Description + +Computes the union of this bounding box and another `GeoRectBounds`. It returns a new `GeoRectBounds` that encompasses both of the original bounds. + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :-------------- | :---------------------------------------- | +| `other` | `GeoRectBounds` | The other bounding box to combine with this one. | + +#### Returns + +`GeoRectBounds` - A new `GeoRectBounds` instance representing the union of the two bounds. + +--- + +### toSpan + +#### Signature + +```kotlin +fun toSpan(): GeoPoint? +``` + +#### Description + +Calculates the latitudinal and longitudinal span of the bounding box. + +#### Returns + +`GeoPoint?` - A `GeoPoint` where the `latitude` represents the latitudinal span in degrees and the `longitude` represents the longitudinal span in degrees. Returns `null` if the bounds are empty. + +--- + +### toUrlValue + +#### Signature + +```kotlin +fun toUrlValue(precision: Int = 6): String +``` + +#### Description + +Formats the bounding box coordinates into a single comma-separated string, suitable for use in URL parameters. The format is `south,west,north,east`. + +#### Parameters + +| Parameter | Type | Description | +| :---------- | :---- | :----------------------------------------------------------------------- | +| `precision` | `Int` | The number of decimal places to use for formatting the coordinates. Defaults to `6`. | + +#### Returns + +`String` - A string representation of the bounds. For empty bounds, it returns `"1.0,180.0,-1.0,-180.0"`. + +--- + +### expandedByDegrees + +#### Signature + +```kotlin +fun expandedByDegrees(latPad: Double, lonPad: Double): GeoRectBounds +``` + +#### Description + +Creates a new `GeoRectBounds` instance that is expanded from the original by a specified amount in every direction. The padding values are added to the north and east boundaries and subtracted from the south and west boundaries. This method correctly handles expansion across the antimeridian. + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :------- | :------------------------------------------------- | +| `latPad` | `Double` | The number of degrees to expand latitude-wise (north and south). | +| `lonPad` | `Double` | The number of degrees to expand longitude-wise (east and west). | + +#### Returns + +`GeoRectBounds` - A new, expanded `GeoRectBounds` instance. + +--- + +### intersects + +#### Signature + +```kotlin +fun intersects(other: GeoRectBounds): Boolean +``` + +#### Description + +Determines if this bounding box has any overlap with another `GeoRectBounds`. The check is inclusive of the boundaries and correctly handles cases where one or both bounds cross the antimeridian. + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :-------------- | :---------------------------------------- | +| `other` | `GeoRectBounds` | The other bounding box to check for intersection. | + +#### Returns + +`Boolean` - `true` if the two bounding boxes intersect, `false` otherwise. Returns `false` if either of the bounds is empty. + +--- + +## Example + +```kotlin +// Assuming GeoPoint and GeoPointInterface are defined +// data class GeoPoint(val latitude: Double, val longitude: Double) : GeoPointInterface + +fun main() { + // 1. Create an empty bounds and extend it with points + val bounds = GeoRectBounds() + println("Is bounds empty initially? ${bounds.isEmpty}") // true + + bounds.extend(GeoPoint(34.0, -118.0)) // Los Angeles + bounds.extend(GeoPoint(40.7, -74.0)) // New York + + println("Is bounds empty after extend? ${bounds.isEmpty}") // false + println("Bounds after adding LA and NYC: $bounds") + // Output: Bounds after adding LA and NYC: ((34.0, -118.0), (40.7, -74.0)) + + // 2. Check if a point is contained within the bounds + val chicago = GeoPoint(41.8, -87.6) + val tokyo = GeoPoint(35.6, 139.6) + println("Does bounds contain Chicago? ${bounds.contains(chicago)}") // true + println("Does bounds contain Tokyo? ${bounds.contains(tokyo)}") // false + + // 3. Get the center of the bounds + val centerPoint = bounds.center + println("Center of the bounds: $centerPoint") + + // 4. Create another bounds and find the union + val europeBounds = GeoRectBounds( + southWest = GeoPoint(48.8, 2.3), // Paris + northEast = GeoPoint(52.5, 13.4) // Berlin + ) + val combinedBounds = bounds.union(europeBounds) + println("Combined bounds of US and Europe: $combinedBounds") + + // 5. Create bounds that cross the antimeridian + val pacificBounds = GeoRectBounds() + pacificBounds.extend(GeoPoint(21.3, -157.8)) // Honolulu + pacificBounds.extend(GeoPoint(-33.8, 151.2)) // Sydney + println("Pacific bounds (crosses antimeridian): $pacificBounds") + // Note: west longitude > east longitude indicates antimeridian crossing + // Output: Pacific bounds (crosses antimeridian): ((-33.8, 151.2), (21.3, -157.8)) + + // 6. Check for intersection + println("Do US and Europe bounds intersect? ${bounds.intersects(europeBounds)}") // false + val hawaiiBounds = GeoRectBounds(southWest = GeoPoint(19.0, -160.0), northEast = GeoPoint(22.0, -154.0)) + println("Do Pacific and Hawaii bounds intersect? ${pacificBounds.intersects(hawaiiBounds)}") // true + + // 7. Get URL-friendly string + println("URL value for US bounds: ${bounds.toUrlValue(precision = 2)}") + // Output: URL value for US bounds: 34.00,-118.00,40.70,-74.00 +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/geocell/HexCellRegistry.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/geocell/HexCellRegistry.kt.md new file mode 100644 index 00000000..6b84b55e --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/geocell/HexCellRegistry.kt.md @@ -0,0 +1,523 @@ +Of course! Here is the high-quality SDK documentation for the provided `HexCellRegistry` code snippet. + +--- + +# HexCellRegistry + +## Table of Contents +- [HexCellRegistry](#hexcellregistryactualmarker) + - [Table of Contents](#table-of-contents) + - [Class: HexCellRegistry](#class-hexcellregistryactualmarker) + - [Constructor](#constructor) + - [Methods](#methods) + - [getCell](#getcell) + - [setPoint](#setpoint) + - [contains](#contains) + - [removePoint](#removepoint) + - [clear](#clear) + - [findNearest](#findnearest) + - [findNearestWithDistance](#findnearestwithdistance) + - [findNearestKWithDistance](#findnearestkwithdistance) + - [findWithinRadiusWithDistance](#findwithinradiuswithdistance) + - [all](#all) + - [getEntryIDsByHexCell](#getentryidsbyhexcell) + - [metersPerPixel](#metersperpixel) + - [findWithinPixelRadius](#findwithinpixelradius) + - [findByIdPrefix](#findbyidprefix) + - [getStats](#getstats) + - [Data Class: RegistryStats](#data-class-registrystats) + - [Properties](#properties) + +## Class: HexCellRegistry + +A thread-safe class for managing a collection of hexagonal grid cells. It provides efficient spatial indexing and querying using an internal KDTree. + +The registry maps entities (like map markers) to the hexagonal cells they occupy at a specific zoom level. It is designed for performance-critical applications, featuring lazy rebuilding of its spatial index to optimize write operations. All public methods are thread-safe. + +### Constructor + +Initializes a new instance of the `HexCellRegistry`. + +**Signature** +```kotlin +class HexCellRegistry( + private val geocell: HexGeocellInterface, + private val zoom: Double, +) +``` + +**Parameters** + +| Parameter | Type | Description | +| :-------- | :-------------------- | :----------------------------------------------------- | +| `geocell` | `HexGeocellInterface` | The hexagonal geocell system used for coordinate logic. | +| `zoom` | `Double` | The map zoom level this registry is responsible for. | + +
+ +## Methods + +### getCell + +Calculates and returns the `HexCell` that would contain a given entity, without adding the entity to the registry. This is a pure calculation method and does not modify the registry's state. + +**Signature** +```kotlin +fun getCell(entity: MarkerEntityInterface): HexCell +``` + +**Parameters** + +| Parameter | Type | Description | +| :-------- | :------------------------------------ | :------------------------------------------- | +| `entity` | `MarkerEntityInterface` | The entity for which to calculate the cell. | + +**Returns** + +`HexCell` - The hexagonal cell corresponding to the entity's position. + +**Example** +```kotlin +val entity: MarkerEntityInterface = /* ... get your entity ... */ +val correspondingCell = hexRegistry.getCell(entity) +println("Entity belongs in cell: ${correspondingCell.id}") +``` + +--- + +### setPoint + +Registers a new entity or updates the position of an existing one. If the entity was previously registered in a different cell, it is automatically moved. This operation marks the internal spatial index as needing a rebuild, which will occur on the next spatial query. + +**Signature** +```kotlin +fun setPoint(entity: MarkerEntityInterface): HexCell +``` + +**Parameters** + +| Parameter | Type | Description | +| :-------- | :------------------------------------ | :---------------------------------------- | +| `entity` | `MarkerEntityInterface` | The entity to add or update in the registry. | + +**Returns** + +`HexCell` - The hexagonal cell where the entity has been placed. + +**Example** +```kotlin +val newEntity: MarkerEntityInterface = /* ... create your entity ... */ +val cell = hexRegistry.setPoint(newEntity) +println("Entity ${newEntity.state.id} was added to cell ${cell.id}") +``` + +--- + +### contains + +Checks if a hexagonal cell with the specified ID exists in the registry. + +**Signature** +```kotlin +fun contains(hexId: String): Boolean +``` + +**Parameters** + +| Parameter | Type | Description | +| :-------- | :------- | :----------------------------------- | +| `hexId` | `String` | The unique ID of the hexagonal cell. | + +**Returns** + +`Boolean` - `true` if a cell with the given ID is registered, `false` otherwise. + +**Example** +```kotlin +if (hexRegistry.contains("8c2a1072b59ffff")) { + println("Cell exists in the registry.") +} +``` + +--- + +### removePoint + +Removes an entity from the registry. If the cell containing the entity becomes empty after its removal, the cell itself is also removed from the registry. + +**Signature** +```kotlin +fun removePoint(entity: MarkerEntityInterface): Boolean +``` + +**Parameters** + +| Parameter | Type | Description | +| :-------- | :------------------------------------ | :------------------------------- | +| `entity` | `MarkerEntityInterface` | The entity to remove. | + +**Returns** + +`Boolean` - `true` if the entity was found and removed, `false` otherwise. + +**Example** +```kotlin +val entityToRemove: MarkerEntityInterface = /* ... get entity to remove ... */ +val wasRemoved = hexRegistry.removePoint(entityToRemove) +if (wasRemoved) { + println("Entity successfully removed.") +} +``` + +--- + +### clear + +Removes all entities and cells from the registry, effectively resetting it to an empty state. The internal spatial index is also cleared. + +**Signature** +```kotlin +fun clear() +``` + +**Example** +```kotlin +hexRegistry.clear() +println("Registry has been cleared. Total cells: ${hexRegistry.getStats().totalCells}") +// Output: Registry has been cleared. Total cells: 0 +``` + +--- + +### findNearest + +Finds the single nearest registered `HexCell` to a given geographic point. This method may trigger a rebuild of the spatial index if the registry has been modified since the last query. + +**Signature** +```kotlin +fun findNearest(point: GeoPointInterface): HexCell? +``` + +**Parameters** + +| Parameter | Type | Description | +| :-------- | :------------------ | :---------------------------------------- | +| `point` | `GeoPointInterface` | The geographic point to search from. | + +**Returns** + +`HexCell?` - The nearest `HexCell`, or `null` if the registry is empty. + +**Example** +```kotlin +val searchPoint = GeoPoint(40.7128, -74.0060) // New York City +val nearestCell = hexRegistry.findNearest(searchPoint) +nearestCell?.let { + println("Nearest cell is ${it.id} at ${it.center.latitude}, ${it.center.longitude}") +} +``` + +--- + +### findNearestWithDistance + +Finds the single nearest registered `HexCell` to a given point and also returns the distance to it. The distance is calculated in the projected coordinate system (e.g., meters). + +**Signature** +```kotlin +fun findNearestWithDistance(point: GeoPointInterface): HexCellWithDistance? +``` + +**Parameters** + +| Parameter | Type | Description | +| :-------- | :------------------ | :---------------------------------------- | +| `point` | `GeoPointInterface` | The geographic point to search from. | + +**Returns** + +`HexCellWithDistance?` - An object containing the nearest cell and the distance, or `null` if the registry is empty. + +**Example** +```kotlin +val searchPoint = GeoPoint(34.0522, -118.2437) // Los Angeles +val result = hexRegistry.findNearestWithDistance(searchPoint) +result?.let { + println("Nearest cell is ${it.cell.id}, distance: ${it.distance} meters") +} +``` + +--- + +### findNearestKWithDistance + +Finds the `k` nearest registered `HexCell` objects to a given point, ordered by distance. + +**Signature** +```kotlin +fun findNearestKWithDistance( + point: GeoPointInterface, + k: Int, +): List +``` + +**Parameters** + +| Parameter | Type | Description | +| :-------- | :------------------ | :------------------------------------------- | +| `point` | `GeoPointInterface` | The geographic point to search from. | +| `k` | `Int` | The maximum number of nearest cells to find. | + +**Returns** + +`List` - A list of up to `k` cells with their distances, sorted from nearest to farthest. The list will be empty if the registry is empty. + +**Example** +```kotlin +val searchPoint = GeoPoint(48.8566, 2.3522) // Paris +val nearestFive = hexRegistry.findNearestKWithDistance(searchPoint, 5) +println("Found ${nearestFive.size} nearby cells:") +nearestFive.forEach { + println("- Cell ${it.cell.id} at distance ${it.distance}") +} +``` + +--- + +### findWithinRadiusWithDistance + +Finds all registered `HexCell` objects within a specified radius of a given point. The radius is interpreted in the units of the projected coordinate system (e.g., meters). + +**Signature** +```kotlin +fun findWithinRadiusWithDistance( + point: GeoPointInterface, + radius: Double, +): List +``` + +**Parameters** + +| Parameter | Type | Description | +| :-------- | :------------------ | :----------------------------------------------------------------------- | +| `point` | `GeoPointInterface` | The center point of the search area. | +| `radius` | `Double` | The search radius, in projected units (typically meters). | + +**Returns** + +`List` - A list of all cells found within the radius, along with their respective distances from the center point. + +**Example** +```kotlin +val searchPoint = GeoPoint(51.5074, -0.1278) // London +val radiusInMeters = 1000.0 +val cellsInRadius = hexRegistry.findWithinRadiusWithDistance(searchPoint, radiusInMeters) +println("Found ${cellsInRadius.size} cells within 1km.") +``` + +--- + +### all + +Returns a complete list of all `HexCell` objects currently in the registry. + +**Signature** +```kotlin +fun all(): List +``` + +**Returns** + +`List` - A list containing all registered hexagonal cells. + +**Example** +```kotlin +val allRegisteredCells = hexRegistry.all() +println("Total registered cells: ${allRegisteredCells.size}") +``` + +--- + +### getEntryIDsByHexCell + +Retrieves the set of all entity IDs associated with a specific `HexCell`. + +**Signature** +```kotlin +fun getEntryIDsByHexCell(hexCell: HexCell): Set? +``` + +**Parameters** + +| Parameter | Type | Description | +| :-------- | :-------- | :---------------------------------------- | +| `hexCell` | `HexCell` | The cell for which to retrieve entity IDs. | + +**Returns** + +`Set?` - An immutable set of entity IDs within the given cell, or `null` if the cell is not registered or contains no entities. + +**Example** +```kotlin +val cell: HexCell = /* ... get a cell from a query ... */ +val entityIds = hexRegistry.getEntryIDsByHexCell(cell) +entityIds?.let { + println("Entities in cell ${cell.id}: $it") +} +``` + +--- + +### metersPerPixel + +A utility function to calculate the approximate real-world distance in meters that corresponds to a given number of pixels on a map display. + +**Note:** This calculation assumes the `HexGeocellInterface` projection returns coordinates in meters. + +**Signature** +```kotlin +fun metersPerPixel( + position: GeoPointInterface, + zoom: Double, + pixels: Double, + tileSize: Int = 256, +): Double +``` + +**Parameters** + +| Parameter | Type | Description | +| :--------- | :------------------ | :----------------------------------------------------------------------- | +| `position` | `GeoPointInterface` | The geographic location (latitude/longitude) for the calculation. | +| `zoom` | `Double` | The current map zoom level. | +| `pixels` | `Double` | The number of pixels for which to calculate the corresponding distance. | +| `tileSize` | `Int` | (Optional) The size of the map tiles in pixels. Defaults to `256`. | + +**Returns** + +`Double` - The calculated distance in meters. + +**Example** +```kotlin +val centerOfScreen = GeoPoint(35.6895, 139.6917) // Tokyo +val currentZoom = 15.0 +val distanceFor100Pixels = hexRegistry.metersPerPixel(centerOfScreen, currentZoom, 100.0) +println("At zoom $currentZoom, 100 pixels is approx. $distanceFor100Pixels meters.") +``` + +--- + +### findWithinPixelRadius + +A convenience method to find all `HexCell` objects within a given pixel radius of a point on the screen. It internally converts the pixel radius to meters using `metersPerPixel` and then performs a spatial search. + +**Signature** +```kotlin +fun findWithinPixelRadius( + position: GeoPointInterface, + zoom: Double, + pixels: Double, + tileSize: Int = 256, +): List +``` + +**Parameters** + +| Parameter | Type | Description | +| :--------- | :------------------ | :----------------------------------------------------------------------- | +| `position` | `GeoPointInterface` | The center point of the search area. | +| `zoom` | `Double` | The current map zoom level. | +| `pixels` | `Double` | The search radius in pixels. | +| `tileSize` | `Int` | (Optional) The size of the map tiles in pixels. Defaults to `256`. | + +**Returns** + +`List` - A list of all cells found within the pixel radius, along with their distances. + +**Example** +```kotlin +val tapPoint = GeoPoint(41.9028, 12.4964) // Rome +val currentZoom = 16.0 +val searchRadiusInPixels = 50.0 +val cellsNearTap = hexRegistry.findWithinPixelRadius(tapPoint, currentZoom, searchRadiusInPixels) +println("Found ${cellsNearTap.size} cells within a 50px radius of the tap.") +``` + +--- + +### findByIdPrefix + +Finds all registered cells whose IDs start with the given prefix. This is useful for querying groups of cells in hierarchical grid systems (e.g., finding all child cells within a parent region). + +**Signature** +```kotlin +fun findByIdPrefix(prefix: String): List +``` + +**Parameters** + +| Parameter | Type | Description | +| :-------- | :------- | :---------------------------------------- | +| `prefix` | `String` | The cell ID prefix to search for. Must not be empty. | + +**Returns** + +`List` - A list of all `HexCell` objects matching the prefix. + +**Example** +```kotlin +// Assuming a hierarchical cell ID system +val regionPrefix = "8a2a107" +val cellsInRegion = hexRegistry.findByIdPrefix(regionPrefix) +println("Found ${cellsInRegion.size} cells in region $regionPrefix.") +``` + +--- + +### getStats + +Retrieves diagnostic statistics about the current state of the registry. This is useful for debugging and monitoring performance. + +**Signature** +```kotlin +fun getStats(): RegistryStats +``` + +**Returns** + +`RegistryStats` - A data object containing statistics such as total cell and entity counts. + +**Example** +```kotlin +val stats = hexRegistry.getStats() +println("Registry Stats:") +println("- Total Cells: ${stats.totalCells}") +println("- Total Entries: ${stats.totalEntries}") +println("- KDTree Built: ${stats.kdTreeBuilt}") +println("- Needs Rebuild: ${stats.needsRebuild}") +``` + +
+ +## Data Class: RegistryStats + +A data class that holds statistics about the `HexCellRegistry`'s state. + +**Signature** +```kotlin +data class RegistryStats( + val totalCells: Int, + val totalEntries: Int, + val kdTreeBuilt: Boolean, + val needsRebuild: Boolean, +) +``` + +### Properties + +| Property | Type | Description | +| :------------- | :-------- | :----------------------------------------------------------------------- | +| `totalCells` | `Int` | The total number of unique hexagonal cells currently in the registry. | +| `totalEntries` | `Int` | The total number of entities (e.g., markers) registered across all cells. | +| `kdTreeBuilt` | `Boolean` | `true` if the KDTree spatial index has been built, `false` otherwise. | +| `needsRebuild` | `Boolean` | `true` if the registry has been modified and the KDTree needs to be rebuilt on the next spatial query. | \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/geocell/HexGeocellImpl.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/geocell/HexGeocellImpl.kt.md new file mode 100644 index 00000000..c69999fe --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/geocell/HexGeocellImpl.kt.md @@ -0,0 +1,406 @@ +Excellent! Here is the high-quality SDK documentation for the provided `HexGeocell` system. + +*** + +# HexGeocell SDK Documentation + +## `HexGeocell` + +### Description + +The `HexGeocell` class provides a comprehensive system for spatial indexing using a hexagonal grid. It allows for the conversion between geographic coordinates (latitude/longitude) and hexagonal cell coordinates, facilitating efficient spatial queries, clustering, and data aggregation on a map. + +This implementation uses a "flat-top" hexagon orientation and an axial coordinate system (`q`, `r`) for representing hexagonal positions. + +### Constructor + +#### Signature + +```kotlin +class HexGeocell( + override val projection: ProjectionInterface, + override val baseHexSideLength: Int = 1000, +) : HexGeocellInterface +``` + +#### Description + +Creates a new instance of the `HexGeocell` system. The behavior of the geocell system is determined by the map projection and the base size of the hexagons. + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `projection` | `ProjectionInterface` | The map projection used for converting between geographic (lat/lng) and Cartesian (XY) coordinates. Example: `WebMercator`. | +| `baseHexSideLength` | `Int` | The side length of a base hexagon (at zoom level 0) in meters. This value is fundamental to the grid's scale. Recommended values vary by use case:
- **High Zoom (15-18):** 100-1000m
- **Medium Zoom (10-15):** 1000-10000m
- **Low Zoom (5-10):** 10000-100000m | + +--- + +## Methods + +### `latLngToHexCoord` + +#### Signature + +```kotlin +fun latLngToHexCoord( + position: GeoPointInterface, + zoom: Double, +): HexCoord +``` + +#### Description + +Converts a geographic coordinate (latitude/longitude) into a hexagonal grid coordinate (`HexCoord`) for a given map zoom level. + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `position` | `GeoPointInterface` | The geographic point to convert. | +| `zoom` | `Double` | The current map zoom level. | + +#### Returns + +| Type | Description | +| :--- | :--- | +| `HexCoord` | The corresponding hexagonal coordinate (`q`, `r`). | + +### `latLngToHexCell` + +#### Signature + +```kotlin +fun latLngToHexCell( + position: GeoPointInterface, + zoom: Double, +): HexCell +``` + +#### Description + +Converts a geographic coordinate into a complete `HexCell` object. This is a higher-level function that returns not just the coordinate but also the cell's center, projected center, and unique ID. + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `position` | `GeoPointInterface` | The geographic point to convert. | +| `zoom` | `Double` | The current map zoom level. | + +#### Returns + +| Type | Description | +| :--- | :--- | +| `HexCell` | The `HexCell` object containing the point. | + +### `hexToLatLngCenter` + +#### Signature + +```kotlin +fun hexToLatLngCenter( + coord: HexCoord, + latHint: Double, + zoom: Double, +): GeoPointInterface +``` + +#### Description + +Calculates the geographic center (latitude/longitude) of a specified hexagonal cell. + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `coord` | `HexCoord` | The hexagonal coordinate of the cell. | +| `latHint` | `Double` | A reference latitude used for accurate scale correction, as hexagon size varies with latitude in Mercator projections. Typically, this should be the latitude of the area of interest. | +| `zoom` | `Double` | The map zoom level for which the calculation is being made. | + +#### Returns + +| Type | Description | +| :--- | :--- | +| `GeoPointInterface` | A geographic point representing the center of the hex cell. | + +### `hexToCellId` + +#### Signature + +```kotlin +fun hexToCellId( + coord: HexCoord, + zoom: Double, +): String +``` + +#### Description + +Generates a unique and human-readable string identifier for a hex cell. The ID is composed of the cell's `q` and `r` coordinates and the integer part of the zoom level, ensuring uniqueness across different zoom levels. + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `coord` | `HexCoord` | The hexagonal coordinate of the cell. | +| `zoom` | `Double` | The map zoom level. | + +#### Returns + +| Type | Description | +| :--- | :--- | +| `String` | The unique cell ID, formatted as `H__Z`. Example: `H10_-5_Z12`. | + +### `hexToPolygonLatLng` + +#### Signature + +```kotlin +fun hexToPolygonLatLng( + coord: HexCoord, + latHint: Double, + zoom: Double, +): List +``` + +#### Description + +Calculates the six vertices of a hexagon in geographic coordinates. The returned list of points can be used to draw the cell's polygon boundary on a map. + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `coord` | `HexCoord` | The hexagonal coordinate of the cell. | +| `latHint` | `Double` | A reference latitude for accurate scale correction. | +| `zoom` | `Double` | The map zoom level. | + +#### Returns + +| Type | Description | +| :--- | :--- | +| `List` | A list containing the six `GeoPointInterface` vertices of the hexagon. | + +### `enclosingCellOf` + +#### Signature + +```kotlin +fun enclosingCellOf( + points: List, + zoom: Double, +): HexCell +``` + +#### Description + +Determines the single hex cell that encloses the geographic centroid of a list of points. This is useful for finding a representative cell for a cluster of markers. The method uses a curvature-aware algorithm to accurately compute the centroid. + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `points` | `List` | A non-empty list of points (e.g., markers) from which to calculate the centroid. | +| `zoom` | `Double` | The map zoom level. | + +#### Returns + +| Type | Description | +| :--- | :--- | +| `HexCell` | The `HexCell` that contains the calculated centroid. | + +**Throws** +- `IllegalArgumentException` if the `points` list is empty. + +### `hexCellsForPointsWithId` + +#### Signature + +```kotlin +fun hexCellsForPointsWithId( + points: List, + zoom: Double, +): Set +``` + +#### Description + +Processes a list of `MarkerState` objects and maps each one to its corresponding `HexCell`. The result is a set of `IdentifiedHexCell` objects, which pairs the original marker's ID with its calculated hex cell. + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `points` | `List` | A list of `MarkerState` objects to process. | +| `zoom` | `Double` | The map zoom level. | + +#### Returns + +| Type | Description | +| :--- | :--- | +| `Set` | A set of `IdentifiedHexCell` objects, linking each original ID to a `HexCell`. | + +### `hexDistance` + +#### Signature + +```kotlin +fun hexDistance( + a: HexCoord, + b: HexCoord, +): Int +``` + +#### Description + +Calculates the distance between two hex coordinates on the grid. This is the "Manhattan distance" on the hexagonal grid, representing the minimum number of steps needed to move from cell `a` to cell `b`. + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `a` | `HexCoord` | The starting hex coordinate. | +| `b` | `HexCoord` | The ending hex coordinate. | + +#### Returns + +| Type | Description | +| :--- | :--- | +| `Int` | The distance in number of cells. | + +### `hexRange` + +#### Signature + +```kotlin +fun hexRange( + center: HexCoord, + radius: Int, +): List +``` + +#### Description + +Returns a list of all `HexCoord`s within a specified grid radius from a central cell. This is useful for "find in area" or neighborhood queries. + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `center` | `HexCoord` | The coordinate of the central cell. | +| `radius` | `Int` | The search radius in number of cells. A radius of 0 returns only the center cell. A radius of 1 returns the center and its 6 neighbors. | + +#### Returns + +| Type | Description | +| :--- | :--- | +| `List` | A list of all `HexCoord`s within the specified range, including the center. | + +--- + +## Data Classes + +### `HexCoord` + +A data class representing a coordinate in a hexagonal grid using the axial coordinate system. + +**Properties** +- `q: Int`: The `q` coordinate in the axial system. +- `r: Int`: The `r` coordinate in the axial system. +- `depth: Int`: An optional depth value, defaults to 0. +- `s: Int`: A computed property for the third cube coordinate (`s = -q - r`), useful for many hex grid algorithms. + +### `HexCell` + +A data class representing a single hexagonal cell with its computed properties. + +**Properties** +- `coord: HexCoord`: The hexagonal coordinate of the cell. +- `centerLatLng: GeoPointInterface`: The geographic coordinate (lat/lng) of the cell's center. +- `centerXY: Offset`: The projected Cartesian coordinate (XY) of the cell's center. +- `id: String`: The unique string identifier for the cell. + +### `IdentifiedHexCell` + +A data class used to associate an original item's ID with its corresponding `HexCell`. + +**Properties** +- `id: String`: The ID of the original item (e.g., a `MarkerState`). +- `cell: HexCell`: The `HexCell` associated with the item. + +--- + +## Example + +The following example demonstrates how to initialize `HexGeocell` and use its core functions to work with hexagonal cells. + +```kotlin +import com.mapconductor.core.features.GeoPoint +import com.mapconductor.core.geocell.HexGeocell +import com.mapconductor.core.projection.WebMercator + +fun main() { + // 1. Initialize the HexGeocell system + // Use a base side length of 50km for medium zoom levels + val hexGeocell = HexGeocell( + projection = WebMercator, + baseHexSideLength = 50000 + ) + + // Define a location (e.g., Tokyo Station) and a zoom level + val tokyoStation = GeoPoint(35.681236, 139.767125) + val zoom = 10.0 + + // 2. Find the hex cell for the location + val cell = hexGeocell.latLngToHexCell(tokyoStation, zoom) + println("--- Cell Information ---") + println("Cell for Tokyo Station: ${cell.id}") + println("Cell Coordinate (q, r): (${cell.coord.q}, ${cell.coord.r})") + println("Cell Center (Lat, Lng): (${cell.centerLatLng.latitude}, ${cell.centerLatLng.longitude})") + + // 3. Get the polygon vertices to draw the cell on a map + val polygon = hexGeocell.hexToPolygonLatLng(cell.coord, tokyoStation.latitude, zoom) + println("\n--- Cell Polygon Vertices ---") + polygon.forEachIndexed { index, point -> + println("Vertex $index: (${point.latitude}, ${point.longitude})") + } + + // 4. Find all neighboring cells within a radius of 1 + val neighbors = hexGeocell.hexRange(center = cell.coord, radius = 1) + println("\n--- Neighbors (radius=1) ---") + println("Total cells in range: ${neighbors.size}") + neighbors.forEach { neighborCoord -> + val neighborId = hexGeocell.hexToCellId(neighborCoord, zoom) + val distance = hexGeocell.hexDistance(cell.coord, neighborCoord) + println("Neighbor: $neighborId, Distance: $distance") + } +} + +/* +Expected Output (values may vary slightly based on projection implementation): + +--- Cell Information --- +Cell for Tokyo Station: H2236_-1100_Z10 +Cell Coordinate (q, r): (2236, -1100) +Cell Center (Lat, Lng): (35.6789..., 139.7638...) + +--- Cell Polygon Vertices --- +Vertex 0: (35.801..., 139.708...) +Vertex 1: (35.801..., 139.819...) +Vertex 2: (35.678..., 139.874...) +Vertex 3: (35.555..., 139.819...) +Vertex 4: (35.555..., 139.708...) +Vertex 5: (35.678..., 139.653...) + +--- Neighbors (radius=1) --- +Total cells in range: 7 +Neighbor: H2235_-1101_Z10, Distance: 1 +Neighbor: H2235_-1100_Z10, Distance: 1 +Neighbor: H2236_-1101_Z10, Distance: 1 +Neighbor: H2236_-1100_Z10, Distance: 0 +Neighbor: H2236_-1099_Z10, Distance: 1 +Neighbor: H2237_-1101_Z10, Distance: 1 +Neighbor: H2237_-1100_Z10, Distance: 1 +*/ +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/geocell/HexGeocellInterface.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/geocell/HexGeocellInterface.kt.md new file mode 100644 index 00000000..6cbffcfa --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/geocell/HexGeocellInterface.kt.md @@ -0,0 +1,285 @@ +# HexGeocellInterface + +The `HexGeocellInterface` defines the contract for a hexagonal geocelling system. It provides a comprehensive set of methods for converting between geographic coordinates (latitude/longitude) and hexagonal grid coordinates. This interface is fundamental for spatial indexing, data aggregation, and performing grid-based analysis on a map. + +Implementations of this interface manage the projection logic and grid calculations required to partition a map into a discrete hexagonal grid at various zoom levels. + +## Properties + +### projection +The map projection used for converting between geographic and world coordinates. + +**Signature** +```kotlin +val projection: ProjectionInterface +``` + +### baseHexSideLength +The side length, in projected units, of a base hexagon at a reference zoom level. This value is used as a basis for scaling hexagons at different zoom levels. + +**Signature** +```kotlin +val baseHexSideLength: Int +``` + +--- + +## Methods + +### latLngToHexCoord +Converts a geographic coordinate (latitude/longitude) to its corresponding hexagonal grid coordinate at a given zoom level. + +**Signature** +```kotlin +fun latLngToHexCoord( + position: GeoPointInterface, + zoom: Double, +): HexCoord +``` + +**Parameters** + +| Parameter | Type | Description | +|------------|---------------------|------------------------------------------------| +| `position` | `GeoPointInterface` | The geographic point to convert. | +| `zoom` | `Double` | The map zoom level for the conversion. | + +**Returns** + +`HexCoord` - The hexagonal coordinate corresponding to the input position. + +--- + +### latLngToHexCell +Converts a geographic coordinate to a `HexCell` object, which encapsulates the hexagonal coordinate and its associated zoom level. + +**Signature** +```kotlin +fun latLngToHexCell( + position: GeoPointInterface, + zoom: Double, +): HexCell +``` + +**Parameters** + +| Parameter | Type | Description | +|------------|---------------------|------------------------------------------------| +| `position` | `GeoPointInterface` | The geographic point to convert. | +| `zoom` | `Double` | The map zoom level for the conversion. | + +**Returns** + +`HexCell` - The `HexCell` containing the input position. + +--- + +### hexToLatLngCenter +Calculates the geographic coordinate (latitude/longitude) of the center of a given hexagonal cell. + +**Signature** +```kotlin +fun hexToLatLngCenter( + coord: HexCoord, + latHint: Double, + zoom: Double, +): GeoPointInterface +``` + +**Parameters** + +| Parameter | Type | Description | +|-----------|------------|--------------------------------------------------------------------------------| +| `coord` | `HexCoord` | The hexagonal coordinate to convert. | +| `latHint` | `Double` | A latitude hint to improve the accuracy of the inverse projection. | +| `zoom` | `Double` | The map zoom level at which the `HexCoord` was originally calculated. | + +**Returns** + +`GeoPointInterface` - The geographic coordinate of the hex cell's center. + +--- + +### hexToCellId +Generates a unique, stable string identifier for a hexagonal cell based on its coordinate and zoom level. This ID is suitable for use as a key in hashmaps or for database storage. + +**Signature** +```kotlin +fun hexToCellId( + coord: HexCoord, + zoom: Double, +): String +``` + +**Parameters** + +| Parameter | Type | Description | +|-----------|------------|------------------------------------| +| `coord` | `HexCoord` | The hexagonal coordinate of the cell. | +| `zoom` | `Double` | The zoom level of the cell. | + +**Returns** + +`String` - A unique string identifier for the cell. + +--- + +### hexToPolygonLatLng +Calculates the geographic coordinates of the six vertices that form the boundary of a hexagonal cell. The vertices are returned in a list, which can be used to draw the hexagon on a map. + +**Signature** +```kotlin +fun hexToPolygonLatLng( + coord: HexCoord, + latHint: Double, + zoom: Double, +): List +``` + +**Parameters** + +| Parameter | Type | Description | +|-----------|------------|--------------------------------------------------------------------------------| +| `coord` | `HexCoord` | The hexagonal coordinate of the cell. | +| `latHint` | `Double` | A latitude hint for improving the accuracy of the inverse projection. | +| `zoom` | `Double` | The map zoom level of the cell. | + +**Returns** + +`List` - A list of six `GeoPointInterface` objects representing the vertices of the hexagon. + +--- + +### enclosingCellOf +Determines the single hexagonal cell at a specified zoom level that completely encloses all the given points. This is useful for finding a common parent cell for a cluster of markers. + +**Signature** +```kotlin +fun enclosingCellOf( + points: List, + zoom: Double, +): HexCell +``` + +**Parameters** + +| Parameter | Type | Description | +|-----------|---------------------|--------------------------------------------------------------------------| +| `points` | `List` | A list of marker states, each containing a geographic position. | +| `zoom` | `Double` | The target zoom level for the enclosing cell. | + +**Returns** + +`HexCell` - The single `HexCell` that encloses all input points. + +--- + +### hexCellsForPointsWithId +Converts a list of points (represented as `MarkerState` objects) into a set of unique hexagonal cells at a given zoom level. Each cell in the returned set is an `IdentifiedHexCell`, which includes its coordinate, zoom level, and a unique ID. + +**Signature** +```kotlin +fun hexCellsForPointsWithId( + points: List, + zoom: Double, +): Set +``` + +**Parameters** + +| Parameter | Type | Description | +|-----------|---------------------|--------------------------------------------------------------------------| +| `points` | `List` | A list of marker states to be placed into hex cells. | +| `zoom` | `Double` | The map zoom level to use for creating the cells. | + +**Returns** + +`Set` - A set of unique `IdentifiedHexCell` objects corresponding to the locations of the input points. + +--- + +### hexDistance +Calculates the grid distance between two hexagonal coordinates. The distance is defined as the minimum number of cells one must traverse to get from cell `a` to cell `b`. + +**Signature** +```kotlin +fun hexDistance( + a: HexCoord, + b: HexCoord, +): Int +``` + +**Parameters** + +| Parameter | Type | Description | +|-----------|------------|----------------------------------| +| `a` | `HexCoord` | The starting hexagonal coordinate. | +| `b` | `HexCoord` | The ending hexagonal coordinate. | + +**Returns** + +`Int` - The distance in number of cells. + +--- + +### hexRange +Finds all hexagonal coordinates within a specified radius from a central coordinate, forming a larger hexagonal area. + +**Signature** +```kotlin +fun hexRange( + center: HexCoord, + radius: Int, +): List +``` + +**Parameters** + +| Parameter | Type | Description | +|-----------|------------|---------------------------------------------------------------------------------------------------------| +| `center` | `HexCoord` | The central hexagonal coordinate. | +| `radius` | `Int` | The radius in number of cells. A radius of `0` returns just the center cell. A radius of `1` returns the center and its 6 immediate neighbors. | + +**Returns** + +`List` - A list of all `HexCoord` objects within the specified range, including the center. + +--- + +## Example + +The following conceptual example demonstrates a common workflow using an implementation of `HexGeocellInterface`. + +```kotlin +// Assume 'hexGeocell' is an initialized instance of a class +// that implements HexGeocellInterface. +// Assume 'GeoPoint' is a class that implements GeoPointInterface. + +val zoomLevel = 10.0 +val myPosition = GeoPoint(latitude = 40.7128, longitude = -74.0060) // New York City + +// 1. Convert a geographic position to a hex coordinate +val hexCoord: HexCoord = hexGeocell.latLngToHexCoord(myPosition, zoomLevel) +println("Hex coordinate for my position: $hexCoord") + +// 2. Find all neighboring hex cells within a radius of 1 +val nearbyHexes: List = hexGeocell.hexRange(hexCoord, 1) +println("Found ${nearbyHexes.size} hexes within radius 1.") // Should print 7 + +// 3. Get the center lat/lng of the first neighbor +val firstNeighborCoord = nearbyHexes.first { it != hexCoord } +val neighborCenter: GeoPointInterface = hexGeocell.hexToLatLngCenter( + coord = firstNeighborCoord, + latHint = myPosition.latitude, + zoom = zoomLevel +) +println("Center of a neighboring cell: ${neighborCenter.latitude}, ${neighborCenter.longitude}") + +// 4. Get the polygon vertices for the original hex cell to draw it on a map +val polygonVertices: List = hexGeocell.hexToPolygonLatLng( + coord = hexCoord, + latHint = myPosition.latitude, + zoom = zoomLevel +) +println("Polygon has ${polygonVertices.size} vertices.") // Should print 6 +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/geocell/KDTree.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/geocell/KDTree.kt.md new file mode 100644 index 00000000..838b5b5f --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/geocell/KDTree.kt.md @@ -0,0 +1,279 @@ +Of course! Here is the high-quality SDK documentation for the provided Kotlin code snippet. + +--- + +# KDTree + +## `KDTree` Class + +A K-D Tree (K-dimensional tree) implementation optimized for efficient 2D spatial queries on `HexCell` objects. This class partitions a set of points (the centers of hex cells) in a 2-dimensional space, allowing for fast searches. + +It supports the following query types: +* **Nearest Neighbor:** Find the single closest point to a given location. +* **K-Nearest Neighbors (k-NN):** Find the `k` closest points to a given location. +* **Radius Search:** Find all points within a certain distance of a given location. + +### Signature + +```kotlin +class KDTree(points: List) +``` + +### Description + +Constructs a `KDTree` by recursively partitioning the provided list of `HexCell` points. The tree is balanced by alternating the splitting axis (X and Y) at each level of depth. If the input list is empty, an empty tree is created. + +### Parameters + +| Parameter | Type | Description | +| :-------- | :--------------- | :---------------------------------------- | +| `points` | `List` | A list of `HexCell` objects to build the tree from. | + +### Example + +```kotlin +// Assuming HexCell and Offset are defined as: +// data class HexCell(val id: String, val centerXY: Offset) +// import androidx.compose.ui.geometry.Offset + +val hexCells = listOf( + HexCell("A", Offset(2f, 3f)), + HexCell("B", Offset(5f, 4f)), + HexCell("C", Offset(9f, 6f)), + HexCell("D", Offset(4f, 7f)), + HexCell("E", Offset(8f, 1f)), + HexCell("F", Offset(7f, 2f)) +) + +// Create a new KDTree instance +val kdTree = KDTree(hexCells) +``` + +--- + +## Methods + +### `nearest` + +Finds the single nearest `HexCell` to a given query point. + +#### Signature + +```kotlin +fun nearest(query: Offset): HexCell? +``` + +#### Description + +Performs a nearest neighbor search to find the `HexCell` in the tree whose center is closest to the specified `query` point. + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :------- | :------------------------------------------- | +| `query` | `Offset` | The coordinate point to search from. | + +#### Returns + +The nearest `HexCell` object, or `null` if the tree is empty. + +#### Example + +```kotlin +val queryPoint = Offset(5f, 5f) +val nearestCell = kdTree.nearest(queryPoint) + +if (nearestCell != null) { + println("Nearest cell is ${nearestCell.id} at ${nearestCell.centerXY}") + // Expected output: Nearest cell is B at Offset(5.0, 4.0) +} +``` + +--- + +### `nearestWithDistance` + +Finds the single nearest `HexCell` to a query point and returns it along with the calculated distance. + +#### Signature + +```kotlin +fun nearestWithDistance(query: Offset): HexCellWithDistance? +``` + +#### Description + +This method extends the `nearest` search by packaging the resulting `HexCell` and its Euclidean distance from the `query` point into a `HexCellWithDistance` object. + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :------- | :------------------------------------------- | +| `query` | `Offset` | The coordinate point to search from. | + +#### Returns + +A `HexCellWithDistance` object containing the nearest cell and the distance, or `null` if the tree is empty. + +#### Example + +```kotlin +// Assuming HexCellWithDistance is defined as: +// data class HexCellWithDistance(val cell: HexCell, val distanceMeters: Double) + +val queryPoint = Offset(5.1f, 4.2f) +val result = kdTree.nearestWithDistance(queryPoint) + +result?.let { + println("Nearest cell is ${it.cell.id} with a distance of ${it.distanceMeters}") + // Expected output: Nearest cell is B with a distance of 0.2236... +} +``` + +--- + +### `nearestKWithDistance` + +Finds the `k` nearest `HexCell`s to a query point, along with their respective distances. + +#### Signature + +```kotlin +fun nearestKWithDistance(query: Offset, k: Int): List +``` + +#### Description + +Performs a k-nearest neighbors (k-NN) search. It returns a list of the `k` closest `HexCell`s to the `query` point, sorted by distance in ascending order. + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :------- | :------------------------------------------- | +| `query` | `Offset` | The coordinate point to search from. | +| `k` | `Int` | The number of nearest neighbors to find. Must be a positive integer. | + +#### Returns + +A `List` containing the `k` nearest cells and their distances, sorted from nearest to farthest. Returns an empty list if the tree is empty. + +#### Example + +```kotlin +val queryPoint = Offset(6f, 3f) +val k = 3 +val nearestThree = kdTree.nearestKWithDistance(queryPoint, k) + +println("Found ${nearestThree.size} neighbors:") +nearestThree.forEach { + println("- Cell ${it.cell.id} at distance ${it.distanceMeters}") +} +// Expected output: +// Found 3 neighbors: +// - Cell F at distance 1.414... +// - Cell B at distance 1.414... +// - Cell A at distance 4.0... +``` + +--- + +### `withinRadiusWithDistance` + +Finds all `HexCell`s within a specified radius of a query point. + +#### Signature + +```kotlin +fun withinRadiusWithDistance(query: Offset, radius: Double): List +``` + +#### Description + +Performs a radius search to find all `HexCell`s whose centers fall within the given `radius` of the `query` point. The results include the distance to each cell and are sorted by distance. + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :------- | :------------------------------------------- | +| `query` | `Offset` | The center point of the search circle. | +| `radius` | `Double` | The radius of the search area. Must be non-negative. | + +#### Returns + +A `List` of all cells found within the radius, sorted by distance. Returns an empty list if no cells are within the radius or if the tree is empty. + +#### Example + +```kotlin +val queryPoint = Offset(7.5f, 2.5f) +val searchRadius = 2.0 +val cellsInRadius = kdTree.withinRadiusWithDistance(queryPoint, searchRadius) + +println("Found ${cellsInRadius.size} cells within a radius of $searchRadius:") +cellsInRadius.forEach { + println("- Cell ${it.cell.id} at distance ${it.distanceMeters}") +} +// Expected output: +// Found 2 cells within a radius of 2.0: +// - Cell F at distance 0.707... +// - Cell E at distance 1.581... +``` + +--- + +### `getStats` + +Retrieves statistics about the internal structure of the `KDTree`. + +#### Signature + +```kotlin +fun getStats(): KDTreeStats +``` + +#### Description + +Provides metrics about the constructed tree, which can be useful for debugging or performance analysis. It returns the total number of nodes, the maximum depth of the tree, and a flag indicating if the tree is empty. + +#### Returns + +A `KDTreeStats` object containing the tree's structural metrics. + +#### Example + +```kotlin +val stats = kdTree.getStats() + +println("Tree is empty: ${stats.isEmpty}") +println("Node count: ${stats.nodeCount}") +println("Max depth: ${stats.maxDepth}") + +// Example output for the tree created earlier: +// Tree is empty: false +// Node count: 6 +// Max depth: 4 +``` + +--- + +## `KDTreeStats` Data Class + +A data class that holds statistics about a `KDTree`'s structure. + +### Signature + +```kotlin +data class KDTreeStats( + val nodeCount: Int, + val maxDepth: Int, + val isEmpty: Boolean +) +``` + +### Properties + +| Property | Type | Description | +| :---------- | :-------- | :------------------------------------------------ | +| `nodeCount` | `Int` | The total number of nodes (cells) in the tree. | +| `maxDepth` | `Int` | The maximum depth from the root to any leaf node. | +| `isEmpty` | `Boolean` | `true` if the tree contains no nodes, otherwise `false`. | \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/groundimage/AbstractGroundImageOverlayRenderer.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/groundimage/AbstractGroundImageOverlayRenderer.kt.md new file mode 100644 index 00000000..22aea5bf --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/groundimage/AbstractGroundImageOverlayRenderer.kt.md @@ -0,0 +1,209 @@ +# AbstractGroundImageOverlayRenderer<ActualGroundImage> + +Provides a foundational implementation for rendering ground image overlays on a map. This abstract class simplifies the creation of platform-specific renderers by handling the common logic of adding, updating, and removing batches of images. + +Subclasses are responsible for implementing the core, platform-specific logic for creating, updating, and removing individual ground image objects (e.g., a `GroundOverlay` on Google Maps or an `ImageLayer` on Mapbox). + +## Generic Parameters + +| Name | Description | +| :--- | :--- | +| `ActualGroundImage` | The concrete, platform-specific class that represents a ground image on the map. | + +## Properties + +### holder +The map view holder that provides access to the map instance and its context. + +**Signature** +```kotlin +abstract val holder: MapViewHolderInterface<*, *> +``` + +### coroutine +The coroutine scope used for launching and managing asynchronous rendering operations. + +**Signature** +```kotlin +abstract val coroutine: CoroutineScope +``` + +## Functions + +### onPostProcess +A lifecycle hook called after a batch of add, change, or remove operations has been processed. Subclasses can override this method to perform any finalization or cleanup tasks, such as refreshing the map view. The default implementation is empty. + +**Signature** +```kotlin +override suspend fun onPostProcess() +``` + +### createGroundImage +**(Abstract)** Creates a new, platform-specific ground image instance on the map. Subclasses must provide an implementation for this method. + +**Signature** +```kotlin +abstract suspend fun createGroundImage(state: GroundImageState): ActualGroundImage? +``` + +**Parameters** + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `state` | `GroundImageState` | An object containing all the properties (e.g., image source, position, dimensions) needed to create the ground image. | + +**Returns** + +`ActualGroundImage?` - The newly created platform-specific ground image object, or `null` if creation fails. + +### updateGroundImageProperties +**(Abstract)** Updates the properties of an existing ground image on the map. Subclasses must provide an implementation for this method. + +**Signature** +```kotlin +abstract suspend fun updateGroundImageProperties( + groundImage: ActualGroundImage, + current: GroundImageEntityInterface, + prev: GroundImageEntityInterface, +): ActualGroundImage? +``` + +**Parameters** + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `groundImage` | `ActualGroundImage` | The existing platform-specific ground image object to be updated. | +| `current` | `GroundImageEntityInterface` | The entity representing the new state of the ground image. | +| `prev` | `GroundImageEntityInterface` | The entity representing the previous state of the ground image. | + +**Returns** + +`ActualGroundImage?` - The updated ground image object, which may be the same instance as the input `groundImage` or a new one, depending on the platform's API. Returns `null` if the update fails. + +### removeGroundImage +**(Abstract)** Removes a ground image from the map. Subclasses must provide an implementation for this method. + +**Signature** +```kotlin +abstract suspend fun removeGroundImage(entity: GroundImageEntityInterface) +``` + +**Parameters** + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `entity` | `GroundImageEntityInterface` | The entity containing the ground image object to be removed. | + +### onAdd +Handles the addition of a batch of new ground images. It iterates through the provided list and calls `createGroundImage` for each item. + +**Signature** +```kotlin +override suspend fun onAdd( + data: List, +): List +``` + +**Parameters** + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `data` | `List` | A list of parameter objects, each containing the state for a new ground image to be created. | + +**Returns** + +`List` - A list of the newly created platform-specific ground image objects. Each element corresponds to an item in the input `data` list. An element will be `null` if the corresponding image creation failed. + +### onChange +Handles property changes for a batch of existing ground images. It iterates through the list and calls `updateGroundImageProperties` for each image that needs an update. + +**Signature** +```kotlin +override suspend fun onChange( + data: List>, +): List +``` + +**Parameters** + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `data` | `List>` | A list of parameter objects, each containing the previous and current states of a ground image to be updated. | + +**Returns** + +`List` - A list of the updated ground image objects. Each element corresponds to an item in the input `data` list. An element will be `null` if the corresponding update failed. + +### onRemove +Handles the removal of a batch of ground images from the map. It iterates through the list of entities and calls `removeGroundImage` for each one. + +**Signature** +```kotlin +override suspend fun onRemove(data: List>) +``` + +**Parameters** + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `data` | `List>` | A list of ground image entities to be removed. | + +## Example + +Below is a conceptual example of how to subclass `AbstractGroundImageOverlayRenderer` for a hypothetical Google Maps implementation. + +```kotlin +// Assume GoogleMap, GroundOverlay, and GroundOverlayOptions are from the Google Maps SDK +import com.google.android.gms.maps.GoogleMap +import com.google.android.gms.maps.model.GroundOverlay +import com.google.android.gms.maps.model.GroundOverlayOptions +import com.mapconductor.core.groundimage.* +import com.mapconductor.core.map.MapViewHolderInterface +import kotlinx.coroutines.CoroutineScope + +class GoogleMapsGroundImageRenderer( + override val holder: MapViewHolderInterface, + override val coroutine: CoroutineScope +) : AbstractGroundImageOverlayRenderer() { + + private val googleMap: GoogleMap = holder.map + + override suspend fun createGroundImage(state: GroundImageState): GroundOverlay? { + // Logic to convert GroundImageState to GroundOverlayOptions + val options = GroundOverlayOptions().apply { + positionFromBounds(state.bounds) + image(state.imageDescriptor) + transparency(1f - state.opacity) + zIndex(state.zIndex) + visible(state.isVisible) + } + return googleMap.addGroundOverlay(options) + } + + override suspend fun updateGroundImageProperties( + groundImage: GroundOverlay, + current: GroundImageEntityInterface, + prev: GroundImageEntityInterface + ): GroundOverlay? { + val currentState = current.state + val prevState = prev.state + + if (currentState.bounds != prevState.bounds) { + groundImage.positionFromBounds(currentState.bounds) + } + if (currentState.imageDescriptor != prevState.imageDescriptor) { + groundImage.setImage(currentState.imageDescriptor) + } + if (currentState.opacity != prevState.opacity) { + groundImage.transparency = 1f - currentState.opacity + } + // ... update other properties as needed + + return groundImage + } + + override suspend fun removeGroundImage(entity: GroundImageEntityInterface) { + entity.groundImage.remove() + } +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/groundimage/GroundImage.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/groundimage/GroundImage.kt.md new file mode 100644 index 00000000..05b0abd9 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/groundimage/GroundImage.kt.md @@ -0,0 +1,149 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet, formatted in Markdown. + +--- + +### `GroundImageState` + +A state holder class that defines and manages a ground image overlay on a map. + +It encapsulates all properties necessary for rendering an image over a specific geographical area, such as its boundaries, the image resource, and its opacity. The properties are observable and mutable, allowing for dynamic updates to the ground overlay in a Compose-based environment. + +#### Signature + +```kotlin +class GroundImageState( + bounds: GeoRectBounds, + image: Drawable, + opacity: Float = 1.0f, + tileSize: Int = GroundImageTileProvider.DEFAULT_TILE_SIZE, + id: String? = null, + extra: Serializable? = null, + onClick: OnGroundImageEventHandler? = null, +) : ComponentState +``` + +#### Parameters + +| Parameter | Type | Description | Optional | +| :--- | :--- | :--- | :--- | +| `bounds` | `GeoRectBounds` | The geographical coordinates that define the rectangular area where the image will be placed. | No | +| `image` | `Drawable` | The `Drawable` resource to be displayed as the overlay. | No | +| `opacity` | `Float` | The opacity of the image, ranging from `0.0f` (fully transparent) to `1.0f` (fully opaque). | Yes, defaults to `1.0f`. | +| `tileSize` | `Int` | The size in pixels for the tiles used to render the image. Affects rendering performance and quality. | Yes, defaults to `GroundImageTileProvider.DEFAULT_TILE_SIZE`. | +| `id` | `String?` | A unique identifier for this ground image. If `null`, a stable ID is automatically generated based on the other properties. | Yes, defaults to `null`. | +| `extra` | `Serializable?` | Optional, serializable data that can be associated with the ground image for custom use cases. | Yes, defaults to `null`. | +| `onClick` | `OnGroundImageEventHandler?` | A callback function that is invoked when the user clicks on the ground image. | Yes, defaults to `null`. | + +#### Properties + +All constructor parameters are exposed as mutable `var` properties. You can modify these properties after initialization to dynamically update the ground image on the map. + +| Property | Type | Description | +| :--- | :--- | :--- | +| `id` | `String` | The unique identifier for the component. | +| `bounds` | `GeoRectBounds` | The geographical area the image covers. | +| `image` | `Drawable` | The image `Drawable` to display. | +| `opacity` | `Float` | The opacity of the image. | +| `tileSize` | `Int` | The size of the rendering tiles. | +| `extra` | `Serializable?` | Extra data associated with the image. | +| `onClick` | `OnGroundImageEventHandler?` | The click event handler. | + +#### Methods + +##### `asFlow` + +Returns a `Flow` that emits a value whenever any property of the `GroundImageState` changes. This is useful for observing state changes in a reactive programming paradigm. The flow only emits when the underlying data has actually changed. + +**Signature** +```kotlin +fun asFlow(): Flow +``` + +**Returns** + +| Type | Description | +| :--- | :--- | +| `Flow` | A flow that emits a unique fingerprint of the state upon any change. | + +#### Example + +```kotlin +// Assume you have a Drawable resource and GeoRectBounds defined +val imageDrawable: Drawable = ContextCompat.getDrawable(context, R.drawable.world_map_overlay)!! +val overlayBounds = GeoRectBounds( + north = 85.0, + south = -85.0, + east = 180.0, + west = -180.0 +) + +// Create a GroundImageState instance with a click handler +val groundImage = GroundImageState( + bounds = overlayBounds, + image = imageDrawable, + opacity = 0.75f, + onClick = { event -> + println("Ground image clicked!") + event.clicked?.let { point -> + println("Click location: Lat ${point.latitude}, Lon ${point.longitude}") + } + } +) + +// You can dynamically update properties later, which will trigger a redraw on the map. +// For example, to make the image more transparent: +groundImage.opacity = 0.5f +``` + +--- + +### `GroundImageEvent` + +A data class that encapsulates information about a click event on a ground image. An instance of this class is passed to the `OnGroundImageEventHandler` when a click occurs. + +#### Signature + +```kotlin +data class GroundImageEvent( + val state: GroundImageState, + val clicked: GeoPoint?, +) +``` + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `state` | `GroundImageState` | The state of the ground image that was clicked. | +| `clicked` | `GeoPoint?` | The geographical coordinates (`GeoPoint`) of the click location. This can be `null` if the exact point cannot be determined. | + +--- + +### `OnGroundImageEventHandler` + +A type alias for a function that handles click events on a `GroundImageState`. + +#### Signature + +```kotlin +typealias OnGroundImageEventHandler = (GroundImageEvent) -> Unit +``` + +#### Usage + +This function type is used for the `onClick` parameter in the `GroundImageState` constructor. + +```kotlin +// Define a handler for the click event +val handleGroundImageClick: OnGroundImageEventHandler = { event -> + println("Image with ID ${event.state.id} was clicked.") + // You can access the state and click location from the event object +} + +// Assign it to a GroundImageState instance +val groundImage = GroundImageState( + bounds = /* ... */, + image = /* ... */, + onClick = handleGroundImageClick +) +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/groundimage/GroundImageCapableInterface.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/groundimage/GroundImageCapableInterface.kt.md new file mode 100644 index 00000000..2204b97a --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/groundimage/GroundImageCapableInterface.kt.md @@ -0,0 +1,167 @@ +# GroundImageCapableInterface + +An interface for components capable of managing and displaying ground images on a map surface. It provides methods for adding, updating, and querying ground images, as well as handling user interactions. + +--- + +## Methods + +### compositionGroundImages + +Asynchronously replaces all currently displayed ground images with a new set. This function is useful for performing a complete refresh of the ground image layer. Being a `suspend` function, it should be called from a coroutine scope. + +**Signature** + +```kotlin +suspend fun compositionGroundImages(data: List) +``` + +**Parameters** + +| Parameter | Type | Description | +|-----------|---------------------------|------------------------------------------------------------------------------------------| +| `data` | `List` | A list of `GroundImageState` objects that define the new set of ground images to be displayed. | + +**Returns** + +This is a suspend function and does not return a value. It completes once the composition operation is finished. + +**Example** + +```kotlin +// Assuming 'mapController' implements GroundImageCapableInterface +// and this code is executed within a coroutine scope. + +val newImages = listOf( + GroundImageState( + id = "historic_map_1890", + image = Image.fromResource(R.drawable.map_1890), + bounds = LatLngBounds(...) + ), + GroundImageState( + id = "weather_overlay_radar", + image = Image.fromUrl("https://.../radar.png"), + bounds = LatLngBounds(...) + ) +) + +// Replace all existing ground images with the new set +mapController.compositionGroundImages(newImages) +``` + +--- + +### updateGroundImage + +Asynchronously updates a single ground image. If a ground image with the same ID as the provided `state` exists, its properties will be updated. If it does not exist, a new ground image may be added. This is a `suspend` function and must be called from a coroutine scope. + +**Signature** + +```kotlin +suspend fun updateGroundImage(state: GroundImageState) +``` + +**Parameters** + +| Parameter | Type | Description | +|-----------|--------------------|------------------------------------------------------------------------------------| +| `state` | `GroundImageState` | The state object containing the ID and updated properties of the ground image. | + +**Returns** + +This is a suspend function and does not return a value. It completes once the update operation is finished. + +**Example** + +```kotlin +// Assuming 'mapController' implements GroundImageCapableInterface +// and this code is executed within a coroutine scope. + +val updatedImageState = GroundImageState( + id = "weather_overlay_radar", + transparency = 0.5f, // Update the transparency of an existing image + image = Image.fromUrl("https://.../latest_radar.png"), // Or update the image source + bounds = LatLngBounds(...) +) + +// Apply the update +mapController.updateGroundImage(updatedImageState) +``` + +--- + +### setOnGroundImageClickListener + +**Deprecated:** Use the `onClick` property within `GroundImageState` instead. + +Sets a global listener to handle click events on any ground image. + +**Signature** + +```kotlin +@Deprecated("Use GroundImageState.onClick instead.") +fun setOnGroundImageClickListener(listener: OnGroundImageEventHandler?) +``` + +**Description** + +This method sets a single listener for click events across all ground images. For more granular, state-driven event handling, it is recommended to set the `onClick` lambda on individual `GroundImageState` objects. + +**Parameters** + +| Parameter | Type | Description | +|-----------|------------------------------|----------------------------------------------------------------------------------------------------| +| `listener` | `OnGroundImageEventHandler?` | An instance of `OnGroundImageEventHandler` to process click events, or `null` to remove the current listener. | + +**Example** + +```kotlin +// This method is deprecated. The following is for reference only. +// Recommended approach: GroundImageState(id = "...", onClick = { ... }) + +val clickListener = object : OnGroundImageEventHandler { + override fun onGroundImageClicked(state: GroundImageState) { + Log.d("Map", "Ground image ${state.id} was clicked.") + } +} + +// Set the global listener +mapController.setOnGroundImageClickListener(clickListener) + +// To remove the listener +mapController.setOnGroundImageClickListener(null) +``` + +--- + +### hasGroundImage + +Synchronously checks if a ground image matching the identifier in the provided `state` object is currently managed by the component. + +**Signature** + +```kotlin +fun hasGroundImage(state: GroundImageState): Boolean +``` + +**Parameters** + +| Parameter | Type | Description | +|-----------|--------------------|------------------------------------------------------------------------------------------------| +| `state` | `GroundImageState` | The `GroundImageState` object, which must contain the ID of the ground image to check for. | + +**Returns** + +`Boolean` - Returns `true` if the ground image exists, `false` otherwise. + +**Example** + +```kotlin +val imageToCheck = GroundImageState(id = "historic_map_1890") + +if (mapController.hasGroundImage(imageToCheck)) { + println("Ground image 'historic_map_1890' is already on the map.") +} else { + println("Ground image 'historic_map_1890' is not on the map.") +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/groundimage/GroundImageComponent.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/groundimage/GroundImageComponent.kt.md new file mode 100644 index 00000000..3c51bb79 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/groundimage/GroundImageComponent.kt.md @@ -0,0 +1,156 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +# GroundImage + +The `GroundImage` composable is used to overlay an image onto a specific geographical area of the map. It must be used within the content lambda of a `MapView` composable. + +There are two overloads for this function. The primary overload accepts individual properties to define the ground image, while the other accepts a pre-configured `GroundImageState` object for more advanced state management. + +--- + +## GroundImage (Declarative) + +This is the primary and most convenient way to add a ground image to the map. You provide the image, its geographical bounds, and other optional properties directly as parameters. + +### Signature + +```kotlin +@Composable +fun MapViewScope.GroundImage( + bounds: GeoRectBounds, + image: Drawable, + opacity: Float = 0.5f, + tileSize: Int = GroundImageTileProvider.DEFAULT_TILE_SIZE, + id: String? = null, + extra: Serializable? = null, + onClick: OnGroundImageEventHandler? = null, +) +``` + +### Description + +A composable function that overlays a `Drawable` image onto a specified rectangular geographical area of the map. The image is automatically anchored to the provided `bounds`. This function should be called within the `MapView` composable's content scope. + +### Parameters + +| Parameter | Type | Description | +|------------|-----------------------------|----------------------------------------------------------------------------------------------------------------------------------------| +| `bounds` | `GeoRectBounds` | The rectangular geographical coordinates where the image will be placed. | +| `image` | `Drawable` | The Android `Drawable` to be displayed on the map. | +| `opacity` | `Float` | The opacity of the image, ranging from `0.0` (fully transparent) to `1.0` (fully opaque). Defaults to `0.5f`. | +| `tileSize` | `Int` | The size of the tiles (in pixels) used to render the image on the map. Defaults to `GroundImageTileProvider.DEFAULT_TILE_SIZE`. | +| `id` | `String?` | An optional unique identifier for the ground image. If not provided, one will be generated internally. | +| `extra` | `Serializable?` | Optional, serializable data to associate with the ground image. This data is passed to the `onClick` handler. | +| `onClick` | `OnGroundImageEventHandler?`| An optional callback lambda that is invoked with the image's `id` and `extra` data when the user clicks on the ground image overlay. | + +### Returns + +This composable does not return a value. + +### Example + +```kotlin +import androidx.compose.runtime.Composable +import androidx.compose.ui.platform.LocalContext +import androidx.core.content.ContextCompat +import com.mapconductor.core.MapView +import com.mapconductor.core.features.GeoRectBounds +import com.mapconductor.core.groundimage.GroundImage + +@Composable +fun MapWithGroundOverlay() { + MapView( + // ... other map properties + ) { + val context = LocalContext.current + val imageDrawable = ContextCompat.getDrawable(context, R.drawable.historical_map_overlay) + + val overlayBounds = GeoRectBounds( + northEast = GeoPoint(40.7128, -74.0060), // New York City NE corner + southWest = GeoPoint(40.7028, -74.0160) // New York City SW corner + ) + + if (imageDrawable != null) { + GroundImage( + bounds = overlayBounds, + image = imageDrawable, + opacity = 0.7f, + id = "historical-nyc-map", + onClick = { id, extra -> + Log.d("MapView", "GroundImage clicked! ID: $id") + } + ) + } + } +} +``` + +--- + +## GroundImage (State-based) + +This overload is useful for advanced use cases where you need to manage the `GroundImageState` externally, for instance, by hoisting it or using `remember`. + +### Signature + +```kotlin +@Composable +fun MapViewScope.GroundImage(state: GroundImageState) +``` + +### Description + +Adds a ground image to the map using a pre-configured `GroundImageState` object. This allows for the state of the ground image (its properties, image, and handlers) to be managed separately from its composition. + +### Parameters + +| Parameter | Type | Description | +|-----------|--------------------|----------------------------------------------------------------------------------------------------------------------------------------| +| `state` | `GroundImageState` | The state object that defines all properties of the ground image, including its bounds, image, opacity, and event handlers. | + +### Returns + +This composable does not return a value. + +### Example + +```kotlin +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.platform.LocalContext +import androidx.core.content.ContextCompat +import com.mapconductor.core.MapView +import com.mapconductor.core.features.GeoRectBounds +import com.mapconductor.core.groundimage.GroundImage +import com.mapconductor.core.groundimage.GroundImageState + +@Composable +fun MapWithManagedGroundOverlay() { + MapView( + // ... other map properties + ) { + val context = LocalContext.current + val imageDrawable = ContextCompat.getDrawable(context, R.drawable.park_layout) + + val parkBounds = GeoRectBounds( + northEast = GeoPoint(34.0522, -118.2437), + southWest = GeoPoint(34.0422, -118.2537) + ) + + val groundImageState = remember(imageDrawable, parkBounds) { + imageDrawable?.let { + GroundImageState( + bounds = parkBounds, + image = it, + opacity = 0.8f, + id = "park-layout-overlay" + ) + } + } + + groundImageState?.let { + GroundImage(state = it) + } + } +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/groundimage/GroundImageController.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/groundimage/GroundImageController.kt.md new file mode 100644 index 00000000..83a231cb --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/groundimage/GroundImageController.kt.md @@ -0,0 +1,230 @@ +Of course! Here is the high-quality SDK documentation for the provided `GroundImageController` code snippet. + +--- + +# GroundImageController + +## Description + +`GroundImageController` is an abstract class that orchestrates the display and management of ground image overlays on a map. It acts as a bridge between the abstract representation of ground images (`GroundImageState`) and their platform-specific rendering (`ActualGroundImage`). + +This controller implements the `OverlayControllerInterface` and manages the complete lifecycle of ground images, including adding, updating, and removing them from the map. It uses a `GroundImageManagerInterface` to track the state of ground image entities and a `GroundImageOverlayRendererInterface` to handle the actual rendering on the map. + +Operations that modify the state of ground images are thread-safe, managed internally by a `Semaphore`. + +**Generic Parameters** + +| Name | Description | +| :--- | :--- | +| `ActualGroundImage` | The platform-specific class or type that represents a rendered ground image overlay (e.g., `GroundOverlay` in Google Maps). | + +## Constructor + +```kotlin +abstract class GroundImageController( + val groundImageManager: GroundImageManagerInterface, + open val renderer: GroundImageOverlayRendererInterface, + override var clickListener: OnGroundImageEventHandler? = null, +) +``` + +### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `groundImageManager` | `GroundImageManagerInterface` | An instance that manages the collection and state of ground image entities. | +| `renderer` | `GroundImageOverlayRendererInterface` | An instance responsible for rendering the ground images on the map. | +| `clickListener` | `OnGroundImageEventHandler?` | An optional listener that is invoked when any ground image managed by this controller is clicked. Defaults to `null`. | + +## Properties + +| Property | Type | Description | +| :--- | :--- | :--- | +| `zIndex` | `Int` | The z-index for the overlay layer, which determines its stacking order on the map. Hardcoded to `2`. | +| `semaphore` | `Semaphore` | A semaphore that ensures thread-safe, sequential access to methods that modify ground images (`add`, `update`, `clear`). | + +## Methods + +### dispatchClick + +#### Signature + +```kotlin +fun dispatchClick(event: GroundImageEvent) +``` + +#### Description + +Dispatches a click event to the appropriate listeners. This method is typically called by the underlying map framework when a user taps on a ground image. It triggers both the `onClick` lambda defined in the `GroundImageState` of the specific image and the controller-wide `clickListener`. + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `event` | `GroundImageEvent` | The event object containing details about the click, including the state of the clicked ground image. | + +### add + +#### Signature + +```kotlin +override suspend fun add(data: List) +``` + +#### Description + +Synchronizes the ground images on the map with the provided list of `GroundImageState` objects. The method intelligently computes the difference between the current state and the new state: +- **Adds** new ground images that are in `data` but not on the map. +- **Updates** existing ground images whose state has changed. +- **Removes** ground images that are on the map but not in `data`. + +This operation is performed atomically and is thread-safe. + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `data` | `List` | The complete list of ground images that should be displayed on the map. | + +### update + +#### Signature + +```kotlin +override suspend fun update(state: GroundImageState) +``` + +#### Description + +Updates a single ground image on the map based on the provided `GroundImageState`. To optimize performance, the update is only performed if the new state's `fingerPrint()` differs from the existing one. This operation is thread-safe. + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `state` | `GroundImageState` | The new state for the ground image to be updated. The `id` within the state is used to identify the image. | + +### clear + +#### Signature + +```kotlin +override suspend fun clear() +``` + +#### Description + +Removes all ground images managed by this controller from the map and clears all internal references. This operation is thread-safe. + +### find + +#### Signature + +```kotlin +override fun find(position: GeoPointInterface): GroundImageEntityInterface? +``` + +#### Description + +Finds and returns the ground image entity located at a specific geographical coordinate. This is useful for identifying which ground image is present at a given point on the map. The search is delegated to the `groundImageManager`. + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `position` | `GeoPointInterface` | The geographical coordinates to search at. | + +#### Returns + +| Type | Description | +| :--- | :--- | +| `GroundImageEntityInterface?` | The found ground image entity, or `null` if no image exists at the specified position. | + +### onCameraChanged + +#### Signature + +```kotlin +override suspend fun onCameraChanged(mapCameraPosition: MapCameraPosition) +``` + +#### Description + +A lifecycle method from `OverlayControllerInterface`. This implementation is empty and serves as a placeholder for future functionality that may need to react to map camera movements. + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `mapCameraPosition` | `MapCameraPosition` | The new position and state of the map camera. | + +### destroy + +#### Signature + +```kotlin +override fun destroy() +``` + +#### Description + +A lifecycle method from `OverlayControllerInterface` for releasing resources. This implementation is empty as the class does not hold any native resources that require explicit cleanup. + +## Example + +Since `GroundImageController` is an abstract class, you must first create a concrete implementation. + +```kotlin +// Assume ActualGroundImage is a String for this example +// and other interfaces are mocked or implemented elsewhere. + +// 1. Create a concrete implementation of GroundImageController +class MyGroundImageController( + groundImageManager: GroundImageManagerInterface, + renderer: GroundImageOverlayRendererInterface, + clickListener: OnGroundImageEventHandler? = null +) : GroundImageController(groundImageManager, renderer, clickListener) { + // No additional implementation needed for this example +} + +// 2. Define some state for our ground images +val initialState = GroundImageState( + id = "image-1", + bounds = /* some GeoBounds object */, + image = /* some ImageDescriptor */, + onClick = { event -> println("Clicked on ${event.state.id}") } +) + +val updatedState = initialState.copy( + transparency = 0.5f // Change transparency +) + +// 3. Instantiate the controller with its dependencies +val myManager = MyGroundImageManager() // A concrete implementation +val myRenderer = MyGroundImageRenderer() // A concrete implementation +val groundImageController = MyGroundImageController(myManager, myRenderer) + +// 4. Use the controller to manage ground images +suspend fun manageImages() { + // Add the initial ground image to the map + println("Adding initial image...") + groundImageController.add(listOf(initialState)) + // The renderer's onAdd method will be called here. + + // Update the ground image with new transparency + println("Updating image transparency...") + groundImageController.update(updatedState) + // The renderer's onChange method will be called here. + + // Find the image at a specific location + val foundImage = groundImageController.find(/* some GeoPoint */) + if (foundImage != null) { + println("Found image with ID: ${foundImage.state.id}") + } + + // Remove all ground images from the map + println("Clearing all images...") + groundImageController.clear() + // The renderer's onRemove method will be called here. +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/groundimage/GroundImageEntity.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/groundimage/GroundImageEntity.kt.md new file mode 100644 index 00000000..49be7522 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/groundimage/GroundImageEntity.kt.md @@ -0,0 +1,90 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +*** + +## GroundImageEntityInterface + +### Signature +```kotlin +interface GroundImageEntityInterface +``` + +### Description +Defines a contract for a ground image entity. This interface provides a standardized way to access the core properties of a ground image, including the image object itself, its current state, and a unique fingerprint derived from that state. + +### Properties +| Property | Type | Description | +|---------------|---------------------------|---------------------------------------------------------------------------------------------------------| +| `groundImage` | `ActualGroundImage` | The underlying ground image object. The specific type is determined by the generic parameter. | +| `state` | `GroundImageState` | Represents the current state and configuration of the ground image (e.g., opacity, visibility, position). | +| `fingerPrint` | `GroundImageFingerPrint` | A unique identifier derived from the ground image's state, used for tracking changes and comparisons. | + +--- + +## GroundImageEntity + +### Signature +```kotlin +data class GroundImageEntity( + override val groundImage: ActualGroundImage, + override val state: GroundImageState, +) : GroundImageEntityInterface +``` + +### Description +A data class that provides a concrete implementation of `GroundImageEntityInterface`. It encapsulates a ground image object and its associated state. The class automatically generates a `fingerPrint` by calling the `fingerPrint()` method on the provided `state` object. + +### Parameters +This table describes the parameters for the `GroundImageEntity` constructor. + +| Parameter | Type | Description | +|---------------|---------------------|-----------------------------------------------------------------------------| +| `groundImage` | `ActualGroundImage` | The underlying ground image object to be encapsulated. | +| `state` | `GroundImageState` | The state object that describes the ground image's properties and configuration. | + +### Properties +This table describes the properties available on an instance of `GroundImageEntity`. + +| Property | Type | Description | +|---------------|---------------------------|---------------------------------------------------------------------------------------------------------| +| `groundImage` | `ActualGroundImage` | The underlying ground image object. | +| `state` | `GroundImageState` | The state object describing the ground image's properties. | +| `fingerPrint` | `GroundImageFingerPrint` | A unique fingerprint automatically calculated from the `state` object by calling its `fingerPrint()` method. | + +### Example +This example demonstrates how to create and use a `GroundImageEntity`. + +```kotlin +// Assume these related data structures are defined elsewhere in the SDK +class SampleMapImage { + // Represents the actual image data or resource +} + +data class GroundImageFingerPrint(val id: String) + +data class GroundImageState( + val opacity: Double, + val isVisible: Boolean +) { + // Generates a unique string based on the state's properties + fun fingerPrint() = GroundImageFingerPrint("opacity=$opacity&isVisible=$isVisible") +} + +// 1. Create an instance of the actual image and its state +val myImage = SampleMapImage() +val myImageState = GroundImageState(opacity = 0.85, isVisible = true) + +// 2. Create the GroundImageEntity +val groundImageEntity = GroundImageEntity( + groundImage = myImage, + state = myImageState +) + +// 3. Access its properties +println("Image object: $groundImageEntity.groundImage") +println("Image state: $groundImageEntity.state") + +// The fingerprint is automatically generated from the state +// Expected output: Fingerprint: GroundImageFingerPrint(id=opacity=0.85&isVisible=true) +println("Fingerprint: ${groundImageEntity.fingerPrint}") +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/groundimage/GroundImageManager.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/groundimage/GroundImageManager.kt.md new file mode 100644 index 00000000..f3bef697 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/groundimage/GroundImageManager.kt.md @@ -0,0 +1,211 @@ +# GroundImageManager + +The `GroundImageManager` class is a concrete implementation of the `GroundImageManagerInterface`. It provides a comprehensive solution for managing a collection of ground image entities on a map. It handles the lifecycle of ground images, including their registration, retrieval, removal, and querying. + +This manager uses a map to store entities, keyed by their unique string identifiers, allowing for efficient lookups. + +## Type Parameters + +| Name | Description | +| ----------------- | ------------------------------------------------------------------------ | +| `ActualGroundImage` | A generic type representing the platform-specific ground image object. | + +--- + +## Methods + +### registerEntity + +Adds a new ground image entity to the manager. If an entity with the same ID already exists, it will be replaced. + +**Signature** +```kotlin +fun registerEntity(entity: GroundImageEntityInterface) +``` + +**Parameters** + +| Parameter | Type | Description | +| --------- | --------------------------------------------------- | ----------------------------------------- | +| `entity` | `GroundImageEntityInterface` | The ground image entity to be registered. | + +**Example** +```kotlin +val manager = GroundImageManager() +val groundImageEntity = // ... create or obtain a GroundImageEntityInterface instance +manager.registerEntity(groundImageEntity) +``` + +--- + +### removeEntity + +Removes a ground image entity from the manager using its unique ID. + +**Signature** +```kotlin +fun removeEntity(id: String): GroundImageEntityInterface? +``` + +**Parameters** + +| Parameter | Type | Description | +| --------- | -------- | ---------------------------------------------- | +| `id` | `String` | The unique identifier of the entity to remove. | + +**Returns** + +| Type | Description | +| ----------------------------------------------------- | ------------------------------------------------------------------------ | +| `GroundImageEntityInterface?` | The removed entity if it was found, or `null` if no entity with the given ID exists. | + +**Example** +```kotlin +val removedEntity = manager.removeEntity("ground-image-id-123") +if (removedEntity != null) { + println("Successfully removed entity.") +} else { + println("Entity not found.") +} +``` + +--- + +### getEntity + +Retrieves a specific ground image entity from the manager by its unique ID. + +**Signature** +```kotlin +fun getEntity(id: String): GroundImageEntityInterface? +``` + +**Parameters** + +| Parameter | Type | Description | +| --------- | -------- | ------------------------------------------------ | +| `id` | `String` | The unique identifier of the entity to retrieve. | + +**Returns** + +| Type | Description | +| ----------------------------------------------------- | ------------------------------------------------------------------------ | +| `GroundImageEntityInterface?` | The entity corresponding to the given ID, or `null` if it is not found. | + +**Example** +```kotlin +val entity = manager.getEntity("ground-image-id-123") +entity?.let { + // Do something with the entity + println("Found entity with ID: ${it.state.id}") +} +``` + +--- + +### hasEntity + +Checks if an entity with the specified ID is currently registered in the manager. + +**Signature** +```kotlin +fun hasEntity(id: String): Boolean +``` + +**Parameters** + +| Parameter | Type | Description | +| --------- | -------- | ---------------------------------------------- | +| `id` | `String` | The unique identifier to check for existence. | + +**Returns** + +| Type | Description | +| --------- | ------------------------------------------------ | +| `Boolean` | `true` if an entity with the specified ID exists, `false` otherwise. | + +**Example** +```kotlin +if (manager.hasEntity("ground-image-id-123")) { + println("The manager contains this entity.") +} else { + println("The manager does not contain this entity.") +} +``` + +--- + +### allEntities + +Retrieves a list of all ground image entities currently managed. + +**Signature** +```kotlin +fun allEntities(): List> +``` + +**Returns** + +| Type | Description | +| ------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | +| `List>` | A `List` containing all registered ground image entities. The list is a snapshot; modifying it will not affect the manager's internal collection. | + +**Example** +```kotlin +val allImages = manager.allEntities() +println("Total ground images: ${allImages.size}") +allImages.forEach { entity -> + println("Entity ID: ${entity.state.id}") +} +``` + +--- + +### clear + +Removes all ground image entities from the manager, leaving it empty. + +**Signature** +```kotlin +fun clear() +``` + +**Example** +```kotlin +manager.registerEntity(someEntity) +println("Entities before clear: ${manager.allEntities().size}") // > 0 +manager.clear() +println("Entities after clear: ${manager.allEntities().size}") // 0 +``` + +--- + +### find + +Finds the first ground image entity whose geographical bounds contain a given geographical position. The order in which entities are checked is not guaranteed. + +**Signature** +```kotlin +fun find(position: GeoPointInterface): GroundImageEntityInterface? +``` + +**Parameters** + +| Parameter | Type | Description | +| ---------- | ------------------- | ------------------------------------------------------------------------ | +| `position` | `GeoPointInterface` | The geographical point used to search for a containing ground image. | + +**Returns** + +| Type | Description | +| ----------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | +| `GroundImageEntityInterface?` | The first matching ground image entity found, or `null` if no entity's bounds contain the specified position. | + +**Example** +```kotlin +val someGeoPoint = // ... create a GeoPointInterface instance +val foundEntity = manager.find(someGeoPoint) +foundEntity?.let { + println("Found an entity at the given position: ${it.state.id}") +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/groundimage/GroundImageOverlay.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/groundimage/GroundImageOverlay.kt.md new file mode 100644 index 00000000..98c9e3e1 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/groundimage/GroundImageOverlay.kt.md @@ -0,0 +1,89 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet, formatted in Markdown. + +--- + +## LocalGroundImageCollector + +A `CompositionLocal` that provides a mechanism for `GroundImage` composables to register themselves with a parent `MapView`. + +### Signature + +```kotlin +val LocalGroundImageCollector: compositionLocalOf> +``` + +### Description + +`LocalGroundImageCollector` is a Jetpack Compose `CompositionLocal` used internally to manage ground image states within a `MapView`. Its primary purpose is to provide a `ChildCollector` down the composable tree. + +This allows child `GroundImage` composables to add their state objects to a central collection managed by the `MapView`. If a `GroundImage` is declared outside the scope of a `MapView`, this local will not be provided, leading to a runtime error. This ensures the correct component hierarchy and prevents misconfiguration. + +Developers will typically not interact with `LocalGroundImageCollector` directly but will benefit from its presence when using the `GroundImage` composable. + +--- + +## GroundImageOverlay + +An overlay class responsible for rendering a collection of ground images on the map. + +### Signature + +```kotlin +class GroundImageOverlay( + override val flow: StateFlow>, +) : MapOverlayInterface +``` + +### Description + +`GroundImageOverlay` acts as a bridge between the declarative `GroundImage` composables and the map's imperative rendering engine. It subscribes to a reactive stream (`StateFlow`) of ground image states collected from the Compose UI. + +During the map's rendering cycle, this class takes the latest set of ground image states and passes them to the map controller for rendering. This process is only executed if the map controller implements the `GroundImageCapableInterface`, ensuring the controller supports ground image rendering. + +### Constructor Parameters + +| Parameter | Type | Description | +| :-------- | :--------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `flow` | `StateFlow>` | A reactive stream that emits the current map of ground image states. The map key is a unique ID for the ground image, and the value is the `GroundImageState` object. | + +### Methods + +#### render + +This function is called by the map's rendering system to draw the collected ground images onto the map. + +**Signature** + +```kotlin +override suspend fun render( + data: MutableMap, + controller: MapViewControllerInterface, +) +``` + +**Description** + +The `render` method receives the current data and the map controller. It checks if the controller is capable of handling ground images (by casting to `GroundImageCapableInterface`) and then invokes the appropriate rendering command (`compositionGroundImages`) with the list of ground image states. + +**Parameters** + +| Parameter | Type | Description | +| :----------- | :----------------------------------------- | :------------------------------------------------------------------------------------------------------ | +| `data` | `MutableMap` | A map containing the latest `GroundImageState` objects to be rendered, keyed by their unique identifiers. | +| `controller` | `MapViewControllerInterface` | The map controller instance responsible for executing low-level drawing commands on the map view. | + +### Example + +The following example demonstrates how `GroundImageOverlay` might be instantiated and used within the map's core architecture. + +```kotlin +// Within the MapView setup +val groundImageStateFlow: StateFlow> = // ... obtain from collector + +// Instantiate the overlay with the state flow +val groundImageOverlay = GroundImageOverlay(flow = groundImageStateFlow) + +// The map's rendering engine would then use this overlay +// during its render pass. For example: +mapRenderer.addOverlay(groundImageOverlay) +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/groundimage/GroundImageOverlayRendererInterface.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/groundimage/GroundImageOverlayRendererInterface.kt.md new file mode 100644 index 00000000..4d7f6ad2 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/groundimage/GroundImageOverlayRendererInterface.kt.md @@ -0,0 +1,187 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +--- + +# GroundImageOverlayRendererInterface + +## Description + +The `GroundImageOverlayRendererInterface` defines a contract for rendering and managing ground image overlays on a map. Implementations of this interface are responsible for handling the lifecycle of ground images, including their creation, modification, and removal, by translating abstract state changes into calls to a specific, underlying map SDK. + +This interface is designed to be asynchronous, using `suspend` functions to ensure that map operations do not block the main thread. + +### Type Parameters + +| Name | Description | +| :--- | :--- | +| `ActualGroundImage` | The native ground image object type from the specific map SDK being used (e.g., `com.google.android.gms.maps.model.GroundOverlay`). | + +--- + +## Nested Interfaces + +### AddParamsInterface + +An interface representing the parameters required to add a new ground image to the map. + +#### Signature + +```kotlin +interface AddParamsInterface +``` + +#### Properties + +| Name | Type | Description | +| :--- | :--- | :--- | +| `state` | `GroundImageState` | Holds the complete configuration and state for the new ground image to be created, such as its image source, geographic bounds, and visual properties. | + +### ChangeParamsInterface + +An interface representing the parameters required to update an existing ground image on the map. + +#### Signature + +```kotlin +interface ChangeParamsInterface +``` + +#### Properties + +| Name | Type | Description | +| :--- | :--- | :--- | +| `current` | `GroundImageEntityInterface` | The entity representing the new, updated state of the ground image. | +| `prev` | `GroundImageEntityInterface` | The entity representing the previous state of the ground image before the change. | + +--- + +## Functions + +### onAdd + +Called when one or more new ground images need to be added to the map. The implementation should create the native map SDK ground image objects based on the provided data. + +#### Signature + +```kotlin +suspend fun onAdd(data: List): List +``` + +#### Parameters + +| Name | Type | Description | +| :--- | :--- | :--- | +| `data` | `List` | A list of parameter objects, each containing the state for a new ground image to be rendered. | + +#### Returns + +**Type**: `List` + +A list of the newly created native `ActualGroundImage` objects. The size and order of this list must match the input `data` list. If a specific ground image fails to be created, its corresponding element in the returned list should be `null`. + +### onChange + +Called when properties of one or more existing ground images have been modified. The implementation should update the corresponding native ground image objects on the map. + +#### Signature + +```kotlin +suspend fun onChange(data: List>): List +``` + +#### Parameters + +| Name | Type | Description | +| :--- | :--- | :--- | +| `data` | `List>` | A list of change sets. Each element contains the previous and current states of a ground image that needs to be updated. | + +#### Returns + +**Type**: `List` + +A list of the updated native `ActualGroundImage` objects. The size and order of this list must match the input `data` list. If an update fails or is not applicable, the corresponding element can be `null`. + +### onRemove + +Called when one or more ground images need to be removed from the map. + +#### Signature + +```kotlin +suspend fun onRemove(data: List>) +``` + +#### Parameters + +| Name | Type | Description | +| :--- | :--- | :--- | +| `data` | `List>` | A list of ground image entities that should be removed from the map. The implementation should use the `ActualGroundImage` within each entity to remove it from the map view. | + +### onPostProcess + +A lifecycle hook called after all add, change, and remove operations for a given update cycle are complete. This can be used for final cleanup, batch processing, or triggering a map refresh if required by the SDK. + +#### Signature + +```kotlin +suspend fun onPostProcess() +``` + +--- + +## Example + +Here is a conceptual example of how to implement `GroundImageOverlayRendererInterface` for a hypothetical map SDK. + +```kotlin +// Assume MapController and MapSDKGroundImage are part of a fictional map SDK. +class MyMapGroundImageRenderer( + private val mapController: MapController +) : GroundImageOverlayRendererInterface { + + override suspend fun onAdd(data: List): List { + return data.map { params -> + // Convert abstract state to map-specific options + val options = createOptionsFromState(params.state) + // Add the ground image to the map + mapController.addGroundImage(options) + } + } + + override suspend fun onChange(data: List>): List { + return data.map { change -> + val nativeImage = change.prev.nativeImage // Get the actual map object + val newState = change.current.state + + // Apply new properties to the existing native image + nativeImage.updateBounds(newState.bounds) + nativeImage.updateBitmap(newState.image) + nativeImage.updateOpacity(newState.opacity) + + nativeImage + } + } + + override suspend fun onRemove(data: List>) { + data.forEach { entity -> + // Remove the ground image from the map + entity.nativeImage?.removeFromMap() + } + } + + override suspend fun onPostProcess() { + // For some map SDKs, you might need to trigger a redraw after batch operations. + mapController.refresh() + println("Ground image processing complete for this cycle.") + } + + private fun createOptionsFromState(state: GroundImageState): MapSDKGroundImageOptions { + // Logic to convert generic GroundImageState to map-specific options + return MapSDKGroundImageOptions().apply { + bounds = state.bounds + image = state.image + opacity = state.opacity + } + } +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/groundimage/GroundImageTileProvider.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/groundimage/GroundImageTileProvider.kt.md new file mode 100644 index 00000000..6889a6b9 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/groundimage/GroundImageTileProvider.kt.md @@ -0,0 +1,139 @@ +Of course! Here is the high-quality SDK documentation for the provided `GroundImageTileProvider` class. + +--- + +# GroundImageTileProvider + +The `GroundImageTileProvider` is a tile provider that renders a georeferenced image, known as a ground overlay, onto map tiles. It implements the `TileProviderInterface`, allowing it to be used as a source for a map layer. + +This class handles the projection of a source bitmap from its geographic coordinates (`GeoRectBounds`) onto the corresponding Web Mercator map tiles for any given zoom level. It includes an in-memory LRU cache to optimize performance by storing recently rendered tiles. + +## Signature + +```kotlin +class GroundImageTileProvider( + val tileSize: Int = DEFAULT_TILE_SIZE, + cacheSizeKb: Int = DEFAULT_CACHE_SIZE_KB, +) : TileProviderInterface +``` + +## Constructor + +### Description + +Creates a new instance of the `GroundImageTileProvider`. + +### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `tileSize` | `Int` | The width and height of the tiles to generate, in pixels. Defaults to `512`. | +| `cacheSizeKb` | `Int` | The size of the in-memory LRU cache for storing rendered tiles, in kilobytes. Defaults to `8192` (8 MB). | + +--- + +## Methods + +### update + +#### Signature + +```kotlin +fun update( + state: GroundImageState, + opacity: Float = state.opacity, +) +``` + +#### Description + +Sets or updates the ground image to be rendered on the map. This method configures the provider with the image, its geographic bounds, and its opacity. + +Calling `update` will invalidate and clear the entire tile cache for this provider, forcing all visible tiles to be re-rendered with the new state. + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `state` | `GroundImageState` | An object containing the ground image state, which must include the `image` (`Drawable`) and its geographic `bounds` (`GeoRectBounds`). | +| `opacity` | `Float` | The opacity of the overlay, clamped between `0.0` (fully transparent) and `1.0` (fully opaque). If provided, this value overrides the opacity from the `state` object. | + +--- + +### renderTile + +#### Signature + +```kotlin +override fun renderTile(request: TileRequest): ByteArray? +``` + +#### Description + +Generates the image data for a single map tile based on the current ground image overlay. This method is part of the `TileProviderInterface` and is typically called by the map rendering engine, not directly by the developer. + +The method first checks its cache for a valid tile. If a cached tile is not found, it renders a new one by calculating the intersection between the ground image and the requested tile bounds. The resulting tile is then cached for future requests. + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `request` | `TileRequest` | An object containing the coordinates (`x`, `y`, `z`) of the tile to be rendered. | + +#### Returns + +| Type | Description | +| :--- | :--- | +| `ByteArray?` | A `ByteArray` containing the PNG-encoded image data for the requested tile. Returns `null` if the ground image does not intersect with the tile's bounds or if no ground image has been set via the `update` method. | + +--- + +## Example + +Here is an example of how to instantiate `GroundImageTileProvider`, update it with a ground image, and add it to a map layer. + +```kotlin +import android.graphics.drawable.Drawable +import com.mapconductor.core.groundimage.GroundImageState +import com.mapconductor.core.groundimage.GroundImageTileProvider +import com.mapconductor.core.features.GeoPoint +import com.mapconductor.core.features.GeoRectBounds + +// Assume you have a mapController and a drawable resource +val mapController: MapController = // ... +val imageDrawable: Drawable = getDrawable(R.drawable.ground_overlay_image) + +// 1. Define the geographic bounds for the image +val southWest = GeoPoint(latitude = 34.0, longitude = -118.2) +val northEast = GeoPoint(latitude = 34.1, longitude = -118.1) +val imageBounds = GeoRectBounds(southWest, northEast) + +// 2. Create the state for the ground image +val groundImageState = GroundImageState( + image = imageDrawable, + bounds = imageBounds, + opacity = 0.85f // Set a default opacity +) + +// 3. Instantiate the GroundImageTileProvider +// You can customize tile size and cache size if needed +val groundImageProvider = GroundImageTileProvider( + tileSize = 512, + cacheSizeKb = 16 * 1024 // 16 MB cache +) + +// 4. Update the provider with the image state +// You can also override the opacity here +groundImageProvider.update(state = groundImageState, opacity = 0.9f) + +// 5. Add the provider to a raster layer on the map +mapController.addLayer( + "ground-overlay-layer", + "ground-overlay-source", + groundImageProvider +) + +// To remove the overlay later, you can update with a state that has an empty image +// or simply remove the layer from the map. +// mapController.removeLayer("ground-overlay-layer") +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/info/DrawInfoBubble.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/info/DrawInfoBubble.kt.md new file mode 100644 index 00000000..998b860d --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/info/DrawInfoBubble.kt.md @@ -0,0 +1,69 @@ +# DrawInfoBubble + +## Signature +```kotlin +@Composable +internal fun DrawInfoBubble( + modifier: Modifier, + bubbleColor: Color, + borderColor: Color, + contentPadding: Dp, + cornerRadius: Dp, + tailSize: Dp, + content: @Composable () -> Unit, +) +``` + +## Description +Renders a customizable info bubble with a triangular tail at the bottom center. This composable is designed to act as a container for other UI elements, commonly used for map annotations, tooltips, or callouts. + +It uses a `Canvas` to draw the bubble shape with a specified fill color and border. The provided `content` is then placed inside with appropriate padding to fit within the bubble's boundaries, accounting for the space occupied by the tail. + +**Note:** This is an `internal` function, intended for use only within its own module. + +## Parameters +| Parameter | Type | Description | +| --- | --- | --- | +| `modifier` | `Modifier` | The `Modifier` to be applied to the bubble container. | +| `bubbleColor` | `Color` | The background `Color` of the bubble. | +| `borderColor` | `Color` | The `Color` of the bubble's border. | +| `contentPadding` | `Dp` | The padding `Dp` applied to the content within the bubble on all sides. The bottom padding is automatically increased to accommodate the tail. | +| `cornerRadius` | `Dp` | The corner radius `Dp` for the rounded corners of the bubble. | +| `tailSize` | `Dp` | The size `Dp` of the triangular tail at the bottom of the bubble. This defines the height and base width of the tail. | +| `content` | `@Composable () -> Unit` | A composable lambda that defines the content to be displayed inside the bubble. | + +## Returns +This composable does not return any value. + +## Example +Here is an example of how to use `DrawInfoBubble` to display a simple text message. + +```kotlin +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp + +@Composable +fun InfoBubbleExample() { + DrawInfoBubble( + modifier = Modifier.padding(16.dp), + bubbleColor = Color.White, + borderColor = Color.Gray, + contentPadding = 12.dp, + cornerRadius = 8.dp, + tailSize = 10.dp + ) { + Text( + text = "Location Details:\nLatitude: 40.7128\nLongitude: -74.0060", + fontSize = 14.sp, + fontWeight = FontWeight.Normal, + color = Color.Black + ) + } +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/info/InfoBubbleCompose.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/info/InfoBubbleCompose.kt.md new file mode 100644 index 00000000..7458de14 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/info/InfoBubbleCompose.kt.md @@ -0,0 +1,141 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +# Info Bubbles + +These composable functions are used to display informational windows, or "bubbles," attached to markers on the map. They must be called from within a `MapViewScope`. + +--- + +## InfoBubble + +Displays a standard, customizable info bubble attached to a map marker. This composable provides a default speech-bubble appearance with a tail pointing towards the specified `marker`. You can customize its colors, corners, padding, and tail size. + +### Signature +```kotlin +@Composable +fun MapViewScope.InfoBubble( + marker: MarkerState, + bubbleColor: Color = Color.Companion.White, + borderColor: Color = Color.Companion.Black, + contentPadding: Dp = 8.dp, + cornerRadius: Dp = 4.dp, + tailSize: Dp = 8.dp, + content: @Composable () -> Unit, +) +``` + +### Description +The `InfoBubble` composable is the standard way to show information for a marker. It automatically draws a rectangular bubble with a triangular tail that points to the marker's anchor point. The bubble's appearance is easily customized through its parameters. The content of the bubble is defined by the `content` composable lambda. + +The bubble is automatically added to the map when the composable enters the composition and removed when it leaves, managed by a `DisposableEffect`. + +### Parameters +| Parameter | Type | Description | +|---|---|---| +| `marker` | `MarkerState` | The marker state to which this info bubble will be attached. | +| `bubbleColor` | `Color` | The background color of the bubble. Defaults to `Color.White`. | +| `borderColor` | `Color` | The color of the bubble's border and tail outline. Defaults to `Color.Black`. | +| `contentPadding` | `Dp` | The padding between the bubble's border and its content. Defaults to `8.dp`. | +| `cornerRadius` | `Dp` | The corner radius for the rectangular part of the bubble. Defaults to `4.dp`. | +| `tailSize` | `Dp` | The size of the triangular tail pointing to the marker. Defaults to `8.dp`. | +| `content` | `@Composable () -> Unit` | The composable content to be displayed inside the bubble. | + +### Returns +This composable does not return a value. + +### Example +Here is an example of showing an `InfoBubble` for a marker when it is selected. + +```kotlin +// Assume this is within a Composable function +val markerState = rememberMarkerState( + position = GeoPosition(34.0522, -118.2437) +) +var isMarkerSelected by remember { mutableStateOf(false) } + +MapView { // MapViewScope + Marker( + state = markerState, + onClick = { + isMarkerSelected = !isMarkerSelected + true // Consume the click event + } + ) + + if (isMarkerSelected) { + InfoBubble( + marker = markerState, + bubbleColor = Color(0xFFE0F7FA), + borderColor = Color(0xFF006064), + cornerRadius = 8.dp, + contentPadding = 12.dp + ) { + Text( + text = "Los Angeles City Hall", + fontWeight = FontWeight.Bold, + color = Color.DarkGray + ) + } + } +} +``` + +--- + +## InfoBubbleCustom + +Renders a fully custom info bubble attached to a map marker, positioned by the map's overlay engine. + +### Signature +```kotlin +@Composable +fun MapViewScope.InfoBubbleCustom( + marker: MarkerState, + tailOffset: Offset, + content: @Composable () -> Unit, +) +``` + +### Description +The `InfoBubbleCustom` composable offers complete control over the visual appearance of an info bubble. Unlike `InfoBubble`, it does not draw any default shape or tail. You are responsible for drawing the entire bubble UI, including any desired shape, background, and tail, within the `content` lambda. + +The map's overlay engine will use the `tailOffset` parameter to correctly position your custom content relative to the marker. This function is ideal when you need a non-standard bubble shape or complex internal layout. + +### Parameters +| Parameter | Type | Description | +|---|---|---| +| `marker` | `MarkerState` | The marker state to which this custom info bubble will be attached. | +| `tailOffset` | `Offset` | Specifies the connection point within your custom content's bounding box, using a relative coordinate system from (0, 0) to (1, 1). For example, `Offset(0.5f, 1.0f)` means the bottom-center of your content will point to the marker. `Offset(0.0f, 0.5f)` would be the middle of the left edge. | +| `content` | `@Composable () -> Unit` | The composable content that defines the entire visual representation of the custom bubble. | + +### Returns +This composable does not return a value. + +### Example +This example demonstrates creating a circular info bubble with a custom background and shadow. The `tailOffset` is set to `Offset(0.5f, 1.0f)` to ensure the bottom-center of the `Card` points to the marker. + +```kotlin +// Assume this is within a Composable function and MapViewScope +val markerState = rememberMarkerState( + position = GeoPosition(40.7128, -74.0060) +) + +// Show a custom bubble for this marker +InfoBubbleCustom( + marker = markerState, + // The connection point is the bottom-center of our custom content. + tailOffset = Offset(0.5f, 1.0f) +) { + // The content lambda is responsible for drawing the entire bubble. + Card( + modifier = Modifier.size(120.dp), + shape = CircleShape, + backgroundColor = Color.Yellow, + elevation = 4.dp + ) { + Box(contentAlignment = Alignment.Center) { + Text("NYC", fontSize = 24.sp) + } + } +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/info/InfoWindowOverlay.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/info/InfoWindowOverlay.kt.md new file mode 100644 index 00000000..22f6a059 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/info/InfoWindowOverlay.kt.md @@ -0,0 +1,113 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +--- + +## InfoBubbleOverlay + +An internal composable used to position and display an info bubble relative to a marker's icon on the map. + +This component calculates the precise screen coordinates for the info bubble based on the marker's position, the size and anchor points of the icon, and the anchor point of the info bubble itself. + +**Note:** This is an internal component and is not intended for direct use. It is used by the map system to render the info bubbles defined in `InfoBubbleEntry`. + +### Signature +```kotlin +@Composable +internal fun InfoBubbleOverlay( + positionOffset: Offset, + iconSize: Size, + iconOffset: Offset, + infoAnchorOffset: Offset, + tailOffset: Offset, + modifier: Modifier = Modifier, + content: @Composable () -> Unit, +) +``` + +### Description +The `InfoBubbleOverlay` is responsible for the complex logic of placing an info bubble's content correctly on the screen. It takes into account multiple offsets and sizes to ensure the bubble appears anchored to the correct point on its associated marker icon. The final position is calculated by applying offsets based on the info bubble's own size, the marker icon's size, and the various anchor points. + +### Parameters +| Parameter | Type | Description | +|-----------|------|-------------| +| `positionOffset` | `Offset` | The screen pixel offset of the marker's geographical position. | +| `iconSize` | `Size` | The size of the marker's icon in pixels. | +| `iconOffset` | `Offset` | The anchor point on the icon that connects to the map's geographical coordinate. The values range from `(0.0, 0.0)` (top-left) to `(1.0, 1.0)` (bottom-right). For example, `(0.5, 1.0)` anchors the bottom-center of the icon to the marker's position. | +| `infoAnchorOffset` | `Offset` | The anchor point on the icon where the info bubble connects. For example, `(0.5, 0.0)` would anchor the bubble to the top-center of the icon. | +| `tailOffset` | `Offset` | The anchor point on the info bubble itself that connects to the `infoAnchorOffset` on the icon. For example, `(0.5, 1.0)` means the bottom-center of the info bubble (where the "tail" usually is) will connect to the icon. | +| `modifier` | `Modifier` | A standard `Modifier` for this composable. | +| `content` | `@Composable () -> Unit` | The composable content to be displayed inside the info bubble. | + +
+ +## InfoBubbleEntry + +A data class that encapsulates the information required to render an info bubble for a specific marker. + +### Signature +```kotlin +data class InfoBubbleEntry( + val marker: MarkerState, + val tailOffset: Offset = Offset(0.5f, 1.0f), + val content: @Composable () -> Unit, +) +``` + +### Description +This data class holds the state for an info bubble, linking it to a `MarkerState` and defining its UI content and anchor point. Instances of `InfoBubbleEntry` are collected by the `MapView` to be rendered using the `InfoBubbleOverlay`. + +### Parameters +| Parameter | Type | Description | +|-----------|------|-------------| +| `marker` | `MarkerState` | The `MarkerState` to which this info bubble is attached. | +| `tailOffset` | `Offset` | The anchor point on the info bubble where its "tail" connects to the marker icon. The default value `Offset(0.5f, 1.0f)` anchors the bottom-center of the bubble to the marker. | +| `content` | `@Composable () -> Unit` | A composable lambda that defines the UI content of the info bubble. | + +### Example +The following example shows how you might define an `InfoBubbleEntry` when creating a marker. When the marker's info window is shown, the `MapView` will use this entry to render the bubble. + +```kotlin +@Composable +fun MyMapScreen() { + val markerState = rememberMarkerState(position = LatLng(35.68, 139.76)) + + // This would be inside your MapView composable + Marker( + state = markerState, + title = "Tokyo Station", + // When the info window is shown, it uses this entry + infoBubble = InfoBubbleEntry( + marker = markerState, + // Custom tail offset if needed, otherwise defaults are used + // tailOffset = Offset(0.5f, 1.0f), + content = { + // Define the UI for the info bubble + Column( + modifier = Modifier + .background(Color.White, RoundedCornerShape(8.dp)) + .padding(12.dp) + ) { + Text("Tokyo Station", fontWeight = FontWeight.Bold) + Text("A major railway station in Tokyo.") + } + } + ) + ) +} +``` + +
+ +## LocalInfoBubbleCollector + +A `CompositionLocal` used to collect info bubble states from markers within a `MapView`. + +### Signature +```kotlin +val LocalInfoBubbleCollector: ProvidableCompositionLocal>> +``` + +### Description +`LocalInfoBubbleCollector` is an internal mechanism that allows the `MapView` to collect `InfoBubbleEntry` definitions from its `Marker` children. It provides a `MutableStateFlow` containing a map of active info bubbles, which the `MapView` then observes to render them as overlays. + +This implementation detail ensures that info bubbles can be defined alongside their respective markers but rendered at the top level of the map, preventing them from being clipped by map tiles. An error is thrown if a component that provides an `InfoBubbleEntry` is not placed within a `MapView`, as the collector would not be available. \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/map/BaseMapViewSaver.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/map/BaseMapViewSaver.kt.md new file mode 100644 index 00000000..166ca33f --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/map/BaseMapViewSaver.kt.md @@ -0,0 +1,196 @@ +# Class `BaseMapViewSaver` + +## Signature + +```kotlin +abstract class BaseMapViewSaver> +``` + +## Description + +An abstract base class for creating `Saver` instances that handle saving and restoring the state of a `MapView`. + +This class provides the core logic for persisting the camera's position (location, zoom, tilt, bearing) and delegates the handling of map design specifics and state object creation to its subclasses. It is designed to be used with Jetpack Compose's `rememberSaveable` to preserve the map state across configuration changes or process death. + +## Type Parameters + +| Name | Description | +| :--- | :--- | +| `T` | The specific type of `MapViewStateInterface` that this saver will manage. | + +--- + +## Functions + +### `createSaver()` + +Creates and returns a `Saver` object configured to save and restore the map view state. This is the primary entry point for using the class. + +The resulting `Saver` orchestrates the entire process, calling the abstract methods (`saveMapDesign`, `createState`, etc.) at the appropriate times. It is intended for use with `rememberSaveable` in a Jetpack Compose UI. + +**Signature** + +```kotlin +fun createSaver(): Saver +``` + +**Returns** + +| Type | Description | +| :--- | :--- | +| `Saver` | A `Saver` instance that can manage the lifecycle of the map view state. | + +**Example** + +Here is an example of how to implement `BaseMapViewSaver` and use it in a Composable. + +1. **Define your custom state and saver:** + + ```kotlin + // 1. Define a custom map view state + data class MyMapViewState( + val id: String, + override var cameraPosition: MapCameraPosition, + val mapStyle: String // Custom property + ) : MapViewStateInterface { + // Implementation of MapViewStateInterface + } + + // 2. Implement the abstract BaseMapViewSaver + class MyMapViewSaver : BaseMapViewSaver() { + override fun saveMapDesign(state: MyMapViewState, bundle: Bundle) { + bundle.putString("mapStyle", state.mapStyle) + } + + override fun createState( + stateId: String, + mapDesignBundle: Bundle?, + cameraPosition: MapCameraPosition + ): MyMapViewState { + val style = mapDesignBundle?.getString("mapStyle") ?: "default_style" + return MyMapViewState(stateId, cameraPosition, style) + } + + override fun getStateId(state: MyMapViewState): String { + return state.id + } + } + ``` + +2. **Use it in your Composable:** + + ```kotlin + @Composable + fun MyMapScreen() { + // Instantiate your custom saver + val saver = remember { MyMapViewSaver().createSaver() } + + // Use rememberSaveable with your saver to manage the state + var mapViewState by rememberSaveable(saver = saver) { + mutableStateOf( + MyMapViewState( + id = "map1", + cameraPosition = MapCameraPosition(GeoPoint.fromLatLong(40.7128, -74.0060)), + mapStyle = "satellite_view" + ) + ) + } + + // Your MapView composable that uses mapViewState + MapView(state = mapViewState) + } + ``` + +--- + +### `saveMapDesign()` + +Saves the custom map design information from the current state into a `Bundle`. Subclasses must implement this method to persist any design-related properties, such as the map style or theme. + +**Signature** + +```kotlin +protected abstract fun saveMapDesign( + state: T, + bundle: Bundle, +) +``` + +**Parameters** + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `state` | `T` | The current map view state instance from which to save data. | +| `bundle` | `Bundle` | The `Bundle` object where the map design data should be written. | + +--- + +### `createState()` + +Creates a new instance of the map view state (`T`) from the restored data. Subclasses must implement this factory method to reconstruct the state object using the provided camera position and map design information. + +**Signature** + +```kotlin +protected abstract fun createState( + stateId: String, + mapDesignBundle: Bundle?, + cameraPosition: MapCameraPosition, +): T +``` + +**Parameters** + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `stateId` | `String` | The unique identifier for the state being restored. | +| `mapDesignBundle` | `Bundle?` | A `Bundle` containing the custom map design data, previously saved by `saveMapDesign`. Can be `null` if no data was saved. | +| `cameraPosition` | `MapCameraPosition` | The restored `MapCameraPosition`. | + +**Returns** + +| Type | Description | +| :--- | :--- | +| `T` | A new instance of the map view state (`T`). | + +--- + +### `getStateId()` + +Extracts a unique identifier from the given state object. This ID is used to track the state instance during the save/restore process. + +**Signature** + +```kotlin +protected abstract fun getStateId(state: T): String +``` + +**Parameters** + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `state` | `T` | The current map view state instance. | + +**Returns** + +| Type | Description | +| :--- | :--- | +| `String` | A `String` representing the unique ID of the state. | + +--- + +### `getCameraPaddings()` + +Provides optional padding values to be applied when restoring the camera position. Subclasses can override this method to specify custom paddings. The default implementation returns `null`, indicating no padding. + +**Signature** + +```kotlin +protected open fun getCameraPaddings(): MapPaddingsInterface? +``` + +**Returns** + +| Type | Description | +| :--- | :--- | +| `MapPaddingsInterface?` | An instance of `MapPaddingsInterface` or `null`. | \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/map/CollectAndUpdateEach.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/map/CollectAndUpdateEach.kt.md new file mode 100644 index 00000000..ae17e192 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/map/CollectAndUpdateEach.kt.md @@ -0,0 +1,115 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +--- + +### `CollectAndUpdateEach` + +`CollectAndUpdateEach` is an internal composable function designed to efficiently observe a collection of component states. For each state, it listens for changes via a provided `Flow`, debounces these changes to prevent excessive updates, and then triggers a specified update action. + +This utility is particularly useful for scenarios where multiple components on the screen (e.g., text fields, sliders) can change rapidly, and you want to batch or delay the corresponding update operations (like saving to a database or making a network call) until the user has paused their interaction. + +**Note:** This is an `internal` composable and is intended for use within its own module. + +### Signature +```kotlin +@OptIn(FlowPreview::class) +@Composable +internal fun CollectAndUpdateEach( + states: kotlinx.coroutines.flow.StateFlow>, + debounce: Duration, + asFlow: (T) -> Flow, + onUpdate: suspend (T) -> Unit, +) +``` + +### Description +The `CollectAndUpdateEach` composable collects a `StateFlow` map of component states. It iterates through each state in the map and sets up a `LaunchedEffect` keyed by the state's unique `id`. + +Inside the effect, it uses the `asFlow` lambda to get a "fingerprint" flow that signals changes for that specific state. It then applies a `debounce` period to this flow. When a debounced value is emitted, the `collectLatest` operator triggers the `onUpdate` suspend function with the corresponding state. `collectLatest` ensures that if a new change arrives while a previous `onUpdate` is still running, the old one is canceled and the new one begins, guaranteeing only the latest state is processed. + +### Parameters +| Parameter | Type | Description | +| :-------- | :---------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `states` | `StateFlow>` | A `StateFlow` that emits a map of component states. The key is a unique `String` identifier, and the value `T` is the component state object. | +| `debounce`| `Duration` | The `kotlin.time.Duration` to wait for after the last change before triggering the `onUpdate` action. This helps prevent rapid, successive updates. | +| `asFlow` | `(T) -> Flow` | A lambda function that takes a component state `T` and returns a `Flow`. This flow is observed for changes. The `FingerPrint` type represents the data that signals a change. | +| `onUpdate`| `suspend (T) -> Unit` | A suspendable lambda function that is executed when a debounced change is detected. It receives the component state `T` that needs to be updated as its argument. | + +### Returns +This composable function does not return any value (`Unit`). Its purpose is to manage side effects by observing state changes and launching update coroutines. + +### Example +Imagine you have several text fields on a screen, and you want to automatically save their content to a server after the user stops typing in any of them. + +First, define the state for a single text field. + +```kotlin +// A simple base interface required by the generic constraint +interface ComponentState { + val id: String +} + +// State holder for a single text field +data class TextFieldState( + override val id: String, + val text: MutableStateFlow = MutableStateFlow("") +) : ComponentState +``` + +Next, in your ViewModel or state holder, manage a map of these states. + +```kotlin +class FormViewModel : ViewModel() { + private val _formFields = MutableStateFlow>( + mutableMapOf( + "firstName" to TextFieldState(id = "firstName"), + "lastName" to TextFieldState(id = "lastName") + ) + ) + val formFields: StateFlow> = _formFields + + // Simulates saving data to a remote server + suspend fun saveField(state: TextFieldState) { + delay(500) // Simulate network latency + println("Saving field '${state.id}' with value: '${state.text.value}'") + } +} +``` + +Finally, use `CollectAndUpdateEach` in your UI to tie everything together. + +```kotlin +@Composable +fun UserFormScreen(viewModel: FormViewModel) { + val fields by viewModel.formFields.collectAsState() + + // This composable will listen for changes in all text fields + // and trigger the save operation after the user stops typing for 1 second. + CollectAndUpdateEach( + states = viewModel.formFields, + debounce = 1.seconds, + asFlow = { textFieldState -> + // For each state, we listen to its text flow for changes. + // The String itself is the "FingerPrint". + textFieldState.text + }, + onUpdate = { textFieldState -> + // When a debounced change occurs, call the save function. + viewModel.saveField(textFieldState) + } + ) + + // UI for displaying the text fields + Column(modifier = Modifier.padding(16.dp)) { + fields.values.forEach { fieldState -> + val text by fieldState.text.collectAsState() + OutlinedTextField( + value = text, + onValueChange = { newValue -> fieldState.text.value = newValue }, + label = { Text("Enter ${fieldState.id}") }, + modifier = Modifier.fillMaxWidth().padding(bottom = 8.dp) + ) + } + } +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/map/LocalMapOverlayRegistry.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/map/LocalMapOverlayRegistry.kt.md new file mode 100644 index 00000000..cc53002a --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/map/LocalMapOverlayRegistry.kt.md @@ -0,0 +1,131 @@ +# SDK Documentation + +This document provides details on the `CompositionLocal` providers available within the MapConductor SDK for Jetpack Compose. These locals are essential for interacting with the map and its components from within your custom composables. + +--- + +## `LocalMapOverlayRegistry` + +A `CompositionLocal` that provides access to the `MapOverlayRegistry` instance. + +### Signature + +```kotlin +val LocalMapOverlayRegistry: CompositionLocal +``` + +### Description + +This `CompositionLocal` provides access to the `MapOverlayRegistry` instance associated with the current `MapView`. The registry is the primary mechanism for adding, removing, and managing map overlays (like markers, polylines, and polygons) declaratively within your composable hierarchy. + +**Important:** Any composable that accesses `LocalMapOverlayRegistry.current` must be a descendant of a `` component in the UI tree. Accessing it outside of a `MapView`'s composition scope will throw a runtime `error`. + +### Returns + +| Type | Description | +| --- | --- | +| `MapOverlayRegistry` | The `MapOverlayRegistry` instance for the current map scope. | + +### Example + +The following example demonstrates how to create a custom composable that accesses the `MapOverlayRegistry` to add a new overlay to the map. + +```kotlin +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import com.mapconductor.core.map.LocalMapOverlayRegistry +import com.mapconductor.core.MapView + +@Composable +fun CustomMarkerOverlay() { + // Access the registry from the composition local. + val overlayRegistry = LocalMapOverlayRegistry.current + + // Use a LaunchedEffect to add the overlay once when the composable enters + // the composition. The registry will handle its lifecycle. + LaunchedEffect(Unit) { + val marker = /* ... create your marker overlay object ... */ + overlayRegistry.addOverlay(marker) + } +} + +// Usage within a MapView +@Composable +fun MyMapScreen() { + MapView { + // CustomMarkerOverlay is a descendant of MapView, so it can + // safely access LocalMapOverlayRegistry. + CustomMarkerOverlay() + } +} +``` + +--- + +## `LocalMapViewController` + +A `CompositionLocal` that provides access to the `MapViewControllerInterface`. + +### Signature + +```kotlin +val LocalMapViewController: CompositionLocal +``` + +### Description + +This `CompositionLocal` provides access to the `MapViewControllerInterface` for the current `MapView`. The controller is used to programmatically manipulate the map's view, such as animating the camera to a new location, changing the zoom level, or updating the map's tilt and bearing. + +**Important:** This local must be accessed from within the composition scope of a `` component. Attempting to access `LocalMapViewController.current` elsewhere will result in a runtime `error`. + +### Returns + +| Type | Description | +| --- | --- | +| `MapViewControllerInterface` | The controller instance for manipulating the current map view. | + +### Example + +This example shows a button that, when clicked, uses the `MapViewControllerInterface` to animate the map camera to a specific geographic coordinate. + +```kotlin +import androidx.compose.material.Button +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import com.mapconductor.core.map.LocalMapViewController +import com.mapconductor.core.MapView +// Other necessary imports for LatLng, CameraUpdateFactory, etc. + +@Composable +fun GoToLocationButton(latitude: Double, longitude: Double, zoom: Float) { + // Access the map controller from the composition local. + val mapController = LocalMapViewController.current + + Button(onClick = { + val targetCoordinates = LatLng(latitude, longitude) + // Use the controller to animate the map camera. + mapController.animateCamera( + CameraUpdateFactory.newLatLngZoom(targetCoordinates, zoom) + ) + }) { + Text("Go to Location") + } +} + +// Usage within a MapView +@Composable +fun MyInteractiveMap() { + MapView { + // This Box and Button are descendants of MapView, allowing them + // to safely access the LocalMapViewController. + Box( + modifier = Modifier.fillMaxSize(), + contentAlignment = Alignment.BottomCenter + ) { + GoToLocationButton(latitude = 34.0522, longitude = -118.2437, zoom = 12f) + } + } +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/map/MapCameraPositionBase.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/map/MapCameraPositionBase.kt.md new file mode 100644 index 00000000..df3af96d --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/map/MapCameraPositionBase.kt.md @@ -0,0 +1,156 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +--- + +# MapCameraPosition + +The `MapCameraPosition` class is an immutable representation of the map's camera state. It encapsulates all the visual properties of the map's viewpoint, such as its geographical location, zoom level, bearing (rotation), and tilt (pitch). + +This class is essential for controlling and inspecting the map's current view. You can use it to programmatically move the map to a specific state or to retrieve the current camera parameters. + +## Constructor + +### Signature + +```kotlin +MapCameraPosition( + position: GeoPointInterface, + zoom: Double = 0.0, + bearing: Double = 0.0, + tilt: Double = 0.0, + paddings: MapPaddingsInterface? = MapPaddings.Companion.Zeros, + visibleRegion: VisibleRegion? = null +) +``` + +### Description + +Creates a new `MapCameraPosition` instance. While `position` is required, other parameters are optional and have sensible defaults. + +### Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| `position` | `GeoPointInterface` | The geographical coordinate (`latitude`, `longitude`) at the center of the camera's view. | +| `zoom` | `Double` | **Optional.** The zoom level of the camera. Higher values are more zoomed in. Defaults to `0.0`. | +| `bearing` | `Double` | **Optional.** The camera's orientation, in degrees, clockwise from North. Defaults to `0.0`. | +| `tilt` | `Double` | **Optional.** The camera's tilt angle, in degrees, from the nadir (straight down). Defaults to `0.0`. | +| `paddings` | `MapPaddingsInterface?` | **Optional.** The padding applied to the map view, which affects the apparent center. Defaults to zero padding. | +| `visibleRegion` | `VisibleRegion?` | **Optional.** The geographic region currently visible on the screen. Defaults to `null`. | + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| `position` | `GeoPoint` | The geographical coordinate at the center of the camera's view. | +| `zoom` | `Double` | The zoom level of the camera. | +| `bearing` | `Double` | The camera's orientation, in degrees, clockwise from North. | +| `tilt` | `Double` | The camera's tilt angle, in degrees, from the nadir. | +| `paddings` | `MapPaddingsInterface?` | The padding applied to the map view. | +| `visibleRegion` | `VisibleRegion?` | The geographic region currently visible on the screen. | + +## Companion Object + +### Default + +Provides a default `MapCameraPosition` instance, centered at latitude/longitude `(0,0)` with `0.0` zoom, bearing, and tilt. + +#### Signature + +```kotlin +val Default: MapCameraPosition +``` + +#### Example + +```kotlin +// Set the map camera to a default, world-out view +map.setCameraPosition(MapCameraPosition.Default) +``` + +## Methods + +### equals + +Compares this `MapCameraPosition` with another for equality. This method uses a small tolerance (`1e-2`) when comparing `zoom`, `bearing`, and `tilt` values to account for floating-point inaccuracies. The `position` is compared exactly. + +#### Signature + +```kotlin +fun equals(other: MapCameraPositionInterface): Boolean +``` + +#### Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| `other` | `MapCameraPositionInterface` | The other camera position to compare against. | + +#### Returns + +`Boolean` - `true` if the positions are considered equal within the defined tolerance, `false` otherwise. + +#### Example + +```kotlin +val position1 = MapCameraPosition(position = GeoPoint(40.7128, -74.0060), zoom = 12.001) +val position2 = MapCameraPosition(position = GeoPoint(40.7128, -74.0060), zoom = 12.002) +val position3 = MapCameraPosition(position = GeoPoint(40.7128, -74.0060), zoom = 12.05) + +// true, because the zoom difference is within the tolerance +val areEqual1 = position1.equals(position2) +println("position1 equals position2: $areEqual1") // true + +// false, because the zoom difference is outside the tolerance +val areEqual2 = position1.equals(position3) +println("position1 equals position3: $areEqual2") // false +``` + +### copy + +Creates a new `MapCameraPosition` instance by copying the current object's properties and optionally overriding specified values. This is useful for creating a modified state from an existing one without altering the original immutable object. + +#### Signature + +```kotlin +fun copy( + position: GeoPointInterface? = this.position, + zoom: Double? = this.zoom, + bearing: Double? = this.bearing, + tilt: Double? = this.tilt, + paddings: MapPaddingsInterface? = this.paddings, + visibleRegion: VisibleRegion? = this.visibleRegion +): MapCameraPosition +``` + +#### Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| `position` | `GeoPointInterface?` | **Optional.** A new geographical coordinate for the camera. If `null`, the current `position` is used. | +| `zoom` | `Double?` | **Optional.** A new zoom level. If `null`, the current `zoom` is used. | +| `bearing` | `Double?` | **Optional.** A new bearing. If `null`, the current `bearing` is used. | +| `tilt` | `Double?` | **Optional.** A new tilt angle. If `null`, the current `tilt` is used. | +| `paddings` | `MapPaddingsInterface?` | **Optional.** New map paddings. If `null`, the current `paddings` are used. | +| `visibleRegion` | `VisibleRegion?` | **Optional.** A new visible region. If `null`, the current `visibleRegion` is used. | + +#### Returns + +`MapCameraPosition` - A new `MapCameraPosition` instance with the updated properties. + +#### Example + +```kotlin +val initialPosition = MapCameraPosition( + position = GeoPoint(34.0522, -118.2437), // Los Angeles + zoom = 10.0 +) + +// Create a new camera position with a higher zoom level and a new bearing +val updatedPosition = initialPosition.copy(zoom = 14.0, bearing = 45.0) + +println("Initial zoom: ${initialPosition.zoom}") // 10.0 +println("Updated zoom: ${updatedPosition.zoom}") // 14.0 +println("Initial bearing: ${initialPosition.bearing}") // 0.0 +println("Updated bearing: ${updatedPosition.bearing}") // 45.0 +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/map/MapDesignTypeInterface.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/map/MapDesignTypeInterface.kt.md new file mode 100644 index 00000000..fef16774 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/map/MapDesignTypeInterface.kt.md @@ -0,0 +1,85 @@ +Of course! Here is the high-quality SDK documentation for the provided Kotlin code snippet. + +--- + +# Interface `MapDesignTypeInterface` + +## Signature +```kotlin +interface MapDesignTypeInterface +``` + +## Description +Defines a generic contract for a map design type. Any class implementing this interface represents a specific design characteristic or style that can be applied to a map, such as its visual theme or data layer. + +This interface ensures that every design type provides a unique identifier (`id`) and a method to retrieve its core value (`getValue`), both of which are of the generic type `T`. + +## Type Parameters + +| Parameter | Description | +|-----------|-------------| +| `T` | The type of the identifier and the value for the design type. This allows for flexibility, such as using `String`, `Int`, or an `Enum`. | + +## Properties + +### `id` +**Signature:** `val id: T` + +A read-only property that serves as the unique identifier for the map design type. + +**Returns** +- `T`: The unique identifier. + +## Methods + +### `getValue()` +**Signature:** `fun getValue(): T` + +Retrieves the value associated with the map design type. In many implementations, this may return the same value as the `id`. + +**Returns** +- `T`: The value of the design type. + +## Example + +Here is an example of an `enum` class that implements `MapDesignTypeInterface` to define a set of available map styles. + +```kotlin +// Define an enum for map styles that implements the interface with String as the type. +enum class MapStyle(private val styleName: String) : MapDesignType_Interface { + STANDARD("standard-v1"), + SATELLITE("satellite-v9"), + DARK("dark-v10"); + + // Implementation of the 'id' property from the interface. + override val id: String + get() = this.name // e.g., "STANDARD" + + // Implementation of the 'getValue' function from the interface. + override fun getValue(): String { + return this.styleName // e.g., "standard-v1" + } +} + +fun applyMapStyle(style: MapDesignTypeInterface) { + val styleIdentifier = style.id + val styleValueForApi = style.getValue() + println("Applying style with ID: '$styleIdentifier' and API value: '$styleValueForApi'") + // ... logic to apply the map style using its value +} + +fun main() { + val currentStyle = MapStyle.DARK + + // You can pass the enum instance directly to any function expecting the interface. + applyMapStyle(currentStyle) + // Outputs: Applying style with ID: 'DARK' and API value: 'dark-v10' + + // You can also access the properties and methods directly. + println("Selected Style ID: ${currentStyle.id}") + // Outputs: Selected Style ID: DARK + + println("Selected Style Value: ${currentStyle.getValue()}") + // Outputs: Selected Style Value: dark-v10 +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/map/MapPaddingsImpl.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/map/MapPaddingsImpl.kt.md new file mode 100644 index 00000000..8356957d --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/map/MapPaddingsImpl.kt.md @@ -0,0 +1,156 @@ +# MapPaddings + +The `MapPaddings` class and `MapPaddingsInterface` define a structure for specifying padding on the four sides of a map view. This is useful for creating a visible region within the map's viewport that is not obscured by other UI elements. + +## MapPaddingsInterface + +An interface that defines the contract for a map padding object. + +### Signature + +```kotlin +interface MapPaddingsInterface +``` + +### Description + +Provides a standardized way to access padding values. Any class that represents map paddings can implement this interface. + +### Properties + +| Property | Type | Description | +|----------|----------|-------------------------------------------| +| `top` | `Double` | The padding from the top edge in pixels. | +| `left` | `Double` | The padding from the left edge in pixels. | +| `bottom` | `Double` | The padding from the bottom edge in pixels.| +| `right` | `Double` | The padding from the right edge in pixels.| + +--- + +## MapPaddings Class + +An open class that provides a concrete implementation of `MapPaddingsInterface`. + +### Signature + +```kotlin +open class MapPaddings : MapPaddingsInterface +``` + +### Description + +Represents the padding values for the four edges of a map view in pixels. It allows developers to define an inset area where map content like markers or routes can be displayed without being covered by UI components. This class is `open`, so it can be subclassed if needed. + +### Constructor + +#### `MapPaddings()` + +Creates a new `MapPaddings` instance with specified padding values. + +##### Signature + +```kotlin +@JvmOverloads +constructor( + top: Double = 0.0, + left: Double = 0.0, + bottom: Double = 0.0, + right: Double = 0.0, +) +``` + +##### Description + +Initializes a `MapPaddings` object. All parameters have a default value of `0.0`. The `@JvmOverloads` annotation enables this constructor to be called from Java with any number of arguments from left to right, with the remaining parameters taking their default values. + +##### Parameters + +| Parameter | Type | Description | Default | +|-----------|----------|-------------------------------------------|---------| +| `top` | `Double` | The padding from the top edge in pixels. | `0.0` | +| `left` | `Double` | The padding from the left edge in pixels. | `0.0` | +| `bottom` | `Double` | The padding from the bottom edge in pixels.| `0.0` | +| `right` | `Double` | The padding from the right edge in pixels.| `0.0` | + +### Companion Object + +#### `Zeros` + +A static instance representing no padding. + +##### Signature + +```kotlin +val Zeros: MapPaddings +``` + +##### Description + +Provides a convenient, pre-defined `MapPaddings` object where all padding values (`top`, `left`, `bottom`, `right`) are `0.0`. + +#### `from()` + +A factory method to create a `MapPaddings` instance from a `MapPaddingsInterface`. + +##### Signature + +```kotlin +fun from(paddings: MapPaddingsInterface): MapPaddings +``` + +##### Description + +Creates a `MapPaddings` instance from any object that implements the `MapPaddingsInterface`. This method includes an optimization: if the provided `paddings` object is already an instance of `MapPaddings`, it is returned directly to avoid unnecessary object creation. + +##### Parameters + +| Parameter | Type | Description | +|------------|------------------------|------------------------------------------------------| +| `paddings` | `MapPaddingsInterface` | An object conforming to the `MapPaddingsInterface`. | + +##### Returns + +| Type | Description | +|---------------|--------------------------------------------------------------------------| +| `MapPaddings` | A `MapPaddings` instance with values copied from the `paddings` parameter. | + +### Example + +```kotlin +// Example of a custom class implementing the interface +data class CustomPaddings( + override val top: Double, + override val left: Double, + override val bottom: Double, + override val right: Double +) : MapPaddingsInterface + +fun main() { + // 1. Create an instance with all custom values + val customPaddings = MapPaddings(top = 100.0, left = 20.0, bottom = 50.0, right = 20.0) + println("Custom Paddings: top=${customPaddings.top}, right=${customPaddings.right}") + // Output: Custom Paddings: top=100.0, right=20.0 + + // 2. Create an instance using default values for bottom and right + val partialPaddings = MapPaddings(top = 150.0, left = 25.0) + println("Partial Paddings: bottom=${partialPaddings.bottom}, right=${partialPaddings.right}") + // Output: Partial Paddings: bottom=0.0, right=0.0 + + // 3. Use the predefined Zeros constant for no padding + val noPaddings = MapPaddings.Zeros + println("Zero Paddings: top=${noPaddings.top}, left=${noPaddings.left}") + // Output: Zero Paddings: top=0.0, left=0.0 + + // 4. Use the `from` factory method with a custom implementation + val myPaddings = CustomPaddings(top = 200.0, left = 0.0, bottom = 0.0, right = 0.0) + val mapPaddingsFromInterface = MapPaddings.from(myPaddings) + println("From Interface: top=${mapPaddingsFromInterface.top}") + // Output: From Interface: top=200.0 + + // 5. The `from` method returns the same instance if it's already a MapPaddings + val originalPaddings = MapPaddings(50.0, 50.0, 50.0, 50.0) + val fromPaddings = MapPaddings.from(originalPaddings) + println("Is same instance: ${originalPaddings === fromPaddings}") + // Output: Is same instance: true +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/map/MapServiceRegistry.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/map/MapServiceRegistry.kt.md new file mode 100644 index 00000000..e85f302a --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/map/MapServiceRegistry.kt.md @@ -0,0 +1,189 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +# Map Service Registry SDK + +This document provides detailed documentation for the Map Service Registry components. This system provides a type-safe service locator pattern for registering and retrieving map-scoped services, with integration into Jetpack Compose. + +## `MapServiceKey` + +### Signature +```kotlin +interface MapServiceKey +``` + +### Description +A typed service key used as a unique identifier to register and retrieve map-scoped services (often called plugins) from a `MapServiceRegistry`. + +It is a best practice to define keys as singleton `object`s to ensure there is only one instance of the key for each service type. + +### Example +```kotlin +// Define a service interface +interface CustomAnalyticsService { + fun logEvent(name: String) +} + +// Define a unique key for the service +object CustomAnalyticsServiceKey : MapServiceKey +``` + +--- + +## `MapServiceRegistry` + +### Signature +```kotlin +interface MapServiceRegistry +``` + +### Description +Defines the contract for a registry that stores and provides access to map-scoped services. This interface allows for different implementations, such as mutable or immutable registries. + +### Methods + +#### `get` +Retrieves a service instance associated with the specified key. + +**Signature** +```kotlin +fun get(key: MapServiceKey): T? +``` + +**Parameters** +| Parameter | Type | Description | +| :-------- | :-------------------- | :---------------------------------------- | +| `key` | `MapServiceKey` | The unique key for the service to retrieve. | + +**Returns** +The service instance of type `T` if it exists in the registry, or `null` otherwise. + +--- + +## `MutableMapServiceRegistry` + +### Signature +```kotlin +class MutableMapServiceRegistry : MapServiceRegistry +``` + +### Description +A mutable, thread-safe implementation of `MapServiceRegistry`. It allows for adding, retrieving, and clearing services at runtime. This class uses a `ConcurrentHashMap` internally to manage services. + +### Methods + +#### `put` +Registers a new service or updates an existing one in the registry. + +**Signature** +```kotlin +fun put(key: MapServiceKey, value: T) +``` + +**Parameters** +| Parameter | Type | Description | +| :-------- | :-------------------- | :---------------------------------------- | +| `key` | `MapServiceKey` | The unique key for the service. | +| `value` | `T` | The service instance to register. | + +#### `get` +Retrieves a service instance associated with the specified key. + +**Signature** +```kotlin +override fun get(key: MapServiceKey): T? +``` + +**Parameters** +| Parameter | Type | Description | +| :-------- | :-------------------- | :---------------------------------------- | +| `key` | `MapServiceKey` | The unique key for the service to retrieve. | + +**Returns** +The service instance of type `T` if it is registered, or `null` otherwise. + +#### `clear` +Removes all services from the registry. + +**Signature** +```kotlin +fun clear() +``` + +--- + +## `EmptyMapServiceRegistry` + +### Signature +```kotlin +object EmptyMapServiceRegistry : MapServiceRegistry +``` + +### Description +A singleton, immutable implementation of `MapServiceRegistry` that contains no services. Its `get` method will always return `null`. It is primarily used as a default or placeholder value, especially for `LocalMapServiceRegistry`. + +--- + +## `LocalMapServiceRegistry` + +### Signature +```kotlin +val LocalMapServiceRegistry: ProvidableCompositionLocal +``` + +### Description +A Jetpack Compose `CompositionLocal` that provides a `MapServiceRegistry` instance to the underlying composition tree. This allows descendant Composables to access map-scoped services without needing to pass the registry down explicitly as a parameter. + +The default value is `EmptyMapServiceRegistry`, which means that if no registry is provided, any attempt to retrieve a service will return `null`. + +### Example +The following example demonstrates how to define a service, register it with `MutableMapServiceRegistry`, provide it to the Composable tree, and access it in a child Composable. + +```kotlin +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.remember + +// 1. Define a service and its key +interface GreeterService { + fun greet(name: String): String +} + +object GreeterServiceKey : MapServiceKey + +class GreeterServiceImpl : GreeterService { + override fun greet(name: String) = "Hello, $name!" +} + +// 2. Create a root Composable that provides the service registry +@Composable +fun AppRoot() { + // Create and remember a mutable registry instance + val serviceRegistry = remember { + MutableMapServiceRegistry().apply { + // Register the service implementation + put(GreeterServiceKey, GreeterServiceImpl()) + } + } + + // Provide the registry to the composition tree + CompositionLocalProvider(LocalMapServiceRegistry provides serviceRegistry) { + // Your app's content, e.g., a map screen + MapScreen() + } +} + +// 3. Access the service in a descendant Composable +@Composable +fun MapScreen() { + // Access the registry from the CompositionLocal + val registry = LocalMapServiceRegistry.current + + // Retrieve the service using its key + val greeterService = registry.get(GreeterServiceKey) + + // Use the service + val greeting = greeterService?.greet("Developer") ?: "Service not found" + + Text(text = greeting) // Displays "Hello, Developer!" +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/map/MapViewBase.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/map/MapViewBase.kt.md new file mode 100644 index 00000000..d836b224 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/map/MapViewBase.kt.md @@ -0,0 +1,141 @@ +Excellent. Here is the high-quality SDK documentation for the provided `MapViewBase` composable. + +*** + +# MapViewBase + +## Signature +```kotlin +@Composable +fun < + SpecificState : MapViewStateInterface<*>, + SpecificController : MapViewControllerInterface, + ActualMapView : View, + ActualMap : Any, + SpecificScope : MapViewScope, + SpecificHolder : MapViewHolderInterface, +> MapViewBase( + state: SpecificState, + cameraState: MutableState, + modifier: Modifier = Modifier, + viewProvider: () -> ActualMapView, + scope: SpecificScope, + registry: MapOverlayRegistry, + serviceRegistry: MapServiceRegistry = EmptyMapServiceRegistry, + sdkInitialize: suspend () -> Boolean = { true }, + holderProvider: suspend (mapView: ActualMapView) -> SpecificHolder, + controllerProvider: suspend (holder: SpecificHolder) -> SpecificController, + onMapLoaded: OnMapLoadedHandler? = null, + customDisposableEffect: (@Composable (InitState, Ref) -> Unit)? = null, + content: (@Composable SpecificScope.() -> Unit)? = null, +) +``` + +## Description + +`MapViewBase` is a fundamental, low-level composable designed to integrate a native Android map view into a Jetpack Compose application. It serves as a generic foundation for creating specific map implementations (e.g., for Google Maps, Mapbox, etc.). + +This component manages the entire lifecycle of the underlying map view, including: +- Asynchronous initialization of the map SDK. +- Creation and management of the native `View`. +- Setup of a map controller to abstract map interactions. +- A declarative API for adding and updating map overlays (like markers, polylines, and polygons) as composable content. + +It uses a `SubcomposeLayout` to correctly layer Compose-based UI (such as info bubbles) on top of the native `AndroidView`, ensuring that overlays are rendered only after the map's size is determined. `CompositionLocalProvider` is used to provide the map controller and overlay collectors to the child `content` composables. + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| `state` | `SpecificState` | The state object for the map, which must implement `MapViewStateInterface`. It typically contains properties like the camera position. | +| `cameraState` | `MutableState` | A mutable state holding the current camera position. This is used for observing and controlling the map's camera from outside the composable. | +| `modifier` | `Modifier` | The `Modifier` to be applied to the root layout of the map component. | +| `viewProvider` | `() -> ActualMapView` | A factory lambda that creates and returns an instance of the native Android map view (e.g., `com.google.android.gms.maps.MapView`). | +| `scope` | `SpecificScope` | An instance of a `MapViewScope` implementation. It is responsible for managing the state collectors for all map overlays (markers, polylines, etc.). | +| `registry` | `MapOverlayRegistry` | The registry that contains the rendering logic for different types of map overlays. It maps overlay state to the corresponding controller actions. | +| `serviceRegistry` | `MapServiceRegistry` | A registry for providing optional map-related services. Defaults to an empty registry if not provided. | +| `sdkInitialize` | `suspend () -> Boolean` | An asynchronous lambda that performs one-time initialization for the map SDK (e.g., setting an API key). It should return `true` on success and `false` on failure. | +| `holderProvider` | `suspend (ActualMapView) -> SpecificHolder` | An asynchronous factory lambda that takes the created native map view and returns a `MapViewHolderInterface`. The holder is responsible for managing the map object's lifecycle (e.g., calling `getMapAsync` in Google Maps). | +| `controllerProvider` | `suspend (SpecificHolder) -> SpecificController` | An asynchronous factory lambda that takes the initialized holder and returns a `MapViewControllerInterface`. The controller provides a unified API to manipulate the map (e.g., add markers, move camera). | +| `onMapLoaded` | `OnMapLoadedHandler?` | An optional callback that is invoked when the map controller is fully initialized and the map is ready to be interacted with. It receives the map's state as an argument. | +| `customDisposableEffect` | `(@Composable (InitState, Ref) -> Unit)?` | An optional composable lambda for executing custom side-effects tied to the map's lifecycle. This is useful for advanced, provider-specific setup or cleanup logic. | +| `content` | `(@Composable SpecificScope.() -> Unit)?` | The composable content to be displayed on the map. This is where you declaratively add map overlays like `Marker`, `Polyline`, and `Circle`. These composables receive the `MapViewScope` as their receiver. | + +## Returns + +This is a composable function and does not have a direct return value. It renders a UI component that displays the native map and its associated overlay content. + +## Example + +`MapViewBase` is a generic component. The following example shows a conceptual implementation for a hypothetical "MyMap" SDK to illustrate how you would use it. + +```kotlin +// Assume these are your concrete implementations for a specific map SDK +class MyMapViewState : MapViewStateInterface { /* ... */ } +class MyMapViewController : MapViewControllerInterface { /* ... */ } +class MyMapView(context: Context) : FrameLayout(context) { /* Native Map View */ } +class MyMap { /* The actual map object from the SDK */ } +class MyMapViewHolder(mapView: MyMapView) : MapViewHolderInterface { /* ... */ } +class MyMapViewScope : MapViewScope() { /* ... */ } + +@Composable +fun MyMapViewComponent(modifier: Modifier = Modifier) { + // 1. Remember the state and scope for your map implementation + val state = remember { MyMapViewState() } + val cameraState = remember { mutableStateOf(null) } + val scope = remember { MyMapViewScope() } + val context = LocalContext.current + + // 2. Provide the concrete implementations to MapViewBase + MapViewBase( + state = state, + cameraState = cameraState, + modifier = modifier, + scope = scope, + registry = scope.buildRegistry(), // Build registry from the scope + + // Provide a factory for the native map view + viewProvider = { MyMapView(context) }, + + // Initialize the SDK (e.g., with an API key) + sdkInitialize = { + MyMapSDK.initialize("YOUR_API_KEY") + true // Return true on success + }, + + // Create a holder that manages the map object lifecycle + holderProvider = { nativeView -> + MyMapViewHolder(nativeView).apply { + // This is where you'd typically call something like getMapAsync + awaitMap() + } + }, + + // Create a controller to interact with the map + controllerProvider = { holder -> + MyMapViewController(holder.getMap()) + }, + + // Callback for when the map is ready + onMapLoaded = { mapState -> + Log.d("MyMap", "Map is fully loaded and ready!") + } + ) { + // 3. Add map overlays declaratively inside the content lambda + Marker( + position = GeoPoint(35.681236, 139.767125), // Tokyo Station + title = "Tokyo Station", + snippet = "The heart of Tokyo's train network." + ) + + Polyline( + points = listOf( + GeoPoint(35.681236, 139.767125), + GeoPoint(35.658581, 139.745433) // Tokyo Tower + ), + color = Color.Blue, + width = 5f + ) + } +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/map/MapViewHolderInterface.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/map/MapViewHolderInterface.kt.md new file mode 100644 index 00000000..6a9782f5 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/map/MapViewHolderInterface.kt.md @@ -0,0 +1,209 @@ +Of course! Here is the high-quality SDK documentation for the provided Kotlin code snippet. + +# MapViewHolderInterface + +## Signature + +```kotlin +interface MapViewHolderInterface +``` + +## Description + +The `MapViewHolderInterface` defines a contract for a view holder that manages a map instance. It serves as an abstraction layer, bridging the gap between the core application logic and a platform-specific map implementation (e.g., Google Maps, Mapbox). + +This interface provides access to the native map view and map objects and defines essential utility functions for converting between geographical coordinates (`GeoPoint`) and screen coordinates (`Offset`). + +The generic types `ActualMapView` and `ActualMap` allow implementers to specify the concrete types of the map view and map objects provided by the underlying map SDK. + +--- + +## Properties + +| Name | Type | Description | +| :-------- | :-------------- | :----------------------------------------------------- | +| `mapView` | `ActualMapView` | The underlying, platform-specific map view instance. | +| `map` | `ActualMap` | The underlying, platform-specific map object instance. | + +--- + +## Functions + +### toScreenOffset + +Converts a geographical coordinate to its corresponding pixel offset on the screen. This is useful for positioning UI elements (like custom markers or overlays) on the map at a specific latitude and longitude. + +**Signature** + +```kotlin +fun toScreenOffset(position: GeoPointInterface): Offset? +``` + +**Parameters** + +| Parameter | Type | Description | +| :--------- | :------------------ | :------------------------------------------------------------------------------------------------------------------------------------- | +| `position` | `GeoPointInterface` | The geographical coordinate (`latitude`, `longitude`) to be converted into a screen offset. | + +**Returns** + +`Offset?`: The screen coordinate as an `Offset(x, y)`, or `null` if the geographical point is not currently visible on the screen or the conversion cannot be performed. + +--- + +### fromScreenOffset + +Asynchronously converts a screen pixel offset (e.g., from a touch event) to its corresponding geographical coordinate on the map. + +As a `suspend` function, it must be called from a coroutine scope. This allows for non-blocking execution, which is often required by map SDKs for projection calculations. + +**Signature** + +```kotlin +suspend fun fromScreenOffset(offset: Offset): GeoPoint? +``` + +**Parameters** + +| Parameter | Type | Description | +| :-------- | :------- | :----------------------------------------------------------------------- | +| `offset` | `Offset` | The screen pixel coordinate (`x`, `y`) to be converted to a geo-coordinate. | + +**Returns** + +`GeoPoint?`: The corresponding `GeoPoint` on the map, or `null` if the offset is outside the map's display area. + +--- + +### fromScreenOffsetSync + +Synchronously converts a screen pixel offset to its corresponding geographical coordinate. + +This function provides a default implementation that returns `null`. Implementers should override this method only if the underlying map SDK supports a synchronous, blocking conversion. Use `fromScreenOffset` for a more universally compatible, non-blocking approach. + +**Signature** + +```kotlin +fun fromScreenOffsetSync(offset: Offset): GeoPoint? = null +``` + +**Parameters** + +| Parameter | Type | Description | +| :-------- | :------- | :----------------------------------------------------------------------- | +| `offset` | `Offset` | The screen pixel coordinate (`x`, `y`) to be converted to a geo-coordinate. | + +**Returns** + +`GeoPoint?`: The corresponding `GeoPoint`, or `null` if the conversion is not supported synchronously or the offset is outside the map's display area. + +--- + +## Example + +Here is an example of how to implement and use the `MapViewHolderInterface` with a hypothetical Google Maps integration. + +### 1. Implementation + +```kotlin +import androidx.compose.ui.geometry.Offset +import com.google.android.gms.maps.GoogleMap +import com.google.android.gms.maps.MapView +import com.google.android.gms.maps.model.LatLng +import com.mapconductor.core.features.GeoPoint +import com.mapconductor.core.features.GeoPointInterface +import com.mapconductor.core.map.MapViewHolderInterface +import kotlinx.coroutines.tasks.await + +// A concrete implementation for Google Maps +class GoogleMapViewHolder( + override val mapView: MapView, + override val map: GoogleMap +) : MapViewHolderInterface { + + override fun toScreenOffset(position: GeoPointInterface): Offset? { + val projection = map.projection + val screenPoint = projection.toScreenLocation(LatLng(position.latitude, position.longitude)) + return if (mapView.getGlobalVisibleRect(android.graphics.Rect(0, 0, mapView.width, mapView.height))) { + Offset(screenPoint.x.toFloat(), screenPoint.y.toFloat()) + } else { + null // The point is not on the visible part of the map + } + } + + override suspend fun fromScreenOffset(offset: Offset): GeoPoint? { + // Google Maps projection is synchronous, but we adhere to the suspend contract. + // For other SDKs, this might involve an await() call. + val latLng = map.projection.fromScreenLocation( + android.graphics.Point(offset.x.toInt(), offset.y.toInt()) + ) + return latLng?.let { GeoPoint(it.latitude, it.longitude) } + } + + override fun fromScreenOffsetSync(offset: Offset): GeoPoint? { + // Since Google Maps supports this synchronously, we can override the default. + val latLng = map.projection.fromScreenLocation( + android.graphics.Point(offset.x.toInt(), offset.y.toInt()) + ) + return latLng?.let { GeoPoint(it.latitude, it.longitude) } + } +} +``` + +### 2. Usage + +```kotlin +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.offset +import androidx.compose.material.Icon +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Place +import androidx.compose.runtime.* +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.mapconductor.core.features.GeoPoint +import kotlinx.coroutines.launch + +@Composable +fun MapWithCustomMarker(mapViewHolder: GoogleMapViewHolder) { + val coroutineScope = rememberCoroutineScope() + + // A fixed geographical point for our marker + val tokyoTowerPoint = GeoPoint(35.6586, 139.7454) + + // State to hold the screen position of our marker + var markerScreenOffset by remember { mutableStateOf(null) } + + // Update the marker's screen position whenever the map moves + LaunchedEffect(mapViewHolder.map.cameraPosition) { + markerScreenOffset = mapViewHolder.toScreenOffset(tokyoTowerPoint) + } + + Box { + // The actual map view would be here + // GoogleMapView(...) + + // Place a custom Composable icon on the map + markerScreenOffset?.let { offset -> + Icon( + imageVector = Icons.Default.Place, + contentDescription = "Tokyo Tower", + modifier = Modifier.offset( + x = offset.x.dp, + y = offset.y.dp + ) + ) + } + } + + // Example of converting a tap location to a GeoPoint + fun handleTap(tapOffset: Offset) { + coroutineScope.launch { + val geoPoint = mapViewHolder.fromScreenOffset(tapOffset) + geoPoint?.let { + println("Tapped at: Lat ${it.latitude}, Lon ${it.longitude}") + } + } + } +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/map/MapViewHolderStore.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/map/MapViewHolderStore.kt.md new file mode 100644 index 00000000..82d39d03 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/map/MapViewHolderStore.kt.md @@ -0,0 +1,238 @@ +Here is the high-quality SDK documentation for the provided code snippet. + +*** + +# StaticHolder + +An abstract generic class that provides a basic in-memory key-value store. It is designed to be extended by other classes that need to manage a collection of objects indexed by string identifiers. + +### Signature + +```kotlin +abstract class StaticHolder +``` + +### Description + +`StaticHolder` serves as a base for creating simple caches or registries. It uses a `MutableMap` internally to store values of a generic type `ValueType` against a `String` key. It provides fundamental methods for adding, retrieving, checking, and removing items from the store. + +## Methods + +### has + +Checks if a value with the specified ID exists in the store. + +**Signature** +```kotlin +fun has(id: String): Boolean +``` + +**Parameters** + +| Parameter | Type | Description | +| :-------- | :----- | :--------------------------- | +| `id` | `String` | The unique identifier for the value. | + +**Returns** + +`Boolean` — `true` if a value with the given `id` is found, `false` otherwise. + +--- + +### get + +Retrieves the value associated with the specified ID from the store. + +**Signature** +```kotlin +fun get(id: String): ValueType? +``` + +**Parameters** + +| Parameter | Type | Description | +| :-------- | :----- | :--------------------------- | +| `id` | `String` | The unique identifier for the value. | + +**Returns** + +`ValueType?` — The value associated with the `id`, or `null` if the `id` does not exist. + +--- + +### set + +Adds a new value to the store or updates the existing value for the specified ID. + +**Signature** +```kotlin +fun set(id: String, viewHolder: ValueType) +``` + +**Parameters** + +| Parameter | Type | Description | +| :----------- | :---------- | :----------------------------------- | +| `id` | `String` | The unique identifier for the value. | +| `viewHolder` | `ValueType` | The value to be stored. | + +--- + +### remove + +Removes the value associated with the specified ID from the store. + +**Signature** +```kotlin +fun remove(id: String) +``` + +**Parameters** + +| Parameter | Type | Description | +| :-------- | :----- | :--------------------------- | +| `id` | `String` | The unique identifier for the value. | + +--- + +### clearAll + +Removes all values from the store, leaving it empty. + +**Signature** +```kotlin +fun clearAll() +``` + +*** + +# MapViewHolderStoreBaseAsync + +An abstract base class for managing a store of map view holders. It extends `StaticHolder` to provide storage functionality and introduces an asynchronous method to get or create map view holders. + +### Signature + +```kotlin +abstract class MapViewHolderStoreBaseAsync< + TMapView, + TMap, + TOptions, +> : StaticHolder>() +``` + +### Description + +`MapViewHolderStoreBaseAsync` is designed to manage the lifecycle of map views, which can be resource-intensive to create. By extending `StaticHolder>`, it inherits methods for storing, retrieving, and removing map view holders. + +The key feature of this class is the `getOrCreate` abstract method, which ensures that map view holders are created asynchronously and only when necessary, promoting efficient resource usage. + +### Generic Type Parameters + +| Parameter | Description | +| :--------- | :----------------------------------------------------------------------- | +| `TMapView` | The type of the native map view UI component (e.g., `MapView`). | +| `TMap` | The type of the underlying map object (e.g., `GoogleMap`, `MapboxMap`). | +| `TOptions` | The type of the configuration options used to create a new map instance. | + +### Inherited Methods + +This class inherits all public methods from `StaticHolder>`: + +* `has(id: String): Boolean` +* `get(id: String): MapViewHolderInterface?` +* `set(id: String, viewHolder: MapViewHolderInterface)` +* `remove(id: String)` +* `clearAll()` + +## Abstract Methods + +### getOrCreate + +Asynchronously gets an existing `MapViewHolderInterface` from the store or creates a new one if it doesn't exist. + +**Signature** +```kotlin +abstract suspend fun getOrCreate( + context: Context, + id: String, + options: TOptions, +): MapViewHolderInterface +``` + +**Description** + +This function first attempts to retrieve a `MapViewHolderInterface` using the provided `id`. If no holder is found, it proceeds to create a new one using the given `context` and `options`. The newly created holder is then stored and returned. This "get-or-create" pattern is ideal for managing shared map resources efficiently. As a `suspend` function, it must be called from a coroutine or another `suspend` function. + +**Parameters** + +| Parameter | Type | Description | +| :-------- | :-------- | :----------------------------------------------------------- | +| `context` | `Context` | The Android `Context` required to create a new map view. | +| `id` | `String` | The unique identifier for the map view holder. | +| `options` | `TOptions` | The configuration options to use if a new holder is created. | + +**Returns** + +`MapViewHolderInterface` — The existing or newly created map view holder. + +### Example + +Below is an example of how you might implement `MapViewHolderStoreBaseAsync` for a specific map provider. + +```kotlin +// Assume these types are defined for a specific map SDK +class MyMapView +class MyMap +class MyMapOptions(val initialZoom: Double) +interface MapViewHolderInterface // From your library + +// 1. Define a concrete MapViewHolder +class MyMapViewHolder( + // ... constructor params +) : MapViewHolderInterface { + // ... implementation +} + +// 2. Implement the abstract store +class MyMapViewHolderStore : MapViewHolderStoreBaseAsync() { + override suspend fun getOrCreate( + context: Context, + id: String, + options: MyMapOptions, + ): MapViewHolderInterface { + // Try to get an existing holder first + get(id)?.let { + println("Returning existing map holder for id: $id") + return it + } + + // If it doesn't exist, create a new one (potentially a long-running task) + println("Creating new map holder for id: $id with zoom: ${options.initialZoom}") + val newHolder = withContext(Dispatchers.IO) { + // Simulate heavy creation logic + delay(500) + MyMapViewHolder(/*...pass context and options...*/) + } + + // Store the new holder before returning it + set(id, newHolder) + return newHolder + } +} + +// 3. Usage in your application +suspend fun setupMap(context: Context) { + val mapStore = MyMapViewHolderStore() + val mapId = "main_map" + val mapOptions = MyMapOptions(initialZoom = 12.0) + + // First call: creates and returns a new holder + val mapHolder1 = mapStore.getOrCreate(context, mapId, mapOptions) + + // Second call: returns the existing holder without creating a new one + val mapHolder2 = mapStore.getOrCreate(context, mapId, mapOptions) + + // Check if they are the same instance + println(mapHolder1 === mapHolder2) // Prints: true +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/map/MapViewState.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/map/MapViewState.kt.md new file mode 100644 index 00000000..0c0d4b2e --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/map/MapViewState.kt.md @@ -0,0 +1,276 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +--- + +# SDK Documentation + +This document provides detailed information about the core map interfaces, classes, and enums for the Map Conductor SDK. + +## `InitState` + +### Signature + +```kotlin +enum class InitState +``` + +### Description + +Represents the various initialization states of the map SDK and its components. This enum is used to track the progress of the map setup, from the initial state to a fully rendered map or a failure state. + +### Enum Values + +| Value | Description | +| ---------------- | ------------------------------------------------------------------------ | +| `NotStarted` | The initialization process has not yet begun. | +| `Initializing` | The SDK is currently in the process of initializing. | +| `SdkInitialized` | The core SDK has been successfully initialized. | +| `MapViewCreated` | The underlying native map view has been created. | +| `MapCreated` | The map object is fully created and ready for interaction. | +| `Failed` | An error occurred at some point during the initialization process. | + +--- + +## `MapViewStateInterface` + +### Signature + +```kotlin +interface MapViewStateInterface +``` + +### Description + +Defines the contract for managing the state of a map view. This includes properties like camera position and map style, as well as methods for manipulating the camera. It is a generic interface that allows for different types of map designs or styles. + +### Type Parameters + +| Name | Description | +| --------------------- | --------------------------------------------------------------------------- | +| `ActualMapDesignType` | The generic type representing the specific map design or style being used. | + +### Properties + +| Property | Type | Description | +| ---------------- | ----------------------- | ------------------------------------------------------------------------ | +| `id` | `String` | A unique identifier for the map view state. | +| `cameraPosition` | `MapCameraPosition` | The current position of the map's camera (location, zoom, tilt, bearing). | +| `mapDesignType` | `ActualMapDesignType` | The current design or style applied to the map. | + +### Methods + +#### `moveCameraTo(cameraPosition)` + +##### Signature + +```kotlin +fun moveCameraTo( + cameraPosition: MapCameraPosition, + durationMillis: Long? = 0, +) +``` + +##### Description + +Moves the map camera to a specified `MapCameraPosition` over a given duration. + +##### Parameters + +| Parameter | Type | Default | Description | +| ---------------- | ------------------- | ------- | ------------------------------------------------------------------------ | +| `cameraPosition` | `MapCameraPosition` | - | The target camera position, including coordinates, zoom, tilt, and bearing. | +| `durationMillis` | `Long?` | `0` | The duration of the camera animation in milliseconds. A value of `0` results in an instantaneous move. | + +#### `moveCameraTo(position)` + +##### Signature + +```kotlin +fun moveCameraTo( + position: GeoPoint, + durationMillis: Long? = 0, +) +``` + +##### Description + +Moves the map camera to a specific geographical coordinate (`GeoPoint`), preserving the current zoom, tilt, and bearing. + +##### Parameters + +| Parameter | Type | Default | Description | +| ---------------- | --------- | ------- | ------------------------------------------------------------------------ | +| `position` | `GeoPoint`| - | The target geographical coordinates (latitude and longitude). | +| `durationMillis` | `Long?` | `0` | The duration of the camera animation in milliseconds. A value of `0` results in an instantaneous move. | + +#### `getMapViewHolder()` + +##### Signature + +```kotlin +fun getMapViewHolder(): MapViewHolderInterface<*, *>? +``` + +##### Description + +Retrieves the `MapViewHolderInterface` associated with this map view state. The view holder is responsible for managing the lifecycle of the actual map view UI component. + +##### Returns + +| Type | Description | +| -------------------------------- | ------------------------------------------------------------------------ | +| `MapViewHolderInterface<*, *>?` | The associated map view holder, or `null` if it is not available. | + +--- + +## `MapViewState` + +### Signature + +```kotlin +abstract class MapViewState : MapViewStateInterface +``` + +### Description + +An abstract base class that provides a partial implementation of the `MapViewStateInterface`. It is intended to be subclassed by concrete map state implementations. + +### Type Parameters + +| Name | Description | +| --------------------- | --------------------------------------------------------------------------- | +| `ActualMapDesignType` | The generic type representing the specific map design or style being used. | + +--- + +## `MapOverlayInterface` + +### Signature + +```kotlin +interface MapOverlayInterface +``` + +### Description + +Defines the contract for a map overlay. An overlay is a layer of custom data that can be rendered on top of the map. This interface is generic, allowing overlays to handle any type of data. + +### Type Parameters + +| Name | Description | +| ---------- | --------------------------------------------------------------------------- | +| `DataType` | The type of data objects that this overlay will manage and render. | + +### Properties + +| Property | Type | Description | +| -------- | ---------------------------------------- | ------------------------------------------------------------------------------------------------------- | +| `flow` | `StateFlow>` | A `StateFlow` that emits the current state of the overlay data, keyed by a unique `String` identifier. | + +### Methods + +#### `render` + +##### Signature + +```kotlin +suspend fun render( + data: MutableMap, + controller: MapViewControllerInterface, +) +``` + +##### Description + +A suspend function responsible for rendering the provided data onto the map. This function is called when the overlay needs to be drawn or updated. + +##### Parameters + +| Parameter | Type | Description | +| ------------ | ---------------------------- | ------------------------------------------------------------------------ | +| `data` | `MutableMap` | The map of data items to be rendered, where the key is a unique ID for each item. | +| `controller` | `MapViewControllerInterface` | The controller for the map view, used to perform drawing operations. | + +--- + +## `MapOverlayRegistry` + +### Signature + +```kotlin +class MapOverlayRegistry +``` + +### Description + +A singleton-like registry responsible for managing all `MapOverlayInterface` instances. It allows for the registration of new overlays and provides a way to retrieve all registered overlays. + +### Methods + +#### `register` + +##### Signature + +```kotlin +fun register(overlay: MapOverlayInterface<*>) +``` + +##### Description + +Registers a new map overlay with the registry. If the overlay has already been registered, the method does nothing. + +##### Parameters + +| Parameter | Type | Description | +| --------- | ------------------------ | ----------------------------------------- | +| `overlay` | `MapOverlayInterface<*>` | The map overlay instance to register. | + +#### `getAll` + +##### Signature + +```kotlin +fun getAll(): List> +``` + +##### Description + +Retrieves a list of all currently registered map overlays. + +##### Returns + +| Type | Description | +| ------------------------------ | ----------------------------------------- | +| `List>` | An immutable list of all registered overlays. | + +### Example + +Here is an example of how to create a custom overlay and register it with the `MapOverlayRegistry`. + +```kotlin +// Define a data class for your overlay items +data class Poi(val id: String, val name: String, val location: GeoPoint) + +// Implement the MapOverlayInterface +class PoiOverlay : MapOverlayInterface { + override val flow: StateFlow> = MutableStateFlow(mutableMapOf()) + + override suspend fun render(data: MutableMap, controller: MapViewControllerInterface) { + // Add logic here to draw each POI on the map using the controller + println("Rendering ${data.size} POIs...") + } +} + +// In your application setup code +fun registerOverlays() { + val registry = MapOverlayRegistry() + val poiOverlay = PoiOverlay() + + // Register the overlay + registry.register(poiOverlay) + + // You can now retrieve all overlays + val allOverlays = registry.getAll() + println("Registered ${allOverlays.size} overlays.") +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/AbstractMarkerController.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/AbstractMarkerController.kt.md new file mode 100644 index 00000000..3957db71 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/AbstractMarkerController.kt.md @@ -0,0 +1,201 @@ +Excellent. Here is the high-quality SDK documentation for the provided Kotlin code snippet. + +*** + +# AbstractMarkerController<ActualMarker> + +## Description + +An abstract base class for managing and rendering a collection of markers on a map. It handles the complete lifecycle of markers, including adding, updating, and removing them in an efficient, state-driven manner. + +This controller is designed to be platform-agnostic. It must be subclassed to implement provider-specific rendering logic for a particular map SDK (e.g., Google Maps, Mapbox). + +**Key Features:** + +* **State-Driven Rendering**: The UI is a direct function of the provided `MarkerState` list. The controller intelligently diffs the new state against the current state to determine what to add, update, or remove. +* **Efficient Batch Processing**: To maintain UI responsiveness, especially with a large number of markers, operations are batched and executed with `yield()` calls to avoid blocking the main thread. +* **Thread Safety**: Employs a `Semaphore` to ensure that concurrent modifications to the marker collection are handled safely and sequentially. +* **Comprehensive Event Handling**: Provides a robust system for handling user interactions like clicks and drags, with both global and per-marker listeners. + +## Generic Parameters + +| Parameter | Description | +| :--- | :--- | +| `` | The platform-specific native marker object type (e.g., `com.google.android.gms.maps.model.Marker`). | + +## Signature + +```kotlin +abstract class AbstractMarkerController( + val markerManager: MarkerManager, + renderer: MarkerOverlayRendererInterface, + override var clickListener: OnMarkerEventHandler? = null, +) : OverlayControllerInterface< + MarkerState, + MarkerEntityInterface, + MarkerState, + > +``` + +### Constructor + +Creates a new instance of the marker controller. + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `markerManager` | `MarkerManager` | The manager responsible for storing and tracking the state of all markers. | +| `renderer` | `MarkerOverlayRendererInterface` | The renderer responsible for drawing and updating the native marker objects on the map. | +| `clickListener` | `OnMarkerEventHandler?` | (Optional) A global listener for marker click events. Defaults to `null`. | + +## Properties + +| Property | Type | Description | +| :--- | :--- | :--- | +| `renderer` | `MarkerOverlayRendererInterface` | The renderer instance used for drawing markers on the map. | +| `zIndex` | `Int` | The z-index for the marker overlay, used to control rendering order. Hardcoded to `10`. | +| `clickListener` | `OnMarkerEventHandler?` | A global event handler invoked when any marker is clicked. | +| `dragStartListener` | `OnMarkerEventHandler?` | A global event handler invoked when a drag gesture starts on any marker. | +| `dragListener` | `OnMarkerEventHandler?` | A global event handler invoked continuously while any marker is being dragged. | +| `dragEndListener` | `OnMarkerEventHandler?` | A global event handler invoked when a drag gesture ends on any marker. | +| `animateStartListener` | `OnMarkerEventHandler?` | A global event handler invoked when a marker animation starts. | +| `animateEndListener` | `OnMarkerEventHandler?` | A global event handler invoked when a marker animation ends. | + +## Methods + +### add + +Adds, updates, or removes markers to match the provided list of `MarkerState` objects. This method performs a diff against the current set of markers and efficiently applies the necessary changes (add, update, remove). Operations are batched to maintain UI responsiveness. + +**Signature** +```kotlin +override suspend fun add(data: List) +``` + +**Parameters** + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `data` | `List` | The complete list of marker states that should be displayed on the map. | + +### update + +Updates a single existing marker with a new state. If a marker with the given `state.id` does not exist, the operation is ignored. This method is an optimized path for updating a single marker and is more efficient than calling `add` with a full list. It also triggers animations if the animation property has changed. + +**Signature** +```kotlin +override suspend fun update(state: MarkerState) +``` + +**Parameters** + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `state` | `MarkerState` | The new state for the marker to be updated. | + +### clear + +Removes all markers managed by this controller from the map. + +**Signature** +```kotlin +override suspend fun clear() +``` + +### destroy + +Cleans up all resources used by the controller and its associated `MarkerManager`. This method is crucial to call when the map is being destroyed or when switching map providers to prevent memory leaks. + +**Signature** +```kotlin +override fun destroy() +``` + +### Event Dispatchers + +These methods are typically called by the platform-specific implementation to propagate events from the map's native markers to the controller's event handling system. + +| Method | Description | +| :--- | :--- | +| `dispatchClick(state: MarkerState)` | Dispatches a click event. This triggers the `onClick` callback on the specific `MarkerState` and the global `clickListener` on the controller. | +| `dispatchDragStart(state: MarkerState)` | Dispatches a drag start event, triggering `onDragStart` on the `MarkerState` and the global `dragStartListener`. | +| `dispatchDrag(state: MarkerState)` | Dispatches a drag event, triggering `onDrag` on the `MarkerState` and the global `dragListener`. | +| `dispatchDragEnd(state: MarkerState)` | Dispatches a drag end event, triggering `onDragEnd` on the `MarkerState` and the global `dragEndListener`. | +| `dispatchAnimateStart(state: MarkerState)` | Dispatches an animation start event, triggering `onAnimateStart` on the `MarkerState` and the global `animateStartListener`. | +| `dispatchAnimateEnd(state: MarkerState)` | Dispatches an animation end event, triggering `onAnimateEnd` on the `MarkerState` and the global `animateEndListener`. | + +## Protected Methods + +### setDraggingState + +Sets the internal dragging state for a marker. This method should be called by subclasses to manage the dragging lifecycle, as the `isDragging` property is not directly accessible. + +**Signature** +```kotlin +protected fun setDraggingState(markerState: MarkerState, dragging: Boolean) +``` + +**Parameters** + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `markerState` | `MarkerState` | The state object of the marker whose dragging status is being changed. | +| `dragging` | `Boolean` | `true` if the marker is being dragged, `false` otherwise. | + +## Example + +Below is a conceptual example of how a developer would use a concrete implementation of `AbstractMarkerController`. + +```kotlin +// Assume 'myMarkerController' is a concrete implementation of AbstractMarkerController +// and has been initialized. + +// 1. Set a global click listener +myMarkerController.clickListener = { markerState -> + Log.d("MapApp", "Marker clicked: ${markerState.id}") +} + +// 2. Define the markers to be displayed +val markerStates = listOf( + MarkerState( + id = "marker-1", + position = LatLng(34.0522, -118.2437), + title = "Los Angeles", + onClick = { state -> Log.d("MapApp", "Clicked on LA marker!") } + ), + MarkerState( + id = "marker-2", + position = LatLng(40.7128, -74.0060), + title = "New York City" + ) +) + +// 3. Add the markers to the map using the controller +// This will add both markers to the map. +viewModelScope.launch { + myMarkerController.add(markerStates) +} + +// 4. Later, update a single marker +val updatedNYState = MarkerState( + id = "marker-2", + position = LatLng(40.7128, -74.0060), + title = "The Big Apple", // Title changed + icon = CustomMapIcon("new_icon") // Icon changed +) + +// This will efficiently update only the NYC marker. +viewModelScope.launch { + myMarkerController.update(updatedNYState) +} + +// 5. Clear all markers from the map +viewModelScope.launch { + myMarkerController.clear() +} + +// 6. Clean up resources when the map is destroyed +override fun onCleared() { + myMarkerController.destroy() + super.onCleared() +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/AbstractMarkerOverlayRenderer.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/AbstractMarkerOverlayRenderer.kt.md new file mode 100644 index 00000000..b921c129 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/AbstractMarkerOverlayRenderer.kt.md @@ -0,0 +1,190 @@ +Of course! Here is the high-quality SDK documentation for the provided Kotlin code snippet. + +--- + +# AbstractMarkerOverlayRenderer + +## Signature + +```kotlin +abstract class AbstractMarkerOverlayRenderer< + MapViewHolderType : MapViewHolderInterface<*, *>, + ActualMarker, +>( + val holder: MapViewHolderType, + val coroutine: CoroutineScope, + val dropAnimateDuration: Long = Settings.Default.markerDropAnimateDuration, + val bounceAnimateDuration: Long = Settings.Default.markerBounceAnimateDuration, +) : MarkerOverlayRendererInterface +``` + +## Description + +`AbstractMarkerOverlayRenderer` is an abstract base class designed for rendering markers on a map. It provides a robust framework for handling common marker animations, such as "drop" and "bounce," using Kotlin Coroutines and Flows. + +As an abstract class, it must be subclassed to provide a concrete implementation for the specific map provider being used (e.g., Google Maps, Mapbox). The primary responsibility of the subclass is to implement the `setMarkerPosition` method, which handles the platform-specific logic for updating a marker's position. + +### Type Parameters + +| Name | Description | +| :--- | :--- | +| `MapViewHolderType` | The type of the map view holder, which must conform to `MapViewHolderInterface`. | +| `ActualMarker` | The type of the underlying, platform-specific marker object (e.g., `com.google.android.gms.maps.model.Marker`). | + +### Constructor Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `holder` | `MapViewHolderType` | The map view holder instance that provides screen-to-geo coordinate conversion. | +| `coroutine` | `CoroutineScope` | The coroutine scope used to launch and manage the animation coroutines. | +| `dropAnimateDuration` | `Long` | The duration of the drop animation in milliseconds. Defaults to `Settings.Default.markerDropAnimateDuration`. | +| `bounceAnimateDuration` | `Long` | The duration of the bounce animation in milliseconds. Defaults to `Settings.Default.markerBounceAnimateDuration`. | + +## Properties + +| Property | Type | Description | +| :--- | :--- | :--- | +| `animateStartListener` | `OnMarkerEventHandler?` | An optional listener that is invoked when a marker animation begins. It receives the `MarkerState` of the animating marker. | +| `animateEndListener` | `OnMarkerEventHandler?` | An optional listener that is invoked when a marker animation completes. It receives the `MarkerState` of the animating marker. | + +## Methods + +### onAnimate + +Triggers an animation for a given marker entity. This method reads the animation type specified in the entity's state and executes the corresponding animation function (`animateMarkerDrop` or `animateMarkerBounce`). + +**Signature** +```kotlin +override suspend fun onAnimate(entity: MarkerEntityInterface) +``` + +**Parameters** +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `entity` | `MarkerEntityInterface` | The marker entity to be animated. The animation to perform is determined by `entity.state.getAnimation()`. | + +### animateMarkerDrop + +Performs a "drop" animation on a marker. The marker animates vertically from the top of the screen to its final geographical position using a linear interpolation. + +**Signature** +```kotlin +fun animateMarkerDrop(entity: MarkerEntityInterface, duration: Long) +``` + +**Parameters** +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `entity` | `MarkerEntityInterface` | The marker entity to animate. | +| `duration` | `Long` | The total duration of the animation in milliseconds. | + +### animateMarkerBounce + +Performs a "bounce" animation on a marker. The marker animates from the top of the screen to its final geographical position, concluding with a bounce effect. + +**Signature** +```kotlin +fun animateMarkerBounce(entity: MarkerEntityInterface, duration: Long) +``` + +**Parameters** +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `entity` | `MarkerEntityInterface` | The marker entity to animate. | +| `duration` | `Long` | The total duration of the animation in milliseconds. | + +### zoomToMetersPerPixel + +A utility function that calculates the distance in meters represented by a single pixel at a given map zoom level. + +**Signature** +```kotlin +fun zoomToMetersPerPixel(zoom: Double, tileSize: Int): Double +``` + +**Parameters** +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `zoom` | `Double` | The current zoom level of the map. | +| `tileSize` | `Int` | The size of the map tiles in pixels (e.g., 256). | + +**Returns** +| Type | Description | +| :--- | :--- | +| `Double` | The number of meters per pixel at the specified zoom level. | + +### setMarkerPosition (Abstract) + +An abstract method that must be implemented by subclasses. This method is responsible for updating the geographical position of the actual, platform-specific marker object on the map. + +**Signature** +```kotlin +abstract fun setMarkerPosition( + markerEntity: MarkerEntityInterface, + position: GeoPoint, +) +``` + +**Parameters** +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `markerEntity` | `MarkerEntityInterface` | The marker entity containing the platform-specific marker object to update. | +| `position` | `GeoPoint` | The new geographical coordinates (`latitude`, `longitude`) to set for the marker. | + +## Example + +The following example demonstrates how to create a concrete implementation of `AbstractMarkerOverlayRenderer` for Google Maps. + +```kotlin +import com.google.android.gms.maps.model.Marker as GoogleMarker +import com.google.android.gms.maps.model.LatLng +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers + +// Assume MapViewHolderType and MarkerEntityInterface are defined elsewhere +// For example, GoogleMapViewHolder and a corresponding MarkerEntity implementation + +class GoogleMapMarkerRenderer( + holder: GoogleMapViewHolder, // A concrete MapViewHolderType + coroutine: CoroutineScope +) : AbstractMarkerOverlayRenderer( + holder = holder, + coroutine = coroutine +) { + /** + * Implements the abstract method to update the position of a Google Maps marker. + */ + override fun setMarkerPosition( + markerEntity: MarkerEntityInterface, + position: GeoPoint + ) { + // Access the platform-specific marker from the entity + val googleMarker = markerEntity.getActualMarker() + + // Convert GeoPoint to Google Maps LatLng and set the position + googleMarker?.position = LatLng(position.latitude, position.longitude) + } +} + +// --- Usage --- + +fun setupMarkerRenderer(mapViewHolder: GoogleMapViewHolder) { + // Create an instance of the concrete renderer + val markerRenderer = GoogleMapMarkerRenderer( + holder = mapViewHolder, + coroutine = CoroutineScope(Dispatchers.Main) + ) + + // Set up an animation end listener + markerRenderer.animateEndListener = { markerState -> + println("Animation finished for marker at: ${markerState.position}") + } + + // To trigger an animation on a marker entity: + // val myMarkerEntity: MarkerEntityInterface = ... + // myMarkerEntity.state.animate(MarkerAnimation.Drop) + // CoroutineScope(Dispatchers.Main).launch { + // markerRenderer.onAnimate(myMarkerEntity) + // } +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/AbstractMarkerRenderingStrategy.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/AbstractMarkerRenderingStrategy.kt.md new file mode 100644 index 00000000..e7ce61b6 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/AbstractMarkerRenderingStrategy.kt.md @@ -0,0 +1,153 @@ +# AbstractMarkerRenderingStrategy + +## Signature +```kotlin +abstract class AbstractMarkerRenderingStrategy( + protected val semaphore: Semaphore, +) : MarkerRenderingStrategyInterface +``` + +## Description +`AbstractMarkerRenderingStrategy` is a base class for implementing custom marker rendering logic. It provides a foundational structure, including default no-op implementations for `onAdd` and `onUpdate` methods and a concrete implementation for `clear`. + +This class is designed to be extended by concrete strategy implementations (e.g., a clustering strategy, a simple rendering strategy). Subclasses are required to provide their own `MarkerManager` instance, which handles the low-level interaction with the map's marker objects. The `Semaphore` in the constructor is intended to help manage concurrency during rendering operations. + +## Generic Type Parameters +| Name | Description | +| :--- | :--- | +| `ActualMarker` | The platform-specific marker object type (e.g., `com.google.android.gms.maps.model.Marker`). | + +## Properties + +### markerManager +An abstract property that must be implemented by subclasses. It provides a `MarkerManager` instance responsible for the low-level creation, deletion, and management of the actual marker objects on the map. + +**Signature** +```kotlin +abstract override val markerManager: MarkerManager +``` + +## Methods + +### clear +Removes all markers managed by this strategy from the map. This is achieved by delegating the call to `markerManager.clear()`. + +**Signature** +```kotlin +override fun clear() +``` + +### onAdd +A suspendable lifecycle method called when a collection of markers should be processed and potentially added to the map. The base implementation is a no-op and immediately returns `false`. + +Subclasses should override this method to implement their specific logic for adding markers, such as filtering based on the viewport, clustering, or custom rendering logic. + +**Signature** +```kotlin +override suspend fun onAdd( + data: List, + viewport: GeoRectBounds, + renderer: MarkerOverlayRendererInterface, +): Boolean +``` + +**Parameters** + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `data` | `List` | A list of marker data objects to be potentially rendered. | +| `viewport` | `GeoRectBounds` | The current visible geographical area of the map. | +| `renderer` | `MarkerOverlayRendererInterface` | The renderer interface used to perform the actual drawing operations on the map. | + +**Returns** + +| Type | Description | +| :--- | :--- | +| `Boolean` | Returns `true` if the strategy handled the event, `false` otherwise. The base implementation always returns `false`. | + +### onUpdate +A suspendable lifecycle method called when a single marker's state has been updated. The base implementation is a no-op and immediately returns `false`. + +Subclasses should override this to handle updates to individual markers, such as changing their position, icon, or other properties. + +**Signature** +```kotlin +override suspend fun onUpdate( + state: MarkerState, + viewport: GeoRectBounds, + renderer: MarkerOverlayRendererInterface, +): Boolean +``` + +**Parameters** + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `state` | `MarkerState` | The updated state of the marker. | +| `viewport` | `GeoRectBounds` | The current visible geographical area of the map. | +| `renderer` | `MarkerOverlayRendererInterface` | The renderer interface used to perform the actual drawing operations on the map. | + +**Returns** + +| Type | Description | +| :--- | :--- | +| `Boolean` | Returns `true` if the strategy handled the event, `false` otherwise. The base implementation always returns `false`. | + +## Example + +Since `AbstractMarkerRenderingStrategy` is an abstract class, you cannot instantiate it directly. Instead, you must extend it to create a concrete strategy. The following example demonstrates how to create a simple rendering strategy that adds all markers to the map. + +```kotlin +import com.mapconductor.core.marker.* +import com.mapconductor.core.features.GeoRectBounds +import kotlinx.coroutines.sync.Semaphore +import kotlinx.coroutines.sync.withPermit + +// Assume 'GoogleMapMarker' is the platform-specific marker type +typealias GoogleMapMarker = com.google.android.gms.maps.model.Marker + +/** + * A simple strategy that renders every marker without any clustering or filtering. + */ +class SimpleRenderingStrategy( + // You would inject your concrete MarkerManager implementation + override val markerManager: MarkerManager +) : AbstractMarkerRenderingStrategy(Semaphore(1)) { + + /** + * Overrides onAdd to render all markers provided in the data list. + */ + override suspend fun onAdd( + data: List, + viewport: GeoRectBounds, + renderer: MarkerOverlayRendererInterface, + ): Boolean { + // Use the semaphore to ensure thread safety during rendering + semaphore.withPermit { + // Clear existing markers before adding new ones + renderer.clear() + data.forEach { markerState -> + // Use the renderer to add each marker to the map + renderer.addMarker(markerState) + } + } + // Return true to indicate the event was handled + return true + } + + /** + * Overrides onUpdate to handle changes to a single marker. + */ + override suspend fun onUpdate( + state: MarkerState, + viewport: GeoRectBounds, + renderer: MarkerOverlayRendererInterface, + ): Boolean { + semaphore.withPermit { + // Find and update the specific marker + renderer.updateMarker(state) + } + return true + } +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/AbstractViewportStrategy.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/AbstractViewportStrategy.kt.md new file mode 100644 index 00000000..99ec38c5 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/AbstractViewportStrategy.kt.md @@ -0,0 +1,152 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +--- + +# AbstractViewportStrategy + +## Signature + +```kotlin +abstract class AbstractViewportStrategy( + semaphore: Semaphore, + geocell: HexGeocellInterface, +) : AbstractMarkerRenderingStrategy +``` + +## Description + +An abstract base class for marker rendering strategies that use viewport-based optimization. This class manages the lifecycle of markers, deciding which markers should be rendered based on whether they fall within the current map viewport. + +It handles the core logic for adding, updating, and removing markers by comparing incoming data with the current state. The platform-specific rendering operations (e.g., creating the actual map marker object) are delegated to a `MarkerOverlayRendererInterface`. Markers outside the viewport are tracked in the `markerManager` but are not passed to the renderer, saving rendering resources. + +Subclasses can extend this class to implement specific rendering behaviors while leveraging the built-in viewport culling logic. + +### Generic Parameters + +| Name | Description | +| :--- | :--- | +| `ActualMarker` | The platform-specific marker object type (e.g., `GoogleMap.Marker`, `Mapbox.Annotation`). | + +## Constructor + +### `AbstractViewportStrategy(semaphore, geocell)` + +Creates an instance of `AbstractViewportStrategy`. + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `semaphore` | `Semaphore` | A coroutine semaphore to ensure thread-safe access to marker collections during rendering operations. | +| `geocell` | `HexGeocellInterface` | The geocell system used for spatial indexing and efficient management of markers. | + +## Properties + +### `markerManager` + +An instance of `MarkerManager` that stores and manages the state of all markers, whether they are currently rendered on the map or not. It uses the provided `geocell` system for spatial organization. + +#### Signature + +```kotlin +override val markerManager: MarkerManager +``` + +## Methods + +### `onAdd` + +Processes a list of marker states to add, update, or remove markers from the map. This method implements the core viewport optimization logic. It determines which markers are new, which have been updated, and which are no longer present. + +Crucially, it only invokes the `renderer` to draw or update markers that are currently within the visible `viewport`. Markers outside the viewport are registered in the `markerManager` without being rendered, ensuring their state is preserved for when the user pans the map. + +#### Signature + +```kotlin +override suspend fun onAdd( + data: List, + viewport: GeoRectBounds, + renderer: MarkerOverlayRendererInterface, +): Boolean +``` + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `data` | `List` | The complete list of marker states that should be displayed. | +| `viewport` | `GeoRectBounds` | The geographical boundaries of the current map viewport. | +| `renderer` | `MarkerOverlayRendererInterface` | The renderer responsible for the actual drawing of markers on the map. | + +#### Returns + +| Type | Description | +| :--- | :--- | +| `Boolean` | Always returns `true`. | + +### `onUpdate` + +Handles the update of a single marker's state. It first checks if the marker's data has meaningfully changed to avoid unnecessary processing. The marker's state is always updated within the internal `markerManager`. However, the visual rendering via the `renderer` is only triggered if the marker's position is within the current `viewport`. + +#### Signature + +```kotlin +override suspend fun onUpdate( + state: MarkerState, + viewport: GeoRectBounds, + renderer: MarkerOverlayRendererInterface, +): Boolean +``` + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `state` | `MarkerState` | The new state for the marker to be updated. | +| `viewport` | `GeoRectBounds` | The geographical boundaries of the current map viewport. | +| `renderer` | `MarkerOverlayRendererInterface` | The renderer responsible for updating the marker on the map. | + +#### Returns + +| Type | Description | +| :--- | :--- | +| `Boolean` | Always returns `true`. | + +### `clear` + +Removes all markers and clears all internal state from the `markerManager`. + +#### Signature + +```kotlin +override fun clear() +``` + +## Example + +Since `AbstractViewportStrategy` is an abstract class, you must create a concrete implementation to use it. The primary purpose of a subclass is to provide any specialized logic, though in many cases, simply extending the class is sufficient. + +```kotlin +import com.google.android.gms.maps.model.Marker as GoogleMarker +import kotlinx.coroutines.sync.Semaphore +import com.mapconductor.core.geocell.HexGeocellInterface +import com.mapconductor.core.marker.AbstractViewportStrategy + +/** + * A concrete implementation of AbstractViewportStrategy for Google Maps. + * This class inherits the viewport optimization logic and can be used directly + * or extended further if custom behavior is needed. + */ +class GoogleMapsViewportStrategy( + semaphore: Semaphore, + geocell: HexGeocellInterface +) : AbstractViewportStrategy(semaphore, geocell) { + // No additional overrides are needed if the default viewport + // culling logic is sufficient for your use case. + // You can add custom logic here if required. +} + +// Usage: +// val strategy = GoogleMapsViewportStrategy(Semaphore(1), HexGeocell()) +// markerController.setStrategy(strategy) +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/DefaultMarkerIcon.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/DefaultMarkerIcon.kt.md new file mode 100644 index 00000000..754ab541 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/DefaultMarkerIcon.kt.md @@ -0,0 +1,289 @@ +This document provides a detailed reference for the Marker Icon SDK, covering classes that generate standard pin-shaped map markers. These classes allow for customization of fill (color, image, or drawable), stroke, labels, and more. + +## `ColorDefaultIcon` + +Creates a standard pin-shaped map marker icon with a solid color fill. This is the most common and straightforward marker type to use. + +### Constructor + +Initializes a new instance of `ColorDefaultIcon`. A convenience constructor is provided with sensible defaults for most parameters. + +**Signature:** +```kotlin +constructor( + fillColor: Color = Color.Red, + strokeColor: Color = Color.White, + strokeWidth: Dp = Settings.Default.iconStroke, + scale: Float = 1f, + label: String? = null, + labelTextColor: Color? = Color.Black, + labelTextSize: TextUnit = 18.sp, + labelTypeFace: Typeface = Typeface.DEFAULT, + labelStrokeColor: Color = Color.White, + infoAnchor: Offset = Offset(0.5f, 0f), + iconSize: Dp = Settings.Default.iconSize, + debug: Boolean = false, +) +``` + +### Parameters + +| Parameter | Type | Description | Default | +| :--- | :--- | :--- | :--- | +| `fillColor` | `Color` | The fill color of the marker body. | `Color.Red` | +| `strokeColor` | `Color` | The color of the marker's outline. | `Color.White` | +| `strokeWidth` | `Dp` | The width of the marker's outline. | `Settings.Default.iconStroke` | +| `scale` | `Float` | The scaling factor for the entire icon. | `1.0f` | +| `label` | `String?` | An optional text label to display inside the marker. | `null` | +| `labelTextColor` | `Color?` | The color of the label text. | `Color.Black` | +| `labelTextSize` | `TextUnit` | The size of the label text. | `18.sp` | +| `labelTypeFace` | `Typeface` | The typeface for the label text. | `Typeface.DEFAULT` | +| `labelStrokeColor` | `Color` | The color of the outline drawn around the label text for better visibility. | `Color.White` | +| `infoAnchor` | `Offset` | The anchor point for an info window, relative to the icon's dimensions (0,0 is top-left, 1,1 is bottom-right). | `Offset(0.5f, 0f)` (top-center) | +| `iconSize` | `Dp` | The base size of the icon before scaling. | `Settings.Default.iconSize` | +| `debug` | `Boolean` | If `true`, a debug frame will be drawn around the icon's canvas. | `false` | + +### Functions + +#### `copy` + +Creates a new `ColorDefaultIcon` instance, allowing you to modify specific properties while keeping others the same. + +**Signature:** +```kotlin +fun copy( + fillColor: Color = this.fillColor, + strokeColor: Color = this.strokeColor, + strokeWidth: Dp = this.strokeWidth, + scale: Float = this.scale, + label: String? = this.label, + labelTextColor: Color? = this.labelTextColor, + labelTextSize: TextUnit = this.labelTextSize, + labelTypeFace: Typeface = this.labelTypeFace, + labelStrokeColor: Color = this.labelStrokeColor, + iconSize: Dp = this.iconSize, + debug: Boolean = this.debug, +): ColorDefaultIcon +``` + +### Example + +```kotlin +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp + +// Create a simple blue marker +val simpleMarker = ColorDefaultIcon(fillColor = Color.Blue) + +// Create a larger, scaled marker with a label +val labeledMarker = ColorDefaultIcon( + fillColor = Color(0xFF009688), // Teal + strokeColor = Color.White, + strokeWidth = 3.dp, + scale = 1.5f, + label = "A", + labelTextColor = Color.White, + labelTextSize = 20.sp, + labelStrokeColor = Color.Black +) +``` + +--- + +## `ImageDefaultIcon` + +Creates a standard pin-shaped map marker icon filled with a `Bitmap` image. The image is scaled to fill the marker shape using a center-crop behavior, preserving its aspect ratio. + +### Constructor + +Initializes a new instance of `ImageDefaultIcon`. + +**Signature:** +```kotlin +constructor( + backgroundImage: Bitmap, + strokeColor: Color = Color.White, + strokeWidth: Dp = Settings.Default.iconStroke, + scale: Float = 1f, + label: String? = null, + // ... other parameters are the same as ColorDefaultIcon +) +``` + +### Parameters + +| Parameter | Type | Description | Default | +| :--- | :--- | :--- | :--- | +| `backgroundImage` | `Bitmap` | The bitmap image to use as the marker's fill. | (none) | +| `strokeColor` | `Color` | The color of the marker's outline. | `Color.White` | +| `strokeWidth` | `Dp` | The width of the marker's outline. | `Settings.Default.iconStroke` | +| `scale` | `Float` | The scaling factor for the entire icon. | `1.0f` | +| `label` | `String?` | An optional text label to display inside the marker. | `null` | +| `...` | | Other parameters are identical to `ColorDefaultIcon`. | | + +### Functions + +#### `copy` + +Creates a new `ImageDefaultIcon` instance, allowing you to modify specific properties. + +**Signature:** +```kotlin +fun copy( + backgroundImage: Bitmap = this.backgroundImage, + // ... other parameters are the same as ColorDefaultIcon.copy +): ImageDefaultIcon +``` + +### Example + +```kotlin +// Assume 'myBitmap' is a Bitmap object loaded from resources or network +val myBitmap: Bitmap = // ... + +// Create a marker filled with the bitmap +val imageMarker = ImageDefaultIcon( + backgroundImage = myBitmap, + strokeWidth = 2.dp, + scale = 1.2f +) +``` + +--- + +## `DrawableDefaultIcon` + +Creates a standard pin-shaped map marker icon filled with a `Drawable`. The drawable is scaled to fill the marker shape. If the drawable has an intrinsic size, its aspect ratio is preserved using a center-crop behavior. + +### Constructor + +Initializes a new instance of `DrawableDefaultIcon`. + +**Signature:** +```kotlin +constructor( + backgroundDrawable: Drawable, + strokeColor: Color = Color.White, + strokeWidth: Dp = Settings.Default.iconStroke, + scale: Float = 1f, + label: String? = null, + // ... other parameters are the same as ColorDefaultIcon +) +``` + +### Parameters + +| Parameter | Type | Description | Default | +| :--- | :--- | :--- | :--- | +| `backgroundDrawable` | `Drawable` | The drawable to use as the marker's fill. | (none) | +| `strokeColor` | `Color` | The color of the marker's outline. | `Color.White` | +| `strokeWidth` | `Dp` | The width of the marker's outline. | `Settings.Default.iconStroke` | +| `scale` | `Float` | The scaling factor for the entire icon. | `1.0f` | +| `label` | `String?` | An optional text label to display inside the marker. | `null` | +| `...` | | Other parameters are identical to `ColorDefaultIcon`. | | + +### Functions + +#### `copy` + +Creates a new `DrawableDefaultIcon` instance, allowing you to modify specific properties. + +**Signature:** +```kotlin +fun copy( + backgroundDrawable: Drawable = this.backgroundDrawable, + // ... other parameters are the same as ColorDefaultIcon.copy +): DrawableDefaultIcon +``` + +### Example + +```kotlin +import androidx.core.content.ContextCompat + +// Assume 'context' is a valid Android Context +val myDrawable = ContextCompat.getDrawable(context, R.drawable.my_gradient_background) + +// Create a marker filled with the drawable +val drawableMarker = myDrawable?.let { + DrawableDefaultIcon( + backgroundDrawable = it, + strokeWidth = 2.dp + ) +} +``` + +--- + +## `DefaultMarkerIcon` + +A type alias for `ColorDefaultIcon`. This is provided for backward compatibility. It is recommended to use `ColorDefaultIcon` directly in new code. + +**Signature:** +```kotlin +typealias DefaultMarkerIcon = ColorDefaultIcon +``` + +--- + +## `AbstractDefaultIcon` + +An abstract base class that provides the common framework for creating pin-shaped marker icons. It handles the rendering of the marker's shape, stroke, and label. + +Developers should typically use one of the concrete subclasses (`ColorDefaultIcon`, `ImageDefaultIcon`, `DrawableDefaultIcon`) or extend this class to create custom fill behaviors. + +### Properties + +| Property | Type | Description | +| :--- | :--- | :--- | +| `strokeColor` | `Color` | The color of the marker's outline. | +| `strokeWidth` | `Dp` | The width of the marker's outline. | +| `scale` | `Float` | The scaling factor for the entire icon. | +| `label` | `String?` | The optional text label to display inside the marker. | +| `labelTextColor` | `Color?` | The color of the label text. | +| `labelTextSize` | `TextUnit` | The size of the label text. | +| `labelTypeFace` | `Typeface` | The typeface for the label text. | +| `labelStrokeColor` | `Color` | The color of the outline drawn around the label text. | +| `iconSize` | `Dp` | The base size of the icon before scaling. | +| `anchor` | `Offset` | The anchor point of the icon, relative to its dimensions. Fixed to `Offset(0.5f, 1f)` (bottom-center). | +| `infoAnchor` | `Offset` | The anchor point for an info window, relative to the icon's dimensions. | +| `debug` | `Boolean` | If `true`, a debug frame is drawn around the icon's canvas. | + +### Abstract Functions + +Subclasses must implement these methods. + +#### `drawMarkerFill` + +Defines how the interior (fill) of the marker is drawn. + +**Signature:** +```kotlin +protected abstract fun drawMarkerFill( + canvas: Canvas, + path: Path, + canvasSize: Float, + iconScale: Float, +) +``` + +**Parameters:** +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `canvas` | `Canvas` | The canvas to draw on. | +| `path` | `Path` | The path defining the marker's shape. | +| `canvasSize` | `Float` | The total size of the drawing area for the marker. | +| `iconScale` | `Float` | The current scale of the icon. | + +#### `getUniqueProperties` + +Returns an object representing the unique properties of the subclass. This is used for `equals` and `hashCode` implementations to ensure correct caching and comparison. + +**Signature:** +```kotlin +protected abstract fun getUniqueProperties(): Any +``` + +**Returns:** +- `Any`: An object (e.g., a `Color`, a `Bitmap` hash) that uniquely identifies the state of the subclass. \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/ImageIcon.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/ImageIcon.kt.md new file mode 100644 index 00000000..a402f9c0 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/ImageIcon.kt.md @@ -0,0 +1,79 @@ +# ImageIcon + +The `ImageIcon` class represents a map marker icon created from an Android `Drawable` object. It provides a flexible way to define icons using various drawable types, such as `BitmapDrawable`, `ColorDrawable`, or `GradientDrawable`. + +This class handles the conversion of the `Drawable` into a `BitmapIcon` suitable for rendering on the map. For efficiency, it caches the generated bitmap, so subsequent requests for the same icon configuration do not require re-rendering. The equality of `ImageIcon` instances is determined by the properties of the underlying `Drawable` and the icon's configuration parameters (`iconSize`, `scale`, `anchor`, etc.), not by object identity. + +## Signature + +```kotlin +class ImageIcon( + image: Drawable, + override val iconSize: Dp = Settings.Default.iconSize, + override val scale: Float = 1.0f, + override val anchor: Offset = Offset(0.5f, 0.5f), + override val infoAnchor: Offset = Offset(0.5f, 0.5f), + override val debug: Boolean = false, +) : AndroidDrawableIcon +``` + +## Parameters + +| Parameter | Type | Description | Default | +|-----------|------|-------------|---------| +| `image` | `Drawable` | The Android `Drawable` to be used as the icon. | (none) | +| `iconSize` | `Dp` | The base size of the icon in density-independent pixels (Dp). | `Settings.Default.iconSize` | +| `scale` | `Float` | A multiplier applied to `iconSize` to scale the icon. A value of `2.0` would double the icon's size. | `1.0f` | +| `anchor` | `Offset` | The anchor point of the icon that is attached to the map's geographical coordinate. An `Offset(0.5f, 0.5f)` represents the center of the icon. `Offset(0.0f, 0.0f)` is the top-left corner. | `Offset(0.5f, 0.5f)` | +| `infoAnchor` | `Offset` | The point on the icon to which an associated info window will be anchored. The coordinate system is the same as for `anchor`. | `Offset(0.5f, 0.5f)` | +| `debug` | `Boolean` | If `true`, enables debug visualizations for the icon, such as drawing its bounding box or anchor point. | `false` | + +## Example + +The following example demonstrates how to create `ImageIcon` instances from both a drawable resource and a programmatically generated `Drawable`. + +```kotlin +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.unit.dp +import androidx.core.content.ContextCompat +import android.graphics.drawable.GradientDrawable +import com.mapconductor.core.marker.ImageIcon +import com.mapconductor.core.marker.Marker +import com.mapconductor.core.types.LatLng + +// Assuming 'context' is an Android Context instance available in your scope + +// 1. Create an ImageIcon from a drawable resource with default settings +val defaultIcon = ImageIcon( + image = ContextCompat.getDrawable(context, R.drawable.ic_marker_default)!! +) + +// 2. Create a larger, semi-transparent red circle icon anchored at the bottom center +val customDrawable = GradientDrawable().apply { + shape = GradientDrawable.OVAL + setColor(0x80FF0000.toInt()) // Semi-transparent red + setSize(100, 100) +} + +val customIcon = ImageIcon( + image = customDrawable, + iconSize = 48.dp, + scale = 1.5f, + anchor = Offset(0.5f, 1.0f) // Anchor at the bottom-center +) + +// 3. Use the icons when creating Markers for the map +val marker1 = Marker( + position = LatLng(34.0522, -118.2437), + icon = defaultIcon, + title = "Los Angeles" +) + +val marker2 = Marker( + position = LatLng(40.7128, -74.0060), + icon = customIcon, + title = "New York City" +) + +// You can now add these markers to your map controller. +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/Marker.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/Marker.kt.md new file mode 100644 index 00000000..473fc498 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/Marker.kt.md @@ -0,0 +1,204 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet, formatted in Markdown. + +--- + +# Marker State Management + +This document provides a detailed reference for the marker state management components within the MapConductor SDK. These components are designed to manage the state and behavior of individual markers on a map within a Jetpack Compose environment. + +## `MarkerState` + +A state holder class that represents and manages the complete state of a single marker on the map. It is designed to be used with Jetpack Compose, as its properties are observable `State` objects. Changes to these properties will trigger recomposition in Composables that read them. + +### Signature + +```kotlin +class MarkerState( + position: GeoPointInterface, + id: String? = null, + var extra: Serializable? = null, + icon: MarkerIconInterface? = null, + animation: MarkerAnimation? = null, + zIndex: Int? = null, + clickable: Boolean = true, + draggable: Boolean = false, + onClick: OnMarkerEventHandler? = null, + onDragStart: OnMarkerEventHandler? = null, + onDrag: OnMarkerEventHandler? = null, + onDragEnd: OnMarkerEventHandler? = null, + onAnimateStart: OnMarkerEventHandler? = null, + onAnimateEnd: OnMarkerEventHandler? = null, +) : ComponentState +``` + +### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `position` | `GeoPointInterface` | **Required.** The geographical coordinates where the marker is placed. | +| `id` | `String?` | An optional unique identifier for the marker. If `null`, a stable ID is automatically generated based on the marker's initial properties. | +| `extra` | `Serializable?` | Optional, serializable data that can be attached to the marker. Useful for storing custom information. | +| `icon` | `MarkerIconInterface?` | The visual representation of the marker. Defaults to the map's default marker icon if `null`. | +| `animation` | `MarkerAnimation?` | An optional animation to be applied to the marker upon its initial appearance. | +| `zIndex` | `Int?` | The stacking order of the marker relative to other map components. Higher values are drawn on top. | +| `clickable` | `Boolean` | Determines if the marker can be clicked. Defaults to `true`. | +| `draggable` | `Boolean` | Determines if the marker can be dragged. Defaults to `false`. | +| `onClick` | `OnMarkerEventHandler?` | A callback invoked when the marker is clicked. The handler receives the `MarkerState` of the clicked marker. | +| `onDragStart` | `OnMarkerEventHandler?` | A callback invoked when a drag gesture starts on the marker. | +| `onDrag` | `OnMarkerEventHandler?` | A callback invoked continuously while the marker is being dragged. | +| `onDragEnd` | `OnMarkerEventHandler?` | A callback invoked when a drag gesture ends. | +| `onAnimateStart` | `OnMarkerEventHandler?` | A callback invoked when a marker animation starts. | +| `onAnimateEnd` | `OnMarkerEventHandler?` | A callback invoked when a marker animation ends. | + +### Properties + +The following properties can be read and modified to dynamically update the marker's state. + +| Property | Type | Description | +| :--- | :--- | :--- | +| `id` | `String` | The unique identifier of the marker. | +| `position` | `GeoPointInterface` | The current geographical position of the marker. | +| `extra` | `Serializable?` | Custom data associated with the marker. | +| `icon` | `MarkerIconInterface?` | The marker's icon. | +| `clickable` | `Boolean` | The marker's clickability. | +| `draggable` | `Boolean` | The marker's draggability. | +| `zIndex` | `Int?` | The marker's z-index. | +| `onClick` | `OnMarkerEventHandler?` | The click event handler. | +| `onDragStart` | `OnMarkerEventHandler?` | The drag start event handler. | +| `onDrag` | `OnMarkerEventHandler?` | The drag event handler. | +| `onDragEnd` | `OnMarkerEventHandler?` | The drag end event handler. | +| `onAnimateStart` | `OnMarkerEventHandler?` | The animation start event handler. | +| `onAnimateEnd` | `OnMarkerEventHandler?` | The animation end event handler. | + +### Methods + +#### `animate` + +Starts, updates, or stops an animation on the marker. + +**Signature** +```kotlin +fun animate(animation: MarkerAnimation?) +``` + +**Parameters** +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `animation` | `MarkerAnimation?` | The animation to apply. Pass `null` to stop any currently running animation. | + +#### `copy` + +Creates a new `MarkerState` instance with specified properties updated. This is useful for immutable state updates. + +**Signature** +```kotlin +fun copy( + id: String? = this.id, + position: GeoPointInterface = this.position, + // ... other parameters +): MarkerState +``` + +**Description** +Returns a new `MarkerState` object that is a copy of the current one, with any provided parameters overriding the existing values. + +**Returns** +| Type | Description | +| :--- | :--- | +| `MarkerState` | A new `MarkerState` instance with the updated properties. | + +#### `asFlow` + +Creates a `Flow` that emits a `MarkerFingerPrint` whenever a significant property of the marker changes. This is an advanced feature for observing state changes reactively and efficiently. + +**Signature** +```kotlin +fun asFlow(): Flow +``` + +**Returns** +| Type | Description | +| :--- | :--- | +| `Flow` | A cold flow that emits a new fingerprint on state change. | + +### Example + +```kotlin +// Assuming GeoPoint and a Bitmap are available +val initialPosition = GeoPoint(40.7128, -74.0060) +val markerIconBitmap: Bitmap = getYourBitmap() + +// 1. Create a MarkerState instance +val markerState = MarkerState( + position = initialPosition, + extra = "marker-123-data", + icon = BitmapIcon( + bitmap = markerIconBitmap, + size = Size(96f, 96f), + anchor = Offset(0.5f, 1.0f) // Anchor to bottom-center + ), + draggable = true, + onClick = { state -> + println("Marker clicked! ID: ${state.id}, Extra data: ${state.extra}") + }, + onDragEnd = { state -> + println("Marker dragged to new position: ${state.position}") + } +) + +// 2. Add the marker to your map's state list +// val markers = remember { mutableStateListOf(markerState) } + +// 3. Later, you can update its properties dynamically +// This will trigger a recomposition if the state is observed +markerState.position = GeoPoint(40.7580, -73.9855) // Move the marker +markerState.clickable = false +``` + +--- + +## `OnMarkerEventHandler` + +A type alias for a function that handles marker-related events. + +### Signature + +```kotlin +typealias OnMarkerEventHandler = (MarkerState) -> Unit +``` + +### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `(MarkerState)` | `MarkerState` | The `MarkerState` instance of the marker that triggered the event. | + +--- + +## `BitmapIcon` + +A data class that defines a marker icon using an Android `Bitmap`. + +### Signature + +```kotlin +data class BitmapIcon( + val bitmap: Bitmap, + val anchor: Offset, + val size: Size, +) +``` + +### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `bitmap` | `Bitmap` | The `Bitmap` image to use for the icon. | +| `anchor` | `Offset` | The anchor point of the icon that aligns with the marker's geographical position. The coordinates are normalized from 0.0 to 1.0, where `(0,0)` is the top-left corner and `(1,1)` is the bottom-right. For example, `Offset(0.5f, 1.0f)` anchors the icon at its bottom-center. | +| `size` | `Size` | The desired display size of the icon on the map. | + +--- + +## `MarkerFingerPrint` + +A data class holding a simplified, hash-based representation of `MarkerState`. This is primarily used internally by `MarkerState.asFlow()` for efficient change detection. Developers typically do not need to interact with this class directly. \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/MarkerAnimation.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/MarkerAnimation.kt.md new file mode 100644 index 00000000..a7ab790a --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/MarkerAnimation.kt.md @@ -0,0 +1,38 @@ +# MarkerAnimation + +An enum defining the types of animations that can be applied to a marker. + +## Signature + +```kotlin +enum class MarkerAnimation +``` + +## Description + +The `MarkerAnimation` enum specifies the animation to be played when a marker is added to the map or when its state is updated. These animations provide visual feedback to the user, enhancing the user experience by drawing attention to marker placement or changes. + +## Enum Values + +| Value | Description | +| :------- | :--------------------------------------------------------------------------------------------------------- | +| `Drop` | The marker appears to drop from the top of the screen to its final position on the map. | +| `Bounce` | The marker plays a continuous bouncing animation at its position. This is useful for highlighting a marker. | + +## Example + +The following example demonstrates how to set a `Drop` animation for a new marker using a hypothetical `MarkerOptions` builder. + +```kotlin +// Create a new marker with a drop animation +val markerOptions = MarkerOptions() + .position(LatLng(40.7128, -74.0060)) + .title("New York City") + .animation(MarkerAnimation.Drop) // Apply the drop animation + +// Add the configured marker to the map +val marker = map.addMarker(markerOptions) + +// To make a marker bounce later, you might do something like this: +marker.setAnimation(MarkerAnimation.Bounce) +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/MarkerCapableInterface.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/MarkerCapableInterface.kt.md new file mode 100644 index 00000000..28ca2765 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/MarkerCapableInterface.kt.md @@ -0,0 +1,240 @@ +Of course! Here is the high-quality SDK documentation for the provided `MarkerCapableInterface` code snippet. + +# Interface `MarkerCapableInterface` + +Defines the contract for a component capable of managing and displaying markers on a map. This interface provides methods for adding, updating, and interacting with markers, as well as checking their existence. + +--- + +## `compositionMarkers` + +Adds or updates a collection of markers on the map. This function is designed for efficiently managing multiple markers at once, recomposing the map's markers to match the provided list of `MarkerState` objects. This is a suspend function and should be called from a coroutine. + +### Signature + +```kotlin +suspend fun compositionMarkers(data: List) +``` + +### Parameters + +| Parameter | Type | Description | +| :-------- | :------------------ | :----------------------------------------------------------------------- | +| `data` | `List` | A list of `MarkerState` objects, each representing a marker to be drawn. | + +### Example + +```kotlin +import kotlinx.coroutines.launch + +// Assuming 'mapController' is an instance that implements MarkerCapableInterface +// and we are in a CoroutineScope + +val marker1 = MarkerState(id = "marker-1", position = LatLng(34.0522, -118.2437)) +val marker2 = MarkerState(id = "marker-2", position = LatLng(40.7128, -74.0060)) + +// Add two markers to the map +coroutineScope.launch { + mapController.compositionMarkers(listOf(marker1, marker2)) +} +``` + +--- + +## `updateMarker` + +Updates a single existing marker on the map based on its `MarkerState`. If a marker with the same ID as the provided state exists, it will be updated. If it does not exist, it may be created. This is a suspend function and should be called from a coroutine. + +### Signature + +```kotlin +suspend fun updateMarker(state: MarkerState) +``` + +### Parameters + +| Parameter | Type | Description | +| :-------- | :------------ | :--------------------------------------------------------- | +| `state` | `MarkerState` | The state object representing the marker to be updated. | + +### Example + +```kotlin +import kotlinx.coroutines.launch + +// Assuming 'mapController' is an instance that implements MarkerCapableInterface +// and we are in a CoroutineScope + +val updatedMarkerState = MarkerState( + id = "marker-1", + position = LatLng(34.0522, -118.2437), + alpha = 0.5f // Update the marker's alpha +) + +// Update the marker on the map +coroutineScope.launch { + mapController.updateMarker(updatedMarkerState) +} +``` + +--- + +## `setOnMarkerDragStart` + +> **Deprecated:** Use the `onDragStart` lambda property on an individual `MarkerState` object for more granular, per-marker control. + +Sets a global listener that is invoked when a user begins dragging any marker on the map. + +### Signature + +```kotlin +@Deprecated("Use MarkerState.onDragStart instead.") +fun setOnMarkerDragStart(listener: OnMarkerEventHandler?) +``` + +### Parameters + +| Parameter | Type | Description | +| :--------- | :--------------------- | :----------------------------------------------------------------------- | +| `listener` | `OnMarkerEventHandler?` | The callback to invoke when a marker drag starts. Pass `null` to clear the listener. | + +--- + +## `setOnMarkerDrag` + +> **Deprecated:** Use the `onDrag` lambda property on an individual `MarkerState` object for more granular, per-marker control. + +Sets a global listener that is invoked repeatedly while a user is dragging any marker on the map. + +### Signature + +```kotlin +@Deprecated("Use MarkerState.onDrag instead.") +fun setOnMarkerDrag(listener: OnMarkerEventHandler?) +``` + +### Parameters + +| Parameter | Type | Description | +| :--------- | :--------------------- | :----------------------------------------------------------------------- | +| `listener` | `OnMarkerEventHandler?` | The callback to invoke while a marker is being dragged. Pass `null` to clear the listener. | + +--- + +## `setOnMarkerDragEnd` + +> **Deprecated:** Use the `onDragEnd` lambda property on an individual `MarkerState` object for more granular, per-marker control. + +Sets a global listener that is invoked when a user finishes dragging any marker on the map. + +### Signature + +```kotlin +@Deprecated("Use MarkerState.onDragEnd instead.") +fun setOnMarkerDragEnd(listener: OnMarkerEventHandler?) +``` + +### Parameters + +| Parameter | Type | Description | +| :--------- | :--------------------- | :----------------------------------------------------------------------- | +| `listener` | `OnMarkerEventHandler?` | The callback to invoke when a marker drag operation ends. Pass `null` to clear the listener. | + +--- + +## `setOnMarkerAnimateStart` + +> **Deprecated:** Use the `onAnimateStart` lambda property on an individual `MarkerState` object for more granular, per-marker control. + +Sets a global listener that is invoked when a marker animation begins. + +### Signature + +```kotlin +@Deprecated("Use MarkerState.onAnimateStart instead.") +fun setOnMarkerAnimateStart(listener: OnMarkerEventHandler?) +``` + +### Parameters + +| Parameter | Type | Description | +| :--------- | :--------------------- | :----------------------------------------------------------------------- | +| `listener` | `OnMarkerEventHandler?` | The callback to invoke when a marker animation starts. Pass `null` to clear the listener. | + +--- + +## `setOnMarkerAnimateEnd` + +> **Deprecated:** Use the `onAnimateEnd` lambda property on an individual `MarkerState` object for more granular, per-marker control. + +Sets a global listener that is invoked when a marker animation completes. + +### Signature + +```kotlin +@Deprecated("Use MarkerState.onAnimateEnd instead.") +fun setOnMarkerAnimateEnd(listener: OnMarkerEventHandler?) +``` + +### Parameters + +| Parameter | Type | Description | +| :--------- | :--------------------- | :----------------------------------------------------------------------- | +| `listener` | `OnMarkerEventHandler?` | The callback to invoke when a marker animation finishes. Pass `null` to clear the listener. | + +--- + +## `setOnMarkerClickListener` + +> **Deprecated:** Use the `onClick` lambda property on an individual `MarkerState` object for more granular, per-marker control. + +Sets a global listener that is invoked when a user clicks on any marker. + +### Signature + +```kotlin +@Deprecated("Use MarkerState.onClick instead.") +fun setOnMarkerClickListener(listener: OnMarkerEventHandler?) +``` + +### Parameters + +| Parameter | Type | Description | +| :--------- | :--------------------- | :----------------------------------------------------------------------- | +| `listener` | `OnMarkerEventHandler?` | The callback to invoke when a marker is clicked. Pass `null` to clear the listener. | + +--- + +## `hasMarker` + +Checks if a specific marker, identified by its `MarkerState`, currently exists on the map. The check is typically based on the marker's unique ID. + +### Signature + +```kotlin +fun hasMarker(state: MarkerState): Boolean +``` + +### Parameters + +| Parameter | Type | Description | +| :-------- | :------------ | :------------------------------------------------------ | +| `state` | `MarkerState` | The state object of the marker to check for. | + +### Returns + +`Boolean` - Returns `true` if a marker with the same ID as the provided state exists, `false` otherwise. + +### Example + +```kotlin +// Assuming 'mapController' is an instance that implements MarkerCapableInterface + +val markerToCheck = MarkerState(id = "marker-1") + +if (mapController.hasMarker(markerToCheck)) { + println("Marker with ID 'marker-1' exists on the map.") +} else { + println("Marker with ID 'marker-1' does not exist.") +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/MarkerCollector.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/MarkerCollector.kt.md new file mode 100644 index 00000000..9137d331 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/MarkerCollector.kt.md @@ -0,0 +1,68 @@ +# MarkerCollector + +### Signature + +```kotlin +class MarkerCollector( + updateDebounce: Duration = Settings.Default.composeEventDebounce, + scope: CoroutineScope = CoroutineScope(Dispatchers.Main.immediate), +) : ChildCollector +``` + +### Description + +The `MarkerCollector` is a specialized class responsible for collecting and managing a group of `MarkerState` objects. It is designed to be used within a map component to handle the state of multiple markers efficiently. + +It functions as a `ChildCollector` by delegating its implementation to `ChildCollectorImpl`. This allows it to collect marker states declared as its children and expose them as a debounced flow. This is particularly useful for optimizing performance by batching rapid updates to marker properties or positions, reducing the number of recompositions or map redraws. + +### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `updateDebounce` | `Duration` | The time duration to wait after the last change before emitting an update. This helps to batch rapid updates and improve performance. Defaults to `Settings.Default.composeEventDebounce`. | +| `scope` | `CoroutineScope` | The coroutine scope in which the collection and debouncing operations will run. Defaults to a scope on the main thread (`CoroutineScope(Dispatchers.Main.immediate)`). | + +### Example + +The `MarkerCollector` is typically instantiated and provided to a map component. Markers are then declaratively added within the map's content, and their states are automatically managed by the collector. + +```kotlin +import androidx.compose.runtime.Composable +import com.mapconductor.core.marker.MarkerCollector +import kotlin.time.Duration.Companion.milliseconds + +// 1. Instantiate the MarkerCollector, optionally overriding default parameters. +val customMarkerCollector = MarkerCollector( + updateDebounce = 200.milliseconds +) + +// A hypothetical Map composable that uses the collector. +@Composable +fun MyMapComponent(markerCollector: MarkerCollector) { + // The map would internally use the collector to listen for marker state changes. + // ... +} + +// 2. Use the collector in your UI. +@Composable +fun MyMapScreen() { + // The collector is passed to the map component. + MyMapComponent(markerCollector = customMarkerCollector) { + // In a real-world scenario, you would declare Marker composables here. + // Their state would be implicitly registered with the `customMarkerCollector`. + + /* + Example with hypothetical Marker composable: + + Marker( + position = LatLng(34.0522, -118.2437), + title = "Los Angeles" + ) + Marker( + position = LatLng(40.7128, -74.0060), + title = "New York City" + ) + */ + } +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/MarkerCompose.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/MarkerCompose.kt.md new file mode 100644 index 00000000..6d292ca6 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/MarkerCompose.kt.md @@ -0,0 +1,172 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +*** + +### `Marker` + +This Composable declaratively adds a single marker to the map. It is the most convenient way to create a marker when you have its individual properties. + +This function must be called from within the `MapViewScope`, such as the content lambda of a `MapView` composable. + +#### Signature +```kotlin +@Composable +fun MapViewScope.Marker( + position: GeoPointInterface, + id: String? = null, + zIndex: Int? = null, + clickable: Boolean = true, + draggable: Boolean = false, + icon: MarkerIconInterface? = null, + animation: MarkerAnimation? = null, + extra: Serializable? = null, + onClick: OnMarkerEventHandler? = null, + onDragStart: OnMarkerEventHandler? = null, + onDrag: OnMarkerEventHandler? = null, + onDragEnd: OnMarkerEventHandler? = null, + onAnimateStart: OnMarkerEventHandler? = null, + onAnimateEnd: OnMarkerEventHandler? = null, +) +``` + +#### Description +Creates and manages a single map marker. The marker's lifecycle is tied to the Composable's lifecycle. When this Composable enters the composition, the marker is added to the map. When it leaves, the marker is removed. + +#### Parameters +| Parameter | Type | Description | +|---|---|---| +| `position` | `GeoPointInterface` | **Required.** The geographic coordinates where the marker will be placed on the map. | +| `id` | `String?` | A unique identifier for the marker. If `null`, a unique ID will be generated. Providing a stable ID is recommended for performance and state management. | +| `zIndex` | `Int?` | The z-index of the marker, which determines its drawing order. Markers with higher z-index values are drawn on top of those with lower values. | +| `clickable` | `Boolean` | If `true`, the marker will be clickable and can receive `onClick` events. Defaults to `true`. | +| `draggable` | `Boolean` | If `true`, the user can drag the marker to a new position. Defaults to `false`. | +| `icon` | `MarkerIconInterface?` | The visual representation (icon) of the marker. If `null`, the map's default marker icon will be used. | +| `animation` | `MarkerAnimation?` | An animation to apply to the marker, such as `DROP` or `PULSE`. | +| `extra` | `Serializable?` | A serializable object containing any extra data you want to associate with the marker. | +| `onClick` | `OnMarkerEventHandler?` | A callback lambda that is invoked when the user clicks on the marker. The marker must be `clickable`. | +| `onDragStart` | `OnMarkerEventHandler?` | A callback lambda invoked when the user starts dragging the marker. The marker must be `draggable`. | +| `onDrag` | `OnMarkerEventHandler?` | A callback lambda invoked repeatedly as the user drags the marker. | +| `onDragEnd` | `OnMarkerEventHandler?` | A callback lambda invoked when the user finishes dragging the marker. | +| `onAnimateStart` | `OnMarkerEventHandler?` | A callback lambda invoked when a marker animation starts. | +| `onAnimateEnd` | `OnMarkerEventHandler?` | A callback lambda invoked when a marker animation ends. | + +#### Returns +This is a Composable function and does not return any value. + +#### Example +```kotlin +MapView { + // A simple, clickable marker at a specific location + Marker( + position = GeoPoint(40.7128, -74.0060), + id = "nyc-marker", + onClick = { markerState -> + println("Clicked on marker: ${markerState.id}") + } + ) + + // A draggable marker with a custom icon and z-index + Marker( + position = GeoPoint(34.0522, -118.2437), + id = "la-marker", + draggable = true, + zIndex = 10, + icon = MarkerIconFactory.fromResource(R.drawable.custom_pin), + onDragEnd = { markerState -> + println("New position for ${markerState.id}: ${markerState.position}") + } + ) +} +``` + +*** + +### `Markers` + +A high-performance Composable for efficiently adding and managing a large number of markers. + +#### Signature +```kotlin +@Composable +fun MapViewScope.Markers(states: List) +``` + +#### Description +This function is designed to render thousands of markers without the significant composition overhead that would occur from using one `Marker` Composable for each item. It performs batched updates in a single effect, making it the ideal choice for displaying large, dynamic datasets on the map. + +When the `states` list changes, this function efficiently calculates the difference and applies the necessary add, remove, or update operations on the map. + +#### Parameters +| Parameter | Type | Description | +|---|---|---| +| `states` | `List` | **Required.** A list of `MarkerState` objects, where each object defines a single marker and its properties. | + +#### Returns +This is a Composable function and does not return any value. + +#### Example +```kotlin +val locations = remember { + // Imagine this list is fetched from a remote API and contains thousands of items + listOf( + LocationData("id1", GeoPoint(48.8566, 2.3522)), + LocationData("id2", GeoPoint(51.5074, -0.1278)), + LocationData("id3", GeoPoint(35.6895, 139.6917)) + ) +} + +// Create a list of MarkerState objects from your data +val markerStates = locations.map { location -> + MarkerState( + id = location.id, + position = location.geoPoint, + clickable = true + ) +} + +MapView { + // Render all markers efficiently + Markers(states = markerStates) +} +``` + +*** + +### `Marker` (State-based) + +A lower-level Composable that adds a single marker to the map using a `MarkerState` object. + +#### Signature +```kotlin +@Composable +fun MapViewScope.Marker(state: MarkerState) +``` + +#### Description +This version of the `Marker` Composable takes a pre-constructed `MarkerState` object. It is useful when you are managing the state of your markers explicitly, for instance, within a `ViewModel` or a `remember` block. The marker's lifecycle is tied to this Composable's presence in the composition. + +#### Parameters +| Parameter | Type | Description | +|---|---|---| +| `state` | `MarkerState` | **Required.** The state object that defines all properties of the marker, including its position, icon, and event handlers. | + +#### Returns +This is a Composable function and does not return any value. + +#### Example +```kotlin +MapView { + // Create and remember a MarkerState object + val markerState = remember { + MarkerState( + id = "paris-marker", + position = GeoPoint(48.8566, 2.3522), + draggable = true, + onClick = { /* ... */ } + ) + } + + // Pass the state object to the Marker composable + Marker(state = markerState) +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/MarkerEntity.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/MarkerEntity.kt.md new file mode 100644 index 00000000..722ac5f1 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/MarkerEntity.kt.md @@ -0,0 +1,93 @@ +Of course. Here is the high-quality SDK documentation for the provided code snippet. + +--- + +### `MarkerEntityInterface` + +#### Signature + +```kotlin +interface MarkerEntityInterface +``` + +#### Description + +Defines the contract for a marker entity within the map system. This generic interface acts as a wrapper around a platform-specific marker object (`ActualMarker`), providing a consistent way to manage its state and lifecycle. + +#### Properties + +| Property | Type | Description | +| :------------ | :-------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------- | +| `marker` | `ActualMarker?` | The underlying, platform-specific marker object (e.g., a Google Maps `Marker`). It is `null` if the marker has not been created or has been removed. | +| `state` | `MarkerState` | An immutable object representing the desired state of the marker, including its position, icon, anchor, etc. | +| `fingerPrint` | `MarkerFingerPrint` | A unique identifier derived from the `state`. It is used internally to efficiently detect changes and optimize rendering. | +| `visible` | `Boolean` | Controls the visibility of the marker on the map. Setting this to `false` hides the marker, and `true` shows it. | +| `isRendered` | `Boolean` | A flag indicating whether the marker is currently rendered on the map canvas. | + +--- + +### `MarkerEntity` + +#### Signature + +```kotlin +class MarkerEntity( + override var marker: ActualMarker?, + override val state: MarkerState, + override var visible: Boolean = true, + override var isRendered: Boolean = false, +) : MarkerEntityInterface +``` + +#### Description + +The default implementation of `MarkerEntityInterface`. This class encapsulates all the information required to manage a single marker on the map, including its platform-specific instance, its desired state, and its current visibility and render status. + +#### Parameters + +| Parameter | Type | Description | +| :----------- | :-------------- | :------------------------------------------------------------------------------------------------------------------------------------- | +| `marker` | `ActualMarker?` | The actual marker object from the underlying map SDK. Can be `null` if the marker hasn't been instantiated yet. | +| `state` | `MarkerState` | The state object containing all configuration for the marker (e.g., position, icon). The `fingerPrint` is automatically generated from this state. | +| `visible` | `Boolean` | (Optional) The initial visibility of the marker. Defaults to `true`. | +| `isRendered` | `Boolean` | (Optional) The initial rendered state of the marker. Defaults to `false`. | + +#### Example + +This example demonstrates how to create and use a `MarkerEntity`. We'll assume the existence of `MapboxMarker` as our platform-specific marker type and a `MarkerState` object. + +```kotlin +// Assume these classes exist for context +// data class LatLng(val latitude: Double, val longitude: Double) +// class MapboxMarker { /* Platform-specific marker implementation */ } +// data class MarkerState(val position: LatLng, val title: String) { +// fun fingerPrint(): MarkerFingerPrint = MarkerFingerPrint(this.hashCode().toString()) +// } +// data class MarkerFingerPrint(val id: String) + +// 1. Define the state for our marker +val markerState = MarkerState( + position = LatLng(40.7128, -74.0060), + title = "New York City" +) + +// 2. Create a MarkerEntity instance. Initially, the platform marker is null. +val markerEntity = MarkerEntity( + marker = null, + state = markerState, + visible = true +) + +// 3. Access its properties +println("Is marker visible? ${markerEntity.visible}") // Output: Is marker visible? true +println("Marker fingerprint: ${markerEntity.fingerPrint.id}") // Output: A hash-based ID + +// After the map renderer creates the actual marker, it can be assigned. +val actualMapboxMarker = MapboxMarker() +markerEntity.marker = actualMapboxMarker +markerEntity.isRendered = true + +// You can later update the visibility +markerEntity.visible = false +println("Is marker visible now? ${markerEntity.visible}") // Output: Is marker visible now? false +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/MarkerEventControllerInterface.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/MarkerEventControllerInterface.kt.md new file mode 100644 index 00000000..86f31455 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/MarkerEventControllerInterface.kt.md @@ -0,0 +1,52 @@ +# MarkerEventControllerInterface + +The `MarkerEventControllerInterface` provides a generic contract for managing and handling events associated with a map marker. + +## Signature + +```java +interface MarkerEventControllerInterface +``` + +## Description + +This interface defines a standardized way to control marker-related events, abstracting the specific implementation details of the underlying map provider. By using a generic type `ActualMarker`, it allows for a consistent event handling architecture regardless of whether the map is powered by Google Maps, Mapbox, or another provider. + +Implementations of this interface are responsible for attaching and detaching event listeners to the native marker object. + +## Type Parameters + +| Parameter | Description | +| :------------- | :------------------------------------------------------------------------------------------------------------------------------------- | +| `ActualMarker` | The concrete class of the marker object from the underlying map provider's SDK (e.g., `com.google.android.gms.maps.model.Marker`). | + +## Example + +While the interface itself cannot be instantiated, here is a conceptual example of how it might be implemented for a specific map provider like Google Maps. + +```java +import com.google.android.gms.maps.model.Marker; +import com.mapconductor.core.marker.MarkerEventControllerInterface; + +// A concrete implementation for Google Maps markers. +public class GoogleMapMarkerEventController implements MarkerEventControllerInterface { + + private final Marker googleMapMarker; + + public GoogleMapMarkerEventController(Marker googleMapMarker) { + this.googleMapMarker = googleMapMarker; + } + + // In a real-world scenario, this class would contain methods + // to add and remove listeners for clicks, drags, etc., + // interacting with the 'googleMapMarker' instance. + + public void setOnClickListener(OnMarkerClickListener listener) { + // Implementation to set a click listener on the Google Map Marker. + } + + public void setOnDragListener(OnMarkerDragListener listener) { + // Implementation to set a drag listener. + } +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/MarkerIcon.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/MarkerIcon.kt.md new file mode 100644 index 00000000..eaa8d091 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/MarkerIcon.kt.md @@ -0,0 +1,128 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet, formatted in Markdown. + +*** + +## Marker Icon API Reference + +This document provides detailed documentation for the marker icon classes and interfaces used for creating custom map markers. + +### `MarkerIconInterface` + +An interface that defines the essential properties and behaviors for a map marker icon. All custom marker icon classes must implement this interface. + +#### **Properties** + +| Property | Type | Description | +| --- | --- | --- | +| `scale` | `Float` | The scaling factor for the icon. | +| `anchor` | `Offset` | The point on the icon (relative to its top-left corner) that is anchored to the geographical coordinate on the map. | +| `iconSize` | `Dp` | The size of the icon in density-independent pixels (Dp). | +| `infoAnchor` | `Offset` | The anchor point for an info window, relative to the icon's top-left corner. | +| `debug` | `Boolean` | A flag to enable debug mode. If `true`, visual aids like a bounding box may be rendered. | + +#### **Functions** + +##### `toBitmapIcon()` + +Converts the marker icon definition into a `BitmapIcon` instance. + +**Signature** +```kotlin +fun toBitmapIcon(): BitmapIcon +``` + +**Description** + +This function is responsible for processing the marker icon's properties and generating a `BitmapIcon`, which is a concrete, renderable bitmap representation of the icon. + +**Returns** + +| Type | Description | +| --- | --- | +| `BitmapIcon` | The generated `BitmapIcon` object. | + +--- + +### `AbstractMarkerIcon` + +An abstract base class that provides a partial implementation of `MarkerIconInterface`. It serves as a convenient starting point for creating custom marker icons. + +**Signature** +```kotlin +abstract class AbstractMarkerIcon : MarkerIconInterface +``` + +**Description** + +This class implements `MarkerIconInterface` and provides a helper function for drawing a debug frame. Subclasses are required to provide concrete implementations for the abstract properties defined in the interface. + +#### **Protected Functions** + +##### `drawDebugFrame()` + +Draws a rectangular border on a `Canvas`, which is useful for debugging the icon's boundaries. + +**Signature** +```kotlin +protected fun drawDebugFrame(canvas: Canvas) +``` + +**Description** + +This function draws a 1-pixel wide black stroke around the edges of the provided `Canvas`. It is typically called when the `debug` property is `true` to help visualize the icon's frame. + +**Parameters** + +| Parameter | Type | Description | +| --- | --- | --- | +| `canvas` | `Canvas` | The canvas on which the debug frame will be drawn. | + +--- + +### `AndroidDrawableIcon` + +An abstract class for creating marker icons from an Android `Drawable` resource. + +**Signature** +```kotlin +abstract class AndroidDrawableIcon(val drawable: Drawable) : AbstractMarkerIcon() +``` + +**Description** + +This class extends `AbstractMarkerIcon` and is designed to work with Android `Drawable` objects. It includes logic to convert a `Drawable` into a `Bitmap`, which can then be used as a map marker. + +#### **Parameters** + +| Parameter | Type | Description | +| --- | --- | --- | +| `drawable` | `Drawable` | The `Drawable` resource to be used for the marker icon. | + +#### **Protected Functions** + +##### `toBitmap()` + +Converts a `Drawable` into a `Bitmap` of a specified size. + +**Signature** +```kotlin +protected fun toBitmap(drawable: Drawable, width: Int, height: Int): Bitmap +``` + +**Description** + +This function creates a `Bitmap` from a `Drawable`. It contains an optimization for `BitmapDrawable` instances: if debug mode is disabled, it scales the underlying bitmap directly for better performance. For all other `Drawable` types, or when debug mode is enabled, it creates a new `Bitmap` and draws the `Drawable` onto it. If the `debug` property is `true`, it will also render a debug frame around the bitmap. + +**Parameters** + +| Parameter | Type | Description | +| --- | --- | --- | +| `drawable` | `Drawable` | The `Drawable` to be converted. | +| `width` | `Int` | The target width of the output `Bitmap` in pixels. | +| `height` | `Int` | The target height of the output `Bitmap` in pixels. | + +**Returns** + +| Type | Description | +| --- | --- | +| `Bitmap` | The resulting `Bitmap` representation of the `Drawable`. | \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/MarkerIngestionEngine.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/MarkerIngestionEngine.kt.md new file mode 100644 index 00000000..2cacf077 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/MarkerIngestionEngine.kt.md @@ -0,0 +1,119 @@ +Excellent. Here is the high-quality SDK documentation for the provided code snippet, formatted in Markdown. + +*** + +# MarkerIngestionEngine + +Provides shared, low-level logic for ingesting and diffing marker data. + +## Description + +The `MarkerIngestionEngine` is a singleton object responsible for processing marker data updates. It compares a new list of `MarkerState` objects against the current state managed by a `MarkerManager`. By performing this diff, it calculates the necessary additions, updates, and removals. + +The engine delegates the actual rendering operations (creating, modifying, or deleting marker visuals on the map) to a platform-specific `MarkerOverlayRendererInterface`. It also contains the core logic for determining whether a marker should be rendered natively or as part of a more performant tile layer, based on provided configuration and rules. + +This centralized engine ensures consistent marker handling behavior across different map SDK implementations. + +## Nested Classes + +### `data class Result` + +A data class that encapsulates the outcome of the `ingest` operation, specifically concerning the state of tiled markers. + +| Property | Type | Description | +| ------------------ | --------- | ------------------------------------------------------------------------------------------------------- | +| `tiledDataChanged` | `Boolean` | Is `true` if the set of tiled markers was modified (e.g., a marker was added to, or removed from, tiling). | +| `hasTiledMarkers` | `Boolean` | Is `true` if there are any markers currently designated as tiled after the operation completes. | + +## Functions + +### ingest`` + +Asynchronously processes a list of `MarkerState` objects to update the markers displayed on the map. + +**Signature** + +```kotlin +suspend fun ingest( + data: List, + markerManager: MarkerManager, + renderer: MarkerOverlayRendererInterface, + defaultMarkerIcon: BitmapIcon, + tilingEnabled: Boolean, + tiledMarkerIds: MutableSet, + shouldTile: (MarkerState) -> Boolean, +): Result +``` + +**Description** + +This function is the core of the marker update process. It performs a diff operation between the incoming `data` list and the current markers tracked by the `markerManager`. It identifies which markers are new, which have been updated, and which should be removed. + +Based on the `tilingEnabled` flag and the `shouldTile` predicate, it also determines whether a marker should be rendered natively or as part of a tile layer. The function then uses the provided `renderer` to apply these changes to the map and updates the `markerManager` and `tiledMarkerIds` set to reflect the new state. + +As a `suspend` function, it must be called from a coroutine or another suspend function. + +**Parameters** + +| Parameter | Type | Description | +| ------------------- | ---------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | +| `data` | `List` | The complete, desired list of marker states to be displayed on the map. | +| `markerManager` | `MarkerManager` | The manager that holds the current state of all marker entities. This function will update it to reflect the new state. | +| `renderer` | `MarkerOverlayRendererInterface` | The platform-specific renderer responsible for adding, changing, and removing the actual marker objects (`ActualMarker`) on the map view. | +| `defaultMarkerIcon` | `BitmapIcon` | The fallback icon to use for markers that do not have a specific icon defined in their `MarkerState`. | +| `tilingEnabled` | `Boolean` | A master switch to enable or disable the marker tiling functionality. If `false`, all markers will be rendered natively. | +| `tiledMarkerIds` | `MutableSet` | A mutable set containing the IDs of markers that are currently tiled. This function will read from and write to this set. | +| `shouldTile` | `(MarkerState) -> Boolean` | A predicate function called for each marker to decide if it should be tiled. This is only invoked if `tilingEnabled` is `true`. | + +**Returns** + +A `Result` object containing two booleans: +* `tiledDataChanged`: Indicates if the composition of the tiled marker set has changed. +* `hasTiledMarkers`: Indicates if any markers are designated for tiling after the operation. + +**Example** + +This example demonstrates how to call the `ingest` function within a coroutine scope. + +```kotlin +import kotlinx.coroutines.runBlocking + +// Assume these are defined elsewhere and implemented for a specific map SDK +// val markerManager: MarkerManager = ... +// val renderer: MarkerOverlayRendererInterface = ... +// val defaultIcon: BitmapIcon = ... +// val newMarkerStates: List = ... + +// A mutable set to track which markers are part of a tile layer +val tiledMarkerIds = mutableSetOf() + +fun processMarkerUpdates() = runBlocking { + // Define the logic for when a marker should be tiled. + // For example, tile markers that are beyond a certain zoom level or have a specific property. + val shouldTilePredicate: (MarkerState) -> Boolean = { markerState -> + // Example logic: tile if a custom property 'isLowImportance' is true + markerState.customProperties["isLowImportance"] as? Boolean ?: false + } + + // Call the ingestion engine + val result = MarkerIngestionEngine.ingest( + data = newMarkerStates, + markerManager = markerManager, + renderer = renderer, + defaultMarkerIcon = defaultIcon, + tilingEnabled = true, + tiledMarkerIds = tiledMarkerIds, + shouldTile = shouldTilePredicate + ) + + // The engine has updated the markerManager and renderer. + // Now, we can use the result to trigger other actions, like refreshing a tile layer. + if (result.tiledDataChanged) { + println("Tiled marker data has changed. Refreshing tile layer...") + // ... logic to bust tile cache and refresh the raster layer + } + + println("Ingestion complete. Tiled markers exist: ${result.hasTiledMarkers}") + println("Current tiled marker IDs: $tiledMarkerIds") +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/MarkerManager.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/MarkerManager.kt.md new file mode 100644 index 00000000..f60a7f3c --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/MarkerManager.kt.md @@ -0,0 +1,299 @@ +Of course! Here is the high-quality SDK documentation for the provided `MarkerManager` class. + +# MarkerManager SDK Documentation + +## `MarkerManagerStats` Data Class + +Provides memory usage statistics for a `MarkerManager` instance, useful for debugging and performance optimization. + +### Signature +```kotlin +data class MarkerManagerStats( + val entityCount: Int, + val hasSpatialIndex: Boolean, + val spatialIndexInitialized: Boolean, + val estimatedMemoryKB: Long, +) +``` + +### Parameters + +| Parameter | Type | Description | +| ------------------------- | ------- | --------------------------------------------------------------------------- | +| `entityCount` | `Int` | The total number of marker entities currently stored in the manager. | +| `hasSpatialIndex` | `Boolean` | `true` if the spatial index has been created, `false` otherwise. | +| `spatialIndexInitialized` | `Boolean` | An alias for `hasSpatialIndex`. | +| `estimatedMemoryKB` | `Long` | A rough estimation of the memory consumed by the manager in kilobytes (KB). | + +--- + +## `MarkerManager` Class + +A generic, thread-safe manager for collections of marker entities. It provides efficient storage and spatial querying capabilities. + +### Description + +`MarkerManager` is designed to handle a large number of markers on a map. It employs a performance optimization strategy: for small datasets (fewer than `minMarkerCount`), it uses simple brute-force searches. For larger datasets, it automatically creates and uses a spatial index (`HexCellRegistry`) to perform fast spatial queries like finding the nearest marker or markers within a specific area. + +This lazy initialization of the spatial index saves memory for use cases with a small number of markers. All operations that modify or access the internal collections are thread-safe. + +The class is generic, where `ActualMarker` represents the concrete marker type of the specific map provider (e.g., `GoogleMap.Marker`, `Mapbox.Marker`). + +### Signature + +```kotlin +open class MarkerManager( + protected val geocell: HexGeocellInterface, + val minMarkerCount: Int, +) +``` + +### Parameters + +| Parameter | Type | Description | +| ---------------- | --------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | +| `geocell` | `HexGeocellInterface` | The hexagonal geocelling system to use for the spatial index. | +| `minMarkerCount` | `Int` | The threshold for creating the spatial index. Spatial queries will use the index only if the number of entities exceeds this value. | + +--- + +## Methods + +### `defaultManager` +A static factory method to create a `MarkerManager` instance with sensible default settings. + +#### Signature +```kotlin +companion object { + fun defaultManager( + geocell: HexGeocellInterface? = null, + minMarkerCount: Int = 2000, + ): MarkerManager +} +``` + +#### Parameters +| Parameter | Type | Description | +| ---------------- | --------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | +| `geocell` | `HexGeocellInterface?` | The geocelling system to use. If `null`, `HexGeocell.defaultGeocell()` is used. | +| `minMarkerCount` | `Int` | The entity count threshold to trigger the use of the spatial index. Defaults to `2000`. | + +#### Returns +A new `MarkerManager` instance. + +#### Example +```kotlin +// Create a manager with default settings +val markerManager = MarkerManager.defaultManager() +``` + +### `lock` +Acquires a write lock on the manager, preventing other threads from reading or writing. This is useful for performing bulk operations atomically. + +> **Note:** You must call `unlock()` to release the lock. Failing to do so will result in a deadlock. + +#### Signature +```kotlin +fun lock() +``` + +### `unlock` +Releases the write lock acquired by `lock()`. + +#### Signature +```kotlin +fun unlock() +``` + +### `registerEntity` +Adds a new marker entity to the manager. The operation is thread-safe. If the spatial index has been initialized, the new entity is also added to it. + +#### Signature +```kotlin +open fun registerEntity(entity: MarkerEntityInterface) +``` + +#### Parameters +| Parameter | Type | Description | +| --------- | ----------------------------------- | ----------------------------------------- | +| `entity` | `MarkerEntityInterface` | The marker entity to add to the manager. | + +### `updateEntity` +Updates an existing entity in the manager. If the entity does not exist, it will be added. This operation is thread-safe. + +#### Signature +```kotlin +open fun updateEntity(entity: MarkerEntityInterface) +``` + +#### Parameters +| Parameter | Type | Description | +| --------- | ----------------------------------- | ----------------------------------------- | +| `entity` | `MarkerEntityInterface` | The marker entity to update or register. | + +### `removeEntity` +Removes a marker entity from the manager by its unique ID. + +#### Signature +```kotlin +open fun removeEntity(id: String): MarkerEntityInterface? +``` + +#### Parameters +| Parameter | Type | Description | +| --------- | -------- | ------------------------------------ | +| `id` | `String` | The unique ID of the entity to remove. | + +#### Returns +The removed `MarkerEntityInterface` if it was found, otherwise `null`. + +### `getEntity` +Retrieves a marker entity by its unique ID. + +#### Signature +```kotlin +open fun getEntity(id: String): MarkerEntityInterface? +``` + +#### Parameters +| Parameter | Type | Description | +| --------- | -------- | ------------------------------------- | +| `id` | `String` | The unique ID of the entity to retrieve. | + +#### Returns +The `MarkerEntityInterface` if found, otherwise `null`. + +### `hasEntity` +Checks if an entity with the specified ID exists in the manager. + +#### Signature +```kotlin +open fun hasEntity(id: String): Boolean +``` + +#### Parameters +| Parameter | Type | Description | +| --------- | -------- | ----------------------------------------- | +| `id` | `String` | The unique ID of the entity to check for. | + +#### Returns +`true` if the entity exists, `false` otherwise. + +### `findNearest` +Finds the marker entity closest to a given geographic position. This method automatically chooses the most efficient search strategy. + +- **Spatial Index:** If the number of entities is greater than `minMarkerCount`, a spatial index is used for a fast search. +- **Brute Force:** If the number of entities is small, a simple and efficient brute-force search is performed. + +#### Signature +```kotlin +open fun findNearest(position: GeoPointInterface): MarkerEntityInterface? +``` + +#### Parameters +| Parameter | Type | Description | +| ---------- | ------------------- | ----------------------------------------- | +| `position` | `GeoPointInterface` | The geographic point to search around. | + +#### Returns +The nearest `MarkerEntityInterface` or `null` if the manager is empty. + +### `findMarkersInBounds` +Finds all marker entities that fall within a given geographic bounding box. This method also uses the spatial index for large datasets and brute-force for smaller ones to ensure optimal performance. + +#### Signature +```kotlin +fun findMarkersInBounds( + bounds: com.mapconductor.core.features.GeoRectBounds, +): List> +``` + +#### Parameters +| Parameter | Type | Description | +| --------- | -------------------------------------------- | ----------------------------------------- | +| `bounds` | `com.mapconductor.core.features.GeoRectBounds` | The rectangular geographic area to search within. | + +#### Returns +A `List` of all `MarkerEntityInterface` instances found inside the bounds. + +### `allEntities` +Returns a snapshot of all marker entities currently in the manager. + +#### Signature +```kotlin +open fun allEntities(): List> +``` + +#### Returns +A `List` containing all `MarkerEntityInterface` instances. + +### `clear` +Removes all entities from the manager and clears the spatial index if it exists. + +#### Signature +```kotlin +open fun clear() +``` + +### `destroy` +Releases all resources held by the `MarkerManager`, including clearing all entities and destroying the spatial index. + +> **CRITICAL:** This method **must** be called when the `MarkerManager` is no longer needed to prevent memory leaks. After `destroy()` is called, any other method call will throw an `IllegalStateException`. + +#### Signature +```kotlin +open fun destroy() +``` + +### `getMemoryStats` +Retrieves statistics about the current memory usage and state of the manager. + +#### Signature +```kotlin +fun getMemoryStats(): MarkerManagerStats +``` + +#### Returns +A `MarkerManagerStats` object containing details about entity count, spatial index state, and estimated memory usage. + +### `metersPerPixel` +A utility function to calculate the approximate distance in meters that one pixel represents on the map at a given latitude, zoom level, and screen density. + +#### Signature +```kotlin +open fun metersPerPixel( + position: GeoPointInterface, + zoom: Double, + pixels: Double, + tileSize: Int = 256, +): Double +``` + +#### Parameters +| Parameter | Type | Description | +| ---------- | ------------------- | ------------------------------------------------------------------------ | +| `position` | `GeoPointInterface` | The geographic position (latitude is used for cosine correction). | +| `zoom` | `Double` | The current zoom level of the map. | +| `pixels` | `Double` | The number of pixels for which to calculate the distance. | +| `tileSize` | `Int` | The size of the map tiles in pixels (usually 256 or 512). Defaults to 256. | + +#### Returns +The calculated distance in meters. + +### `findByIdPrefix` +Searches the spatial index for `HexCell`s that match a given ID prefix. + +> **Note:** This method will only return results if the spatial index has been initialized (i.e., when the entity count has exceeded `minMarkerCount`). Otherwise, it returns an empty list. + +#### Signature +```kotlin +open fun findByIdPrefix(prefix: String): List +``` + +#### Parameters +| Parameter | Type | Description | +| --------- | -------- | ----------------------------------------- | +| `prefix` | `String` | The `HexCell` ID prefix to search for. | + +#### Returns +A `List` of matching `HexCell`s, or an empty list if the index is not active or no matches are found. \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/MarkerOverlay.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/MarkerOverlay.kt.md new file mode 100644 index 00000000..338e16df --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/MarkerOverlay.kt.md @@ -0,0 +1,88 @@ +# SDK Documentation + +This document provides detailed information about the `LocalMarkerCollector` and `MarkerOverlay` components, which are essential for managing and rendering markers on the map. + +## `LocalMarkerCollector` + +### Signature + +```kotlin +val LocalMarkerCollector: ProvidableCompositionLocal> +``` + +### Description + +A `CompositionLocal` used to provide a `ChildCollector` instance down the Composable tree. This mechanism allows individual `Marker` composables to register their state with a parent `MapView` component. + +It is crucial that any composable accessing `LocalMarkerCollector` is a descendant of a `MapView` that provides a value for it. Failure to do so will result in an `IllegalStateException` with the message: "Marker must be under the ". + +### Example + +While you typically won't interact with `LocalMarkerCollector` directly as an end-user, it is used internally by `Marker` composables to register with the map. + +```kotlin +// Hypothetical internal usage within a Marker composable +@Composable +fun Marker( + // ... marker properties + state: MarkerState +) { + // Access the collector provided by a parent MapView + val collector = LocalMarkerCollector.current + + // Use the collector to add or update the marker's state in the map's collection + LaunchedEffect(state) { + collector.add(state) + } +} +``` + +--- + +## `MarkerOverlay` + +### Signature + +```kotlin +class MarkerOverlay( + override val flow: StateFlow>, +) : MapOverlayInterface +``` + +### Description + +`MarkerOverlay` is an implementation of the `MapOverlayInterface` specifically designed for handling map markers. It acts as a bridge between the declarative marker state management system and the imperative map controller. It observes a `StateFlow` of marker data and uses a `MarkerCapableInterface` to instruct the map controller to render the markers on the map view. + +### Constructor Parameters + +| Parameter | Type | Description | +|-----------|------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------| +| `flow` | `StateFlow>` | A state flow that emits the current collection of markers to be displayed. The map's key is a unique identifier for the marker, and the value is the `MarkerState` object containing its properties. | + +### Methods + +#### `render` + +##### Signature + +```kotlin +override suspend fun render( + data: MutableMap, + controller: MapViewControllerInterface, +) +``` + +##### Description + +This function is called by the map's rendering engine to update the visual representation of markers. It receives the latest marker data and the map controller. The method checks if the controller is capable of handling markers (by implementing `MarkerCapableInterface`) and then delegates the rendering task to the controller's `compositionMarkers` method. + +##### Parameters + +| Parameter | Type | Description | +|--------------|------------------------------|----------------------------------------------------------------------------------------------------------------------------------------| +| `data` | `MutableMap` | The most recent map of marker states to be rendered on the map. | +| `controller` | `MapViewControllerInterface` | The map view controller responsible for interacting with the underlying map SDK. It must conform to `MarkerCapableInterface` to render markers. | + +##### Returns + +This is a `suspend` function that does not return a value. \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/MarkerOverlayRendererInterface.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/MarkerOverlayRendererInterface.kt.md new file mode 100644 index 00000000..26ad9d35 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/MarkerOverlayRendererInterface.kt.md @@ -0,0 +1,193 @@ +Excellent. Here is the high-quality SDK documentation for the provided Kotlin code snippet. + +--- + +# Interface `MarkerOverlayRendererInterface` + +## Description + +The `MarkerOverlayRendererInterface` defines a contract for rendering and managing marker overlays on a map. It provides a standardized API for handling the lifecycle of markers, including their addition, modification, removal, and animation. + +This interface is designed to be implemented by platform-specific renderers (e.g., for Google Maps, Mapbox, or other map SDKs), abstracting the underlying map implementation details from the core logic. + +The generic type `ActualMarker` represents the native marker object provided by the specific map SDK being used (e.g., `com.google.android.gms.maps.model.Marker`). + +## Properties + +### `animateStartListener` + +**Signature:** `var animateStartListener: OnMarkerEventHandler?` + +**Description:** +An optional event handler that is invoked when a marker animation begins. This listener can be used to trigger actions at the start of an animation. + +### `animateEndListener` + +**Signature:** `var animateEndListener: OnMarkerEventHandler?` + +**Description:** +An optional event handler that is invoked when a marker animation completes. This is useful for synchronizing state or triggering follow-up actions after an animation finishes. + +--- + +## Nested Interfaces + +### `AddParamsInterface` + +**Description:** +A data structure that encapsulates all the necessary information to add a new marker to the map. + +| Property | Type | Description | +| :----------- | :----------- | :--------------------------------------------------- | +| `state` | `MarkerState`| The state of the new marker (e.g., position, rotation). | +| `bitmapIcon` | `BitmapIcon` | The icon to be used for the marker's visual representation. | + +### `ChangeParamsInterface` + +**Description:** +A data structure that holds the information required to update an existing marker's properties. + +| Property | Type | Description | +| :----------- | :------------------------------------ | :--------------------------------------------------------- | +| `current` | `MarkerEntityInterface` | The entity representing the **new** state of the marker. | +| `bitmapIcon` | `BitmapIcon` | The new icon to be applied to the marker. | +| `prev` | `MarkerEntityInterface` | The entity representing the **previous** state of the marker. | + +--- + +## Functions + +### `onAdd` + +**Signature:** `suspend fun onAdd(data: List): List` + +**Description:** +Adds a batch of new markers to the map. The implementation should create native marker objects based on the provided data and add them to the map view. This function is a `suspend` function and should be called from a coroutine. + +**Parameters:** +| Parameter | Type | Description | +| :-------- | :---------------------------- | :----------------------------------------------------------------------- | +| `data` | `List` | A list of `AddParamsInterface` objects, each describing a new marker to add. | + +**Returns:** +`List` - A list containing the newly created native `ActualMarker` objects. The order of this list must correspond to the input `data` list. If a marker fails to be created, its corresponding element in the returned list should be `null`. + +### `onChange` + +**Signature:** `suspend fun onChange(data: List>): List` + +**Description:** +Updates a batch of existing markers on the map. This can include changes to position, icon, rotation, or other visual properties. This function is a `suspend` function and should be called from a coroutine. + +**Parameters:** +| Parameter | Type | Description | +| :-------- | :----------------------------------------- | :-------------------------------------------------------------------------- | +| `data` | `List>` | A list of `ChangeParamsInterface` objects, each describing an update for an existing marker. | + +**Returns:** +`List` - A list containing the updated native `ActualMarker` objects. The order of this list must correspond to the input `data` list. If a marker fails to be updated, its corresponding element should be `null`. + +### `onRemove` + +**Signature:** `suspend fun onRemove(data: List>)` + +**Description:** +Removes a batch of markers from the map. The implementation should find the corresponding native markers and remove them from the map view. This function is a `suspend` function and should be called from a coroutine. + +**Parameters:** +| Parameter | Type | Description | +| :-------- | :------------------------------------ | :----------------------------------------------------------------------- | +| `data` | `List>` | A list of `MarkerEntityInterface` objects representing the markers to be removed. | + +### `onAnimate` + +**Signature:** `suspend fun onAnimate(entity: MarkerEntityInterface)` + +**Description:** +Performs an animation on a single marker, typically for smooth position transitions. The implementation is responsible for the visual interpolation between the marker's previous and current state. This function is a `suspend` function and should be called from a coroutine. + +**Parameters:** +| Parameter | Type | Description | +| :-------- | :------------------------------------ | :--------------------------------------------------- | +| `entity` | `MarkerEntityInterface` | The marker entity that needs to be animated. | + +### `onPostProcess` + +**Signature:** `suspend fun onPostProcess()` + +**Description:** +A lifecycle hook that is called after a batch of `onAdd`, `onChange`, or `onRemove` operations has been fully processed. This function can be used for finalization tasks such as forcing a map redraw, cleaning up resources, or logging. This function is a `suspend` function and should be called from a coroutine. + +--- + +## Example + +Here is a conceptual example of how to implement `MarkerOverlayRendererInterface` for Google Maps. + +```kotlin +import com.google.android.gms.maps.GoogleMap +import com.google.android.gms.maps.model.Marker +import com.google.android.gms.maps.model.MarkerOptions +import com.google.android.gms.maps.model.LatLng +import com.google.android.gms.maps.model.BitmapDescriptorFactory + +// Assuming MarkerEntityInterface, OnMarkerEventHandler, etc. are defined elsewhere. +// typealias ActualMarker = com.google.android.gms.maps.model.Marker + +class GoogleMapMarkerRenderer(private val googleMap: GoogleMap) : MarkerOverlayRendererInterface { + + override var animateStartListener: OnMarkerEventHandler? = null + override var animateEndListener: OnMarkerEventHandler? = null + + override suspend fun onAdd(data: List): List { + return data.map { params -> + val markerOptions = MarkerOptions() + .position(LatLng(params.state.latitude, params.state.longitude)) + .icon(BitmapDescriptorFactory.fromBitmap(params.bitmapIcon.bitmap)) + .anchor(params.bitmapIcon.anchorU, params.bitmapIcon.anchorV) + .rotation(params.state.rotation) + + googleMap.addMarker(markerOptions) + } + } + + override suspend fun onChange(data: List>): List { + return data.map { params -> + val nativeMarker = params.current.nativeMarker + nativeMarker?.apply { + position = LatLng(params.current.state.latitude, params.current.state.longitude) + setIcon(BitmapDescriptorFactory.fromBitmap(params.bitmapIcon.bitmap)) + setAnchor(params.bitmapIcon.anchorU, params.bitmapIcon.anchorV) + rotation = params.current.state.rotation + } + nativeMarker + } + } + + override suspend fun onRemove(data: List>) { + data.forEach { entity -> + entity.nativeMarker?.remove() + } + } + + override suspend fun onAnimate(entity: MarkerEntityInterface) { + val nativeMarker = entity.nativeMarker ?: return + val newPosition = LatLng(entity.state.latitude, entity.state.longitude) + + // Trigger start listener + animateStartListener?.invoke(entity) + + // (Implementation for smooth animation logic would go here) + // For simplicity, we just update the position. + nativeMarker.position = newPosition + + // Trigger end listener + animateEndListener?.invoke(entity) + } + + override suspend fun onPostProcess() { + // No-op for this simple example. Could be used to trigger a camera update + // or other batch-completion tasks. + } +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/MarkerRenderingStrategyInterface.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/MarkerRenderingStrategyInterface.kt.md new file mode 100644 index 00000000..2209f741 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/MarkerRenderingStrategyInterface.kt.md @@ -0,0 +1,112 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +--- + +# MarkerRenderingStrategyInterface + +## Description + +The `MarkerRenderingStrategyInterface` defines a contract for handling the rendering and management of markers on a map, especially in response to camera movements. This interface employs a strategy pattern, allowing for different implementations to be created for various map providers (e.g., Google Maps, Mapbox) to ensure optimal performance and behavior. + +Implementations of this interface are responsible for deciding which markers to add, remove, or update based on the map's current viewport and camera position. This is crucial for performance-intensive features like marker clustering or culling markers that are outside the visible area. + +The generic type `` represents the native marker class provided by the underlying map SDK (e.g., `com.google.android.gms.maps.model.Marker`). + +## Properties + +### markerManager + +Provides access to the `MarkerManager` instance that this strategy uses to manage the lifecycle of the actual marker objects on the map. + +**Signature** +```kotlin +val markerManager: MarkerManager +``` + +--- + +## Functions + +### clear + +Removes all markers currently managed by this strategy from the map. This is typically called when the marker layer is being completely torn down. + +**Signature** +```kotlin +fun clear() +``` + +--- + +### onAdd + +Handles the addition of a new list of markers. The implementation should process this list and decide which markers to render on the map, typically based on whether they fall within the current `viewport`. + +**Signature** +```kotlin +suspend fun onAdd( + data: List, + viewport: GeoRectBounds, + renderer: MarkerOverlayRendererInterface, +): Boolean +``` + +**Parameters** + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `data` | `List` | A list of `MarkerState` objects representing the markers to be added. | +| `viewport` | `GeoRectBounds` | The current visible geographical bounds of the map. | +| `renderer` | `MarkerOverlayRendererInterface` | The renderer responsible for creating and drawing the actual marker objects on the map. | + +**Returns** + +`Boolean` - Returns `true` if the operation resulted in a change to the rendered markers, `false` otherwise. + +--- + +### onUpdate + +Handles the update of a single marker's state. The implementation should find the corresponding marker on the map and update its visual representation if necessary. + +**Signature** +```kotlin +suspend fun onUpdate( + state: MarkerState, + viewport: GeoRectBounds, + renderer: MarkerOverlayRendererInterface, +): Boolean +``` + +**Parameters** + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `state` | `MarkerState` | The updated state for a single marker. | +| `viewport` | `GeoRectBounds` | The current visible geographical bounds of the map. | +| `renderer` | `MarkerOverlayRendererInterface` | The renderer used for updating the marker's visual properties. | + +**Returns** + +`Boolean` - Returns `true` if the marker was visually updated on the map, `false` otherwise. + +--- + +### onCameraChanged + +Handles camera position changes. This is a critical function for performance optimization. The implementation should use the new camera position to update the set of visible markers, such as by adding markers that have entered the viewport and removing those that have left. + +**Signature** +```kotlin +suspend fun onCameraChanged( + cameraPosition: MapCameraPosition, + renderer: MarkerOverlayRendererInterface, +) +``` + +**Parameters** + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `cameraPosition` | `MapCameraPosition` | The new position, zoom, tilt, and bearing of the map camera. | +| `renderer` | `MarkerOverlayRendererInterface` | The renderer used for adding or removing markers from the map. | \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/MarkerRenderingSupport.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/MarkerRenderingSupport.kt.md new file mode 100644 index 00000000..c4e07afd --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/MarkerRenderingSupport.kt.md @@ -0,0 +1,136 @@ +# SDK Documentation + +## `MarkerRenderingSupport` + +### Description + +The `MarkerRenderingSupport` interface provides a map-scoped capability for creating and managing marker rendering components. It is designed to be used by plugins, such as marker clustering systems, to create renderers and event controllers for groups of markers. This decouples the marker rendering logic from the main map controller, allowing for a more modular and extensible architecture. + +An implementation of this interface acts as a factory for creating the necessary objects to display and interact with markers on the map. + +--- + +### `createMarkerRenderer` + +Creates a `MarkerOverlayRendererInterface` responsible for the visual representation of markers on the map. The appearance and behavior of the rendered markers are dictated by the provided rendering strategy. + +**Signature** +```kotlin +fun createMarkerRenderer( + strategy: MarkerRenderingStrategyInterface, +): MarkerOverlayRendererInterface +``` + +**Parameters** + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `strategy` | `MarkerRenderingStrategyInterface` | The strategy that defines how markers should be displayed. | + +**Returns** + +| Type | Description | +| :--- | :--- | +| `MarkerOverlayRendererInterface` | A new instance of a marker overlay renderer. | + +--- + +### `createMarkerEventController` + +Creates a `MarkerEventControllerInterface` to manage user interactions (e.g., clicks, taps) with the markers handled by a specific renderer. + +**Signature** +```kotlin +fun createMarkerEventController( + controller: StrategyMarkerController, + renderer: MarkerOverlayRendererInterface, +): MarkerEventControllerInterface +``` + +**Parameters** + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `controller` | `StrategyMarkerController` | The high-level strategy controller that manages the marker logic. | +| `renderer` | `MarkerOverlayRendererInterface` | The renderer associated with the markers this controller will manage. | + +**Returns** + +| Type | Description | +| :--- | :--- | +| `MarkerEventControllerInterface` | A new instance of a marker event controller. | + +--- + +### `registerMarkerEventController` + +Registers a `MarkerEventControllerInterface` with the map system. Once registered, the controller will be active and can start listening for and handling user interaction events on its associated markers. + +**Signature** +```kotlin +fun registerMarkerEventController(controller: MarkerEventControllerInterface) +``` + +**Parameters** + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `controller` | `MarkerEventControllerInterface` | The event controller to register. | + +--- + +### `mapLoadedState` + +Provides a `StateFlow` that emits the loading status of the map. Observers can collect from this flow to receive updates: `true` indicates the map is fully loaded and ready for interaction, while `false` indicates it is not. This is useful for deferring marker-related operations until the map is initialized. + +The default implementation returns `null`, signifying that not all map implementations may provide this state flow. + +**Signature** +```kotlin +val mapLoadedState: StateFlow? +``` + +**Returns** + +| Type | Description | +| :--- | :--- | +| `StateFlow?` | A state flow representing the map's loaded state, or `null` if not available. | + +--- + +### `onMarkerRenderingReady` + +A lifecycle callback that is invoked when the marker rendering system is fully initialized and ready. Implementations can override this method to perform any necessary setup or initialization tasks that depend on the rendering system being available. This function has an empty default implementation. + +**Signature** +```kotlin +fun onMarkerRenderingReady() +``` + +--- + +## `MarkerRenderingSupportKey` + +### Description + +A singleton object that serves as a registry key for looking up an instance of `MarkerRenderingSupport` from the `LocalMapServiceRegistry`. This key is essential for obtaining the map-specific marker rendering capabilities at runtime. + +**Signature** +```kotlin +object MarkerRenderingSupportKey : MapServiceKey> +``` + +### Example + +You can use `MarkerRenderingSupportKey` to retrieve the `MarkerRenderingSupport` service from the service registry. + +```kotlin +// Assuming 'mapServiceRegistry' is an instance of LocalMapServiceRegistry +val markerRenderingSupport = mapServiceRegistry[MarkerRenderingSupportKey] + +// Now you can use the service to create renderers and controllers +if (markerRenderingSupport != null) { + val renderer = markerRenderingSupport.createMarkerRenderer(myStrategy) + // ... and so on +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/MarkerTileRasterLayerCallback.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/MarkerTileRasterLayerCallback.kt.md new file mode 100644 index 00000000..3d81b69d --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/MarkerTileRasterLayerCallback.kt.md @@ -0,0 +1,103 @@ +# MarkerTileRasterLayerCallback + +### Signature + +```kotlin +fun interface MarkerTileRasterLayerCallback +``` + +### Description + +This is a functional interface that acts as a callback for managing a `RasterLayer` from a `MarkerController`. Its primary purpose is to decouple the `MarkerController` from the `RasterLayerController`, allowing for more modular and maintainable code. + +An implementation of this interface is provided to a `MarkerController` to handle updates to its associated raster layer. The controller will invoke the `onRasterLayerUpdate` method when the raster layer needs to be added, modified, or removed. + +--- + +## onRasterLayerUpdate + +This function is called when a marker's tile raster layer needs to be added, updated, or removed from the map. + +### Signature + +```kotlin +suspend fun onRasterLayerUpdate(state: RasterLayerState?) +``` + +### Description + +The `suspend` modifier indicates that this function is designed to be called from a coroutine and may perform long-running or asynchronous operations, such as I/O or heavy computation, without blocking the main thread. + +### Parameters + +| Parameter | Type | Description | +|-----------|----------------------|----------------------------------------------------------------------------------------------------------------------------------------| +| `state` | `RasterLayerState?` | The desired state for the raster layer. Provide a `RasterLayerState` object to add or update the layer. Pass `null` to remove the layer. | + +### Example + +Here is an example of how to implement the `MarkerTileRasterLayerCallback` and use it to manage a raster layer. + +```kotlin +import kotlinx.coroutines.runBlocking + +// Assume RasterLayerController is a class that manages raster layers on the map. +class RasterLayerController { + fun addOrUpdateLayer(state: RasterLayerState) { + println("Adding or updating raster layer: ${state.id}") + // ... logic to add/update the layer on the map + } + + fun removeLayerByPrefix(layerIdPrefix: String) { + println("Removing raster layer with ID prefix: $layerIdPrefix") + // ... logic to find and remove the layer from the map + } +} + +// Assume MarkerController is configured with this callback. +// The markerId helps to uniquely identify the layer to be removed. +fun createMarkerCallback( + rasterLayerController: RasterLayerController, + markerId: String +): MarkerTileRasterLayerCallback { + // Since it's a functional interface, we can use a lambda. + return MarkerTileRasterLayerCallback { state -> + if (state != null) { + // If state is provided, add or update the raster layer. + rasterLayerController.addOrUpdateLayer(state) + } else { + // If state is null, remove the raster layer associated with this marker. + // We use the markerId to construct a unique prefix for the layerId to remove. + val layerIdPrefixToRemove = "marker-raster-$markerId" + rasterLayerController.removeLayerByPrefix(layerIdPrefixToRemove) + } + } +} + +// A placeholder for RasterLayerState for the example to be self-contained. +data class RasterLayerState(val id: String, val sourceId: String) + +// Main function to demonstrate usage +fun main() = runBlocking { + val rasterController = RasterLayerController() + val myMarkerId = "marker-123" + + // Create the callback instance for a specific marker. + val markerCallback = createMarkerCallback(rasterController, myMarkerId) + + // --- Scenario 1: Add a new layer --- + // Simulate the MarkerController calling the callback to add a layer. + println("Scenario 1: Adding a layer") + val newState = RasterLayerState(id = "marker-raster-$myMarkerId-xyz", sourceId = "my-source") + markerCallback.onRasterLayerUpdate(newState) + // Expected Output: Adding or updating raster layer: marker-raster-marker-123-xyz + println() + + + // --- Scenario 2: Remove the layer --- + // Simulate the MarkerController calling the callback to remove the layer. + println("Scenario 2: Removing the layer") + markerCallback.onRasterLayerUpdate(null) + // Expected Output: Removing raster layer with ID prefix: marker-raster-marker-123 +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/MarkerTileRenderer.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/MarkerTileRenderer.kt.md new file mode 100644 index 00000000..1f98fb7a --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/MarkerTileRenderer.kt.md @@ -0,0 +1,126 @@ +Excellent. Here is the high-quality SDK documentation for the provided code snippet. + +*** + +# MarkerTileRenderer + +## Signature + +```kotlin +class MarkerTileRenderer( + val markerManager: MarkerManager, + val tileSize: Int, + cacheSizeBytes: Int, + private val debugTileOverlay: Boolean = false, + private val iconScaleCallback: ((MarkerState, Int) -> Double)? = null, +) : TileProviderInterface +``` + +## Description + +The `MarkerTileRenderer` is a highly efficient renderer that generates map tiles with marker icons. It implements the `TileProviderInterface`, making it suitable for use with a local tile server to display markers as a raster layer on any map SDK. This approach provides a flexible, SDK-agnostic way to render a large number of markers. + +Key features include: +- **Fixed Pixel Size:** Markers maintain a consistent size on the screen across different zoom levels. +- **Dynamic Scaling:** An optional callback allows for adjusting marker size based on zoom level or other state. +- **Efficient Caching:** An internal LruCache stores recently rendered tiles to reduce redundant processing and improve performance. +- **Decluttering:** By querying markers within the tile's bounds, it naturally supports decluttering strategies implemented in the `MarkerManager`. +- **Thread Safety:** The `renderTile` method is designed to be called concurrently from multiple threads. + +The generic parameter `` represents the specific type of marker object being managed by the provided `MarkerManager`. + +## Constructor + +| Parameter | Type | Description | +|---|---|---| +| `markerManager` | `MarkerManager` | The `MarkerManager` instance that provides the marker data to be rendered. | +| `tileSize` | `Int` | The size of the tiles to be generated, specified in density-independent pixels (dp). A standard value is `256`. | +| `cacheSizeBytes` | `Int` | The maximum size of the in-memory tile cache, specified in bytes. | +| `debugTileOverlay` | `Boolean` | (Optional) If `true`, a debug overlay with tile coordinates and marker counts will be drawn on each tile. Defaults to `false`. | +| `iconScaleCallback` | `((MarkerState, Int) -> Double)?` | (Optional) A callback function to dynamically adjust the scale of each marker's icon. It receives the `MarkerState` and the current `zoom` level and should return a scale multiplier (e.g., `1.0` for normal size). Defaults to `null`. | + +## Methods + +### invalidate + +Invalidates the internal tile cache. This forces all tiles to be re-rendered on their next request. This method should be called whenever the underlying marker data in the `MarkerManager` changes (e.g., markers are added, removed, or their state is updated) to ensure the map displays the most current data. + +**Signature** +```kotlin +fun invalidate() +``` + +**Returns** +`Unit` + +### clear + +Clears all cached tiles from memory. This is functionally equivalent to `invalidate()`. + +**Signature** +```kotlin +fun clear() +``` + +**Returns** +`Unit` + +### renderTile + +Renders a single map tile based on the provided `TileRequest`. This method is part of the `TileProviderInterface` and is typically called by a tile server. It queries the `MarkerManager` for markers within the tile's geographic bounds, draws them onto a bitmap, and returns the result as a PNG-encoded `ByteArray`. + +**Signature** +```kotlin +override fun renderTile(request: TileRequest): ByteArray? +``` + +**Parameters** +| Parameter | Type | Description | +|---|---|---| +| `request` | `TileRequest` | An object containing the tile coordinates (`x`, `y`, `z`) for the tile to be rendered. | + +**Returns** +`ByteArray?` - A byte array containing the PNG image data for the rendered tile. Returns `null` if the tile contains no markers (and `debugTileOverlay` is `false`) or if the request coordinates are invalid. + +## Example + +Here is an example of how to set up and use the `MarkerTileRenderer`. + +```kotlin +import com.mapconductor.core.marker.MarkerTileRenderer +import com.mapconductor.core.marker.MarkerManager +import com.mapconductor.core.marker.MarkerState + +// Assume MyMarker is your custom marker data class +data class MyMarker(val id: String, val name: String) + +// 1. Initialize a MarkerManager +val markerManager = MarkerManager() + +// 2. Create an instance of MarkerTileRenderer +val markerRenderer = MarkerTileRenderer( + markerManager = markerManager, + tileSize = 256, // Standard tile size in dp + cacheSizeBytes = 32 * 1024 * 1024, // 32 MB cache + debugTileOverlay = false, + iconScaleCallback = { markerState, zoom -> + // Example: Make markers smaller at lower zoom levels + if (zoom < 10) 0.8 else 1.0 + } +) + +// 3. This renderer would then be passed to a local tile server, +// which in turn provides tiles to a raster layer on your map. +// (The following is conceptual and depends on your map SDK) +// +// val localTileServer = LocalTileServer(provider = markerRenderer) +// map.addLayer(RasterLayer(source = localTileServer.source)) + +// 4. When you update your markers, invalidate the renderer's cache +// to force the tiles to be redrawn with the new data. +val newMarkerState = MarkerState(position = GeoPoint(40.7128, -74.0060)) +markerManager.addMarker("nyc", MyMarker("nyc", "New York"), newMarkerState) + +// Invalidate to reflect the change on the map +markerRenderer.invalidate() +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/MarkerTilingOptions.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/MarkerTilingOptions.kt.md new file mode 100644 index 00000000..3c9118b2 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/MarkerTilingOptions.kt.md @@ -0,0 +1,87 @@ +# MarkerTilingOptions + +The `MarkerTilingOptions` data class provides configuration for marker tiling optimization. + +## Description + +When dealing with a large number of static markers, rendering performance can be a concern. Marker tiling is an optimization technique that addresses this by rendering groups of markers as image tiles instead of as individual objects. This significantly reduces the per-marker add/update overhead in the underlying native map SDKs, leading to a smoother user experience. + +This class allows you to enable, disable, and fine-tune the behavior of the marker tiling engine. + +## Parameters + +| Parameter | Type | Description | Default | +| :--- | :--- | :--- | :--- | +| `enabled` | `Boolean` | If `true`, enables the marker tiling optimization. Set to `false` to disable this feature entirely. | `true` | +| `debugTileOverlay` | `Boolean` | If `true`, draws a debug overlay onto each marker tile. The overlay includes top/left border lines and a label containing the tile's z/x/y coordinates and basic rendering statistics. This is useful for debugging caching or scaling artifacts. | `false` | +| `minMarkerCount` | `Int` | The minimum number of markers that must be present on the map before the tiling optimization is activated. | `2000` | +| `cacheSize` | `Int` | The maximum size of the in-memory tile cache in bytes. | `8388608` (8MB) | +| `iconScaleCallback` | `((MarkerState, Int) -> Double)?` | An optional callback function to apply an additional scale multiplier to a marker's icon based on the current map zoom level. The function receives the `MarkerState` and the current `zoom` level and must return a `Double` representing the scale multiplier. The renderer computes the final scale as follows: `effectiveScale = (markerState.icon?.scale ?: 1.0) * (iconScaleCallback?.invoke(markerState, zoom) ?: 1.0)` | `null` | + +## Companion Object + +### Disabled + +A pre-configured instance that completely disables marker tiling. + +**Signature** +```kotlin +val Disabled: MarkerTilingOptions +``` + +**Usage** +```kotlin +val tilingOptions = MarkerTilingOptions.Disabled +// Equivalent to MarkerTilingOptions(enabled = false) +``` + +### Default + +A pre-configured instance with the default marker tiling settings. + +**Signature** +```kotlin +val Default: MarkerTilingOptions +``` + +**Usage** +```kotlin +val tilingOptions = MarkerTilingOptions.Default +// Equivalent to MarkerTilingOptions() +``` + +## Example + +The following examples demonstrate how to create and use `MarkerTilingOptions`. + +```kotlin +import com.mapconductor.core.marker.MarkerTilingOptions +import com.mapconductor.core.marker.MarkerState + +// 1. Use the default tiling options +val defaultOptions = MarkerTilingOptions.Default + +// 2. Use a custom configuration +val customOptions = MarkerTilingOptions( + enabled = true, + minMarkerCount = 1000, + cacheSize = 16 * 1024 * 1024, // 16MB cache + debugTileOverlay = true +) + +// 3. Use a custom configuration with a dynamic icon scale callback +// This example makes icons larger at higher zoom levels. +val scalingOptions = MarkerTilingOptions( + minMarkerCount = 500, + iconScaleCallback = { markerState: MarkerState, zoom: Int -> + when { + zoom > 15 -> 1.5 + zoom > 10 -> 1.2 + else -> 1.0 + } + } +) + +// 4. Disable marker tiling completely +val disabledOptions = MarkerTilingOptions.Disabled +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/StrategyMarkerController.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/StrategyMarkerController.kt.md new file mode 100644 index 00000000..e365c214 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/marker/StrategyMarkerController.kt.md @@ -0,0 +1,243 @@ +# SDK Documentation: StrategyMarkerController + +## `StrategyMarkerController` + +### Description + +The `StrategyMarkerController` is a generic controller responsible for managing the lifecycle and rendering of map markers. It acts as an overlay controller, implementing the `OverlayControllerInterface`. + +This class employs a strategy pattern through the `MarkerRenderingStrategyInterface`. This design allows for flexible and interchangeable rendering logic, such as simple marker rendering, clustering, or other custom behaviors, based on the current map viewport. The controller coordinates the `strategy` and a `renderer` to efficiently add, update, and display markers. It also manages and dispatches user interaction events like clicks, drags, and animations. + +The generic type `` allows this controller to work with any platform-specific marker object (e.g., a marker from Google Maps, Mapbox, or another map provider). + +### Signature + +```kotlin +class StrategyMarkerController( + private val strategy: MarkerRenderingStrategyInterface, + private val renderer: MarkerOverlayRendererInterface, + override var clickListener: OnMarkerEventHandler? = null, +) : OverlayControllerInterface< + MarkerState, + MarkerEntityInterface, + MarkerState, + > +``` + +### Parameters + +| Parameter | Type | Description | +|-----------|------|-------------| +| `strategy` | `MarkerRenderingStrategyInterface` | The rendering strategy that defines how markers are processed and displayed based on the map's state (e.g., clustering, culling). | +| `renderer` | `MarkerOverlayRendererInterface` | The renderer responsible for the actual drawing and animation of marker objects on the map canvas. | +| `clickListener` | `OnMarkerEventHandler?` | An optional global event handler that is invoked whenever any marker managed by this controller is clicked. Defaults to `null`. | + +--- + +## Properties + +| Property | Type | Description | +|----------|------|-------------| +| `markerManager` | `MarkerManager` | Provides access to the underlying manager that tracks all marker entities. | +| `zIndex` | `Int` | The z-index of the overlay, which determines its stacking order on the map. Hardcoded to `10`. | +| `clickListener` | `OnMarkerEventHandler?` | A global listener for marker click events. | +| `dragStartListener` | `OnMarkerEventHandler?` | A global listener invoked when a marker drag operation begins. | +| `dragListener` | `OnMarkerEventHandler?` | A global listener invoked continuously while a marker is being dragged. | +| `dragEndListener` | `OnMarkerEventHandler?` | A global listener invoked when a marker drag operation ends. | +| `animateStartListener` | `OnMarkerEventHandler?` | A global listener invoked when a marker animation begins. | +| `animateEndListener` | `OnMarkerEventHandler?` | A global listener invoked when a marker animation completes. | + +--- + +## Methods + +### add + +Adds a list of markers to be managed by the controller. The rendering logic is delegated to the configured `strategy`, which typically evaluates which markers are visible within the current map viewport. If the map camera is not yet initialized, the markers are queued and added once the camera position becomes available. + +#### Signature + +```kotlin +override suspend fun add(data: List) +``` + +#### Parameters + +| Parameter | Type | Description | +|-----------|------|-------------| +| `data` | `List` | A list of `MarkerState` objects representing the markers to be added. | + +--- + +### update + +Updates the state of a single existing marker. This method can be used to change a marker's properties, such as its position or icon. The update logic is delegated to the configured `strategy`. + +#### Signature + +```kotlin +override suspend fun update(state: MarkerState) +``` + +#### Parameters + +| Parameter | Type | Description | +|-----------|------|-------------| +| `state` | `MarkerState` | The new `MarkerState` for the marker to be updated. The marker is identified by its `id`. | + +--- + +### clear + +Removes all markers managed by this controller from the map. + +#### Signature + +```kotlin +override suspend fun clear() +``` + +--- + +### getEntity + +Retrieves the underlying marker entity (the wrapper around the platform-specific marker object) by its unique identifier. + +#### Signature + +```kotlin +fun getEntity(id: String): MarkerEntityInterface? +``` + +#### Parameters + +| Parameter | Type | Description | +|-----------|------|-------------| +| `id` | `String` | The unique identifier of the marker. | + +#### Returns + +| Type | Description | +|------|-------------| +| `MarkerEntityInterface?` | The `MarkerEntityInterface` if a marker with the specified `id` is found; otherwise, `null`. | + +--- + +### find + +Finds the nearest marker entity to a given geographical point. This is useful for implementing features like "tap to select nearest marker". + +#### Signature + +```kotlin +override fun find(position: GeoPointInterface): MarkerEntityInterface? +``` + +#### Parameters + +| Parameter | Type | Description | +|-----------|------|-------------| +| `position` | `GeoPointInterface` | The geographical coordinates to search near. | + +#### Returns + +| Type | Description | +|------|-------------| +| `MarkerEntityInterface?` | The nearest `MarkerEntityInterface` if one exists within the search parameters of the strategy; otherwise, `null`. | + +--- + +### onCameraChanged + +A lifecycle method that should be called by the map framework whenever the map's camera position changes (e.g., due to panning, zooming, or tilting). The controller uses the new camera position to update the visibility of markers via the configured `strategy`. + +#### Signature + +```kotlin +override suspend fun onCameraChanged(mapCameraPosition: MapCameraPosition) +``` + +#### Parameters + +| Parameter | Type | Description | +|-----------|------|-------------| +| `mapCameraPosition` | `MapCameraPosition` | An object containing the new camera position, zoom level, and visible region. | + +--- + +### destroy + +Cleans up all resources used by the controller. This method calls `clear()` to remove all markers from the map. It should be called when the controller is no longer needed to prevent memory leaks. + +#### Signature + +```kotlin +override fun destroy() +``` + +--- + +### Event Dispatchers + +These methods are typically called by the `MarkerOverlayRendererInterface` implementation to propagate platform-specific events back to the controller, which then notifies the appropriate listeners. + +| Method | Description | +|--------|-------------| +| `dispatchClick(state: MarkerState)` | Dispatches a click event. Invokes the marker's `onClick` handler and the controller's `clickListener`. | +| `dispatchDragStart(state: MarkerState)` | Dispatches a drag start event. Invokes the marker's `onDragStart` handler and the controller's `dragStartListener`. | +| `dispatchDrag(state: MarkerState)` | Dispatches a drag event. Invokes the marker's `onDrag` handler and the controller's `dragListener`. | +| `dispatchDragEnd(state: MarkerState)` | Dispatches a drag end event. Invokes the marker's `onDragEnd` handler and the controller's `dragEndListener`. | +| `dispatchAnimateStart(state: MarkerState)` | Dispatches an animation start event. Invokes the marker's `onAnimateStart` handler and the controller's `animateStartListener`. | +| `dispatchAnimateEnd(state: MarkerState)` | Dispatches an animation end event. Invokes the marker's `onAnimateEnd` handler and the controller's `animateEndListener`. | + +--- + +## Example + +The following conceptual example demonstrates how to instantiate and use the `StrategyMarkerController`. + +```kotlin +// 1. Define dependencies (strategy and renderer are platform-specific) +val myMarkerRenderingStrategy: MarkerRenderingStrategyInterface = MyClusteringStrategy() +val myMarkerRenderer: MarkerOverlayRendererInterface = MyMapMarkerRenderer(map) + +// 2. Instantiate the controller with a global click listener +val markerController = StrategyMarkerController( + strategy = myMarkerRenderingStrategy, + renderer = myMarkerRenderer, + clickListener = { markerState -> + println("Global click on marker: ${markerState.id}") + } +) + +// 3. Add markers to the controller +val markersToAdd = listOf( + MarkerState(id = "marker1", position = GeoPoint(40.7128, -74.0060)), + MarkerState(id = "marker2", position = GeoPoint(34.0522, -118.2437)) +) + +// Use a coroutine scope to call suspend functions +coroutineScope.launch { + markerController.add(markersToAdd) +} + +// 4. Update a marker's state +val updatedState = MarkerState( + id = "marker1", + position = GeoPoint(40.7130, -74.0062), // New position + icon = "new_icon_resource" +) + +coroutineScope.launch { + markerController.update(updatedState) +} + +// 5. Find the closest marker to a point +val searchPoint = GeoPoint(34.0520, -118.2435) +val nearestMarkerEntity = markerController.find(searchPoint) +println("Nearest marker ID: ${nearestMarkerEntity?.state?.id}") + +// 6. Clean up when the controller is no longer needed +// Typically called in a lifecycle method like onDestroy() or onDispose() +markerController.destroy() +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polygon/AbstractPolygonOverlayRenderer.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polygon/AbstractPolygonOverlayRenderer.kt.md new file mode 100644 index 00000000..cb09ab78 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polygon/AbstractPolygonOverlayRenderer.kt.md @@ -0,0 +1,267 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +--- + +# AbstractPolygonOverlayRenderer + +## Signature + +```kotlin +abstract class AbstractPolygonOverlayRenderer : PolygonOverlayRendererInterface +``` + +## Description + +`AbstractPolygonOverlayRenderer` provides a foundational implementation for rendering and managing polygon overlays on a map. It serves as a base class that handles the core logic for processing add, change, and remove operations on a set of polygons. + +This class separates the generic lifecycle management of overlays from the platform-specific rendering details. Subclasses are required to implement the abstract methods (`createPolygon`, `updatePolygonProperties`, `removePolygon`) to interact with the native map SDK's polygon objects. + +## Generic Parameters + +| Parameter | Description | +|-----------|-------------| +| `ActualPolygon` | The native polygon object type from the specific map provider's SDK (e.g., `com.google.android.gms.maps.model.Polygon` for Google Maps). | + +## Properties + +| Property | Type | Description | +|----------|------|-------------| +| `holder` | `MapViewHolderInterface<*, *>` | An abstract property that must be implemented to provide access to the map view holder, which manages the map instance. | +| `coroutine` | `CoroutineScope` | An abstract property that must be implemented to provide a coroutine scope for managing asynchronous operations related to map rendering. | + +## Methods + +### Abstract Methods + +These methods must be implemented by any subclass to provide platform-specific rendering logic. + +#### createPolygon + +Creates a new native polygon on the map based on the provided state. + +**Signature** +```kotlin +abstract suspend fun createPolygon(state: PolygonState): ActualPolygon? +``` + +**Description** +This function is called when a new polygon needs to be added to the map. The implementation should use the properties within the `state` object to construct and configure a native polygon object and add it to the map. + +**Parameters** +| Parameter | Type | Description | +|-----------|--------------|-------------| +| `state` | `PolygonState` | An object containing all the properties (e.g., points, fill color, stroke width) for the new polygon. | + +**Returns** +`ActualPolygon?`: The newly created native polygon object or `null` if the creation failed. + +--- + +#### updatePolygonProperties + +Updates the properties of an existing native polygon on the map. + +**Signature** +```kotlin +abstract suspend fun updatePolygonProperties( + polygon: ActualPolygon, + current: PolygonEntityInterface, + prev: PolygonEntityInterface, +): ActualPolygon? +``` + +**Description** +This function is called when a polygon's properties have changed. The implementation should efficiently update the visual properties of the given `polygon` object by comparing the `current` and `prev` states. + +**Parameters** +| Parameter | Type | Description | +|-----------|---------------------------------------|-------------| +| `polygon` | `ActualPolygon` | The native polygon object to be updated. | +| `current` | `PolygonEntityInterface` | The entity wrapper containing the new state of the polygon. | +| `prev` | `PolygonEntityInterface` | The entity wrapper containing the previous state of the polygon. | + +**Returns** +`ActualPolygon?`: The updated native polygon object, or a new instance if the underlying map SDK requires replacement. Returns `null` if the update fails. + +--- + +#### removePolygon + +Removes a native polygon from the map. + +**Signature** +```kotlin +abstract suspend fun removePolygon(entity: PolygonEntityInterface) +``` + +**Description** +This function is called when a polygon needs to be removed from the map. The implementation should find the corresponding native polygon object within the `entity` and remove it from the map view. + +**Parameters** +| Parameter | Type | Description | +|-----------|---------------------------------------|-------------| +| `entity` | `PolygonEntityInterface` | The entity wrapper for the polygon to be removed. It contains the native polygon object. | + +**Returns** +`Unit` + +--- + +### Implemented Methods + +These methods are part of the `PolygonOverlayRendererInterface` and are implemented by this abstract class. They orchestrate the calls to the abstract methods defined above. + +#### onAdd + +Handles the addition of a batch of new polygons. + +**Signature** +```kotlin +override suspend fun onAdd(data: List): List +``` + +**Description** +This method iterates through a list of polygon creation parameters and calls `createPolygon` for each one, effectively adding a batch of polygons to the map. + +**Parameters** +| Parameter | Type | Description | +|-----------|----------------------------------------------------------|-------------| +| `data` | `List` | A list of parameters, where each element contains the state for a new polygon. | + +**Returns** +`List`: A list containing the newly created native polygon objects. Each element corresponds to an item in the input `data` list. + +--- + +#### onChange + +Handles property changes for a batch of existing polygons. + +**Signature** +```kotlin +override suspend fun onChange( + data: List>, +): List +``` + +**Description** +This method iterates through a list of changed polygons and calls `updatePolygonProperties` for each one, applying the new properties. + +**Parameters** +| Parameter | Type | Description | +|-----------|-----------------------------------------------------------------------|-------------| +| `data` | `List>` | A list of parameters, where each element contains the previous and current state of a polygon. | + +**Returns** +`List`: A list of the updated native polygon objects. + +--- + +#### onRemove + +Handles the removal of a batch of polygons. + +**Signature** +```kotlin +override suspend fun onRemove(data: List>) +``` + +**Description** +This method iterates through a list of polygon entities and calls `removePolygon` for each one to remove them from the map. + +**Parameters** +| Parameter | Type | Description | +|-----------|---------------------------------------------|-------------| +| `data` | `List>` | A list of polygon entities to be removed. | + +**Returns** +`Unit` + +--- + +#### onPostProcess + +A lifecycle hook called after a batch of add, change, and remove operations is complete. + +**Signature** +```kotlin +override suspend fun onPostProcess() +``` + +**Description** +The default implementation is empty. Subclasses can override this method to perform any final processing, cleanup, or view refresh actions that are needed after a batch update. + +**Returns** +`Unit` + +## Example + +The following example demonstrates how to create a concrete renderer for Google Maps by extending `AbstractPolygonOverlayRenderer`. + +```kotlin +import com.google.android.gms.maps.GoogleMap +import com.google.android.gms.maps.model.Polygon +import com.google.android.gms.maps.model.PolygonOptions +import com.mapconductor.core.map.MapViewHolderInterface +import com.mapconductor.core.polygon.* +import kotlinx.coroutines.CoroutineScope + +// Assume GoogleMapViewHolder and other related classes are defined elsewhere +class GoogleMapPolygonOverlayRenderer( + override val holder: MapViewHolderInterface, + override val coroutine: CoroutineScope +) : AbstractPolygonOverlayRenderer() { + + private val googleMap: GoogleMap? + get() = holder.getMap() + + override suspend fun createPolygon(state: PolygonState): Polygon? { + val map = googleMap ?: return null + + val polygonOptions = PolygonOptions().apply { + addAll(state.points) + fillColor(state.fillColor) + strokeColor(state.strokeColor) + strokeWidth(state.strokeWidth) + zIndex(state.zIndex) + clickable(state.isClickable) + } + + // Add the polygon to the Google Map + return map.addPolygon(polygonOptions) + } + + override suspend fun updatePolygonProperties( + polygon: Polygon, + current: PolygonEntityInterface, + prev: PolygonEntityInterface + ): Polygon? { + val currentState = current.state + val prevState = prev.state + + // Efficiently update only the properties that have changed + if (currentState.points != prevState.points) { + polygon.points = currentState.points + } + if (currentState.fillColor != prevState.fillColor) { + polygon.fillColor = currentState.fillColor + } + if (currentState.strokeColor != prevState.strokeColor) { + polygon.strokeColor = currentState.strokeColor + } + // ... update other properties as needed ... + + return polygon + } + + override suspend fun removePolygon(entity: PolygonEntityInterface) { + // Remove the polygon from the map + entity.polygon.remove() + } + + override suspend fun onPostProcess() { + // Optional: Perform any final actions after a batch update, + // e.g., refreshing a cluster manager if it's affected. + } +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polygon/Polygon.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polygon/Polygon.kt.md new file mode 100644 index 00000000..b8426140 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polygon/Polygon.kt.md @@ -0,0 +1,202 @@ +Excellent. Here is the high-quality SDK documentation for the provided code snippet. + +*** + +# Polygon SDK Documentation + +This document provides detailed documentation for the `PolygonState` class and its related components, which are used to manage and display polygons on a map within the MapConductor ecosystem. + +## `PolygonState` + +### Signature +```kotlin +class PolygonState(...) : ComponentState +``` + +### Description +The `PolygonState` class is a state holder that defines the properties and behavior of a single polygon on the map. It is designed to be used in a reactive environment like Jetpack Compose, where changes to its properties will automatically trigger UI updates. + +This class encapsulates all aspects of a polygon, including its geometry (vertices and holes), visual appearance (colors and stroke width), and interactivity (click handling). + +### Parameters +The `PolygonState` is initialized with the following parameters: + +| Parameter | Type | Default | Description | +|---|---|---|---| +| `points` | `List` | - | A list of geographical points that define the outer boundary of the polygon. The list should represent a closed loop (the first and last points should be the same). | +| `holes` | `List>` | `emptyList()` | A list of inner boundaries (holes). Each inner list represents a separate hole and must be a closed loop of points. | +| `id` | `String?` | `null` | An optional unique identifier for the polygon. If `null`, a stable ID is automatically generated based on the polygon's properties. | +| `strokeColor` | `Color` | `Color.Black` | The color of the polygon's outline. | +| `strokeWidth` | `Dp` | `2.dp` | The width of the polygon's outline. | +| `fillColor` | `Color` | `Color.Transparent` | The color used to fill the area of the polygon. | +| `geodesic` | `Boolean` | `false` | If `true`, the polygon's edges are drawn as geodesic lines, which follow the curvature of the Earth. If `false`, edges are drawn as straight lines on the map's 2D projection. | +| `zIndex` | `Int` | `0` | The drawing order of the polygon relative to other map overlays. Polygons with a higher `zIndex` are drawn on top of those with a lower `zIndex`. | +| `extra` | `Serializable?` | `null` | Optional, user-defined data that can be associated with the polygon. This data must be serializable. | +| `onClick` | `OnPolygonEventHandler?` | `null` | A callback lambda function that is invoked when the user clicks on the polygon. It receives a `PolygonEvent` object. | + +--- + +## Methods + +### `copy` +Creates a new `PolygonState` instance with optionally modified properties. + +#### Signature +```kotlin +fun copy( + points: List = this.points, + holes: List> = this.holes, + id: String? = this.id, + strokeColor: Color = this.strokeColor, + strokeWidth: Dp = this.strokeWidth, + fillColor: Color = this.fillColor, + geodesic: Boolean = this.geodesic, + zIndex: Int = this.zIndex, + extra: Serializable? = this.extra, + onClick: OnPolygonEventHandler? = this.onClick, +): PolygonState +``` + +#### Description +This function creates a shallow copy of the `PolygonState` object. You can override any of the existing properties by providing new values as arguments. This is useful for creating a new state based on an existing one without modifying the original. + +#### Returns +A new `PolygonState` instance with the updated properties. + +--- + +### `asFlow` +Returns a `Flow` that emits updates when the polygon's state changes. + +#### Signature +```kotlin +fun asFlow(): Flow +``` + +#### Description +This function provides a reactive stream of the polygon's state. It returns a `Flow` that emits a `PolygonFingerPrint` whenever any property of the `PolygonState` changes. The flow is configured with `distinctUntilChanged()`, ensuring that emissions only occur when the fingerprint is actually different from the previous one. This is highly efficient for observing state changes. + +#### Returns +A `Flow` that emits a new value upon a state change. + +--- + +## Related Data Classes & Type Aliases + +### `PolygonEvent` +A data class that encapsulates information about a click event on a polygon. + +#### Signature +```kotlin +data class PolygonEvent( + val state: PolygonState, + val clicked: GeoPointInterface, +) +``` + +#### Properties +| Property | Type | Description | +|---|---|---| +| `state` | `PolygonState` | The `PolygonState` of the polygon that was clicked. | +| `clicked` | `GeoPointInterface` | The specific geographical coordinate (`GeoPointInterface`) on the polygon where the click occurred. | + +--- + +### `OnPolygonEventHandler` +A type alias for the polygon click event handler function. + +#### Signature +```kotlin +typealias OnPolygonEventHandler = (PolygonEvent) -> Unit +``` + +#### Description +This defines the signature for a function that handles polygon click events. It takes a single `PolygonEvent` parameter and returns `Unit`. + +--- + +### `PolygonFingerPrint` +A data class used for efficient change detection of a `PolygonState`. + +#### Signature +```kotlin +data class PolygonFingerPrint( + val id: Int, + val strokeColor: Int, + val strokeWidth: Int, + val fillColor: Int, + val geodesic: Int, + val zIndex: Int, + val points: Int, + val holes: Int, + val extra: Int, +) +``` + +#### Description +This class holds a collection of hash codes representing the properties of a `PolygonState`. It is used internally by the `asFlow()` method to efficiently determine if the polygon's state has changed. Developers typically do not need to interact with this class directly. + +--- + +## Example + +The following example demonstrates how to create a `PolygonState`, handle click events, and use the `copy()` method to create a modified version. + +```kotlin +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp +import com.mapconductor.core.features.GeoPoint // Assuming a concrete implementation + +fun main() { + // 1. Define the vertices for the polygon's outer boundary + val outerBoundary = listOf( + GeoPoint(40.7128, -74.0060), // New York City + GeoPoint(34.0522, -118.2437), // Los Angeles + GeoPoint(29.7604, -95.3698), // Houston + GeoPoint(40.7128, -74.0060) // Close the loop + ) + + // 2. Define the vertices for a hole inside the polygon + val holeBoundary = listOf( + GeoPoint(39.0, -98.0), + GeoPoint(38.0, -108.0), + GeoPoint(37.0, -98.0), + GeoPoint(39.0, -98.0) // Close the loop + ) + + // 3. Define a click handler + val onPolygonClick: OnPolygonEventHandler = { event -> + println("Polygon with ID ${event.state.id} was clicked at ${event.clicked}.") + // You can access any property of the clicked polygon + println("Fill color is ${event.state.fillColor}") + } + + // 4. Create an instance of PolygonState + val myPolygon = PolygonState( + points = outerBoundary, + holes = listOf(holeBoundary), + strokeColor = Color.Blue, + strokeWidth = 5.dp, + fillColor = Color.Blue.copy(alpha = 0.3f), + geodesic = true, + zIndex = 1, + extra = "MyCustomData123", + onClick = onPolygonClick + ) + + println("Original Polygon ID: ${myPolygon.id}") + println("Original Fill Color: ${myPolygon.fillColor}") + + // 5. Use the copy() method to create a new state with a different fill color + val highlightedPolygon = myPolygon.copy( + fillColor = Color.Red.copy(alpha = 0.5f), + zIndex = 2 // Bring it to the front + ) + + println("Highlighted Polygon ID: ${highlightedPolygon.id}") + println("Highlighted Fill Color: ${highlightedPolygon.fillColor}") + + // This `highlightedPolygon` can now be used to update the UI, showing + // the polygon in a "highlighted" state. +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polygon/PolygonCapableInterface.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polygon/PolygonCapableInterface.kt.md new file mode 100644 index 00000000..755e74b5 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polygon/PolygonCapableInterface.kt.md @@ -0,0 +1,161 @@ +# PolygonCapableInterface + +The `PolygonCapableInterface` defines a contract for components that can display and manage polygons on a map. It provides methods for adding, updating, checking, and handling interactions with polygons. + +--- + +## Methods + +### compositionPolygons + +Sets or replaces the entire collection of polygons displayed on the map. This function is asynchronous and should be called from a coroutine. Any polygons currently on the map that are not in the provided `data` list will be removed. + +#### Signature + +```kotlin +suspend fun compositionPolygons(data: List) +``` + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :------------------- | :----------------------------------------------------------- | +| `data` | `List` | A list of `PolygonState` objects to be displayed on the map. | + +#### Returns + +This is a suspend function and does not return a value. + +#### Example + +```kotlin +// Assuming 'mapController' implements PolygonCapableInterface +// and 'polygon1State' and 'polygon2State' are valid PolygonState objects. + +val polygonList = listOf(polygon1State, polygon2State) + +// Launch a coroutine to update the polygons on the map +coroutineScope.launch { + mapController.compositionPolygons(polygonList) +} +``` + +--- + +### updatePolygon + +Updates the properties of a single, existing polygon on the map. The specific polygon to be updated is identified by an ID within the provided `PolygonState` object. If no polygon with the matching ID exists, the call may be ignored. This function is asynchronous. + +#### Signature + +```kotlin +suspend fun updatePolygon(state: PolygonState) +``` + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :------------- | :----------------------------------------------------------------------- | +| `state` | `PolygonState` | The `PolygonState` object containing the updated properties for a polygon. | + +#### Returns + +This is a suspend function and does not return a value. + +#### Example + +```kotlin +// Create an updated state for an existing polygon with id "polygon-1" +val updatedPolygonState = PolygonState( + id = "polygon-1", + points = newListOfGeoPoints, + fillColor = Color.BLUE +) + +// Launch a coroutine to apply the update +coroutineScope.launch { + mapController.updatePolygon(updatedPolygonState) +} +``` + +--- + +### setOnPolygonClickListener + +

+ @Deprecated
+ This method is deprecated. Use the onClick lambda property within the PolygonState object for handling click events on a per-polygon basis. +

+ +Sets a global listener for click events on any polygon. + +#### Signature + +```kotlin +fun setOnPolygonClickListener(listener: OnPolygonEventHandler?) +``` + +#### Parameters + +| Parameter | Type | Description | +| :--------- | :---------------------- | :----------------------------------------------------------------------- | +| `listener` | `OnPolygonEventHandler?` | The event handler to be invoked when a polygon is clicked. Pass `null` to remove the current listener. | + +#### Returns + +This function does not return a value. + +#### Example + +```kotlin +// Note: This method is deprecated. + +// Define a listener +val polygonClickListener = object : OnPolygonEventHandler { + override fun onPolygonClick(polygonId: String) { + println("Polygon with ID $polygonId was clicked.") + } +} + +// Set the listener +mapController.setOnPolygonClickListener(polygonClickListener) + +// To remove the listener +mapController.setOnPolygonClickListener(null) +``` + +--- + +### hasPolygon + +Checks if a polygon with a specific ID (derived from the `PolygonState`) exists on the map. + +#### Signature + +```kotlin +fun hasPolygon(state: PolygonState): Boolean +``` + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :------------- | :------------------------------------------------------------------------------------------------------ | +| `state` | `PolygonState` | The `PolygonState` object representing the polygon to check for. The check is typically based on the `id` property. | + +#### Returns + +| Type | Description | +| :-------- | :----------------------------------------------- | +| `Boolean` | Returns `true` if the polygon exists, `false` otherwise. | + +#### Example + +```kotlin +val polygonToCheck = PolygonState(id = "polygon-alpha") + +if (mapController.hasPolygon(polygonToCheck)) { + println("Polygon 'polygon-alpha' is currently on the map.") +} else { + println("Polygon 'polygon-alpha' was not found.") +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polygon/PolygonComponent.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polygon/PolygonComponent.kt.md new file mode 100644 index 00000000..6ac93902 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polygon/PolygonComponent.kt.md @@ -0,0 +1,210 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +# Polygon + +The `Polygon` composable adds a closed shape to the map, defined by a series of coordinates. It can be used to highlight areas, define boundaries, or create other custom shapes. Polygons can have solid fill and stroke colors, and can also contain interior holes. + +There are three overloads for the `Polygon` composable, allowing you to create a polygon from a list of points, from a rectangular bound, or from a pre-configured `PolygonState` object. + +--- + +## Polygon (from points) + +This is the primary composable for creating a custom-shaped polygon on the map. You define the polygon's outer boundary by providing a list of geographical points. + +### Signature + +```kotlin +@Composable +fun MapViewScope.Polygon( + points: List, + holes: List> = emptyList(), + id: String? = null, + strokeColor: Color = Color.Black, + strokeWidth: Dp = 1.dp, + fillColor: Color = Color.Transparent, + geodesic: Boolean = false, + zIndex: Int = 0, + extra: Serializable? = null, + onClick: OnPolygonEventHandler? = null, +) +``` + +### Description + +This composable draws a polygon on the map defined by a list of vertices for its outer boundary and an optional list of holes. The polygon's appearance and behavior, such as colors, stroke width, and click handling, can be customized. The shape is automatically closed by connecting the last point to the first. + +### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `points` | `List` | **Required.** A list of vertices for the polygon's outer boundary. The list must contain at least three points. | +| `holes` | `List>` | A list of hole definitions. Each hole is itself a list of vertices defining an inner boundary. Defaults to `emptyList()`. | +| `id` | `String?` | An optional unique identifier for the polygon. Defaults to `null`. | +| `strokeColor` | `Color` | The color of the polygon's outline. Defaults to `Color.Black`. | +| `strokeWidth` | `Dp` | The width of the polygon's outline. Defaults to `1.dp`. | +| `fillColor` | `Color` | The color inside the polygon. Defaults to `Color.Transparent`. | +| `geodesic` | `Boolean` | If `true`, the polygon's edges are drawn as the shortest path on the Earth's surface (a geodesic line). If `false`, edges are drawn as straight lines on the map's 2D projection. Defaults to `false`. | +| `zIndex` | `Int` | The stacking order of this polygon relative to other map overlays. Polygons with a higher `zIndex` are drawn on top of those with a lower `zIndex`. Defaults to `0`. | +| `extra` | `Serializable?` | Optional serializable data to associate with the polygon, which can be retrieved later (e.g., in the `onClick` handler). Defaults to `null`. | +| `onClick` | `OnPolygonEventHandler?` | A callback lambda that is invoked when the user clicks on the polygon. The handler receives the polygon's `id` and `extra` data. Defaults to `null`. | + +### Example + +Here is an example of creating a triangular polygon with a rectangular hole inside. + +```kotlin +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp +import com.mapconductor.core.MapViewScope +import com.mapconductor.core.features.GeoPoint + +@Composable +fun MapViewScope.MyPolygonExample() { + // Define the outer boundary of the polygon (a triangle) + val outerPoints = listOf( + GeoPoint(40.7128, -74.0060), // New York + GeoPoint(34.0522, -118.2437), // Los Angeles + GeoPoint(29.7604, -95.3698) // Houston + ) + + // Define a hole inside the polygon + val holePoints = listOf( + GeoPoint(35.0, -98.0), + GeoPoint(35.0, -99.0), + GeoPoint(36.0, -99.0), + GeoPoint(36.0, -98.0) + ) + + Polygon( + points = outerPoints, + holes = listOf(holePoints), + strokeColor = Color.Blue, + strokeWidth = 3.dp, + fillColor = Color.Blue.copy(alpha = 0.3f), + geodesic = true, + zIndex = 1, + onClick = { id, extra -> + println("Polygon clicked! ID: $id") + } + ) +} +``` + +--- + +## Polygon (from bounds) + +A convenience composable for creating a rectangular polygon from a `GeoRectBounds` object. + +### Signature + +```kotlin +@Composable +fun MapViewScope.Polygon( + bounds: GeoRectBounds, + id: String? = null, + strokeColor: Color = Color.Black, + strokeWidth: Dp = 1.dp, + fillColor: Color = Color.Transparent, + geodesic: Boolean = false, + zIndex: Int = 0, + extra: Serializable? = null, + onClick: OnPolygonEventHandler? = null, +) +``` + +### Description + +This composable simplifies the creation of a rectangular polygon. It takes a `GeoRectBounds` object and automatically generates the four corner points to draw the rectangle on the map. + +### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `bounds` | `GeoRectBounds` | **Required.** The geographical rectangle that defines the polygon's shape. | +| `id` | `String?` | An optional unique identifier for the polygon. Defaults to `null`. | +| `strokeColor` | `Color` | The color of the polygon's outline. Defaults to `Color.Black`. | +| `strokeWidth` | `Dp` | The width of the polygon's outline. Defaults to `1.dp`. | +| `fillColor` | `Color` | The color inside the polygon. Defaults to `Color.Transparent`. | +| `geodesic` | `Boolean` | If `true`, the polygon's edges are drawn as geodesic lines. Defaults to `false`. | +| `zIndex` | `Int` | The stacking order of this polygon relative to other map overlays. Defaults to `0`. | +| `extra` | `Serializable?` | Optional serializable data to associate with the polygon. Defaults to `null`. | +| `onClick` | `OnPolygonEventHandler?` | A callback lambda that is invoked when the user clicks on the polygon. Defaults to `null`. | + +### Example + +```kotlin +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp +import com.mapconductor.core.MapViewScope +import com.mapconductor.core.features.GeoPoint +import com.mapconductor.core.features.GeoRectBounds + +@Composable +fun MapViewScope.MyBoundedPolygonExample() { + val coloradoBounds = GeoRectBounds( + northEast = GeoPoint(41.0, -102.05), + southWest = GeoPoint(37.0, -109.05) + ) + + Polygon( + bounds = coloradoBounds, + strokeColor = Color.Red, + strokeWidth = 2.dp, + fillColor = Color.Red.copy(alpha = 0.2f), + id = "colorado-boundary" + ) +} +``` + +--- + +## Polygon (from state) + +An advanced composable that adds a polygon to the map using a pre-configured `PolygonState` object. + +### Signature + +```kotlin +@Composable +fun MapViewScope.Polygon(state: PolygonState) +``` + +### Description + +This composable is typically used for advanced scenarios where the polygon's state is managed externally. It adds a polygon defined by the given `PolygonState` to the map and handles its lifecycle, adding it on composition and removing it on disposal. + +### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `state` | `PolygonState` | **Required.** The state object that defines all properties of the polygon, including its points, holes, and styling. | + +### Example + +```kotlin +import androidx.compose.runtime.remember +import androidx.compose.ui.graphics.Color +import com.mapconductor.core.MapViewScope +import com.mapconductor.core.features.GeoPoint +import com.mapconductor.core.polygon.PolygonState + +@Composable +fun MapViewScope.MyStatefulPolygonExample() { + val polygonState = remember { + PolygonState( + points = listOf( + GeoPoint(40.7128, -74.0060), + GeoPoint(34.0522, -118.2437), + GeoPoint(29.7604, -95.3698) + ), + fillColor = Color.Green.copy(alpha = 0.5f), + id = "stateful-polygon" + ) + } + + // The polygon is drawn on the map and its lifecycle is managed automatically. + Polygon(state = polygonState) +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polygon/PolygonController.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polygon/PolygonController.kt.md new file mode 100644 index 00000000..19aed7fb --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polygon/PolygonController.kt.md @@ -0,0 +1,211 @@ +# PolygonController + +## Description + +The `PolygonController` is an abstract base class responsible for managing a collection of polygons on a map. It orchestrates the addition, update, and removal of polygons, handles user interactions like clicks, and synchronizes the polygon state with the map view. + +This controller implements a diffing mechanism to efficiently update the map, only applying changes, additions, or removals as needed. It delegates the platform-specific rendering to a `PolygonOverlayRendererInterface` and state management to a `PolygonManagerInterface`. + +All data modification operations (`add`, `update`, `clear`) are thread-safe, ensuring that concurrent modifications do not corrupt the state. + +### Generic Parameters + +| Name | Description | +| :--- | :--- | +| `ActualPolygon` | The concrete polygon type of the underlying map SDK (e.g., `com.google.android.gms.maps.model.Polygon`). | + +## Constructor + +### Signature + +```kotlin +abstract class PolygonController( + val polygonManager: PolygonManagerInterface, + open val renderer: PolygonOverlayRendererInterface, + override var clickListener: OnPolygonEventHandler? = null, +) +``` + +### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `polygonManager` | `PolygonManagerInterface` | An instance that manages the lifecycle and state of polygon entities. | +| `renderer` | `PolygonOverlayRendererInterface` | An instance responsible for the platform-specific drawing of polygons on the map. | +| `clickListener` | `OnPolygonEventHandler?` | An optional global event handler that is invoked when any polygon managed by this controller is clicked. Defaults to `null`. | + +## Properties + +### clickListener + +A global event handler that is invoked when any polygon managed by this controller is clicked. This is called in addition to any specific `onClick` handler defined in the polygon's `PolygonState`. + +**Signature** +```kotlin +open var clickListener: OnPolygonEventHandler? +``` + +### zIndex + +The z-index of the polygon layer, which determines its drawing order relative to other map overlays. Higher values are drawn on top. + +**Signature** +```kotlin +override val zIndex: Int = 3 +``` + +## Methods + +### add + +Asynchronously adds, updates, or removes polygons to match the provided list of `PolygonState` objects. The method performs a diffing operation to efficiently determine which polygons are new, which have been modified, and which should be removed. All operations are performed atomically. + +**Signature** +```kotlin +override suspend fun add(data: List) +``` + +**Parameters** + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `data` | `List` | The complete and final list of polygon states to be displayed on the map. | + +### update + +Asynchronously updates a single polygon based on its new `PolygonState`. If the state has not changed (based on an internal `fingerPrint` comparison), the operation is skipped to improve performance. + +**Signature** +```kotlin +override suspend fun update(state: PolygonState) +``` + +**Parameters** + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `state` | `PolygonState` | The new state for the polygon to be updated. The polygon is identified by `state.id`. | + +### clear + +Asynchronously removes all polygons currently managed by this controller from the map. + +**Signature** +```kotlin +override suspend fun clear() +``` + +### find + +Finds the topmost polygon entity at a given geographical position. This is useful for identifying which polygon a user has tapped on. + +**Signature** +```kotlin +override fun find(position: GeoPointInterface): PolygonEntityInterface? +``` + +**Parameters** + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `position` | `GeoPointInterface` | The geographical coordinate to search at. | + +**Returns** + +| Type | Description | +| :--- | :--- | +| `PolygonEntityInterface?` | The found polygon entity, or `null` if no polygon exists at the specified position. | + +### dispatchClick + +Dispatches a click event to the appropriate listeners. This method invokes both the polygon-specific `onClick` handler (if defined in its `PolygonState`) and the controller's global `clickListener`. + +**Signature** +```kotlin +fun dispatchClick(event: PolygonEvent) +``` + +**Parameters** + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `event` | `PolygonEvent` | The click event object, containing details about the clicked polygon. | + +### onCameraChanged + +A callback invoked when the map camera position changes. The base implementation is empty and is intended to be overridden by subclasses for custom logic, such as implementing level-of-detail rendering. + +**Signature** +```kotlin +override suspend fun onCameraChanged(mapCameraPosition: MapCameraPosition) +``` + +**Parameters** + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `mapCameraPosition` | `MapCameraPosition` | An object containing the new camera position, zoom, tilt, and bearing. | + +### destroy + +Cleans up resources used by the controller. The base implementation is empty, but subclasses should override it to release any native resources or listeners. + +**Signature** +```kotlin +override fun destroy() +``` + +## Example + +Here is an example of how to create a concrete implementation of `PolygonController` for Google Maps and use it. + +```kotlin +import com.google.android.gms.maps.model.Polygon as GoogleMapPolygon +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch + +// 1. Define a concrete controller class +class GoogleMapPolygonController( + polygonManager: PolygonManagerInterface, + renderer: PolygonOverlayRendererInterface, + clickListener: OnPolygonEventHandler? = null +) : PolygonController(polygonManager, renderer, clickListener) { + + // Optionally, override methods for custom behavior + override suspend fun onCameraChanged(mapCameraPosition: MapCameraPosition) { + super.onCameraChanged(mapCameraPosition) + // Add custom logic, e.g., simplify polygon shapes on zoom out + println("Camera changed: zoom=${mapCameraPosition.zoom}") + } +} + +// 2. Instantiate and use the controller +fun setupPolygonController( + manager: PolygonManagerInterface, + renderer: PolygonOverlayRendererInterface +) { + val polygonController = GoogleMapPolygonController(manager, renderer) + + // Define the states for the polygons you want to display + val polygonState1 = PolygonState(id = "polygon-1", points = listOf(...)) + val polygonState2 = PolygonState(id = "polygon-2", points = listOf(...)) + val allPolygons = listOf(polygonState1, polygonState2) + + // Use a coroutine to add the polygons to the map + CoroutineScope(Dispatchers.Main).launch { + polygonController.add(allPolygons) + } + + // Later, to update a single polygon + val updatedState1 = polygonState1.copy(fillColor = Color.BLUE) + CoroutineScope(Dispatchers.Main).launch { + polygonController.update(updatedState1) + } + + // To clear all polygons + CoroutineScope(Dispatchers.Main).launch { + polygonController.clear() + } +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polygon/PolygonEntity.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polygon/PolygonEntity.kt.md new file mode 100644 index 00000000..bba25da5 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polygon/PolygonEntity.kt.md @@ -0,0 +1,101 @@ +Of course! Here is the high-quality SDK documentation for the provided Kotlin code snippet. + +*** + +## PolygonEntityInterface + +### Signature + +```kotlin +interface PolygonEntityInterface { + val polygon: ActualPolygon + val state: PolygonState + val fingerPrint: PolygonFingerPrint +} +``` + +### Description + +The `PolygonEntityInterface` defines the contract for an object that represents a polygon entity within the system. It encapsulates a geometric polygon object along with its current state and a unique fingerprint derived from that state. This interface is designed to be generic, allowing it to work with any specific polygon implementation. + +### Generic Type Parameters + +| Parameter | Description | +|---|---| +| `` | The specific class or type of the polygon object being represented (e.g., a map-specific polygon class). | + +### Properties + +| Property | Type | Description | +|---|---|---| +| `polygon` | `ActualPolygon` | The underlying geometric polygon object. | +| `state` | `PolygonState` | The current state of the polygon (e.g., `Added`, `Modified`, `Unchanged`). | +| `fingerPrint` | `PolygonFingerPrint` | A unique identifier representing the polygon's current state. This is useful for efficient state comparison and tracking changes. | + +*** + +## PolygonEntity + +### Signature + +```kotlin +data class PolygonEntity( + override val polygon: ActualPolygon, + override val state: PolygonState, +) : PolygonEntityInterface +``` + +### Description + +`PolygonEntity` is a data class that provides a concrete implementation of the `PolygonEntityInterface`. It serves as a simple, immutable container for a polygon object and its associated state. + +A key feature of this class is that it automatically computes the `fingerPrint` property by calling the `fingerPrint()` method on the provided `state` object. + +### Parameters + +| Parameter | Type | Description | +|---|---|---| +| `polygon` | `ActualPolygon` | The underlying geometric polygon object. | +| `state` | `PolygonState` | The current state of the polygon. The `fingerPrint` for this entity will be derived from this state object. | + +### Properties + +| Property | Type | Description | +|---|---|---| +| `polygon` | `ActualPolygon` | The underlying geometric polygon object passed in the constructor. | +| `state` | `PolygonState` | The current state of the polygon passed in the constructor. | +| `fingerPrint` | `PolygonFingerPrint` | **(Read-only)** A unique identifier automatically generated by invoking the `fingerPrint()` method on the `state` object. | + +### Example + +Here is an example of how to create an instance of `PolygonEntity`. This assumes the existence of a `MapboxPolygon` class for the geometry and a `PolygonState.Added` object for the state. + +```kotlin +// Assume these classes are defined elsewhere +// data class MapboxPolygon(val id: String, val coordinates: List>) +// sealed class PolygonState { +// object Added : PolygonState() { fun fingerPrint() = PolygonFingerPrint("added_fp") } +// // ... other states +// } +// data class PolygonFingerPrint(val value: String) + +// 1. Define the actual polygon object +val myPolygon = MapboxPolygon( + id = "polygon-123", + coordinates = listOf(0.0 to 0.0, 10.0 to 0.0, 10.0 to 10.0, 0.0 to 10.0) +) + +// 2. Define the state for the polygon +val initialState = PolygonState.Added + +// 3. Create an instance of PolygonEntity +val polygonEntity = PolygonEntity( + polygon = myPolygon, + state = initialState +) + +// 4. Access the properties +println("Polygon ID: ${polygonEntity.polygon.id}") // "Polygon ID: polygon-123" +println("Polygon State: ${polygonEntity.state}") // "Polygon State: PolygonState$Added@..." +println("Fingerprint: ${polygonEntity.fingerPrint.value}") // "Fingerprint: added_fp" +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polygon/PolygonManager.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polygon/PolygonManager.kt.md new file mode 100644 index 00000000..27176155 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polygon/PolygonManager.kt.md @@ -0,0 +1,227 @@ +# PolygonManager<ActualPolygon> + +## Description + +The `PolygonManager` class provides a comprehensive solution for managing a collection of polygon entities on a map. It offers standard CRUD (Create, Read, Update, Delete) operations and a powerful spatial query method, `find`, to determine which polygon contains a specific geographic coordinate. + +This manager is designed for performance and accuracy, with built-in support for: +- **Z-indexing**: Prioritizes polygons with a higher `zIndex` when multiple polygons overlap. +- **Geodesic correctness**: Accurately performs hit-testing on geodesic polygons by densifying their edges to follow the curvature of the Earth. +- **Complex polygons**: Correctly handles polygons with holes and those that cross the antimeridian. + +The class is generic, allowing it to work with any underlying platform-specific polygon implementation (e.g., Google Maps Polygons, Mapbox Polygons) by specifying the `ActualPolygon` type. + +## Type Parameters + +| Name | Description | +|---|---| +| `` | The concrete type of the underlying polygon object managed by the entities (e.g., `com.google.android.gms.maps.model.Polygon`). | + +## Constructor + +### PolygonManager() + +Creates a new, empty instance of the `PolygonManager`. + +#### Signature +```kotlin +PolygonManager() +``` + +## Methods + +### registerEntity +Registers a polygon entity with the manager. If an entity with the same ID already exists, it will be overwritten. + +#### Signature +```kotlin +fun registerEntity(entity: PolygonEntityInterface) +``` + +#### Parameters +| Parameter | Type | Description | +|---|---|---| +| `entity` | `PolygonEntityInterface` | The polygon entity to register. The entity's ID is used as the key for storage. | + +--- + +### removeEntity +Removes a polygon entity from the manager based on its unique ID. + +#### Signature +```kotlin +fun removeEntity(id: String): PolygonEntityInterface? +``` + +#### Parameters +| Parameter | Type | Description | +|---|---|---| +| `id` | `String` | The unique identifier of the polygon entity to remove. | + +#### Returns +`PolygonEntityInterface?`: The removed entity instance, or `null` if no entity with the specified ID was found. + +--- + +### getEntity +Retrieves a polygon entity by its unique ID. + +#### Signature +```kotlin +fun getEntity(id: String): PolygonEntityInterface? +``` + +#### Parameters +| Parameter | Type | Description | +|---|---|---| +| `id` | `String` | The unique identifier of the polygon entity to retrieve. | + +#### Returns +`PolygonEntityInterface?`: The entity instance corresponding to the given ID, or `null` if not found. + +--- + +### hasEntity +Checks if a polygon entity with the specified ID is registered with the manager. + +#### Signature +```kotlin +fun hasEntity(id: String): Boolean +``` + +#### Parameters +| Parameter | Type | Description | +|---|---|---| +| `id` | `String` | The unique identifier to check for. | + +#### Returns +`Boolean`: Returns `true` if an entity with the given ID exists, `false` otherwise. + +--- + +### allEntities +Returns a list of all polygon entities currently registered with the manager. + +#### Signature +```kotlin +fun allEntities(): List> +``` + +#### Returns +`List>`: A list containing all registered entity instances. The order of entities in the list is not guaranteed. + +--- + +### clear +Removes all polygon entities from the manager, leaving it in an empty state. + +#### Signature +```kotlin +fun clear() +``` + +--- + +### find +Finds the top-most polygon entity that contains the given geographic coordinate. + +The search algorithm iterates through all registered polygons, sorted by their `zIndex` in descending order. This ensures that if multiple polygons overlap at the given position, the one with the highest `zIndex` (visually "on top") is returned. + +The method uses the winding number algorithm for a robust point-in-polygon test. It correctly handles complex cases, including: +- **Holes:** A point located within a polygon's hole is considered outside the polygon. +- **Antimeridian:** Polygons that cross the 180° longitude line are handled correctly. +- **Geodesic Edges:** If an entity's `geodesic` property is `true`, its edges are densified to approximate great-circle paths on the globe, leading to a more accurate hit test. +- **On-Edge Points:** A point lying on the boundary of a polygon is considered to be inside it. + +#### Signature +```kotlin +fun find(position: GeoPointInterface): PolygonEntityInterface? +``` + +#### Parameters +| Parameter | Type | Description | +|---|---|---| +| `position` | `GeoPointInterface` | The geographic coordinate (latitude and longitude) to test. | + +#### Returns +`PolygonEntityInterface?`: The entity of the containing polygon with the highest `zIndex`, or `null` if the point is not inside any registered polygon. + +## Example + +```kotlin +import com.mapconductor.core.features.GeoPointInterface +import com.mapconductor.core.polygon.PolygonManager +import com.mapconductor.core.polygon.PolygonEntityInterface + +// Helper classes and interfaces for a self-contained example. +// In a real application, these would be part of your project structure. +data class GeoPoint(override val latitude: Double, override val longitude: Double) : GeoPointInterface + +interface PolygonStateInterface { + val id: String + val points: List + val holes: List> + val geodesic: Boolean + val zIndex: Int +} + +data class MyPolygonState( + override val id: String, + override val points: List, + override val holes: List> = emptyList(), + override val geodesic: Boolean = false, + override val zIndex: Int = 0 +) : PolygonStateInterface + +class MyPolygonEntity(override val state: MyPolygonState) : PolygonEntityInterface { + override val actualPolygon: Unit? = null // Represents a platform-specific polygon object +} + +fun main() { + // 1. Initialize the manager. The type parameter is used for this example. + val polygonManager = PolygonManager() + + // 2. Define and register a polygon entity (a square around Denver, CO). + val denverZonePoints = listOf( + GeoPoint(40.0, -105.2), + GeoPoint(40.0, -104.8), + GeoPoint(39.5, -104.8), + GeoPoint(39.5, -105.2) + ) + val denverZoneState = MyPolygonState(id = "denver-zone", points = denverZonePoints, zIndex = 1) + val denverZoneEntity = MyPolygonEntity(denverZoneState) + + polygonManager.registerEntity(denverZoneEntity) + println("Registered entity with ID: ${denverZoneEntity.state.id}") + + // 3. Use the find() method to check for points. + val pointInside = GeoPoint(39.7, -105.0) // A point inside the square + val pointOutside = GeoPoint(41.0, -105.0) // A point north of the square + + val foundEntity = polygonManager.find(pointInside) + println("Find inside point: Found entity with ID '${foundEntity?.state?.id}'") + + val notFoundEntity = polygonManager.find(pointOutside) + println("Find outside point: Found entity with ID '${notFoundEntity?.state?.id}'") + + // 4. Use other management methods. + val retrievedEntity = polygonManager.getEntity("denver-zone") + println("Get entity 'denver-zone': Success = ${retrievedEntity != null}") + + val hasEntity = polygonManager.hasEntity("denver-zone") + println("Manager has 'denver-zone': $hasEntity") + + // 5. Remove the entity and verify. + polygonManager.removeEntity("denver-zone") + val hasEntityAfterRemove = polygonManager.hasEntity("denver-zone") + println("Manager has 'denver-zone' after removal: $hasEntityAfterRemove") +} + +// Expected Output: +// Registered entity with ID: denver-zone +// Find inside point: Found entity with ID 'denver-zone' +// Find outside point: Found entity with ID 'null' +// Get entity 'denver-zone': Success = true +// Manager has 'denver-zone': true +// Manager has 'denver-zone' after removal: false +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polygon/PolygonOverlay.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polygon/PolygonOverlay.kt.md new file mode 100644 index 00000000..4b4fc393 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polygon/PolygonOverlay.kt.md @@ -0,0 +1,93 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +--- + +## `LocalPolygonCollector` + +A `CompositionLocal` used to provide a `ChildCollector` down the Composable tree. This collector is essential for gathering `Polygon` composables declared within a `MapView`. + +### Signature + +```kotlin +val LocalPolygonCollector: ProvidableCompositionLocal> +``` + +### Description + +`LocalPolygonCollector` is a mechanism used internally by the `MapView` to manage `Polygon` children. Any `Polygon` composable must be a direct or indirect child of a `MapView` composable. + +If you attempt to use a `Polygon` outside of a `MapView` hierarchy, the app will throw an `IllegalStateException` with the message: "Polygon must be under the ". + +### Example + +This shows the correct and incorrect placement of a `Polygon` composable. + +```kotlin +// Correct: Polygon is a descendant of MapView +MapView( + cameraPosition = /* ... */ +) { + Polygon( + points = listOf( + LatLng(34.0522, -118.2437), + LatLng(34.0532, -118.2447), + LatLng(34.0542, -118.2427) + ) + ) +} + +// Incorrect: This will cause a runtime crash +Polygon( + points = listOf(/* ... */) +) +``` + +## `PolygonOverlay` + +A map overlay class responsible for managing and rendering a collection of polygons on the map. It observes a reactive stream of polygon states and delegates the rendering task to a capable map controller. + +### Signature + +```kotlin +class PolygonOverlay( + override val flow: StateFlow>, +) : MapOverlayInterface +``` + +### Description + +`PolygonOverlay` acts as a layer on the map dedicated to drawing polygons. It subscribes to a `StateFlow` that provides the state of all polygons to be displayed. When the state updates, this class works with the `MapViewControllerInterface` to efficiently update the polygons on the map view, handling additions, removals, and modifications. This class is typically managed by the `MapView` and not instantiated directly by the user. + +### Constructor + +#### `PolygonOverlay(flow)` + +Creates a new instance of the polygon overlay. + +| Parameter | Type | Description | +| :-------- | :----------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------- | +| `flow` | `StateFlow>` | A reactive stream that emits the current state of all polygons to be displayed. The map's key is a unique ID for the polygon, and the value is its `PolygonState`. | + +### Methods + +#### `render` + +Renders the given polygon data onto the map using the provided controller. + +**Signature** +```kotlin +suspend fun render( + data: MutableMap, + controller: MapViewControllerInterface, +) +``` + +**Description** +This function is called by the map rendering engine when polygon updates are needed. It checks if the `controller` implements the `PolygonCapableInterface`. If it does, it passes the list of `PolygonState` objects to the controller's `compositionPolygons` method for native rendering. + +**Parameters** + +| Parameter | Type | Description | +| :----------- | :--------------------------------- | :----------------------------------------------------------------------------- | +| `data` | `MutableMap` | The current map of polygon states to be rendered. | +| `controller` | `MapViewControllerInterface` | The map view controller responsible for executing rendering commands. | \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polygon/PolygonOverlayRendererInterface.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polygon/PolygonOverlayRendererInterface.kt.md new file mode 100644 index 00000000..a7381bbe --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polygon/PolygonOverlayRendererInterface.kt.md @@ -0,0 +1,205 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +--- + +# PolygonOverlayRendererInterface + +## Signature + +```kotlin +interface PolygonOverlayRendererInterface +``` + +## Description + +The `PolygonOverlayRendererInterface` defines a contract for rendering and managing polygon overlays on a map. It abstracts the platform-specific details of polygon manipulation, allowing for a consistent approach across different map providers (e.g., Google Maps, Mapbox). + +Implement this interface to create a custom renderer that handles the lifecycle of polygons, including their creation, update, and removal from the map view. The renderer operates in batches to ensure efficient updates. + +### Type Parameters + +| Name | Description | +| --------------- | ------------------------------------------------------------------------------------------------------- | +| `ActualPolygon` | The generic type representing the platform-specific polygon object (e.g., `com.google.android.gms.maps.model.Polygon`). | + +--- + +## Nested Interfaces + +### AddParamsInterface + +Represents the parameters required to add a new polygon to the map. + +#### Signature + +```kotlin +interface AddParamsInterface +``` + +#### Parameters + +| Name | Type | Description | +| ------- | -------------- | ------------------------------------------------ | +| `state` | `PolygonState` | The state object containing all properties for the new polygon. | + +--- + +### ChangeParamsInterface + +Represents the parameters required to update an existing polygon. It provides both the previous and current states to allow for efficient, targeted updates. + +#### Signature + +```kotlin +interface ChangeParamsInterface +``` + +#### Type Parameters + +| Name | Description | +| --------------- | ------------------------------------------------------------------------------------------------------- | +| `ActualPolygon` | The generic type representing the platform-specific polygon object. | + +#### Parameters + +| Name | Type | Description | +| --------- | ----------------------------------------- | ------------------------------------------------------------------------ | +| `current` | `PolygonEntityInterface` | The entity representing the new, updated state of the polygon. | +| `prev` | `PolygonEntityInterface` | The entity representing the previous state of the polygon before the change. | + +--- + +## Functions + +### onAdd + +This function is called to add a batch of new polygons to the map. The implementation should create the native polygon objects and add them to the map view. + +#### Signature + +```kotlin +suspend fun onAdd(data: List): List +``` + +#### Parameters + +| Name | Type | Description | +| ------ | ---------------------------- | ------------------------------------------------------ | +| `data` | `List` | A list of parameter objects for the polygons to be added. | + +#### Returns + +`List`: A list of the newly created, platform-specific polygon objects. The size and order of this list must match the input `data` list. If a polygon fails to be created, the corresponding element in the list should be `null`. + +--- + +### onChange + +This function is called to process a batch of updates for existing polygons. The implementation should compare the `prev` and `current` states for each item and apply only the necessary changes to the native polygon objects for optimal performance. + +#### Signature + +```kotlin +suspend fun onChange(data: List>): List +``` + +#### Parameters + +| Name | Type | Description | +| ------ | --------------------------------------------- | ------------------------------------------------------------------------ | +| `data` | `List>` | A list of objects, each containing the previous and current states for a polygon to be updated. | + +#### Returns + +`List`: A list of the updated, platform-specific polygon objects. The size and order of this list must match the input `data` list. + +--- + +### onRemove + +This function is called to remove a batch of polygons from the map. + +#### Signature + +```kotlin +suspend fun onRemove(data: List>) +``` + +#### Parameters + +| Name | Type | Description | +| ------ | ------------------------------------------- | ------------------------------------------------ | +| `data` | `List>` | A list of polygon entities to be removed from the map. | + +--- + +### onPostProcess + +A lifecycle callback that is invoked after all `onAdd`, `onChange`, and `onRemove` operations for a single update cycle have been completed. This function can be used for cleanup, finalization, or triggering map-wide updates if necessary. + +#### Signature + +```kotlin +suspend fun onPostProcess() +``` + +--- + +## Example + +Here is a skeleton implementation of `PolygonOverlayRendererInterface` for Google Maps. + +```kotlin +import com.google.android.gms.maps.GoogleMap +import com.google.android.gms.maps.model.Polygon +import com.google.android.gms.maps.model.PolygonOptions + +// Assuming PolygonEntityInterface and PolygonState are defined elsewhere +// data class PolygonEntityInterface(val id: String, val state: PolygonState, val actual: ActualPolygon?) +// data class PolygonState(val points: List, val fillColor: Int, val strokeColor: Int, val zIndex: Float) + +class GoogleMapsPolygonRenderer(private val map: GoogleMap) : PolygonOverlayRendererInterface { + + override suspend fun onAdd(data: List): List { + return data.map { params -> + val state = params.state + val polygonOptions = PolygonOptions() + .addAll(state.points) + .fillColor(state.fillColor) + .strokeColor(state.strokeColor) + .zIndex(state.zIndex) + + map.addPolygon(polygonOptions) + } + } + + override suspend fun onChange(data: List>): List { + return data.map { params -> + val polygon = params.prev.actual + val newState = params.current.state + + polygon?.apply { + // Apply changes based on the new state + points = newState.points + fillColor = newState.fillColor + strokeColor = newState.strokeColor + zIndex = newState.zIndex + } + + polygon // Return the updated polygon + } + } + + override suspend fun onRemove(data: List>) { + data.forEach { entity -> + entity.actual?.remove() + } + } + + override suspend fun onPostProcess() { + // Optional: Perform any cleanup or finalization after a batch update. + // For example, you could refresh the map view if needed. + println("Polygon batch processing complete.") + } +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polygon/PolygonRasterTileRenderer.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polygon/PolygonRasterTileRenderer.kt.md new file mode 100644 index 00000000..ed64f90d --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polygon/PolygonRasterTileRenderer.kt.md @@ -0,0 +1,122 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +--- + +# PolygonRasterTileRenderer + +The `PolygonRasterTileRenderer` class is a tile provider responsible for rendering complex polygons onto raster map tiles. It implements the `TileProviderInterface`, allowing it to be integrated into a map's rendering pipeline. + +This renderer can draw polygons with solid fills, strokes, and interior holes. It supports both standard rhumb line and geodesic (great-circle) edges, making it suitable for displaying large geographical areas accurately. By rendering polygons into image tiles on demand, it offers an efficient way to display large, static geometric data on a map. + +The renderer's properties (such as `points`, `fillColor`, etc.) are thread-safe and can be updated dynamically, with changes reflected in subsequently rendered tiles. + +## Signature + +```kotlin +class PolygonRasterTileRenderer( + private val tileSizePx: Int = 256, +) : TileProviderInterface +``` + +## Constructor + +### `PolygonRasterTileRenderer(tileSizePx)` + +Creates a new instance of the polygon tile renderer. + +#### Parameters + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `tileSizePx` | `Int` | `256` | The width and height of the tiles to be rendered, in pixels. | + +## Properties + +The following properties can be configured to customize the appearance and behavior of the rendered polygon. + +| Property | Type | Description | +|---|---|---| +| `points` | `List` | The list of geographic points that define the outer boundary of the polygon. The polygon will not be rendered if this list is empty. | +| `holes` | `List>` | A list of inner rings (holes) to be cut out from the main polygon. Each element in the list is another list of `GeoPointInterface` defining a single hole. | +| `fillColor` | `Int` | The ARGB integer color used to fill the polygon's area. Use `android.graphics.Color` constants or a custom integer value. Defaults to `Color.TRANSPARENT`. | +| `strokeColor` | `Int` | The ARGB integer color for the polygon's outline. The stroke is only drawn if `strokeWidthPx` is greater than 0 and the color is not fully transparent. Defaults to `Color.TRANSPARENT`. | +| `strokeWidthPx` | `Float` | The width of the polygon's outline in pixels. If set to `0f`, no stroke will be drawn. Defaults to `0f`. | +| `geodesic` | `Boolean` | If `true`, the polygon edges are rendered as geodesic lines (the shortest path on the Earth's surface), which appear as curves on a Mercator projection. If `false`, edges are rendered as straight rhumb lines on the map. Defaults to `false`. | +| `outerBounds` | `GeoRectBounds?` | An optional rectangular bounding box for the polygon. If provided, the renderer will quickly skip rendering tiles that do not intersect with these bounds, significantly improving performance. It is highly recommended to set this for large or complex polygons. | + +## Methods + +### renderTile + +Renders a single map tile based on the provided `TileRequest`. This method is typically called by the map's tile rendering engine. It calculates which part of the polygon is visible on the requested tile, projects the coordinates, and draws the polygon shape onto a bitmap. The final bitmap is then compressed into a PNG byte array. + +#### Signature + +```kotlin +fun renderTile(request: TileRequest): ByteArray? +``` + +#### Parameters + +| Parameter | Type | Description | +|-----------|------|-------------| +| `request` | `TileRequest` | An object containing the tile's zoom level (`z`) and coordinates (`x`, `y`). | + +#### Returns + +**Type**: `ByteArray?` + +A `ByteArray` containing the rendered tile as a PNG image. +- Returns a pre-rendered transparent tile if the `points` list is empty or if the tile is outside the `outerBounds`. +- Returns `null` if the requested tile coordinates are invalid (e.g., `y` is out of range for the given zoom level). + +## Example + +The following example demonstrates how to create and configure a `PolygonRasterTileRenderer` to draw a polygon with a hole. + +```kotlin +import android.graphics.Color +import com.mapconductor.core.features.GeoPoint +import com.mapconductor.core.polygon.PolygonRasterTileRenderer + +// 1. Instantiate the renderer +val polygonRenderer = PolygonRasterTileRenderer(tileSizePx = 512) + +// 2. Define the outer boundary of the polygon (e.g., a square) +val outerRing = listOf( + GeoPoint(40.7128, -74.0060), // New York City + GeoPoint(34.0522, -118.2437), // Los Angeles + GeoPoint(25.7617, -80.1918), // Miami + GeoPoint(40.7128, -74.0060) // Close the ring +) + +// 3. Define an inner boundary for a hole (e.g., a triangle inside the square) +val holeRing = listOf( + GeoPoint(39.7392, -104.9903), // Denver + GeoPoint(36.1699, -115.1398), // Las Vegas + GeoPoint(33.4484, -112.0740) // Phoenix +) + +// 4. Configure the renderer's properties +polygonRenderer.apply { + points = outerRing + holes = listOf(holeRing) + fillColor = Color.argb(128, 0, 0, 255) // Semi-transparent blue + strokeColor = Color.rgb(0, 0, 139) // Dark blue + strokeWidthPx = 3.0f + geodesic = true // Render with curved lines on the map +} + +// 5. Add the renderer to a tile provider layer on your map +// (The exact API may vary based on your map framework) +// +// mapController.addLayer( +// TileProviderLayer( +// tileProvider = polygonRenderer, +// id = "my-polygon-layer" +// ) +// ) + +// The map will now request tiles from polygonRenderer, which will return +// PNG images of the specified polygon. +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polygon/PolygonUnion.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polygon/PolygonUnion.kt.md new file mode 100644 index 00000000..1e5d5231 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polygon/PolygonUnion.kt.md @@ -0,0 +1,151 @@ +Of course! Here is the high-quality SDK documentation for the provided Kotlin code snippet. + +# Polygon Hole Utilities + +This document provides details on extension functions for the `PolygonState` class, designed to merge and normalize the polygon's interior holes. + +--- + +## `unionHoles()` + +Merges overlapping holes within a `PolygonState` into single, non-overlapping holes. + +### Signature + +```kotlin +fun PolygonState.unionHoles(): PolygonState +``` + +### Description + +The `unionHoles()` function is a non-mutating operation that processes the `holes` of a `PolygonState` object. It identifies any overlapping holes and combines them into a new set of distinct, non-overlapping holes. + +This operation is useful for cleaning up complex polygons that may have been generated with intersecting inner boundaries. The function returns a **new** `PolygonState` instance containing the merged holes, leaving the original object unmodified. + +Key characteristics: +- **Planar Geometry**: The union calculation is performed on a 2D plane (longitude/latitude). For very large polygons or those near the Earth's poles, the results may differ from what would be expected with geodesic calculations. +- **Winding Order**: The resulting holes are normalized to have a clockwise winding order, which is a common convention for polygon holes in many rendering systems. +- **Error Handling**: If the polygon has one or zero holes, or if the union process fails for any reason, the function safely returns the original, unmodified `PolygonState` instance. + +### Parameters + +This is an extension function and does not take any direct parameters. It operates on the `PolygonState` instance it is called on. + +### Returns + +| Type | Description | +|----------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `PolygonState` | A new `PolygonState` instance with merged holes. Returns the original `PolygonState` if it has fewer than two holes or if the union process fails for any reason. | + +### Example + +```kotlin +// Assume a polygonState with two overlapping holes. +// The `holes` property is a List>. +val polygonWithOverlappingHoles: PolygonState = createPolygonWithTwoHoles() + +println("Number of holes before: ${polygonWithOverlappingHoles.holes.size}") +// Expected output: Number of holes before: 2 + +// Union the holes to create a new polygon state +val newPolygonState = polygonWithOverlappingHoles.unionHoles() + +// The original state is not modified +println("Number of holes in original after: ${polygonWithOverlappingHoles.holes.size}") +// Expected output: Number of holes in original after: 2 + +// The new state has the merged holes +println("Number of holes in new state: ${newPolygonState.holes.size}") +// Expected output: Number of holes in new state: 1 +``` + +--- + +## `union()` + +A concise alias for the `unionHoles()` function. + +### Signature + +```kotlin +fun PolygonState.union(): PolygonState +``` + +### Description + +This function is a convenient shorthand for `unionHoles()`. It offers the exact same functionality and behavior but with a shorter name for more readable callsites. + +### Parameters + +None. + +### Returns + +| Type | Description | +|----------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `PolygonState` | A new `PolygonState` instance with merged holes. Returns the original `PolygonState` if it has fewer than two holes or if the union process fails for any reason. | + +### Example + +```kotlin +// Assume a polygonState with overlapping holes +val polygonState: PolygonState = createPolygonWithTwoHoles() + +// Use the alias to union the holes +val resultState = polygonState.union() + +// resultState now contains the single, merged hole +println("Number of holes in result: ${resultState.holes.size}") +// Expected output: Number of holes in result: 1 +``` + +--- + +## `unionHolesInPlace()` + +Performs the hole union operation and modifies the original `PolygonState` object directly. + +### Signature + +```kotlin +fun PolygonState.unionHolesInPlace(): PolygonState +``` + +### Description + +This function provides an in-place, mutating alternative to `unionHoles()`. It calculates the union of any overlapping holes and then updates the `holes` property of the original `PolygonState` object with the result. + +This method is more memory-efficient if you do not need to preserve the polygon's original state, as it avoids allocating a new `PolygonState` object. + +**Note**: For this function to work as expected, the `holes` property of your `PolygonState` class must be a mutable variable (`var`). + +### Parameters + +None. + +### Returns + +| Type | Description | +|----------------|----------------------------------------------------------------------------------------------------------------------------------------| +| `PolygonState` | The original, now-mutated `PolygonState` instance. Returns the same instance unmodified if there was nothing to union or if an error occurred. | + +### Example + +```kotlin +// Assume a polygonState where the `holes` property is a `var` +val polygonState: PolygonState = createMutablePolygonWithTwoHoles() + +println("Number of holes before: ${polygonState.holes.size}") +// Expected output: Number of holes before: 2 + +val returnedState = polygonState.unionHolesInPlace() + +// The original state object has been modified +println("Number of holes after: ${polygonState.holes.size}") +// Expected output: Number of holes after: 1 + +// The returned object is the same instance as the original +val isSameInstance = returnedState === polygonState +println("Is the returned object the same instance? $isSameInstance") +// Expected output: Is the returned object the same instance? true +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polyline/AbstractPolylineOverlayRenderer.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polyline/AbstractPolylineOverlayRenderer.kt.md new file mode 100644 index 00000000..09e22b34 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polyline/AbstractPolylineOverlayRenderer.kt.md @@ -0,0 +1,227 @@ +# AbstractPolylineOverlayRenderer + +### Description + +An abstract base class that provides a foundational implementation for rendering and managing polyline overlays on a map. This class implements the core logic for handling add, change, and remove operations by delegating the platform-specific rendering details to its subclasses. + +Developers should extend this class to create a concrete renderer for a specific map provider (e.g., Google Maps, Mapbox). Subclasses are required to implement the abstract methods for creating, updating, and removing the actual polyline objects on the map. + +### Signature + +```kotlin +abstract class AbstractPolylineOverlayRenderer : PolylineOverlayRendererInterface +``` + +### Abstract Properties + +Subclasses must provide implementations for the following properties. + +| Property | Type | Description | +|---|---|---| +| `holder` | `MapViewHolderInterface<*, *>` | The map view holder instance that this renderer is associated with. | +| `coroutine` | `CoroutineScope` | The coroutine scope used for launching suspend functions and managing their lifecycle. | + +--- + +### Abstract Methods + +Subclasses must implement the following methods to handle platform-specific polyline rendering. + +#### createPolyline + +Creates a new, platform-specific polyline object based on the provided state. + +**Signature** +```kotlin +abstract suspend fun createPolyline(state: PolylineState): ActualPolyline? +``` + +**Parameters** +| Parameter | Type | Description | +|---|---|---| +| `state` | `PolylineState` | The initial state (e.g., points, color, width) for the new polyline. | + +**Returns** +| Type | Description | +|---|---| +| `ActualPolyline?` | The newly created platform-specific polyline object, or `null` if creation fails. | + +--- + +#### updatePolylineProperties + +Updates the properties of an existing polyline on the map. + +**Signature** +```kotlin +abstract suspend fun updatePolylineProperties( + polyline: ActualPolyline, + current: PolylineEntityInterface, + prev: PolylineEntityInterface, +): ActualPolyline? +``` + +**Parameters** +| Parameter | Type | Description | +|---|---|---| +| `polyline` | `ActualPolyline` | The native polyline object to be updated. | +| `current` | `PolylineEntityInterface` | The entity containing the new, updated state of the polyline. | +| `prev` | `PolylineEntityInterface` | The entity containing the previous state of the polyline, for comparison. | + +**Returns** +| Type | Description | +|---|---| +| `ActualPolyline?` | The updated polyline object, or `null` if the update fails. | + +--- + +#### removePolyline + +Removes a polyline from the map. + +**Signature** +```kotlin +abstract suspend fun removePolyline(entity: PolylineEntityInterface) +``` + +**Parameters** +| Parameter | Type | Description | +|---|---|---| +| `entity` | `PolylineEntityInterface` | The polyline entity to be removed. Its `polyline` property contains the native object to remove. | + +--- + +### Methods + +#### onPostProcess + +A lifecycle hook called after a batch of add, change, or remove operations has been processed. This method can be overridden to perform custom actions, such as refreshing the map view. The default implementation is empty. + +**Signature** +```kotlin +override suspend fun onPostProcess() +``` + +--- + +#### onAdd + +Handles the addition of new polylines. This method iterates through the list of `AddParamsInterface` and calls the abstract `createPolyline` method for each item. + +**Signature** +```kotlin +override suspend fun onAdd( + data: List +): List +``` + +**Parameters** +| Parameter | Type | Description | +|---|---|---| +| `data` | `List` | A list of parameters for the polylines to be added. | + +**Returns** +| Type | Description | +|---|---| +| `List` | A list containing the newly created `ActualPolyline` objects, with `null` for any that failed to be created. | + +--- + +#### onChange + +Handles changes to existing polylines. This method iterates through the list of `ChangeParamsInterface` and calls the abstract `updatePolylineProperties` method for each item. + +**Signature** +```kotlin +override suspend fun onChange( + data: List> +): List +``` + +**Parameters** +| Parameter | Type | Description | +|---|---|---| +| `data` | `List>` | A list of parameters describing the changes for each polyline. | + +**Returns** +| Type | Description | +|---|---| +| `List` | A list containing the updated `ActualPolyline` objects, with `null` for any that failed to be updated. | + +--- + +#### onRemove + +Handles the removal of polylines. This method iterates through the list of `PolylineEntityInterface` and calls the abstract `removePolyline` method for each item. + +**Signature** +```kotlin +override suspend fun onRemove(data: List>) +``` + +**Parameters** +| Parameter | Type | Description | +|---|---|---| +| `data` | `List>` | A list of polyline entities to be removed from the map. | + +--- + +### Example + +Here is an example of how to create a concrete renderer for a hypothetical `CustomMapPolyline` object. + +```kotlin +// Assume CustomMapPolyline is the native polyline class for a specific map SDK +class CustomMapPolyline { + // Platform-specific properties and methods +} + +// Concrete implementation of the renderer +class CustomMapPolylineRenderer( + override val holder: MapViewHolderInterface<*, *>, + override val coroutine: CoroutineScope +) : AbstractPolylineOverlayRenderer() { + + // Get the native map object from the holder + private val map = holder.getMap() + + override suspend fun createPolyline(state: PolylineState): CustomMapPolyline? { + // Logic to create a new CustomMapPolyline on the map + val newPolyline = CustomMapPolyline() + // ... set properties on newPolyline from state (points, color, etc.) + // ... add newPolyline to the map + println("Creating polyline with ${state.points.size} points.") + return newPolyline + } + + override suspend fun updatePolylineProperties( + polyline: CustomMapPolyline, + current: PolylineEntityInterface, + prev: PolylineEntityInterface + ): CustomMapPolyline? { + // Logic to update an existing CustomMapPolyline + val currentState = current.state + val prevState = prev.state + + // Example: only update points if they have changed + if (currentState.points != prevState.points) { + // ... polyline.setPoints(currentState.points) + } + + // Example: only update color if it has changed + if (currentState.color != prevState.color) { + // ... polyline.setColor(currentState.color) + } + + println("Updating polyline.") + return polyline + } + + override suspend fun removePolyline(entity: PolylineEntityInterface) { + // Logic to remove the CustomMapPolyline from the map + val polylineToRemove = entity.polyline + // ... map.remove(polylineToRemove) + println("Removing polyline.") + } +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polyline/Polyline.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polyline/Polyline.kt.md new file mode 100644 index 00000000..bc08e25e --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polyline/Polyline.kt.md @@ -0,0 +1,216 @@ +Of course! Here is the high-quality SDK documentation for the provided Kotlin code snippet. + +--- + +# PolylineState + +The `PolylineState` class manages the state and appearance of a single polyline on the map. It is designed for use within a Jetpack Compose environment, making its properties observable for reactive UI updates. + +This class encapsulates all properties of a polyline, such as its geographical points, color, width, and behavior. It also provides mechanisms for handling user interactions like clicks. + +## `PolylineState` + +### Signature + +```kotlin +class PolylineState( + points: List, + id: String? = null, + strokeColor: Color = Color.Black, + strokeWidth: Dp = 1.dp, + geodesic: Boolean = false, + zIndex: Int = 0, + extra: Serializable? = null, + onClick: OnPolylineEventHandler? = null, +) : ComponentState +``` + +### Description + +Creates and manages the state for a polyline component on the map. If an `id` is not provided, a unique ID is automatically generated based on the polyline's properties. All properties are mutable and observable by the Compose runtime. + +### Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| `points` | `List` | A list of `GeoPointInterface` objects that define the vertices of the polyline. | +| `id` | `String?` | An optional unique identifier for the polyline. If `null`, a stable ID is generated from the other properties. Defaults to `null`. | +| `strokeColor` | `Color` | The color of the polyline stroke. Defaults to `Color.Black`. | +| `strokeWidth` | `Dp` | The width of the polyline stroke. Defaults to `1.dp`. | +| `geodesic` | `Boolean` | If `true`, the polyline is drawn as a geodesic curve, which is the shortest path between two points on the Earth's surface. If `false`, it's drawn as a straight line on the 2D map projection. Defaults to `false`. | +| `zIndex` | `Int` | The drawing order of the polyline. Polylines with higher `zIndex` values are drawn on top of those with lower values. Defaults to `0`. | +| `extra` | `Serializable?` | Optional, serializable data that can be attached to the polyline state. Useful for storing custom metadata. Defaults to `null`. | +| `onClick` | `OnPolylineEventHandler?` | A lambda function that is invoked when the user clicks on the polyline. The handler receives a `PolylineEvent` object. Defaults to `null`. | + +## Methods + +### `copy` + +Creates a new `PolylineState` instance, allowing for the modification of specific properties while retaining the others. This is the recommended way to update polyline state in an immutable fashion. + +#### Signature + +```kotlin +fun copy( + points: List = this.points, + id: String? = this.id, + strokeColor: Color = this.strokeColor, + strokeWidth: Dp = this.strokeWidth, + geodesic: Boolean = this.geodesic, + zIndex: Int = this.zIndex, + extra: Serializable? = this.extra, + onClick: OnPolylineEventHandler? = this.onClick, +): PolylineState +``` + +#### Parameters + +The parameters are identical to the `PolylineState` constructor and allow you to override any of the existing properties. + +#### Returns + +| Type | Description | +| --- | --- | +| `PolylineState` | A new `PolylineState` instance with the updated properties. | + +### `asFlow` + +Returns a `Flow` that emits a `PolylineFingerPrint` whenever any of the polyline's properties change. This is useful for observing state changes reactively and triggering updates efficiently. + +#### Signature + +```kotlin +fun asFlow(): Flow +``` + +#### Returns + +| Type | Description | +| --- | --- | +| `Flow` | A Kotlin Flow that emits a new fingerprint upon state change. | + +### `fingerPrint` + +Generates a lightweight `PolylineFingerPrint` of the current state. This is primarily used internally for efficient change detection within the `asFlow` stream. + +#### Signature + +```kotlin +fun fingerPrint(): PolylineFingerPrint +``` + +#### Returns + +| Type | Description | +| --- | --- | +| `PolylineFingerPrint` | A `PolylineFingerPrint` object representing the current state. | + +## Related Types + +### `OnPolylineEventHandler` + +A type alias for the function that handles click events on a polyline. + +#### Signature + +```kotlin +typealias OnPolylineEventHandler = (PolylineEvent) -> Unit +``` + +### `PolylineEvent` + +A data class that represents a click event on a polyline. It is passed to the `OnPolylineEventHandler` when a click occurs. + +#### Signature + +```kotlin +data class PolylineEvent( + val state: PolylineState, + val clicked: GeoPointInterface, +) +``` + +#### Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| `state` | `PolylineState` | The state of the polyline that was clicked. | +| `clicked` | `GeoPointInterface` | The geographical point on the polyline where the click occurred. | + +### `PolylineFingerPrint` + +A data class that holds a lightweight, hash-based representation of a `PolylineState`. It is used for efficient change detection in reactive streams. This is mainly for internal use. + +#### Signature + +```kotlin +data class PolylineFingerPrint( + val id: Int, + val strokeColor: Int, + val strokeWidth: Int, + val geodesic: Int, + val zIndex: Int, + val points: Int, + val extra: Int, +) +``` + +## Example + +Here is an example of how to create and manage a `PolylineState`. + +```kotlin +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.setValue +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp +import com.mapconductor.core.features.GeoPointInterface +import com.mapconductor.core.polyline.PolylineState + +// Assume a simple implementation of GeoPointInterface for the example +data class GeoPoint(val latitude: Double, val longitude: Double) : GeoPointInterface { + // Interface members would be implemented here +} + +fun main() { + // 1. Define the points for the polyline + val routePoints = listOf( + GeoPoint(latitude = 34.0522, longitude = -118.2437), // Los Angeles + GeoPoint(latitude = 39.7392, longitude = -104.9903), // Denver + GeoPoint(latitude = 41.8781, longitude = -87.6298) // Chicago + ) + + // 2. Define a click handler + val onPolylineClick: OnPolylineEventHandler = { event -> + println("Polyline with ID ${event.state.id} was clicked!") + println("Click location: Lat=${event.clicked.latitude}, Lon=${event.clicked.longitude}") + // You could use event.state.extra to retrieve custom data + } + + // 3. Create an initial PolylineState + var polyline by mutableStateOf( + PolylineState( + points = routePoints, + strokeColor = Color.Blue, + strokeWidth = 5.dp, + geodesic = true, + zIndex = 1, + extra = "Route 66 segment", + onClick = onPolylineClick + ) + ) + + println("Initial polyline color: ${polyline.strokeColor}") + + // 4. Update the state using the copy() method + // This creates a new state, which would trigger a recomposition in a Compose UI + polyline = polyline.copy(strokeColor = Color.Red) + + println("Updated polyline color: ${polyline.strokeColor}") + + // Simulate a click event for demonstration + val simulatedClickPoint = GeoPoint(latitude = 39.7392, longitude = -104.9903) + polyline.onClick?.invoke(PolylineEvent(state = polyline, clicked = simulatedClickPoint)) +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polyline/PolylineCapableInterface.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polyline/PolylineCapableInterface.kt.md new file mode 100644 index 00000000..badc1a23 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polyline/PolylineCapableInterface.kt.md @@ -0,0 +1,127 @@ +# PolylineCapableInterface + +An interface for components that have the capability to display and manage polylines on a map. This interface provides a standard contract for adding, updating, and interacting with polylines. + +--- + +## Methods + +### compositionPolylines + +Asynchronously composes a list of polylines on the map. This function synchronizes the polylines displayed on the map with the provided list of `PolylineState` objects. It will add new polylines, update existing ones, and remove any polylines that are not in the new list. This is the preferred method for managing a collection of polylines declaratively. + +#### Signature + +```kotlin +suspend fun compositionPolylines(data: List) +``` + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :-------------------- | :----------------------------------------------------------------------- | +| `data` | `List` | The complete list of polyline states to be rendered on the map. | + +#### Example + +```kotlin +// Assuming 'mapController' implements PolylineCapableInterface +val polylineStates = listOf( + PolylineState(id = "route1", points = listOf(...)), + PolylineState(id = "route2", points = listOf(...), color = Color.BLUE) +) + +// Asynchronously update the map to show only the polylines in the list +coroutineScope.launch { + mapController.compositionPolylines(polylineStates) +} +``` + +--- + +### updatePolyline + +Asynchronously adds a new polyline or updates an existing one based on the provided `PolylineState`. If a polyline with the same ID already exists on the map, its properties will be updated. Otherwise, a new polyline will be created and added. + +#### Signature + +```kotlin +suspend fun updatePolyline(state: PolylineState) +``` + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :------------ | :----------------------------------------------- | +| `state` | `PolylineState` | The state of the polyline to add or update. | + +#### Example + +```kotlin +// Create a new polyline state with updated properties +val updatedRouteState = PolylineState( + id = "route1", + points = newPointList, + width = 12f +) + +// Asynchronously update the specific polyline on the map +coroutineScope.launch { + mapController.updatePolyline(updatedRouteState) +} +``` + +--- + +### setOnPolylineClickListener + +Sets a global click listener for all polylines managed by this component. + +> **Deprecated:** This method is deprecated. Use the `onClick` lambda property within the `PolylineState` for each individual polyline instead. This provides more granular control and is aligned with modern, state-driven UI patterns. + +#### Signature + +```kotlin +@Deprecated("Use PolylineState.onClick instead.") +fun setOnPolylineClickListener(listener: OnPolylineEventHandler?) +``` + +#### Parameters + +| Parameter | Type | Description | +| :--------- | :----------------------- | :----------------------------------------------------------------------- | +| `listener` | `OnPolylineEventHandler?` | The event handler to be invoked when any polyline is clicked. Set to `null` to remove the listener. | + +--- + +### hasPolyline + +Checks if a polyline matching the given `PolylineState` exists on the map. The check is typically performed using the unique identifier from the `PolylineState`. + +#### Signature + +```kotlin +fun hasPolyline(state: PolylineState): Boolean +``` + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :------------ | :----------------------------------------------------------------------- | +| `state` | `PolylineState` | The polyline state to check for. The lookup is based on the polyline's ID. | + +#### Returns + +**Boolean** - Returns `true` if a polyline with the same ID exists, and `false` otherwise. + +#### Example + +```kotlin +val routeToCheck = PolylineState(id = "route_alpha") + +if (mapController.hasPolyline(routeToCheck)) { + println("Polyline 'route_alpha' is already on the map.") +} else { + println("Polyline 'route_alpha' is not on the map.") +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polyline/PolylineComponent.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polyline/PolylineComponent.kt.md new file mode 100644 index 00000000..e0be15d0 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polyline/PolylineComponent.kt.md @@ -0,0 +1,188 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +--- + +# Polyline + +The `Polyline` composable adds a line or a series of connected line segments to the map. It is a flexible overlay that can be used to represent paths, routes, or boundaries. + +This function must be called within the scope of a `MapView` composable. + +There are three overloads for creating a `Polyline`: +1. [From a list of points](#polyline-from-a-list-of-points): The primary method for creating a polyline from an ordered list of geographical coordinates. +2. [From a bounding box](#polyline-from-a-bounding-box): A convenience method to draw a rectangular outline from a `GeoRectBounds` object. +3. [From a state object](#polyline-from-a-state-object): A lower-level method that uses a `PolylineState` object for more advanced state management. + +--- + +## Polyline (from a list of points) + +This composable draws a polyline on the map by connecting an ordered list of `GeoPointInterface` vertices. + +### Signature +```kotlin +@Composable +fun MapViewScope.Polyline( + points: List, + id: String? = null, + strokeColor: Color = Color.Black, + strokeWidth: Dp = 1.dp, + geodesic: Boolean = false, + zIndex: Int = 0, + extra: Serializable? = null, + onClick: OnPolylineEventHandler? = null, +) +``` + +### Description +This is the primary function for creating a polyline. You provide a list of geographical points, and it renders a line connecting them in the specified order. You can customize the appearance (color, width), behavior (geodesic), and interactivity (click handler) of the polyline. + +### Parameters +| Parameter | Type | Description | +|---------------|---------------------------|----------------------------------------------------------------------------------------------------------------------------------------| +| `points` | `List` | **Required.** An ordered list of `GeoPointInterface` objects that define the vertices of the polyline. | +| `id` | `String?` | An optional unique identifier for the polyline. This can be useful for finding or managing the polyline later. Defaults to `null`. | +| `strokeColor` | `Color` | The color of the polyline. Defaults to `Color.Black`. | +| `strokeWidth` | `Dp` | The width of the polyline stroke in density-independent pixels (`Dp`). Defaults to `1.dp`. | +| `geodesic` | `Boolean` | If `true`, the segments of the polyline are drawn as geodesic curves that follow the curvature of the Earth. Defaults to `false`. | +| `zIndex` | `Int` | The stacking order of this polyline relative to other map overlays. Polylines with higher `zIndex` values are drawn on top. Defaults to `0`. | +| `extra` | `Serializable?` | Optional, extra serializable data to associate with the polyline. This can be retrieved in event handlers. Defaults to `null`. | +| `onClick` | `OnPolylineEventHandler?` | A callback lambda that is invoked when the user clicks on the polyline. Defaults to `null`. | + +### Example +```kotlin +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp +import com.mapconductor.core.MapView +import com.mapconductor.core.features.GeoPoint +import com.mapconductor.core.polyline.Polyline + +// Inside a Composable function +MapView { + val routePoints = listOf( + GeoPoint(40.7128, -74.0060), // New York + GeoPoint(34.0522, -118.2437), // Los Angeles + GeoPoint(41.8781, -87.6298) // Chicago + ) + + Polyline( + points = routePoints, + id = "us-trip-route", + strokeColor = Color.Blue, + strokeWidth = 4.dp, + geodesic = true, + onClick = { polylineState -> + println("Clicked on polyline with ID: ${polylineState.id}") + } + ) +} +``` + +--- + +## Polyline (from a bounding box) + +This composable is a convenience function that draws a rectangular polyline outlining a given `GeoRectBounds`. + +### Signature +```kotlin +@Composable +fun MapViewScope.Polyline( + bounds: GeoRectBounds, + id: String? = null, + strokeColor: Color = Color.Black, + strokeWidth: Dp = 1.dp, + geodesic: Boolean = false, + zIndex: Int = 0, + extra: Serializable? = null, + onClick: OnPolylineEventHandler? = null, +) +``` + +### Description +This function simplifies the process of drawing a rectangle on the map. It takes a `GeoRectBounds` object and automatically generates the five points (four corners and a closing point) needed to draw a closed rectangular polyline. + +### Parameters +| Parameter | Type | Description | +|---------------|---------------------------|----------------------------------------------------------------------------------------------------------------------------------------| +| `bounds` | `GeoRectBounds` | **Required.** The geographical bounding box to be outlined by the polyline. | +| `id` | `String?` | An optional unique identifier for the polyline. Defaults to `null`. | +| `strokeColor` | `Color` | The color of the polyline. Defaults to `Color.Black`. | +| `strokeWidth` | `Dp` | The width of the polyline stroke in density-independent pixels (`Dp`). Defaults to `1.dp`. | +| `geodesic` | `Boolean` | If `true`, the segments of the polyline are drawn as geodesic curves. Defaults to `false`. | +| `zIndex` | `Int` | The stacking order of this polyline relative to other map overlays. Polylines with higher `zIndex` values are drawn on top. Defaults to `0`. | +| `extra` | `Serializable?` | Optional, extra serializable data to associate with the polyline. Defaults to `null`. | +| `onClick` | `OnPolylineEventHandler?` | A callback lambda that is invoked when the user clicks on the polyline. Defaults to `null`. | + +### Example +```kotlin +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp +import com.mapconductor.core.MapView +import com.mapconductor.core.features.GeoPoint +import com.mapconductor.core.features.GeoRectBounds +import com.mapconductor.core.polyline.Polyline + +// Inside a Composable function +MapView { + val coloradoBounds = GeoRectBounds( + northEast = GeoPoint(41.0, -102.05), + southWest = GeoPoint(37.0, -109.05) + ) + + Polyline( + bounds = coloradoBounds, + id = "colorado-border", + strokeColor = Color.Red, + strokeWidth = 3.dp + ) +} +``` + +--- + +## Polyline (from a state object) + +This is a lower-level composable that draws a polyline on the map using a `PolylineState` object. + +### Signature +```kotlin +@Composable +fun MapViewScope.Polyline(state: PolylineState) +``` + +### Description +This function is intended for more advanced use cases where you need to manage the entire state of a polyline as a single object. It handles the lifecycle of the polyline on the map, adding it when the composable enters the composition and removing it upon disposal. For most common scenarios, the other `Polyline` overloads are recommended. + +### Parameters +| Parameter | Type | Description | +|-----------|---------------|---------------------------------------------------------------------------------------------------------| +| `state` | `PolylineState` | **Required.** A state object that encapsulates all properties of the polyline, including its geometry, appearance, and event handlers. | + +### Example +```kotlin +import androidx.compose.runtime.remember +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp +import com.mapconductor.core.MapView +import com.mapconductor.core.features.GeoPoint +import com.mapconductor.core.polyline.Polyline +import com.mapconductor.core.polyline.PolylineState + +// Inside a Composable function +MapView { + val polylineState = remember { + PolylineState( + points = listOf( + GeoPoint(48.8566, 2.3522), // Paris + GeoPoint(51.5074, -0.1278) // London + ), + id = "paris-london-route", + strokeColor = Color.Magenta, + strokeWidth = 5.dp + ) + } + + Polyline(state = polylineState) +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polyline/PolylineController.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polyline/PolylineController.kt.md new file mode 100644 index 00000000..def1698f --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polyline/PolylineController.kt.md @@ -0,0 +1,218 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +--- + +# PolylineController + +## Description + +An abstract base class responsible for managing the lifecycle of polyline overlays on a map. It acts as a coordinator between the data state (`PolylineState`), the state management logic (`PolylineManager`), and the platform-specific rendering (`PolylineOverlayRenderer`). + +All public methods that modify the state of polylines (`add`, `update`, `clear`) are thread-safe, using a semaphore to ensure that operations are executed sequentially and prevent race conditions. + +This class is designed to be extended by a concrete implementation specific to a map provider (e.g., `GoogleMapPolylineController`). + +### Generic Type Parameters + +| Parameter | Description | +| :--- | :--- | +| `ActualPolyline` | The platform-specific polyline object type (e.g., `com.google.android.gms.maps.model.Polyline`). | + +## Constructor + +```kotlin +abstract class PolylineController( + val polylineManager: PolylineManagerInterface, + open val renderer: PolylineOverlayRendererInterface, + override var clickListener: OnPolylineEventHandler? = null, +) +``` + +Creates a new instance of `PolylineController`. + +### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `polylineManager` | `PolylineManagerInterface` | The manager responsible for storing and querying polyline entities. | +| `renderer` | `PolylineOverlayRendererInterface` | The renderer responsible for drawing and updating polylines on the map. | +| `clickListener` | `OnPolylineEventHandler?` | An optional global listener that is invoked when any polyline managed by this controller is clicked. Defaults to `null`. | + +## Properties + +| Property | Type | Description | +| :--- | :--- | :--- | +| `polylineManager` | `PolylineManagerInterface` | The manager for polyline state. | +| `renderer` | `PolylineOverlayRendererInterface` | The renderer for drawing polylines. | +| `clickListener` | `OnPolylineEventHandler?` | A global click listener for all polylines managed by this controller. | +| `zIndex` | `Int` | The z-index for the polyline layer, fixed at `5`. | + +## Methods + +### dispatchClick + +Dispatches a click event. This method triggers both the polyline-specific `onClick` handler (defined in `PolylineState`) and the controller's global `clickListener`. + +**Signature** +```kotlin +fun dispatchClick(event: PolylineEvent) +``` + +**Parameters** + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `event` | `PolylineEvent` | The polyline event object containing details about the click. | + +### add + +Adds, updates, or removes polylines to synchronize the map with the provided list of `PolylineState`. The method performs a diff against the current polylines and efficiently applies only the necessary changes (additions, updates, removals) via the `renderer`. This operation is thread-safe. + +**Signature** +```kotlin +override suspend fun add(data: List) +``` + +**Parameters** + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `data` | `List` | The complete list of polyline states that should be displayed on the map. | + +### update + +Updates a single existing polyline based on the new `PolylineState`. For efficiency, it first checks if the state has actually changed using a fingerprint comparison. If there are changes, it delegates the update to the `renderer`. This operation is thread-safe. + +**Signature** +```kotlin +override suspend fun update(state: PolylineState) +``` + +**Parameters** + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `state` | `PolylineState` | The new state for the polyline to be updated. | + +### clear + +Removes all polylines currently managed by this controller from the map. This operation is thread-safe. + +**Signature** +```kotlin +override suspend fun clear() +``` + +### find + +Finds the topmost polyline entity at a given geographic coordinate. The search tolerance may depend on the current map camera position (e.g., zoom level). + +**Signature** +```kotlin +override fun find(position: GeoPointInterface): PolylineEntityInterface? +``` + +**Parameters** + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `position` | `GeoPointInterface` | The geographic coordinates to search at. | + +**Returns** + +| Type | Description | +| :--- | :--- | +| `PolylineEntityInterface?` | The found polyline entity, or `null` if no polyline is found at the specified position. | + +### findWithClosestPoint + +Performs a hit test at the given geographic coordinate and returns a detailed result, including the polyline entity and the closest point on that polyline to the given coordinate. + +**Signature** +```kotlin +fun findWithClosestPoint(position: GeoPointInterface): PolylineHitResult? +``` + +**Parameters** + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `position` | `GeoPointInterface` | The geographic coordinates to search at. | + +**Returns** + +| Type | Description | +| :--- | :--- | +| `PolylineHitResult?` | A `PolylineHitResult` object containing the entity and closest point, or `null` if no polyline is found. | + +### onCameraChanged + +Callback method invoked when the map's camera position changes. The controller stores this position to use in calculations for other methods, such as `find`. + +**Signature** +```kotlin +override suspend fun onCameraChanged(mapCameraPosition: MapCameraPosition) +``` + +**Parameters** + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `mapCameraPosition` | `MapCameraPosition` | The new camera position of the map. | + +### destroy + +Cleans up resources used by the controller. In this base implementation, this method is empty as there are no specific native resources to release. + +**Signature** +```kotlin +override fun destroy() +``` + +## Example + +The following example demonstrates how to create a concrete implementation of `PolylineController` and use it to manage polylines on a map. + +```kotlin +// Assume MyMapPolyline is the platform-specific polyline class +// e.g., com.google.android.gms.maps.model.Polyline + +// 1. Define a concrete implementation of the PolylineController +class MyMapPolylineController( + polylineManager: PolylineManagerInterface, + renderer: PolylineOverlayRendererInterface, + clickListener: OnPolylineEventHandler? = null +) : PolylineController(polylineManager, renderer, clickListener) { + // Custom logic for your specific map implementation can be added here +} + +// 2. In your map setup code, instantiate the controller with its dependencies +val myPolylineManager = MyPolylineManager() // Your implementation of PolylineManagerInterface +val myPolylineRenderer = MyPolylineRenderer() // Your implementation of PolylineOverlayRendererInterface +val polylineController = MyMapPolylineController(myPolylineManager, myPolylineRenderer) + +// 3. Use the controller to manage polylines +suspend fun updatePolylinesOnMap() { + val polylineStates = listOf( + PolylineState(id = "route-66", points = listOf(geoPoint1, geoPoint2)), + PolylineState(id = "scenic-drive", points = listOf(geoPoint3, geoPoint4), color = Color.BLUE) + ) + + // Add the polylines to the map + polylineController.add(polylineStates) + + // Later, update a single polyline + val updatedState = PolylineState(id = "route-66", points = listOf(geoPoint1, geoPoint2, geoPoint5)) + polylineController.update(updatedState) + + // Find a polyline at a specific location from a user tap + val userTapLocation: GeoPointInterface = // ... get from map click event + val clickedPolyline = polylineController.find(userTapLocation) + if (clickedPolyline != null) { + println("User clicked on polyline with ID: ${clickedPolyline.state.id}") + } + + // When finished, clear all polylines + polylineController.clear() +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polyline/PolylineEntity.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polyline/PolylineEntity.kt.md new file mode 100644 index 00000000..5680e0c6 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polyline/PolylineEntity.kt.md @@ -0,0 +1,143 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +*** + +# PolylineEntity + +A generic wrapper class that represents a polyline on the map. It encapsulates the native map polyline object (`ActualPolyline`), its descriptive state (`PolylineState`), and derived properties like its fingerprint and geographic bounds. + +This class provides a consistent interface for managing polylines regardless of the underlying map provider and optimizes performance by caching computed properties like the bounding box. + +## Signature + +```kotlin +class PolylineEntity( + override val polyline: ActualPolyline, + override val state: PolylineState, +) : PolylineEntityInterface +``` + +## Constructor + +### PolylineEntity() + +Creates a new instance of `PolylineEntity`. + +#### Signature + +```kotlin +PolylineEntity( + polyline: ActualPolyline, + state: PolylineState +) +``` + +#### Parameters + +| Parameter | Type | Description | +|-----------|------------------|-------------| +| `polyline` | `ActualPolyline` | The native polyline object from the specific map SDK. | +| `state` | `PolylineState` | An object containing the descriptive state of the polyline, such as its points, color, width, and geodesic property. | + +## Properties + +### polyline + +The underlying native polyline object provided by the map's SDK. This allows for direct interaction with the map-specific object if needed. + +#### Signature + +```kotlin +val polyline: ActualPolyline +``` + +#### Returns + +The native `ActualPolyline` object. + +### state + +The state object that defines the polyline's properties, including its vertices (`points`), color, width, visibility, and whether it should be rendered as a geodesic line. + +#### Signature + +```kotlin +val state: PolylineState +``` + +#### Returns + +The `PolylineState` object associated with this entity. + +### fingerPrint + +A unique fingerprint generated from the `PolylineState`. This is useful for efficiently comparing polyline states to detect changes without performing a deep comparison of all properties. + +#### Signature + +```kotlin +val fingerPrint: PolylineFingerPrint +``` + +#### Returns + +A `PolylineFingerPrint` object representing the current state. + +### bounds + +Calculates and returns the geographic bounding box (`GeoRectBounds`) that completely encloses the polyline. The calculation method depends on the `geodesic` property in the `PolylineState`: + +- **Non-geodesic:** The bounds are calculated by including all the vertex points of the polyline. +- **Geodesic:** The bounds are calculated by sampling intermediate points along each great-circle arc between vertices. This ensures the bounding box correctly accounts for the curvature of the Earth, which can cause the line to extend beyond the simple bounding box of its vertices (e.g., a line crossing the 180th meridian or near the poles). + +The result is cached for performance. The cache is invalidated and the bounds are recalculated only when the polyline's points or its `geodesic` property change. + +#### Signature + +```kotlin +val bounds: GeoRectBounds +``` + +#### Returns + +The calculated `GeoRectBounds` for the polyline. + +## Example + +```kotlin +// Assume the existence of these classes for the example +// data class GeoPoint(val latitude: Double, val longitude: Double) +// data class PolylineState(val id: String, val points: List, val geodesic: Boolean) +// class MockNativePolyline { /* ... */ } + +// 1. Define the state for a geodesic polyline +val polylineState = PolylineState( + id = "p1", + points = listOf( + GeoPoint(latitude = 40.7128, longitude = -74.0060), // New York + GeoPoint(latitude = 48.8566, longitude = 2.3522) // Paris + ), + geodesic = true +) + +// 2. Create a mock native polyline object +val nativePolyline = MockNativePolyline() + +// 3. Instantiate the PolylineEntity +val polylineEntity = PolylineEntity( + polyline = nativePolyline, + state = polylineState +) + +// 4. Access the computed properties +// The bounds will be calculated to include the geodesic curve between NYC and Paris. +val geoBounds = polylineEntity.bounds +println("Polyline Bounds: $geoBounds") + +// Access the fingerprint for change detection +val fingerprint = polylineEntity.fingerPrint +println("Polyline Fingerprint: $fingerprint") + +// Accessing bounds again will return the cached value without recalculation +val cachedBounds = polylineEntity.bounds +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polyline/PolylineManager.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polyline/PolylineManager.kt.md new file mode 100644 index 00000000..66df4dbc --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polyline/PolylineManager.kt.md @@ -0,0 +1,249 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet, formatted in Markdown. + +--- + +# PolylineManager + +## `PolylineManager` + +### Description + +The `PolylineManager` class is a comprehensive utility for managing a collection of polyline entities on a map. It provides a robust API for registering, retrieving, and removing polylines. Its key feature is the `find` method, which performs efficient hit-testing to identify the polyline closest to a given geographical coordinate, making it ideal for handling user interactions like taps on polylines. + +The manager is generic, denoted by ``, which represents the underlying platform-specific polyline object being managed. + +### Methods + +#### **registerEntity** + +##### Signature + +```kotlin +fun registerEntity(entity: PolylineEntityInterface) +``` + +##### Description + +Registers a new polyline entity with the manager. If an entity with the same ID already exists, it will be overwritten. + +##### Parameters + +| Parameter | Type | Description | +| :-------- | :------------------------------------------ | :--------------------------------------- | +| `entity` | `PolylineEntityInterface` | The polyline entity to add to the manager. | + +--- + +#### **removeEntity** + +##### Signature + +```kotlin +fun removeEntity(id: String): PolylineEntityInterface? +``` + +##### Description + +Removes a polyline entity from the manager using its unique identifier. + +##### Parameters + +| Parameter | Type | Description | +| :-------- | :------- | :----------------------------------------- | +| `id` | `String` | The unique ID of the polyline entity to remove. | + +##### Returns + +**`PolylineEntityInterface?`** + +The removed entity, or `null` if no entity with the given ID was found. + +--- + +#### **getEntity** + +##### Signature + +```kotlin +fun getEntity(id: String): PolylineEntityInterface? +``` + +##### Description + +Retrieves a polyline entity from the manager by its unique identifier. + +##### Parameters + +| Parameter | Type | Description | +| :-------- | :------- | :------------------------------------------- | +| `id` | `String` | The unique ID of the polyline entity to retrieve. | + +##### Returns + +**`PolylineEntityInterface?`** + +The `PolylineEntityInterface` corresponding to the given ID, or `null` if it's not found. + +--- + +#### **hasEntity** + +##### Signature + +```kotlin +fun hasEntity(id: String): Boolean +``` + +##### Description + +Checks if an entity with the specified ID is registered with the manager. + +##### Parameters + +| Parameter | Type | Description | +| :-------- | :------- | :------------------------------------------- | +| `id` | `String` | The unique ID of the polyline entity to check for. | + +##### Returns + +**`Boolean`** + +Returns `true` if an entity with the specified ID exists, `false` otherwise. + +--- + +#### **allEntities** + +##### Signature + +```kotlin +fun allEntities(): List> +``` + +##### Description + +Returns a list of all polyline entities currently registered with the manager. + +##### Returns + +**`List>`** + +A `List` containing all registered `PolylineEntityInterface` objects. + +--- + +#### **clear** + +##### Signature + +```kotlin +fun clear() +``` + +##### Description + +Removes all polyline entities from the manager, leaving it empty. + +--- + +#### **find** + +##### Signature + +```kotlin +fun find( + position: GeoPointInterface, + cameraPosition: MapCameraPosition? = null, +): PolylineHitResult? +``` + +##### Description + +Finds the polyline entity closest to a specified geographical position (e.g., a user's tap). The search is constrained by a tap tolerance radius defined in the application settings. This method calculates the search radius in meters based on the current map zoom level and efficiently identifies the polyline segment nearest to the given position. It correctly handles both geodesic and linear (straight-line) polylines. + +##### Parameters + +| Parameter | Type | Description | +| :--------------- | :-------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `position` | `GeoPointInterface` | The geographical coordinate to search around, typically the location of a tap event. | +| `cameraPosition` | `MapCameraPosition?` | **Optional.** The current state of the map camera, including zoom and visible region. Providing this improves performance by filtering out off-screen polylines. | + +##### Returns + +**`PolylineHitResult?`** + +A `PolylineHitResult` object containing the closest entity and the exact point of intersection, or `null` if no polyline is found within the tap tolerance. + +### Example + +```kotlin +// Assume these interfaces and classes are defined elsewhere +// val myPolylineEntity: PolylineEntityInterface = ... +// val tapLocation: GeoPointInterface = ... +// val currentCameraPosition: MapCameraPosition = ... + +// 1. Initialize the manager +val polylineManager = PolylineManager() + +// 2. Register a polyline entity +polylineManager.registerEntity(myPolylineEntity) + +// 3. Find the closest polyline to a tap location +val hitResult = polylineManager.find( + position = tapLocation, + cameraPosition = currentCameraPosition +) + +// 4. Process the result +if (hitResult != null) { + println("Hit polyline with ID: ${hitResult.entity.state.id}") + println("Closest point on polyline: ${hitResult.closestPoint.latitude}, ${hitResult.closestPoint.longitude}") + // Highlight the polyline or show an info window +} else { + println("No polyline was tapped.") +} +``` + +--- + +## Related Data Classes + +### `PolylineHitResult` + +#### Description + +A data class that encapsulates the result of a successful hit test performed by `PolylineManager.find`. It holds the entity that was hit and the specific point on that entity's path closest to the search location. + +#### Properties + +| Property | Type | Description | +| :------------- | :------------------------------------------ | :----------------------------------------------------------------------------- | +| `entity` | `PolylineEntityInterface` | The polyline entity that was found to be closest to the search position. | +| `closestPoint` | `GeoPointInterface` | The specific point on the `entity`'s path that is nearest to the search position. | + +--- + +## Interfaces + +### `PolylineManagerInterface` + +#### Description + +This interface defines the public contract for a polyline manager. The `PolylineManager` class is the concrete implementation of this interface. + +#### Methods + +```kotlin +interface PolylineManagerInterface { + fun registerEntity(entity: PolylineEntityInterface) + fun removeEntity(id: String): PolylineEntityInterface? + fun getEntity(id: String): PolylineEntityInterface? + fun hasEntity(id: String): Boolean + fun allEntities(): List> + fun clear() + fun find( + position: GeoPointInterface, + cameraPosition: MapCameraPosition? = null, + ): PolylineHitResult? +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polyline/PolylineOverlay.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polyline/PolylineOverlay.kt.md new file mode 100644 index 00000000..a3d2e886 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polyline/PolylineOverlay.kt.md @@ -0,0 +1,103 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet, formatted in Markdown. + +*** + +## `LocalPolylineCollector` + +A `CompositionLocal` that provides access to a collector for polyline states. + +### Signature + +```kotlin +val LocalPolylineCollector: ProvidableCompositionLocal> +``` + +### Description + +`LocalPolylineCollector` is a `CompositionLocal` used to provide a `ChildCollector` down the Composable tree. Its primary purpose is to allow `Polyline` composables to register their state with the parent ``. + +It is crucial that any composable utilizing this `CompositionLocal` is a descendant of a `` component, which is responsible for providing the collector instance. Failure to do so will result in an `IllegalStateException`. + +### Example + +The following conceptual example shows how a `Polyline` composable would use `LocalPolylineCollector` to register its state. + +```kotlin +@Composable +fun Polyline( + // Unique identifier for this polyline + id: String, + // Other polyline properties like points, color, etc. + points: List, + color: Color, + width: Float +) { + // Access the collector from the composition + val collector = LocalPolylineCollector.current + + // Create the state for this polyline + val polylineState = remember(id, points, color, width) { + PolylineState(id = id, points = points, color = color, width = width) + } + + // Use an effect to register and clean up the polyline state + DisposableEffect(collector, polylineState) { + collector.add(polylineState) + onDispose { + collector.remove(polylineState) + } + } +} +``` + +## `PolylineOverlay` Class + +An overlay class responsible for managing and rendering a collection of polylines on the map. + +### Signature + +```kotlin +class PolylineOverlay( + override val flow: StateFlow>, +) : MapOverlayInterface +``` + +### Description + +`PolylineOverlay` implements the `MapOverlayInterface` to serve as a dedicated layer for polylines. It observes a `StateFlow` containing the states of all polylines and delegates the rendering task to a compatible map controller. This class effectively bridges the declarative polyline state with the underlying map view's rendering engine. + +### Constructor Parameters + +| Parameter | Type | Description | +| :-------- | :--------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `flow` | `StateFlow>` | A state flow that emits a map of polyline states. The map's key is a unique `String` identifier for each polyline, and the value is the `PolylineState` object to be rendered. | + +### Methods + +#### `render` + +Renders the polylines on the map using a compatible controller. + +**Signature** + +```kotlin +override suspend fun render( + data: MutableMap, + controller: MapViewControllerInterface, +) +``` + +**Description** + +This function is invoked by the map's rendering system. It attempts to cast the provided `controller` to a `PolylineCapableInterface`. If the cast is successful, it calls the controller's `compositionPolylines` method, passing the current list of polyline states to be drawn on the map. If the controller does not implement `PolylineCapableInterface`, no polylines will be rendered by this overlay. + +**Parameters** + +| Parameter | Type | Description | +| :----------- | :--------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------- | +| `data` | `MutableMap` | A map containing the state of all polylines to be rendered in the current frame. | +| `controller` | `MapViewControllerInterface` | The map controller instance. For polylines to be rendered, this controller must implement the `PolylineCapableInterface`. | + +**Returns** + +This is a `suspend` function and does not return a value. \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polyline/PolylineOverlayRendererInterface.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polyline/PolylineOverlayRendererInterface.kt.md new file mode 100644 index 00000000..53e0f074 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/polyline/PolylineOverlayRendererInterface.kt.md @@ -0,0 +1,195 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +--- + +# PolylineOverlayRendererInterface + +The `PolylineOverlayRendererInterface` defines a contract for rendering and managing polyline overlays on a map. It is designed to be implemented by platform-specific renderers, abstracting the underlying map SDK's polyline handling (e.g., Google Maps, Mapbox). + +This interface handles the lifecycle of polylines, including their creation, modification, and removal, through a set of asynchronous methods. + +**Signature** +```kotlin +interface PolylineOverlayRendererInterface +``` + +### Type Parameters + +| Name | Description | +| :--- | :--- | +| `ActualPolyline` | The generic type representing the platform-specific polyline object (e.g., `com.google.android.gms.maps.model.Polyline`). | + +--- + +## Nested Interfaces + +### AddParamsInterface + +An interface that encapsulates the parameters required to add a new polyline to the map. + +**Signature** +```kotlin +interface AddParamsInterface +``` + +**Properties** + +| Name | Type | Description | +| :--- | :--- | :--- | +| `state` | `PolylineState` | The state object defining the properties of the new polyline, such as its points, color, width, and z-index. | + +### ChangeParamsInterface + +An interface that holds the data required to update an existing polyline. + +**Signature** +```kotlin +interface ChangeParamsInterface +``` + +**Properties** + +| Name | Type | Description | +| :--- | :--- | :--- | +| `current` | `PolylineEntityInterface` | The entity representing the new, updated state of the polyline. | +| `prev` | `PolylineEntityInterface` | The entity representing the previous state of the polyline before the change. | + +--- + +## Functions + +### onAdd + +Asynchronously adds a batch of new polylines to the map based on the provided state data. + +**Signature** +```kotlin +suspend fun onAdd(data: List): List +``` + +**Parameters** + +| Name | Type | Description | +| :--- | :--- | :--- | +| `data` | `List` | A list of parameter objects, where each object defines a polyline to be added. | + +**Returns** + +`List` - A list containing the newly created, platform-specific `ActualPolyline` objects. The order of this list corresponds to the input `data` list. An element will be `null` if the creation of a specific polyline failed. + +### onChange + +Asynchronously updates a batch of existing polylines on the map. + +**Signature** +```kotlin +suspend fun onChange(data: List>): List +``` + +**Parameters** + +| Name | Type | Description | +| :--- | :--- | :--- | +| `data` | `List>` | A list of change objects, each containing the previous and current state of a polyline to be updated. | + +**Returns** + +`List` - A list containing the updated `ActualPolyline` objects. An element can be `null` if an update operation failed. + +### onRemove + +Asynchronously removes a batch of polylines from the map. + +**Signature** +```kotlin +suspend fun onRemove(data: List>) +``` + +**Parameters** + +| Name | Type | Description | +| :--- | :--- | :--- | +| `data` | `List>` | A list of polyline entities to be removed from the map. | + +### onPostProcess + +A lifecycle callback executed after all `onAdd`, `onChange`, and `onRemove` operations in a single update cycle are complete. This can be used for final cleanup, batch updates, or triggering a map refresh. + +**Signature** +```kotlin +suspend fun onPostProcess() +``` + +--- + +## Example + +Here is an example of a simplified, hypothetical implementation of `PolylineOverlayRendererInterface` for a fictional map framework. + +```kotlin +// Define dummy classes for the example +data class MyMapPolyline(var id: String, var points: List, var color: Int) +data class PolylineState(val id: String, val points: List, val color: Int) +interface PolylineEntityInterface { + val nativePolyline: T? + val state: PolylineState +} + +// Example implementation of the renderer +class MyMapPolylineRenderer : PolylineOverlayRendererInterface { + + private val managedPolylines = mutableMapOf() + + override suspend fun onAdd(data: List): List { + println("Adding ${data.size} polylines...") + return data.map { params -> + val state = params.state + // In a real implementation, you would call the map SDK here + val newPolyline = MyMapPolyline( + id = state.id, + points = state.points, + color = state.color + ) + managedPolylines[state.id] = newPolyline + println("Added polyline with id: ${state.id}") + newPolyline + } + } + + override suspend fun onChange(data: List>): List { + println("Changing ${data.size} polylines...") + return data.map { params -> + val currentEntity = params.current + val nativePolyline = currentEntity.nativePolyline + + if (nativePolyline != null) { + // Apply changes to the native polyline object + nativePolyline.points = currentEntity.state.points + nativePolyline.color = currentEntity.state.color + println("Changed polyline with id: ${nativePolyline.id}") + nativePolyline + } else { + println("Change failed: native polyline not found for id: ${currentEntity.state.id}") + null + } + } + } + + override suspend fun onRemove(data: List>) { + println("Removing ${data.size} polylines...") + data.forEach { entity -> + val polylineId = entity.state.id + // In a real implementation, you would remove the polyline from the map + if (managedPolylines.remove(polylineId) != null) { + println("Removed polyline with id: $polylineId") + } + } + } + + override suspend fun onPostProcess() { + // For example, trigger a redraw of the map view if needed + println("Post-processing: All polyline operations are complete for this cycle.") + // mapView.invalidate() + } +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/projection/Earth.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/projection/Earth.kt.md new file mode 100644 index 00000000..20ca6578 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/projection/Earth.kt.md @@ -0,0 +1,40 @@ +# Earth + +The `Earth` object provides a collection of fundamental, globally-accepted constants related to the Earth's dimensions. These values are based on the World Geodetic System (WGS 84) ellipsoid model and are provided in meters. + +As a Kotlin `object`, it is a singleton, and its properties can be accessed directly without instantiation. + +## Signature + +```kotlin +object Earth +``` + +## Properties + +The `Earth` object contains the following constant properties: + +| Property | Type | Description | +| ---------------------- | ------ | ------------------------------------------------------------------------------------------------------- | +| `CIRCUMFERENCE_METERS` | Double | The equatorial circumference of the Earth in meters. Value: `40075016.686`. | +| `RADIUS_METERS` | Double | The equatorial radius of the Earth in meters, as defined by the WGS 84 ellipsoid. Value: `6378137.0`. | + +## Example + +This example demonstrates how to access the constants from the `Earth` object. + +```kotlin +import com.mapconductor.core.projection.Earth + +fun main() { + val radius = Earth.RADIUS_METERS + val circumference = Earth.CIRCUMFERENCE_METERS + + println("Earth's Radius: $radius meters") + println("Earth's Circumference: $circumference meters") +} + +// Expected Output: +// Earth's Radius: 6378137.0 meters +// Earth's Circumference: 40075016.686 meters +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/projection/ProjectionInterface.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/projection/ProjectionInterface.kt.md new file mode 100644 index 00000000..55d49db1 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/projection/ProjectionInterface.kt.md @@ -0,0 +1,114 @@ +# ProjectionInterface + +The `ProjectionInterface` defines a contract for converting between geographical coordinates and screen pixel coordinates. This interface is essential for rendering geographical data on a 2D surface, such as a map view, and for translating screen interactions back into geographical locations. + +--- + +## `project()` + +Projects a geographical coordinate (`GeoPointInterface`) to a screen coordinate (`Offset`). This is used to determine where a specific latitude/longitude point should be drawn on the screen. + +### Signature + +```kotlin +fun project(position: GeoPointInterface): Offset +``` + +### Description + +This method takes a geographical point, which typically contains latitude and longitude, and converts it into a 2D Cartesian coordinate (`Offset`) that represents a pixel position on the display. + +### Parameters + +| Parameter | Type | Description | +|------------|---------------------|-------------------------------------------| +| `position` | `GeoPointInterface` | The geographical coordinate to project. | + +### Returns + +An `Offset` object representing the corresponding (x, y) pixel coordinate on the screen. + +--- + +## `unproject()` + +Performs the reverse projection, converting a screen coordinate (`Offset`) back into a geographical coordinate (`GeoPointInterface`). This is useful for handling user interactions like taps or clicks on the map to identify the corresponding geographical location. + +### Signature + +```kotlin +fun unproject(point: Offset): GeoPointInterface +``` + +### Description + +This method takes a 2D screen coordinate (`Offset`) and converts it back into its corresponding geographical coordinate (`GeoPointInterface`), which typically contains latitude and longitude. + +### Parameters + +| Parameter | Type | Description | +|-----------|----------|---------------------------------------------------| +| `point` | `Offset` | The screen (x, y) pixel coordinate to unproject. | + +### Returns + +A `GeoPointInterface` object representing the geographical coordinate corresponding to the screen point. + +--- + +### Example + +The following conceptual example demonstrates how `ProjectionInterface` might be used within a map component to draw a marker and handle a tap event. + +```kotlin +import androidx.compose.foundation.Canvas +import androidx.compose.foundation.gestures.detectTapGestures +import androidx.compose.ui.Modifier +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.graphics.Color +import com.mapconductor.core.features.GeoPoint +import com.mapconductor.core.features.GeoPointInterface +import com.mapconductor.core.projection.ProjectionInterface + +// A hypothetical map view that uses a projection +class MapView(val projection: ProjectionInterface) { + + // A point of interest to display on the map + private val poi: GeoPointInterface = GeoPoint(latitude = 40.7128, longitude = -74.0060) // New York City + + // Function to draw the map content + fun draw(canvas: Canvas) { + // 1. Project the GeoPoint to a screen Offset + val screenPosition: Offset = projection.project(poi) + + // Draw a circle at the projected position + canvas.drawCircle( + color = Color.Red, + radius = 10f, + center = screenPosition + ) + } + + // Function to handle user taps + fun handleTap(tapPosition: Offset) { + // 2. Unproject the screen tap position to a GeoPoint + val tappedGeoPoint: GeoPointInterface = projection.unproject(tapPosition) + + println("User tapped at: Lat=${tappedGeoPoint.latitude}, Lon=${tappedGeoPoint.longitude}") + } +} + +// A conceptual Composable using the MapView +@Composable +fun MapScreen(mapView: MapView) { + Canvas(modifier = Modifier.fillMaxSize().pointerInput(Unit) { + detectTapGestures { offset -> + // Pass the tap offset to the handler + mapView.handleTap(offset) + } + }) { + // Draw the map content + mapView.draw(this) + } +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/projection/WGS84.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/projection/WGS84.kt.md new file mode 100644 index 00000000..e34d9eb6 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/projection/WGS84.kt.md @@ -0,0 +1,85 @@ +Of course! Here is the high-quality SDK documentation for the provided Kotlin code snippet. + +--- + +# WGS84 + +A singleton object that provides methods for Spherical Mercator projection. This projection is widely used in web mapping systems (like Google Maps and OpenStreetMap) to represent the WGS84 geographic coordinate system on a 2D plane. + +This object facilitates the conversion between geographic coordinates (`GeoPointInterface`) and 2D world coordinates (`Offset`), which are essential for rendering map data on a screen. The projection calculations are based on a standard 256x256 pixel map tile. + +## Methods + +### project + +```kotlin +fun project(position: GeoPointInterface): Offset +``` + +Projects a geographic coordinate (`GeoPointInterface`) into a 2D world coordinate (`Offset`) using the Spherical Mercator projection. + +#### Parameters + +| Parameter | Type | Description | +|------------|---------------------|---------------------------------------------------| +| `position` | `GeoPointInterface` | The geographic point (latitude/longitude) to project. | + +#### Returns + +An `Offset` object representing the x and y coordinates on the 2D projected plane, scaled to a 256x256 world space. + +#### Example + +```kotlin +import com.mapconductor.core.features.GeoPoint +import com.mapconductor.core.projection.WGS84 +import androidx.compose.ui.geometry.Offset + +// Define a geographic point for Lisbon, Portugal +val lisbon = GeoPoint(latitude = 38.7223, longitude = -9.1393) + +// Project the geographic point to 2D coordinates +val projectedPoint: Offset = WGS84.project(lisbon) + +// The result is the 2D representation of the location +println("Projected coordinates: $projectedPoint") +// Expected output might be similar to: Offset(125.55755, 96.3428) +``` + +--- + +### unproject + +```kotlin +fun unproject(point: Offset): GeoPointInterface +``` + +Performs the inverse projection, converting a 2D world coordinate (`Offset`) back into a geographic coordinate (`GeoPointInterface`). This is useful for determining the latitude and longitude corresponding to a specific point on the map, such as a user's tap location. + +#### Parameters + +| Parameter | Type | Description | +|-----------|----------|--------------------------------------------------------------------------| +| `point` | `Offset` | The 2D world coordinate (x, y) to unproject, based on a 256x256 tile system. | + +#### Returns + +A `GeoPointInterface` object representing the corresponding latitude and longitude. + +#### Example + +```kotlin +import com.mapconductor.core.features.GeoPointInterface +import com.mapconductor.core.projection.WGS84 +import androidx.compose.ui.geometry.Offset + +// A 2D point from a map interaction (e.g., a tap) +val screenPoint = Offset(125.55755f, 96.3428f) + +// Convert the 2D point back to its geographic coordinate +val geoPoint: GeoPointInterface = WGS84.unproject(screenPoint) + +// The result is the geographic location (latitude/longitude) +println("Unprojected GeoPoint: Lat=${geoPoint.latitude}, Lon=${geoPoint.longitude}") +// Expected output might be similar to: Unprojected GeoPoint: Lat=38.7223..., Lon=-9.1393... +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/projection/WebMercator.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/projection/WebMercator.kt.md new file mode 100644 index 00000000..5f8328eb --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/projection/WebMercator.kt.md @@ -0,0 +1,97 @@ +# WebMercator + +The `WebMercator` object provides utility methods for converting between geographic coordinates (latitude and longitude) and the Web Mercator projection (EPSG:3857) coordinates. This projection is a standard for web-based mapping services. + +This object implements the `ProjectionInterface`. + +--- + +## project + +Projects a geographic coordinate (latitude and longitude) into a 2D Cartesian coordinate (x, y) using the Web Mercator projection. + +### Signature + +```kotlin +fun project(position: GeoPointInterface): Offset +``` + +### Description + +This function takes a `GeoPointInterface` object, which represents a point on the Earth's surface with latitude and longitude, and converts it into a 2D planar coordinate `Offset`. This is essential for rendering geographic data on a flat map surface. + +### Parameters + +| Parameter | Type | Description | +|------------|---------------------|--------------------------------------------------------------| +| `position` | `GeoPointInterface` | The geographic point to project, containing latitude and longitude. | + +### Returns + +**Type:** `Offset` + +An `Offset` object representing the projected Cartesian coordinates (x, y) in meters. + +### Example + +```kotlin +import com.mapconductor.core.features.GeoPoint +import com.mapconductor.core.projection.WebMercator +import androidx.compose.ui.geometry.Offset + +// Define a geographic point for New York City +val nycGeoPoint = GeoPoint(latitude = 40.7128, longitude = -74.0060) + +// Project the geographic point to Web Mercator coordinates +val projectedOffset: Offset = WebMercator.project(nycGeoPoint) + +// projectedOffset.x will be approximately -8238322.5 +// projectedOffset.y will be approximately 4970144.5 +println("Projected Coordinates: x=${projectedOffset.x}, y=${projectedOffset.y}") +``` + +--- + +## unproject + +Converts a 2D Cartesian coordinate (x, y) from the Web Mercator projection back into a geographic coordinate (latitude and longitude). + +### Signature + +```kotlin +fun unproject(point: Offset): GeoPointInterface +``` + +### Description + +This function performs the inverse operation of `project`. It takes a 2D `Offset` in Web Mercator coordinates and converts it back to its corresponding geographic location represented by a `GeoPointInterface`. + +### Parameters + +| Parameter | Type | Description | +|-----------|----------|----------------------------------------------| +| `point` | `Offset` | The Cartesian point (x, y) in meters to unproject. | + +### Returns + +**Type:** `GeoPointInterface` + +A `GeoPointInterface` object representing the geographic coordinate (latitude, longitude). Note that the `altitude` of the returned point will always be `null`. + +### Example + +```kotlin +import com.mapconductor.core.features.GeoPointInterface +import com.mapconductor.core.projection.WebMercator +import androidx.compose.ui.geometry.Offset + +// Define a Web Mercator coordinate +val projectedOffset = Offset(x = -8238322.5f, y = 4970144.5f) + +// Unproject the coordinate back to a geographic point +val geoPoint: GeoPointInterface = WebMercator.unproject(projectedOffset) + +// geoPoint.latitude will be approximately 40.7128 +// geoPoint.longitude will be approximately -74.0060 +println("Unprojected GeoPoint: lat=${geoPoint.latitude}, lon=${geoPoint.longitude}") +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/raster/RasterLayer.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/raster/RasterLayer.kt.md new file mode 100644 index 00000000..830c30f1 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/raster/RasterLayer.kt.md @@ -0,0 +1,212 @@ +Excellent! Here is the high-quality SDK documentation for the provided code snippet, formatted in Markdown. + +--- + +## `RasterLayerState` + +### Description + +Manages the state and configuration of a single raster layer on the map. This class is stateful and designed to be used within a Jetpack Compose environment. It holds all configurable properties of a raster layer, such as its source, opacity, and visibility. Changes to its properties will automatically trigger UI updates in a reactive framework. + +### Constructor Signature + +```kotlin +class RasterLayerState( + source: RasterLayerSource, + opacity: Float = 1.0f, + visible: Boolean = true, + zIndex: Int = 0, + userAgent: String? = null, + debug: Boolean = false, + id: String? = null, + extraHeaders: Map? = null +) +``` + +### Parameters + +| Parameter | Type | Default | Description | +| :--- | :--- | :--- | :--- | +| `source` | `RasterLayerSource` | - | The source of the raster tiles (e.g., a URL template). | +| `opacity` | `Float` | `1.0f` | The layer's opacity, ranging from `0.0` (fully transparent) to `1.0` (fully opaque). | +| `visible` | `Boolean` | `true` | Toggles the visibility of the layer. If `false`, the layer will not be rendered. | +| `zIndex` | `Int` | `0` | The stacking order of the layer. Layers with a higher `zIndex` are drawn on top of layers with a lower `zIndex`. | +| `userAgent` | `String?` | `null` | The custom User-Agent string to use for network requests when fetching tiles. If `null`, the system default is used. | +| `debug` | `Boolean` | `false` | Enables debug mode for the layer, which may overlay debugging information like tile boundaries. | +| `id` | `String?` | `null` | A unique identifier for the layer. If not provided, a stable ID is generated based on the layer's initial properties. | +| `extraHeaders` | `Map?` | `null` | A map of additional HTTP headers to include in tile requests, such as for authentication tokens. | + +### Properties + +The `RasterLayerState` class exposes its constructor parameters as mutable properties. Changes to these properties will trigger recomposition in a Compose environment. + +| Property | Type | Description | +| :--- | :--- | :--- | +| `id` | `String` | The unique identifier for the layer. This is a read-only property. | +| `source` | `RasterLayerSource` | The source of the raster tiles. | +| `opacity` | `Float` | The layer's opacity. | +| `visible` | `Boolean` | The visibility of the layer. | +| `zIndex` | `Int` | The stacking order of the layer. | +| `userAgent` | `String?` | The custom User-Agent string for network requests. | +| `debug` | `Boolean` | The debug mode status for the layer. | +| `extraHeaders` | `Map?` | Additional HTTP headers for tile requests. | + +### Methods + +#### `copy()` + +Creates a shallow copy of the `RasterLayerState`, allowing you to create a new instance with modified properties while keeping others unchanged. + +**Signature** +```kotlin +fun copy( + source: RasterLayerSource = this.source, + opacity: Float = this.opacity, + visible: Boolean = this.visible, + zIndex: Int = this.zIndex, + debug: Boolean = this.debug, + userAgent: String? = this.userAgent, + id: String? = this.id, + extraHeaders: Map? = this.extraHeaders +): RasterLayerState +``` + +**Returns** + +| Type | Description | +| :--- | :--- | +| `RasterLayerState` | A new `RasterLayerState` instance. | + +#### `fingerPrint()` + +Generates a `RasterLayerFingerPrint` object representing the current state of the layer. This is useful for efficient state comparison and change detection. + +**Signature** +```kotlin +fun fingerPrint(): RasterLayerFingerPrint +``` + +**Returns** + +| Type | Description | +| :--- | :--- | +| `RasterLayerFingerPrint` | A fingerprint object containing hash codes of the layer's properties. | + +#### `asFlow()` + +Returns a `Flow` that emits a new `RasterLayerFingerPrint` whenever a property of the `RasterLayerState` changes. This is built on top of Jetpack Compose's `snapshotFlow` and is configured to only emit on distinct changes to the state's fingerprint. + +**Signature** +```kotlin +fun asFlow(): Flow +``` + +**Returns** + +| Type | Description | +| :--- | :--- | +| `Flow` | A flow that emits the layer's fingerprint upon state changes. | + +### Example + +```kotlin +import androidx.compose.runtime.remember +import androidx.compose.runtime.Composable +import androidx.compose.material.Button +import androidx.compose.material.Slider +import androidx.compose.material.Text + +// Assuming RasterLayerSource and MapComponent are defined elsewhere +// val rasterSource = RasterLayerSource("https://.../{z}/{x}/{y}.png") + +@Composable +fun MapScreen() { + // 1. Create and remember a RasterLayerState instance + val satelliteLayerState = remember { + RasterLayerState( + source = rasterSource, + opacity = 0.8f, + zIndex = 1, + extraHeaders = mapOf("Authorization" to "Bearer YOUR_TOKEN") + ) + } + + // 2. Use the state in your map component + MapComponent(rasterLayers = listOf(satelliteLayerState)) + + // 3. Modify the state based on user interaction + Button(onClick = { + // Toggle visibility + satelliteLayerState.visible = !satelliteLayerState.visible + }) { + Text("Toggle Satellite Layer") + } + + Slider( + value = satelliteLayerState.opacity, + onValueChange = { newOpacity -> + // Update opacity + satelliteLayerState.opacity = newOpacity + }, + valueRange = 0f..1f + ) +} +``` + +--- + +## `RasterLayerFingerPrint` + +### Description + +A data class that represents a unique snapshot of a `RasterLayerState`'s properties. It holds the hash codes of each property, providing a lightweight and efficient way to check for state changes, for example, within a `Flow`. + +### Properties + +| Property | Type | Description | +| :--- | :--- | :--- | +| `id` | `Int` | The hash code of the layer's ID. | +| `source` | `Int` | The hash code of the layer's source. | +| `opacity` | `Int` | The hash code of the layer's opacity. | +| `visible` | `Int` | The hash code of the layer's visibility status. | +| `zIndex` | `Int` | The hash code of the layer's z-index. | +| `userAgent` | `Int` | The hash code of the layer's User-Agent string. | +| `debug` | `Int` | The hash code of the layer's debug status. | +| `extra` | `Int` | The hash code of the layer's extra headers. | + +--- + +## `RasterLayerEvent` + +### Description + +A data class that encapsulates an event related to a raster layer. It is typically used in event handlers to pass the current state of the layer that triggered the event. + +### Properties + +| Property | Type | Description | +| :--- | :--- | :--- | +| `state` | `RasterLayerState` | The state of the raster layer at the time of the event. | + +--- + +## `OnRasterLayerEventHandler` + +### Description + +A type alias for a function that processes `RasterLayerEvent` objects. This provides a convenient and readable way to define event handlers for raster layer interactions, such as clicks or taps. + +### Signature + +```kotlin +typealias OnRasterLayerEventHandler = (RasterLayerEvent) -> Unit +``` + +### Example + +```kotlin +val handleRasterLayerClick: OnRasterLayerEventHandler = { event -> + println("Layer clicked! ID: ${event.state.id}, Source: ${event.state.source}") + // You can now access any property from event.state +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/raster/RasterLayerCapableInterface.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/raster/RasterLayerCapableInterface.kt.md new file mode 100644 index 00000000..686afb18 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/raster/RasterLayerCapableInterface.kt.md @@ -0,0 +1,202 @@ +# Interface `RasterLayerCapableInterface` + +The `RasterLayerCapableInterface` defines a contract for classes that can manage and display raster layers on a map. It provides a standardized way to handle the composition, updating, and querying of raster layers. Implement this interface in any class, such as a map controller or view, that needs to interact with a collection of raster data layers. + +--- + +## `compositionRasterLayers` + +Composes a new set of raster layers, replacing any existing ones. This function asynchronously updates the map to display exactly the layers specified in the input list, in the given order. It is useful for setting or resetting the entire raster layer stack. + +### Signature + +```kotlin +suspend fun compositionRasterLayers(data: List) +``` + +### Description + +This asynchronous suspend function takes a complete list of `RasterLayerState` objects and applies them to the map. The existing collection of raster layers is replaced with the new one. The function will handle adding new layers, removing obsolete ones, and reordering existing ones to match the provided list. + +### Parameters + +| Parameter | Type | Description | +| :-------- | :----------------------- | :-------------------------------------------------------------------------- | +| `data` | `List` | The complete and ordered list of raster layer states to be displayed on the map. | + +### Example + +Here is an example of how a class might implement and use `compositionRasterLayers`. + +```kotlin +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking + +// Assume RasterLayerState is a data class like: +// data class RasterLayerState(val id: String, val url: String, val opacity: Float) + +class MapController : RasterLayerCapableInterface { + private val currentLayers = mutableMapOf() + + override suspend fun compositionRasterLayers(data: List) { + println("Composing ${data.size} raster layers...") + // In a real implementation, this would involve complex logic + // to add, remove, and reorder layers on a map view. + currentLayers.clear() + data.forEach { layer -> currentLayers[layer.id] = layer } + println("Composition complete. Current layers: ${currentLayers.keys}") + } + + // other interface methods... + override suspend fun updateRasterLayer(state: RasterLayerState) { /* ... */ } + override fun hasRasterLayer(state: RasterLayerState): Boolean { /* ... */ return false } +} + +fun main() = runBlocking { + val mapController = MapController() + + val initialLayers = listOf( + RasterLayerState("layer1", "http://example.com/tiles/weather", 0.8f), + RasterLayerState("layer2", "http://example.com/tiles/terrain", 1.0f) + ) + + // Set the initial layers on the map + launch { + mapController.compositionRasterLayers(initialLayers) + } +} +``` + +--- + +## `updateRasterLayer` + +Adds a new raster layer or updates an existing one. This function is ideal for making targeted changes to a single layer without affecting the rest of the layer stack. + +### Signature + +```kotlin +suspend fun updateRasterLayer(state: RasterLayerState) +``` + +### Description + +This asynchronous suspend function takes a single `RasterLayerState` object and applies its state to the map. If a layer with the same unique identifier already exists, its properties (e.g., opacity, visibility) are updated. If no such layer exists, a new one is added to the map. + +### Parameters + +| Parameter | Type | Description | +| :-------- | :---------------- | :----------------------------------------------------------------------- | +| `state` | `RasterLayerState` | The state object representing the raster layer to be added or updated. | + +### Example + +```kotlin +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking + +class MapController : RasterLayerCapableInterface { + private val currentLayers = mutableMapOf() + + override suspend fun updateRasterLayer(state: RasterLayerState) { + println("Updating layer with ID: ${state.id}") + // In a real implementation, this would update a layer's properties + // (e.g., opacity) or add it if it's new. + currentLayers[state.id] = state + println("Layer ${state.id} updated with opacity ${state.opacity}.") + } + + // other interface methods... + override suspend fun compositionRasterLayers(data: List) { /* ... */ } + override fun hasRasterLayer(state: RasterLayerState): Boolean { /* ... */ return false } +} + +fun main() = runBlocking { + val mapController = MapController() + + val weatherLayer = RasterLayerState("layer1", "http://example.com/tiles/weather", 0.5f) + + // Add or update the weather layer + launch { + mapController.updateRasterLayer(weatherLayer) + } + + // Later, update its opacity + val updatedWeatherLayer = weatherLayer.copy(opacity = 0.9f) + launch { + mapController.updateRasterLayer(updatedWeatherLayer) + } +} +``` + +--- + +## `hasRasterLayer` + +Checks if a specific raster layer is currently part of the map's layer stack. + +### Signature + +```kotlin +fun hasRasterLayer(state: RasterLayerState): Boolean +``` + +### Description + +This synchronous function determines whether a raster layer corresponding to the provided `RasterLayerState` exists on the map. The check is typically performed using a unique identifier within the `state` object. + +### Parameters + +| Parameter | Type | Description | +| :-------- | :---------------- | :----------------------------------------------------------------------- | +| `state` | `RasterLayerState` | The state object of the raster layer to check for. | + +### Returns + +| Type | Description | +| :-------- | :----------------------------------------------------- | +| `Boolean` | Returns `true` if the layer exists, `false` otherwise. | + +### Example + +```kotlin +class MapController : RasterLayerCapableInterface { + private val currentLayers = mutableMapOf() + + init { + // Pre-populate with a layer for the example + val existingLayer = RasterLayerState("layer1", "http://example.com/tiles/weather", 1.0f) + currentLayers[existingLayer.id] = existingLayer + } + + override fun hasRasterLayer(state: RasterLayerState): Boolean { + return currentLayers.containsKey(state.id) + } + + // other interface methods... + override suspend fun compositionRasterLayers(data: List) { /* ... */ } + override suspend fun updateRasterLayer(state: RasterLayerState) { /* ... */ } +} + +fun main() { + val mapController = MapController() + + val layerToCheck = RasterLayerState("layer1", "http://...", 1.0f) + val nonExistentLayer = RasterLayerState("layer99", "http://...", 1.0f) + + if (mapController.hasRasterLayer(layerToCheck)) { + println("Layer '${layerToCheck.id}' exists on the map.") + } else { + println("Layer '${layerToCheck.id}' does not exist on the map.") + } + + if (mapController.hasRasterLayer(nonExistentLayer)) { + println("Layer '${nonExistentLayer.id}' exists on the map.") + } else { + println("Layer '${nonExistentLayer.id}' does not exist on the map.") + } +} +// Expected Output: +// Layer 'layer1' exists on the map. +// Layer 'layer99' does not exist on the map. +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/raster/RasterLayerComponent.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/raster/RasterLayerComponent.kt.md new file mode 100644 index 00000000..6ba09460 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/raster/RasterLayerComponent.kt.md @@ -0,0 +1,141 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +--- + +## RasterLayer + +The `RasterLayer` composable is used to add a raster tile layer to the map. A raster layer is composed of a grid of image tiles, typically fetched from a remote server, which are stitched together to form a complete map image. + +This composable must be called from within the scope of a `MapView` composable. The layer is automatically added to the map when it enters the composition and removed when it leaves. + +There are two overloads for this function: +1. A convenience overload that accepts individual properties like `source`, `opacity`, and `visible`. This is the most common way to add a raster layer. +2. An overload that accepts a `RasterLayerState` object, which is useful for advanced cases where the layer's state is managed externally. + +*** + +### RasterLayer (Property-based) + +This composable adds a raster layer to the map by defining its properties directly. It is the recommended approach for most use cases. + +#### Signature +```kotlin +@Composable +fun MapViewScope.RasterLayer( + source: RasterLayerSource, + opacity: Float = 1.0f, + visible: Boolean = true, + zIndex: Int = 0, + userAgent: String? = null, + id: String? = null, + extraHeaders: Map? = null, +) +``` + +#### Description +Creates and manages a raster layer on the map. You define the tile source and can optionally customize its appearance and behavior, such as opacity, visibility, and draw order. The layer's lifecycle is automatically managed by Jetpack Compose. + +#### Parameters +| Parameter | Type | Description | Default | +|----------------|-------------------------|----------------------------------------------------------------------------------------------------------------------------------------|---------| +| `source` | `RasterLayerSource` | The source of the raster tiles. This object defines where and how to fetch the map tiles (e.g., a URL template). | (none) | +| `opacity` | `Float` | The opacity of the layer, ranging from `0.0` (fully transparent) to `1.0` (fully opaque). | `1.0f` | +| `visible` | `Boolean` | Toggles the visibility of the layer. If `false`, the layer will not be rendered. | `true` | +| `zIndex` | `Int` | The vertical stacking order of the layer. Layers with a higher `zIndex` are drawn on top of layers with a lower `zIndex`. | `0` | +| `userAgent` | `String?` | An optional custom `User-Agent` string to be sent with the tile requests. | `null` | +| `id` | `String?` | A unique identifier for the layer. If not provided, a unique ID will be generated internally. | `null` | +| `extraHeaders` | `Map?` | An optional map of extra HTTP headers to be included in the tile requests. Useful for authentication tokens or other custom headers. | `null` | + +#### Returns +This composable does not return a value. It adds the specified raster layer to the map as a side effect. + +#### Example +Here is an example of adding a standard OpenStreetMap raster layer to a `MapView`. + +```kotlin +import androidx.compose.runtime.Composable +import com.mapconductor.core.MapView +import com.mapconductor.core.raster.RasterLayer +import com.mapconductor.core.raster.source.RasterTileSource + +@Composable +fun MapWithRasterLayer() { + MapView { + // Add a raster layer from a tile server + RasterLayer( + source = RasterTileSource( + url = "https://a.tile.openstreetmap.org/{z}/{x}/{y}.png" + ), + opacity = 0.8f, + zIndex = 1 + ) + } +} +``` + +*** + +### RasterLayer (State-based) + +This overload adds a raster layer using a `RasterLayerState` object. This is useful for advanced scenarios where you need to hoist and manage the layer's state outside the composable, for instance, in a `ViewModel`. + +#### Signature +```kotlin +@Composable +fun MapViewScope.RasterLayer(state: RasterLayerState) +``` + +#### Description +Adds a raster layer to the map using a provided `RasterLayerState` instance. The composable observes the `state` object; any changes to its properties will be automatically reflected on the map layer. This allows for dynamic updates to the layer's properties from external logic. + +#### Parameters +| Parameter | Type | Description | +|-----------|--------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `state` | `RasterLayerState` | An object that encapsulates all properties of the raster layer. Changes to this state object will cause the layer on the map to update. | + +#### Returns +This composable does not return a value. + +#### Example +In this example, the `RasterLayerState` is created and remembered within the composable. A button is provided to dynamically change the layer's opacity, demonstrating how the map updates when the state changes. + +```kotlin +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.runtime.getValue +import androidx.compose.runtime.setValue +import androidx.compose.material.Button +import androidx.compose.material.Text +import com.mapconductor.core.MapView +import com.mapconductor.core.raster.RasterLayer +import com.mapconductor.core.raster.RasterLayerState +import com.mapconductor.core.raster.source.RasterTileSource + +@Composable +fun ControllableRasterLayer() { + // Hoist the state to control it from other UI elements + var layerState by remember { + mutableStateOf( + RasterLayerState( + source = RasterTileSource( + url = "https://a.tile.openstreetmap.org/{z}/{x}/{y}.png" + ), + opacity = 1.0f + ) + ) + } + + MapView { + // Add the layer using the state object + RasterLayer(state = layerState) + } + + Button(onClick = { + // Modify the state to update the layer on the map + val newOpacity = if (layerState.opacity > 0.5f) 0.3f else 1.0f + layerState = layerState.copy(opacity = newOpacity) + }) { + Text("Toggle Opacity") + } +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/raster/RasterLayerController.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/raster/RasterLayerController.kt.md new file mode 100644 index 00000000..70ac0007 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/raster/RasterLayerController.kt.md @@ -0,0 +1,210 @@ +# SDK Documentation: RasterLayerController + +## `RasterLayerController` + +### Description + +An abstract class responsible for managing the lifecycle of raster layers on a map. It acts as a bridge between the desired state of the layers (`RasterLayerState`) and their actual representation on the map (`ActualLayer`). + +The controller handles adding, updating, and removing raster layers in a synchronized and efficient manner. It uses a `RasterLayerManager` to track layer entities and a `RasterLayerOverlayRenderer` to handle the platform-specific rendering logic. All operations that modify the layer state are protected by a semaphore to ensure thread safety and prevent race conditions. + +This class implements the `OverlayControllerInterface`. + +**Type Parameters** + +| Name | Description | +| :--- | :--- | +| `ActualLayer` | The concrete, platform-specific layer object that is rendered on the map. Must be a non-nullable type (`Any`). | + +### Constructor + +```kotlin +abstract class RasterLayerController( + val rasterLayerManager: RasterLayerManagerInterface, + open val renderer: RasterLayerOverlayRendererInterface, + override var clickListener: OnRasterLayerEventHandler? = null, +) +``` + +**Parameters** + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `rasterLayerManager` | `RasterLayerManagerInterface` | An instance that manages the storage and retrieval of raster layer entities. | +| `renderer` | `RasterLayerOverlayRendererInterface` | An instance responsible for the actual rendering of layers on the map. | +| `clickListener` | `OnRasterLayerEventHandler?` | An optional listener to handle click events on the raster layers. Defaults to `null`. | + +### Properties + +#### `zIndex` + +The stacking order of the overlay. This value is currently fixed at `0`. + +**Signature** +```kotlin +override val zIndex: Int = 0 +``` + +--- + +#### `semaphore` + +A semaphore that allows only one permit, used to ensure that all layer modification operations (add, update, remove, etc.) are executed atomically and serially. This prevents race conditions when multiple updates occur concurrently. + +**Signature** +```kotlin +val semaphore = Semaphore(1) +``` + +--- + +### Methods + +#### `add` + +Synchronizes the map's raster layers with a provided list of `RasterLayerState` objects. + +This method performs a diffing operation against the currently managed layers. It determines which layers need to be added, which need to be updated, and which should be removed. It then delegates these operations to the `renderer`. The entire process is executed as a single, atomic operation. + +**Signature** +```kotlin +override suspend fun add(data: List) +``` + +**Parameters** + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `data` | `List` | The complete and definitive list of raster layers that should be displayed on the map. | + +--- + +#### `update` + +Updates a single existing raster layer identified by its state's ID. + +This method checks for changes by comparing the `fingerPrint` of the new state with the existing one. If no changes are detected, the operation is skipped to improve performance. If the layer with the given ID does not exist, the method does nothing. + +**Signature** +```kotlin +override suspend fun update(state: RasterLayerState) +``` + +**Parameters** + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `state` | `RasterLayerState` | The new state for the raster layer to be updated. | + +--- + +#### `upsert` + +Adds or updates a single layer without affecting other existing layers. + +This is useful for managing a specific raster layer independently from the main collection of layers, such as an internal layer used for marker tiling. If a layer with the same ID already exists, it will be updated; otherwise, a new layer will be added. + +**Signature** +```kotlin +suspend fun upsert(state: RasterLayerState) +``` + +**Parameters** + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `state` | `RasterLayerState` | The state of the layer to add or update. | + +**Example** + +```kotlin +// Assume 'myLayerController' is an instance of a RasterLayerController implementation +// and 'myInternalLayerState' is a RasterLayerState for a debugging overlay. + +// This will add or update the debugging layer without removing any other layers +// that might have been added via the 'add' method. +myLayerController.upsert(myInternalLayerState) +``` + +--- + +#### `removeById` + +Removes a single raster layer by its unique identifier without clearing other layers. + +If a layer with the specified ID is found, it is removed from both the `rasterLayerManager` and the map via the `renderer`. If no such layer exists, the method does nothing. + +**Signature** +```kotlin +suspend fun removeById(id: String) +``` + +**Parameters** + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `id` | `String` | The unique ID of the raster layer to remove. | + +--- + +#### `clear` + +Removes all raster layers currently managed by this controller from the map. + +**Signature** +```kotlin +override suspend fun clear() +``` + +--- + +#### `find` + +Finds a raster layer entity at a given geographical position. + +**Note:** The base implementation does not support this operation and always returns `null`. Subclasses must override this method to provide find functionality. + +**Signature** +```kotlin +override fun find(position: GeoPointInterface): RasterLayerEntityInterface? +``` + +**Parameters** + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `position` | `GeoPointInterface` | The geographical coordinate to search at. | + +**Returns** + +| Type | Description | +| :--- | :--- | +| `RasterLayerEntityInterface?` | Always returns `null` in this base class. | + +--- + +#### `onCameraChanged` + +A callback method invoked when the map's camera position changes. It delegates the event to the `renderer`, which may use it to optimize layer rendering (e.g., for tiled layers). + +**Signature** +```kotlin +override suspend fun onCameraChanged(mapCameraPosition: MapCameraPosition) +``` + +**Parameters** + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `mapCameraPosition` | `MapCameraPosition` | The new position and state of the map camera. | + +--- + +#### `destroy` + +Cleans up resources used by the controller. The base implementation is empty as it does not manage any native resources directly. Subclasses should override this method to release any resources they have allocated. + +**Signature** +```kotlin +override fun destroy() +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/raster/RasterLayerEntity.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/raster/RasterLayerEntity.kt.md new file mode 100644 index 00000000..12fad0b9 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/raster/RasterLayerEntity.kt.md @@ -0,0 +1,95 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +*** + +## `RasterLayerEntityInterface` + +A generic interface that defines the contract for a raster layer entity. It encapsulates a raster layer object along with its state and a unique fingerprint derived from that state. This provides a standardized way to handle different types of raster layers and their properties. + +### Signature + +```kotlin +interface RasterLayerEntityInterface +``` + +### Type Parameters + +| Parameter | Description | +|---|---| +| `` | The specific type of the underlying raster layer object being managed. | + +### Properties + +| Property | Type | Description | +|---|---|---| +| `layer` | `ActualLayer` | The actual raster layer object. | +| `state` | `RasterLayerState` | An object representing the current state of the raster layer (e.g., visibility, opacity, source). | +| `fingerPrint` | `RasterLayerFingerPrint` | A unique identifier representing the layer's current state. | + +*** + +## `RasterLayerEntity` + +A data class that provides a concrete implementation of the `RasterLayerEntityInterface`. It holds a reference to the actual layer object and its state. The `fingerPrint` is automatically computed from the provided `state` upon instantiation, making it convenient for state tracking and comparison. + +### Signature + +```kotlin +data class RasterLayerEntity( + override val layer: ActualLayer, + override val state: RasterLayerState, +) : RasterLayerEntityInterface +``` + +### Parameters + +| Parameter | Type | Description | +|---|---|---| +| `layer` | `ActualLayer` | The actual raster layer object to be encapsulated. | +| `state` | `RasterLayerState` | The `RasterLayerState` object that defines the layer's configuration. | + +### Properties + +| Property | Type | Description | +|---|---|---| +| `layer` | `ActualLayer` | The actual raster layer object provided in the constructor. | +| `state` | `RasterLayerState` | The current state of the raster layer provided in the constructor. | +| `fingerPrint` | `RasterLayerFingerPrint` | A unique fingerprint automatically generated by calling `state.fingerPrint()`. This value is computed once during object creation. | + +### Example + +This example demonstrates how to create an instance of `RasterLayerEntity`. Note that `MapboxRasterLayer`, `RasterLayerState`, and `RasterLayerFingerPrint` are hypothetical classes used for illustration. + +```kotlin +// Assume these supporting classes are defined elsewhere in your project +class MapboxRasterLayer(val sourceId: String) +class RasterLayerFingerPrint(val id: String) + +data class RasterLayerState(val source: String, val opacity: Double = 1.0) { + fun fingerPrint(): RasterLayerFingerPrint { + return RasterLayerFingerPrint("source=$source&opacity=$opacity") + } +} + +// 1. Create the actual layer object +val satelliteLayer = MapboxRasterLayer("satellite-v9") + +// 2. Define the state for the layer +val layerState = RasterLayerState(source = "mapbox://satellite-v9", opacity = 0.85) + +// 3. Create the RasterLayerEntity instance +val rasterEntity = RasterLayerEntity( + layer = satelliteLayer, + state = layerState +) + +// The fingerprint is automatically computed from the state +println("Layer Source ID: ${rasterEntity.layer.sourceId}") +// > Layer Source ID: satellite-v9 + +println("Layer State: ${rasterEntity.state}") +// > Layer State: RasterLayerState(source=mapbox://satellite-v9, opacity=0.85) + +println("Layer Fingerprint: ${rasterEntity.fingerPrint.id}") +// > Layer Fingerprint: source=mapbox://satellite-v9&opacity=0.85 +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/raster/RasterLayerManager.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/raster/RasterLayerManager.kt.md new file mode 100644 index 00000000..ea998962 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/raster/RasterLayerManager.kt.md @@ -0,0 +1,202 @@ +# RasterLayerManager + +The `RasterLayerManager` is a generic class responsible for managing a collection of raster layer entities. It provides a simple, in-memory key-value store for registering, retrieving, and removing entities using their unique string identifiers. This manager implements the `RasterLayerManagerInterface`. + +The generic type parameter `ActualLayer` represents the concrete type of the underlying layer object used in a specific map rendering system. + +**Class Signature** +```kotlin +class RasterLayerManager : RasterLayerManagerInterface +``` + +--- + +## Methods + +### registerEntity +Adds a new raster layer entity to the manager. If an entity with the same ID already exists, it will be replaced. + +**Signature** +```kotlin +fun registerEntity(entity: RasterLayerEntityInterface) +``` + +**Description** +This method uses the entity's ID as the key to store it in the manager's internal collection. + +**Parameters** +| Parameter | Type | Description | +|-----------|------|-------------| +| `entity` | `RasterLayerEntityInterface` | The raster layer entity to register. The entity's ID is used as the key. | + +**Returns** +`Unit` - This method does not return a value. + +**Example** +```kotlin +// Assuming 'myEntity' is an instance of a class implementing RasterLayerEntityInterface +// and the manager is initialized. +val manager = RasterLayerManager() +manager.registerEntity(myEntity) +``` + +--- + +### removeEntity +Removes a raster layer entity from the manager based on its unique ID. + +**Signature** +```kotlin +fun removeEntity(id: String): RasterLayerEntityInterface? +``` + +**Parameters** +| Parameter | Type | Description | +|-----------|------|-------------| +| `id` | `String` | The unique ID of the entity to remove. | + +**Returns** +The removed `RasterLayerEntityInterface` if it was found, or `null` if no entity with the specified ID exists. + +**Example** +```kotlin +val manager = RasterLayerManager() +// ... register an entity with id "raster-01" +val removedEntity = manager.removeEntity("raster-01") +if (removedEntity != null) { + println("Entity raster-01 was removed.") +} else { + println("Entity raster-01 not found.") +} +``` + +--- + +### getEntity +Retrieves a raster layer entity by its unique ID. + +**Signature** +```kotlin +fun getEntity(id: String): RasterLayerEntityInterface? +``` + +**Parameters** +| Parameter | Type | Description | +|-----------|------|-------------| +| `id` | `String` | The unique ID of the entity to retrieve. | + +**Returns** +The `RasterLayerEntityInterface` corresponding to the given ID, or `null` if the entity is not found. + +**Example** +```kotlin +val manager = RasterLayerManager() +// ... register an entity with id "raster-01" +val entity = manager.getEntity("raster-01") +entity?.let { + println("Found entity: ${it.state.id}") +} +``` + +--- + +### hasEntity +Checks if an entity with the specified ID is registered with the manager. + +**Signature** +```kotlin +fun hasEntity(id: String): Boolean +``` + +**Parameters** +| Parameter | Type | Description | +|-----------|------|-------------| +| `id` | `String` | The unique ID of the entity to check for. | + +**Returns** +`true` if an entity with the given ID exists, `false` otherwise. + +**Example** +```kotlin +val manager = RasterLayerManager() +// ... register an entity with id "raster-01" +if (manager.hasEntity("raster-01")) { + println("Manager contains entity with ID raster-01.") +} +``` + +--- + +### allEntities +Retrieves a list of all raster layer entities currently registered with the manager. + +**Signature** +```kotlin +fun allEntities(): List> +``` + +**Returns** +A `List` containing all registered `RasterLayerEntityInterface` objects. The list will be empty if no entities are registered. + +**Example** +```kotlin +val manager = RasterLayerManager() +// ... register multiple entities +val all = manager.allEntities() +println("Total entities: ${all.size}") +for (entity in all) { + println("Entity ID: ${entity.state.id}") +} +``` + +--- + +### clear +Removes all raster layer entities from the manager, leaving it empty. + +**Signature** +```kotlin +fun clear() +``` + +**Returns** +`Unit` - This method does not return a value. + +**Example** +```kotlin +val manager = RasterLayerManager() +// ... register multiple entities +println("Entities before clear: ${manager.allEntities().size}") // e.g., prints "Entities before clear: 5" +manager.clear() +println("Entities after clear: ${manager.allEntities().size}") // prints "Entities after clear: 0" +``` + +--- + +### find +Finds a raster layer entity at a specific geographic position. + +**Signature** +```kotlin +fun find(position: GeoPointInterface): RasterLayerEntityInterface? +``` + +**Description** +This method is designed to perform a spatial query to find an entity at the given coordinates. + +**Note:** The current implementation is a stub and always returns `null`. This method is intended for future functionality. + +**Parameters** +| Parameter | Type | Description | +|-----------|------|-------------| +| `position` | `GeoPointInterface` | The geographic coordinate to search at. | + +**Returns** +Currently, this method always returns `null`. In a future implementation, it would return the found `RasterLayerEntityInterface` or `null` if no entity is found at the specified position. + +**Example** +```kotlin +val manager = RasterLayerManager() +val somePosition: GeoPointInterface = // ... create a GeoPointInterface instance +val foundEntity = manager.find(somePosition) // foundEntity will be null with the current implementation +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/raster/RasterLayerOverlay.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/raster/RasterLayerOverlay.kt.md new file mode 100644 index 00000000..97544d97 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/raster/RasterLayerOverlay.kt.md @@ -0,0 +1,104 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +*** + +# Raster Layer SDK + +This document provides detailed information about the `LocalRasterLayerCollector` and `RasterLayerOverlay` components, which are essential for managing and rendering raster data on the map. + +## LocalRasterLayerCollector + +A `CompositionLocal` that provides a mechanism for `RasterLayer` composables to register themselves with a parent `MapView`. + +### Signature + +```kotlin +val LocalRasterLayerCollector: ProvidableCompositionLocal> +``` + +### Description + +`LocalRasterLayerCollector` is a Jetpack Compose `CompositionLocal` that holds an instance of `ChildCollector`. Its primary purpose is to allow child `RasterLayer` composables to pass their state up the composition tree to the `MapView`. + +This collector is provided by the `MapView` component. Any attempt to access it from a composable that is not a descendant of `MapView` will result in an `IllegalStateException`. + +### Example + +While you typically won't interact with this directly, it is used internally by `RasterLayer` composables to register their state. + +```kotlin +// Conceptual usage within a RasterLayer composable +@Composable +fun RasterLayer( + // ... other parameters +) { + // Access the collector provided by a parent MapView + val collector = LocalRasterLayerCollector.current + + // The RasterLayer composable uses the collector to register its state, + // making the map aware of its presence and properties. + DisposableEffect(Unit) { + val state = RasterLayerState(...) + val id = collector.addChild(state) + onDispose { + collector.removeChild(id) + } + } +} +``` + +## RasterLayerOverlay + +An overlay class responsible for managing and rendering a collection of raster layers on the map. + +### Signature + +```kotlin +class RasterLayerOverlay( + override val flow: StateFlow>, +) : MapOverlayInterface +``` + +### Description + +`RasterLayerOverlay` implements the `MapOverlayInterface` to serve as the bridge between the declarative `RasterLayer` composables and the map's rendering engine. It observes a `StateFlow` of raster layer states and, during the render pass, delegates the actual drawing commands to a compatible map controller. + +### Constructor + +#### Signature + +```kotlin +RasterLayerOverlay(flow: StateFlow>) +``` + +#### Parameters + +| Parameter | Type | Description | +|-----------|------------------------------------------------------|---------------------------------------------------------------------------------------------------------| +| `flow` | `StateFlow>` | A state flow that emits the current collection of raster layers to be displayed, indexed by a unique ID. | + +### Methods + +#### render + +This function is called by the map system to render the raster layers onto the map view. + +##### Signature + +```kotlin +override suspend fun render( + data: MutableMap, + controller: MapViewControllerInterface, +) +``` + +##### Description + +The `render` method is invoked during the map's drawing cycle. It receives the current set of `RasterLayerState` data and the active `MapViewControllerInterface`. It then attempts to cast the controller to a `RasterLayerCapableInterface`. If successful, it calls the controller's `compositionRasterLayers` method, passing the list of layer states to be rendered on the map. This effectively delegates the platform-specific rendering logic to the controller. + +##### Parameters + +| Parameter | Type | Description | +|--------------|--------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------| +| `data` | `MutableMap` | The most recent map of raster layer states to render. The key is the layer's unique identifier, and the value is the state object. | +| `controller` | `MapViewControllerInterface` | The map view controller that manages the map's state and rendering operations. It must conform to `RasterLayerCapableInterface` for rendering to occur. | \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/raster/RasterLayerOverlayRendererInterface.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/raster/RasterLayerOverlayRendererInterface.kt.md new file mode 100644 index 00000000..0be5383c --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/raster/RasterLayerOverlayRendererInterface.kt.md @@ -0,0 +1,214 @@ +# RasterLayerOverlayRendererInterface + +## Description + +The `RasterLayerOverlayRendererInterface` defines a contract for rendering and managing the lifecycle of raster layer overlays on a map. Implementations of this interface are responsible for translating abstract raster layer state changes (add, change, remove) into concrete operations for a specific, underlying map SDK (e.g., Google Maps, Mapbox). + +All operations are designed to be asynchronous and are executed within a provided `CoroutineScope`. + +### Generic Type Parameters + +| Parameter | Description | +| :--- | :--- | +| `` | The concrete layer class of the underlying map SDK (e.g., `TileOverlay` for Google Maps). | + +## Properties + +### coroutine + +The coroutine scope in which all suspend functions of this interface will be executed. The implementation must provide this scope. + +**Signature** +```kotlin +abstract val coroutine: CoroutineScope +``` + +--- + +## Functions + +### onAdd + +Called to add one or more new raster layers to the map. This function should process the provided layer states, create the corresponding native map layers, and add them to the map view. + +**Signature** +```kotlin +suspend fun onAdd(data: List): List +``` + +**Parameters** + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `data` | `List` | A list of objects, where each object contains the state for a new layer to be added. | + +**Returns** + +A `List` where each element corresponds to the newly created native map layer object at the same index as the input `data`. An element will be `null` if the layer creation failed for any reason. + +--- + +### onChange + +Called to update one or more existing raster layers. This function should handle changes to layer properties such as opacity, visibility, or tile sources. + +**Signature** +```kotlin +suspend fun onChange(data: List>): List +``` + +**Parameters** + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `data` | `List>` | A list of objects, each containing the previous and current state of a layer to be updated. | + +**Returns** + +A `List` where each element is the updated native map layer object corresponding to the input `data`. An element can be `null` if the update results in the layer being removed or if an error occurs. + +--- + +### onRemove + +Called to remove one or more raster layers from the map. + +**Signature** +```kotlin +suspend fun onRemove(data: List>) +``` + +**Parameters** + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `data` | `List>` | A list of layer entities to be removed from the map. | + +--- + +### onCameraChanged + +An optional callback invoked whenever the map's camera position changes. This can be overridden to implement logic that depends on the current map view, such as adjusting layer details based on zoom level. The default implementation is empty. + +**Signature** +```kotlin +suspend fun onCameraChanged(mapCameraPosition: MapCameraPosition) {} +``` + +**Parameters** + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `mapCameraPosition` | `MapCameraPosition` | An object containing the current camera state (e.g., target, zoom, tilt, bearing). | + +--- + +### onPostProcess + +A lifecycle hook called after a batch of add, change, or remove operations has been completed. This can be used for final cleanup, batching updates, or forcing a map redraw to ensure all changes are visually applied. + +**Signature** +```kotlin +suspend fun onPostProcess() +``` + +--- + +## Nested Interfaces + +### AddParamsInterface + +A data structure holding the parameters required to add a new raster layer. + +| Property | Type | Description | +| :--- | :--- | :--- | +| `state` | `RasterLayerState` | The state of the raster layer to be added. | + +### ChangeParamsInterface + +A data structure holding the parameters required to change an existing raster layer. + +| Property | Type | Description | +| :--- | :--- | :--- | +| `current` | `RasterLayerEntityInterface` | The new entity representing the layer's updated state. | +| `prev` | `RasterLayerEntityInterface` | The old entity representing the layer's state before the change. | + +--- + +## Example + +The following example demonstrates a conceptual implementation of `RasterLayerOverlayRendererInterface` for a hypothetical map SDK. + +```kotlin +import com.mapconductor.core.map.MapCameraPosition +import com.mapconductor.core.raster.* +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch + +// Assume these are classes from a hypothetical map SDK +class NativeMapSDK { + fun addTileOverlay(options: Any): NativeTileOverlay { /* ... */ return NativeTileOverlay() } + fun removeOverlay(overlay: NativeTileOverlay) { /* ... */ } +} +class NativeTileOverlay { + fun setOpacity(opacity: Float) { /* ... */ } +} + +/** + * An example implementation that bridges the interface with a hypothetical map SDK. + * + * @param mapSDK The instance of the native map object. + * @param coroutine The CoroutineScope for running suspend functions. + */ +class MyRasterLayerRenderer( + private val mapSDK: NativeMapSDK, + override val coroutine: CoroutineScope +) : RasterLayerOverlayRendererInterface { + + override suspend fun onAdd(data: List): List { + return data.map { params -> + // Logic to convert abstract state to native options + val nativeOptions = createOptionsFromState(params.state) + // Add the overlay to the actual map + mapSDK.addTileOverlay(nativeOptions) + } + } + + override suspend fun onChange(data: List>): List { + return data.map { params -> + val nativeLayer = params.prev.actual + // Apply changes to the existing native layer + nativeLayer?.apply { + // Example: Update opacity based on the new state + // setOpacity(params.current.state.opacity) + } + nativeLayer // Return the updated layer + } + } + + override suspend fun onRemove(data: List>) { + data.forEach { entity -> + entity.actual?.let { nativeLayer -> + // Remove the layer from the actual map + mapSDK.removeOverlay(nativeLayer) + } + } + } + + override suspend fun onCameraChanged(mapCameraPosition: MapCameraPosition) { + // Optional: React to camera changes, e.g., for level-of-detail adjustments + println("Camera moved to zoom level: ${mapCameraPosition.zoom}") + } + + override suspend fun onPostProcess() { + // Optional: Force a map refresh if needed by the underlying SDK + // mapSDK.invalidate() + } + + private fun createOptionsFromState(state: RasterLayerState): Any { + // In a real implementation, you would convert the RasterLayerState + // into a native TileOverlayOptions object. + return Any() + } +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/raster/RasterLayerSource.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/raster/RasterLayerSource.kt.md new file mode 100644 index 00000000..1110b58d --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/raster/RasterLayerSource.kt.md @@ -0,0 +1,146 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +--- + +## RasterLayerSource + +`RasterLayerSource` is a sealed class that defines the various sources from which a raster tile layer can be loaded. It provides a type-safe way to specify the configuration for different raster data providers. + +The available source types are: +* `UrlTemplate`: For sources defined by a URL template with x, y, and z placeholders. +* `TileJson`: For sources defined by a URL pointing to a TileJSON manifest file. +* `ArcGisService`: For sources from an ArcGIS MapServer or ImageServer service. + +### Companion Object + +#### `DEFAULT_TILE_SIZE` +The default size of the map tiles in pixels. + +**Signature** +```kotlin +const val DEFAULT_TILE_SIZE: Int = 512 +``` + +--- + +## TileScheme + +An enum defining the tile coordinate system for raster layers. + +**Signature** +```kotlin +enum class TileScheme { + XYZ, + TMS, +} +``` + +**Values** + +| Value | Description | +| :---- | :---------- | +| `XYZ` | The standard "slippy map" tile scheme, where the origin (0,0) is at the top-left corner of the map. This is the most common scheme, used by services like OpenStreetMap and Google Maps. | +| `TMS` | The Tile Map Service scheme, where the origin (0,0) is at the bottom-left corner of the map. | + +--- + +## RasterLayerSource.UrlTemplate + +A data class representing a raster layer source defined by a URL template. This is a flexible way to load tiles from various services that follow a predictable URL structure. + +**Signature** +```kotlin +data class UrlTemplate( + val template: String, + val tileSize: Int = DEFAULT_TILE_SIZE, + val minZoom: Int? = null, + val maxZoom: Int? = null, + val attribution: String? = null, + val scheme: TileScheme = TileScheme.XYZ, +) : RasterLayerSource() +``` + +**Description** +This source type uses a string template to generate URLs for individual map tiles. The template should include placeholders `{x}`, `{y}`, and `{z}` which will be replaced with the tile's column, row, and zoom level, respectively. + +**Parameters** + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `template` | `String` | The URL template for fetching tiles. Must contain `{x}`, `{y}`, and `{z}` placeholders. For example: `https://a.tile.openstreetmap.org/{z}/{x}/{y}.png`. | +| `tileSize` | `Int` | The size of the tiles in pixels. Defaults to `512`. | +| `minZoom` | `Int?` | The minimum zoom level at which this layer is available. Defaults to `null`. | +| `maxZoom` | `Int?` | The maximum zoom level at which this layer is available. Defaults to `null`. | +| `attribution` | `String?` | The attribution text to display on the map for this layer's data source. Defaults to `null`. | +| `scheme` | `TileScheme` | The tile coordinate system to use. Defaults to `TileScheme.XYZ`. | + +**Example** +```kotlin +// Create a raster layer source for OpenStreetMap tiles +val osmSource = RasterLayerSource.UrlTemplate( + template = "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", + tileSize = 256, + maxZoom = 19, + attribution = "© OpenStreetMap contributors" +) +``` + +--- + +## RasterLayerSource.TileJson + +A data class representing a raster layer source defined by a URL pointing to a TileJSON resource. + +**Signature** +```kotlin +data class TileJson( + val url: String, +) : RasterLayerSource() +``` + +**Description** +This source type loads its configuration from a TileJSON file. The TileJSON file is a JSON object that contains metadata about the tileset, including the URL template, zoom levels, and attribution. + +**Parameters** + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `url` | `String` | The URL of the TileJSON resource (e.g., `https://example.com/tiles.json`). | + +**Example** +```kotlin +// Create a raster layer source from a TileJSON endpoint +val mapboxStreetsSource = RasterLayerSource.TileJson( + url = "https://api.mapbox.com/v4/mapbox.streets.json?access_token=YOUR_ACCESS_TOKEN" +) +``` + +--- + +## RasterLayerSource.ArcGisService + +A data class representing a raster layer source from an ArcGIS MapServer or ImageServer service. + +**Signature** +```kotlin +data class ArcGisService( + val serviceUrl: String, +) : RasterLayerSource() +``` + +**Description** +This source type connects to an ArcGIS REST service endpoint to fetch map tiles. The SDK will automatically handle communication with the service to retrieve tile URLs and metadata. + +**Parameters** + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `serviceUrl` | `String` | The base URL of the ArcGIS MapServer or ImageServer service (e.g., `https://.../MapServer`). | + +**Example** +```kotlin +// Create a raster layer source from an ArcGIS World Imagery service +val arcGisImagerySource = RasterLayerSource.ArcGisService( + serviceUrl = "https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer" +) +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/spherical/CalculateMetersPerPixel.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/spherical/CalculateMetersPerPixel.kt.md new file mode 100644 index 00000000..fb68fa40 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/spherical/CalculateMetersPerPixel.kt.md @@ -0,0 +1,102 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +*** + +## Spherical Geometry Utilities + +This document provides details on utility functions for performing calculations related to spherical geometry on Web Mercator maps. + +### `calculateMetersPerPixel` + +#### Signature + +```kotlin +fun calculateMetersPerPixel( + latitude: Double, + zoom: Double, + tileSize: Double = 256.0 +): Double +``` + +#### Description + +Calculates the number of meters represented by a single pixel on a Web Mercator map for a given latitude and zoom level. + +The calculation is based on the standard Web Mercator projection, where the map is stretched vertically as the distance from the equator increases. This function accounts for this distortion by adjusting the scale based on the cosine of the latitude. At zoom level 0, the entire world circumference fits within a single tile. Each subsequent zoom level doubles the resolution. + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `latitude` | `Double` | The geographical latitude in degrees, ranging from -90.0 to 90.0. | +| `zoom` | `Double` | The map zoom level. Higher values correspond to greater detail. | +| `tileSize` | `Double` | The size of the map tiles in pixels. The default is `256.0`. | + +#### Returns + +**Type:** `Double` + +Returns the number of meters per pixel at the specified latitude and zoom level. + +#### Example + +```kotlin +val latitude = 40.7128 // New York City +val zoomLevel = 12.0 + +val metersPerPixel = calculateMetersPerPixel(latitude = latitude, zoom = zoomLevel) + +// At latitude 40.7128 and zoom 12.0, 1 pixel represents approximately 11.8 meters. +println("At latitude $latitude and zoom $zoomLevel, 1 pixel represents approximately $metersPerPixel meters.") +``` + +--- + +### `meterToPixel` + +#### Signature + +```kotlin +fun meterToPixel( + meter: Double, + latitude: Double, + zoom: Double, + tileSize: Double = 256.0 +): Double +``` + +#### Description + +Converts a distance in meters to its equivalent size in pixels at a specific latitude and zoom level on a Web Mercator map. This function is the inverse of `calculateMetersPerPixel` and is useful for drawing shapes, markers, or overlays with real-world dimensions on a map. + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `meter` | `Double` | The distance in meters to be converted to pixels. | +| `latitude` | `Double` | The geographical latitude in degrees where the conversion is applied. | +| `zoom` | `Double` | The map zoom level. | +| `tileSize` | `Double` | The size of the map tiles in pixels. Common values are `256.0` (e.g., Google Maps) and `512.0` (e.g., Mapbox v10+). The default is `256.0`. | + +#### Returns + +**Type:** `Double` + +Returns the equivalent size in pixels for the given meter value. + +#### Example + +```kotlin +val distanceInMeters = 1000.0 // 1 kilometer +val latitude = 34.0522 // Los Angeles +val zoomLevel = 14.0 + +val sizeInPixels = meterToPixel( + meter = distanceInMeters, + latitude = latitude, + zoom = zoomLevel +) + +// 1000.0 meters at latitude 34.0522 and zoom 14.0 is equivalent to approximately 254.0 pixels. +println("$distanceInMeters meters at latitude $latitude and zoom $zoomLevel is equivalent to approximately $sizeInPixels pixels.") +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/spherical/CalculatePositionAtDistance.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/spherical/CalculatePositionAtDistance.kt.md new file mode 100644 index 00000000..94f2dd0d --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/spherical/CalculatePositionAtDistance.kt.md @@ -0,0 +1,60 @@ +# `calculatePositionAtDistance` + +Calculates a destination `GeoPoint` on the Earth's surface given a starting point, a distance, and an initial bearing. + +This function uses a spherical model of the Earth for its calculations, which provides a good approximation for most applications. + +### Signature +```kotlin +fun calculatePositionAtDistance( + center: GeoPoint, + distanceMeters: Double, + bearingDegrees: Double, +): GeoPoint +``` + +### Description +This function determines the geographic coordinates of a point that is a specified distance and direction away from a given starting point. The calculation is based on spherical trigonometry, treating the Earth as a perfect sphere. + +### Parameters +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `center` | `GeoPoint` | The starting geographical point, containing latitude and longitude. | +| `distanceMeters` | `Double` | The distance to travel from the `center` point, specified in meters. | +| `bearingDegrees` | `Double` | The initial bearing (azimuth) from the `center` point, specified in degrees. North is 0°, East is 90°, South is 180°, and West is 270°. | + +### Returns +| Type | Description | +| :--- | :--- | +| `GeoPoint` | A new `GeoPoint` object representing the calculated destination coordinates. | + +### Example +Here's how to calculate a position 100 kilometers due east (90°) of a starting point in San Francisco. + +```kotlin +import com.mapconductor.core.features.GeoPoint +import com.mapconductor.core.spherical.calculatePositionAtDistance + +fun main() { + // Define the starting point (e.g., somewhere in San Francisco) + val sanFrancisco = GeoPoint.fromLatLong(latitude = 37.7749, longitude = -122.4194) + + // Define the distance and bearing + val distance = 100_000.0 // 100 kilometers in meters + val bearing = 90.0 // 90 degrees (due East) + + // Calculate the destination point + val destinationPoint = calculatePositionAtDistance( + center = sanFrancisco, + distanceMeters = distance, + bearingDegrees = bearing + ) + + // The result is a new GeoPoint approximately 100km east of San Francisco + println("Starting point: $sanFrancisco") + println("Destination point: $destinationPoint") + // Expected output might be something like: + // Starting point: GeoPoint(latitude=37.7749, longitude=-122.4194) + // Destination point: GeoPoint(latitude=37.7733, longitude=-121.2833) +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/spherical/ClosestPointOnSegment.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/spherical/ClosestPointOnSegment.kt.md new file mode 100644 index 00000000..23b82741 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/spherical/ClosestPointOnSegment.kt.md @@ -0,0 +1,79 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +--- + +### `closestPointOnSegment` + +
+ +#### Signature + +```kotlin +fun closestPointOnSegment( + startPoint: Offset, + endPoint: Offset, + testPoint: Offset +): Offset +``` + +#### Description + +Calculates the point on a 2D line segment that is closest to a given test point. + +This function operates by projecting the `testPoint` onto the infinite line defined by `startPoint` and `endPoint`. The resulting projection is then clamped to the boundaries of the line segment itself. This ensures that if the projection falls outside the segment, the function returns the nearest endpoint (`startPoint` or `endPoint`). If the `startPoint` and `endPoint` are identical, the `startPoint` is returned. + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `startPoint` | `Offset` | The starting point of the line segment. | +| `endPoint` | `Offset` | The ending point of the line segment. | +| `testPoint` | `Offset` | The point from which to find the closest point on the segment. | + +#### Returns + +An `Offset` object representing the coordinates of the point on the segment `[startPoint, endPoint]` that is nearest to `testPoint`. + +#### Example + +The following example demonstrates how to find the closest point on a horizontal line segment from a point located above it. + +```kotlin +import androidx.compose.ui.geometry.Offset + +fun main() { + // Define a line segment from (0, 0) to (10, 0) + val start = Offset(0f, 0f) + val end = Offset(10f, 0f) + + // 1. Test with a point whose projection is within the segment + val testPointInside = Offset(5f, 5f) + val closestPoint1 = closestPointOnSegment(start, end, testPointInside) + + println("Segment: [$start, $end]") + println("Test Point: $testPointInside") + // The closest point is the perpendicular projection onto the segment. + println("Closest Point: $closestPoint1") // Expected: Offset(5.0, 0.0) + println("---") + + // 2. Test with a point whose projection is outside the segment + val testPointOutside = Offset(15f, 3f) + val closestPoint2 = closestPointOnSegment(start, end, testPointOutside) + + println("Segment: [$start, $end]") + println("Test Point: $testPointOutside") + // The projection (15, 0) is outside the segment, so the result is clamped to the endpoint. + println("Closest Point: $closestPoint2") // Expected: Offset(10.0, 0.0) +} + +/* +Output: +Segment: [Offset(0.0, 0.0), Offset(10.0, 0.0)] +Test Point: Offset(5.0, 5.0) +Closest Point: Offset(5.0, 0.0) +--- +Segment: [Offset(0.0, 0.0), Offset(10.0, 0.0)] +Test Point: Offset(15.0, 3.0) +Closest Point: Offset(10.0, 0.0) +*/ +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/spherical/CreateInterpolatePoints.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/spherical/CreateInterpolatePoints.kt.md new file mode 100644 index 00000000..91c4d0ff --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/spherical/CreateInterpolatePoints.kt.md @@ -0,0 +1,87 @@ +Of course! Here is the high-quality SDK documentation for the provided Kotlin code snippet. + +--- + +### `createInterpolatePoints` + +#### Signature + +```kotlin +fun createInterpolatePoints( + points: List, + maxSegmentLength: Double = 10000.0 +): List +``` + +#### Description + +This function densifies a polyline (a list of geographic points) by interpolating additional points along the geodesic path between each pair of consecutive points. It ensures that the straight-line distance on the WGS84 ellipsoid between any two adjacent points in the returned list is less than or equal to the specified `maxSegmentLength`. + +This is useful for processes that require a higher resolution path than the one originally provided, such as for accurate rendering on a map or for collision detection along a route. + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `points` | `List` | The list of geographic points that define the original polyline. The list must contain at least one point. | +| `maxSegmentLength` | `Double` | The maximum desired distance in meters between consecutive points in the output list. **Default**: `10000.0` (10 kilometers). | + +#### Returns + +**`List`** + +A new list of `GeoPointInterface` objects that includes the original points plus the interpolated points. The points are ordered to form a continuous, higher-resolution path. If the input list contains fewer than two points, a copy of the original list is returned. + +#### Example + +Below is an example of how to use `createInterpolatePoints` to add points to a path between two locations. + +```kotlin +// Assume GeoPointInterface and a concrete implementation exist: +interface GeoPointInterface { + val latitude: Double + val longitude: Double +} + +data class GeoPoint( + override val latitude: Double, + override val longitude: Double +) : GeoPointInterface + +fun main() { + // Define two points far apart (e.g., San Francisco to Los Angeles) + val sanFrancisco = GeoPoint(latitude = 37.7749, longitude = -122.4194) + val losAngeles = GeoPoint(latitude = 34.0522, longitude = -118.2437) + + val originalPath = listOf(sanFrancisco, losAngeles) + println("Original number of points: ${originalPath.size}") + + // Densify the path so that segments are no longer than 100km (100,000 meters) + val densifiedPath = createInterpolatePoints( + points = originalPath, + maxSegmentLength = 100000.0 // 100 km + ) + + println("Number of points after interpolation: ${densifiedPath.size}") + + // Print the first 5 points of the new path + densifiedPath.take(5).forEachIndexed { index, point -> + println( + "Point ${index + 1}: Lat=${"%.4f".format(point.latitude)}, " + + "Lon=${"%.4f".format(point.longitude)}" + ) + } +} + +/* +Expected Output: + +Original number of points: 2 +Number of points after interpolation: 7 +Point 1: Lat=37.7749, Lon=-122.4194 +Point 2: Lat=37.1594, Lon=-121.7428 +Point 3: Lat=36.5403, Lon=-121.0593 +Point 4: Lat=35.9175, Lon=-120.3689 +Point 5: Lat=35.2911, Lon=-119.6716 +*/ +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/spherical/CreateLinearInterpolatePoints.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/spherical/CreateLinearInterpolatePoints.kt.md new file mode 100644 index 00000000..a6955f28 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/spherical/CreateLinearInterpolatePoints.kt.md @@ -0,0 +1,56 @@ +# createLinearInterpolatePoints + +### Signature +```kotlin +fun createLinearInterpolatePoints( + points: List, + fractionStep: Double = 0.01 +): List +``` + +### Description +Generates a new list of points by performing linear interpolation along the path defined by a given list of geographic points. This function effectively "densifies" a polyline by adding intermediate points between each pair of consecutive vertices. The original vertices from the input list are preserved and included in the output. + +The density of the new points is controlled by the `fractionStep` parameter. For each segment between two consecutive points, the function adds new points at intervals of `fractionStep` along the great-circle path. + +### Parameters +| Parameter | Type | Description | +|----------------|---------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `points` | `List` | A list of two or more `GeoPointInterface` objects representing the vertices of the path to be densified. | +| `fractionStep` | `Double` | The fractional increment used for interpolation between each pair of points. Must be > 0.0 and <= 1.0. A smaller value creates a denser path. Defaults to `0.01`. | + +### Returns +| Type | Description | +|---------------------------|---------------------------------------------------------------------------------------------------------| +| `List` | A new list containing the original points and all the generated intermediate points in sequence. | + +### Example +Suppose you have a simple path defined by two points and you want to add a point at the halfway mark. You can achieve this by setting `fractionStep` to `0.5`. + +```kotlin +// Assume GeoPoint is a data class implementing GeoPointInterface +// and Spherical.linearInterpolate is available. + +// 1. Define the start and end points of the path +val startPoint = GeoPoint(latitude = 40.7128, longitude = -74.0060) // New York City +val endPoint = GeoPoint(latitude = 34.0522, longitude = -118.2437) // Los Angeles + +val path = listOf(startPoint, endPoint) + +// 2. Generate a densified path with a point at the 50% mark (midpoint) +val densifiedPath = createLinearInterpolatePoints( + points = path, + fractionStep = 0.5 +) + +// 3. The resulting list will contain 3 points: +// - The original startPoint +// - The new interpolated midpoint +// - The original endPoint +println("Original path had ${path.size} points.") +println("Densified path now has ${densifiedPath.size} points.") + +// Expected Output: +// Original path had 2 points. +// Densified path now has 3 points. +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/spherical/CreateOppositeMeridianPoint.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/spherical/CreateOppositeMeridianPoint.kt.md new file mode 100644 index 00000000..11b375f3 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/spherical/CreateOppositeMeridianPoint.kt.md @@ -0,0 +1,71 @@ +Of course! Here is the high-quality SDK documentation for the given Kotlin code snippet. + +*** + +### `createOppositeMeridianPoint` + +Creates a point on the opposite meridian (the 180° / -180° longitude line) while preserving the original point's latitude and altitude. + +#### Signature + +```kotlin +fun createOppositeMeridianPoint(point: GeoPointInterface): GeoPoint +``` + +#### Description + +This function calculates and returns a new `GeoPoint` located on the antimeridian (the 180° or -180° longitude line). The new point will have the same latitude and altitude as the source `point`. + +The longitude for the new point is determined based on the hemisphere of the input `point`: +- If the input longitude is in the Eastern Hemisphere (>= 0°), the new longitude is set to -180.0°. +- If the input longitude is in the Western Hemisphere (< 0°), the new longitude is set to 180.0°. + +This is useful for calculations and visualizations that involve wrapping around the globe at the International Date Line. If the source point's altitude is `null`, the resulting point's altitude will be `0.0`. + +#### Parameters + +| Parameter | Type | Description | +|-----------|---------------------|----------------------------------------------------------------------------------------| +| `point` | `GeoPointInterface` | The original geographical point from which to create the opposite meridian equivalent. | + +#### Returns + +| Type | Description | +|------------|----------------------------------------------------------------------------------------------------------------------------------------| +| `GeoPoint` | A new `GeoPoint` instance on the opposite meridian with the same latitude and altitude as the input. Defaults to an altitude of `0.0` if the original is `null`. | + +#### Example + +The following example demonstrates how to use `createOppositeMeridianPoint` for points in both the Eastern and Western Hemispheres. + +```kotlin +// Assume GeoPoint and GeoPointInterface are defined as follows: +// interface GeoPointInterface { +// val latitude: Double +// val longitude: Double +// val altitude: Double? +// } +// data class GeoPoint(...) : GeoPointInterface + +fun main() { + // Case 1: A point in the Eastern Hemisphere (Tokyo) + val tokyo = GeoPoint(latitude = 35.6895, longitude = 139.6917, altitude = 40.0) + val oppositeOfTokyo = createOppositeMeridianPoint(tokyo) + + println("Original Point (Tokyo): $tokyo") + // > Original Point (Tokyo): GeoPoint(latitude=35.6895, longitude=139.6917, altitude=40.0) + println("Opposite Meridian Point: $oppositeOfTokyo") + // > Opposite Meridian Point: GeoPoint(latitude=35.6895, longitude=-180.0, altitude=40.0) + + println("---") + + // Case 2: A point in the Western Hemisphere (New York) with null altitude + val newYork = GeoPoint(latitude = 40.7128, longitude = -74.0060, altitude = null) + val oppositeOfNewYork = createOppositeMeridianPoint(newYork) + + println("Original Point (New York): $newYork") + // > Original Point (New York): GeoPoint(latitude=40.7128, longitude=-74.006, altitude=null) + println("Opposite Meridian Point: $oppositeOfNewYork") + // > Opposite Meridian Point: GeoPoint(latitude=40.7128, longitude=180.0, altitude=0.0) +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/spherical/ExpandBounds.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/spherical/ExpandBounds.kt.md new file mode 100644 index 00000000..eab7e09c --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/spherical/ExpandBounds.kt.md @@ -0,0 +1,62 @@ +### `expandBounds` + +#### Signature + +```kotlin +fun expandBounds(bounds: GeoRectBounds, margin: Double): GeoRectBounds +``` + +#### Description + +Calculates a new `GeoRectBounds` by expanding the given `bounds` by a specified margin factor. The expansion is proportional to the original dimensions (latitude and longitude span) and is applied outward from the center of the bounds. + +If the input `bounds` are empty or if their center or span cannot be determined, the original `bounds` object is returned unmodified. + +#### Parameters + +| Parameter | Type | Description | +|-----------|-----------------|----------------------------------------------------------------------------------------------------------------------------------------| +| `bounds` | `GeoRectBounds` | The original rectangular geographic bounds to expand. | +| `margin` | `Double` | The proportional factor to expand the bounds. For example, a value of `0.1` increases the total width and height by 10% (5% on each side). | + +#### Returns + +| Type | Description | +|-----------------|----------------------------------------------------------------------------------------------------------------------------------------| +| `GeoRectBounds` | A new `GeoRectBounds` instance representing the expanded area, or the original `bounds` if they could not be expanded (e.g., if empty). | + +#### Example + +The following example demonstrates how to expand a `GeoRectBounds` object by a 25% margin. + +```kotlin +import com.mapconductor.core.features.GeoPoint +import com.mapconductor.core.features.GeoRectBounds +import com.mapconductor.core.spherical.expandBounds + +fun main() { + // 1. Define the initial geographical bounds. + // Let's assume the bounds span 2 degrees in latitude and 2 degrees in longitude. + val initialBounds = GeoRectBounds().apply { + extend(GeoPoint(latitude = 40.0, longitude = -80.0)) // Southwest corner + extend(GeoPoint(latitude = 42.0, longitude = -78.0)) // Northeast corner + } + println("Original Bounds: $initialBounds") + // Assuming a toString() output like: + // > Original Bounds: GeoRectBounds(sw=[lat=40.0, lon=-80.0], ne=[lat=42.0, lon=-78.0]) + + // 2. Define the expansion margin (25%). + val margin = 0.25 + + // 3. Call expandBounds to get the new, larger bounds. + val expandedBounds = expandBounds(initialBounds, margin) + + println("Expanded Bounds (25% margin): $expandedBounds") + // The original span is 2 degrees lat and 2 degrees lon. + // The margin adds (2 * 0.25) = 0.5 degrees to the total span of each dimension. + // This means 0.25 degrees are added to each side (top, bottom, left, right). + // Expected new SW corner: lat=39.75, lon=-80.25 + // Expected new NE corner: lat=42.25, lon=-77.75 + // > Expanded Bounds (25% margin): GeoRectBounds(sw=[lat=39.75, lon=-80.25], ne=[lat=42.25, lon=-77.75]) +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/spherical/GeoNearest.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/spherical/GeoNearest.kt.md new file mode 100644 index 00000000..54401661 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/spherical/GeoNearest.kt.md @@ -0,0 +1,85 @@ +# SDK Documentation + +This document provides detailed information about the `GeoNearest` object and its associated data structures for performing geodesic calculations. + +## `ClosestHit` + +A data class that encapsulates the result of a nearest point calculation. It contains the closest point on a segment, the distance to it, and the calculation mode that was used. + +**Properties** + +| Property | Type | Description | +| :--- | :--- | :--- | +| `radiusMeters` | `Double` | The shortest distance in meters from the reference point to the line segment. | +| `hit` | `GeoPointInterface` | The geographic coordinates of the closest point on the line segment. | +| `mode` | `String` | The calculation mode used: `"planar"` for the local plane approximation or `"spherical"` for the great-circle method. | + +--- + +## `GeoNearest` + +A utility object for performing geodesic calculations related to finding the nearest points on the Earth's surface. + +### `closestIntersection` + +**Signature** + +```kotlin +fun closestIntersection( + P: GeoPointInterface, + A: GeoPointInterface, + B: GeoPointInterface +): ClosestHit +``` + +**Description** + +Calculates the point on the geodesic line segment defined by points `A` and `B` that is closest to a given reference point `P`. + +This function employs a dynamic calculation strategy for optimal performance and accuracy: +- **Planar Mode**: If the maximum distance between any two of the three points (`P`, `A`, `B`) is less than or equal to 50 km, a local planar approximation (equirectangular projection) is used. This method is fast and sufficiently accurate for short distances. +- **Spherical Mode**: For distances greater than 50 km, a more rigorous spherical model is used, treating the segment `AB` as a great-circle arc. This ensures high accuracy over long distances where the Earth's curvature is significant. + +If the closest point on the great-circle containing the segment `AB` lies outside the arc `AB`, the function returns the closer of the two endpoints (`A` or `B`). + +**Parameters** + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `P` | `GeoPointInterface` | The reference point. | +| `A` | `GeoPointInterface` | The starting point of the line segment. | +| `B` | `GeoPointInterface` | The ending point of the line segment. | + +**Returns** + +A `ClosestHit` object containing the details of the closest point. See the `ClosestHit` class documentation for more details. + +**Example** + +```kotlin +import com.mapconductor.core.features.GeoPoint +import com.mapconductor.core.spherical.GeoNearest + +fun main() { + // Define the reference point and the line segment endpoints + val referencePoint = GeoPoint(35.6812, 139.7671) // Tokyo Station + val segmentStart = GeoPoint(35.6895, 139.6917) // Shinjuku Station + val segmentEnd = GeoPoint(35.7295, 139.7140) // Ikebukuro Station + + // Find the closest point on the segment to the reference point + val result = GeoNearest.closestIntersection( + P = referencePoint, + A = segmentStart, + B = segmentEnd + ) + + println("Calculation Mode: ${result.mode}") + println("Closest Point (Hit): Lat=${result.hit.latitude}, Lon=${result.hit.longitude}") + println("Distance (Radius): ${"%.2f".format(result.radiusMeters)} meters") + + // Example Output (values are illustrative): + // Calculation Mode: planar + // Closest Point (Hit): Lat=35.7093, Lon=139.7029 + // Distance (Radius): 6054.32 meters +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/spherical/GeographicLibCalculator.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/spherical/GeographicLibCalculator.kt.md new file mode 100644 index 00000000..b25f1f79 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/spherical/GeographicLibCalculator.kt.md @@ -0,0 +1,115 @@ +# SDK Documentation: GeographicLibCalculator + +## `GeographicLibCalculator` + +### Description + +A utility object that provides geodesic calculations using the GeographicLib library, based on the WGS84 ellipsoid. It offers methods for computing distances and interpolating points along a geodesic path. + +--- + +## Methods + +### `computeDistanceBetween` + +#### Signature + +```kotlin +fun computeDistanceBetween( + from: GeoPointInterface, + to: GeoPointInterface +): Double +``` + +#### Description + +Calculates the shortest distance (geodesic) between two geographical points on the WGS84 ellipsoid. + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :------------------ | :--------------------------- | +| `from` | `GeoPointInterface` | The starting geographical point. | +| `to` | `GeoPointInterface` | The ending geographical point. | + +#### Returns + +**`Double`** + +The geodesic distance between the two points in meters. + +#### Example + +```kotlin +import com.mapconductor.core.features.GeoPoint +import com.mapconductor.core.spherical.GeographicLibCalculator + +// Define two points (e.g., San Francisco and Los Angeles) +val sf = GeoPoint(37.7749, -122.4194) +val la = GeoPoint(34.0522, -118.2437) + +// Calculate the distance between them +val distanceInMeters = GeographicLibCalculator.computeDistanceBetween(sf, la) + +println("Distance: $distanceInMeters meters") +// Expected output might be around: Distance: 559120.539653327 meters +``` + +--- + +### `interpolate` + +#### Signature + +```kotlin +fun interpolate( + from: GeoPointInterface, + to: GeoPointInterface, + fraction: Double +): GeoPoint +``` + +#### Description + +Calculates an intermediate point along the geodesic line between two given points. The position of the intermediate point is determined by a fraction of the total distance. + +The altitude of the resulting point is interpolated as follows: +- If both `from` and `to` have an altitude, the new altitude is linearly interpolated. +- If only one point has an altitude, its altitude is used for the result. +- If neither point has an altitude, the resulting altitude is `0.0`. + +#### Parameters + +| Parameter | Type | Description | +| :--------- | :------------------ | :------------------------------------------------------------------------------------------------------ | +| `from` | `GeoPointInterface` | The starting geographical point. | +| `to` | `GeoPointInterface` | The ending geographical point. | +| `fraction` | `Double` | The fractional distance from the `from` point towards the `to` point. A value of `0.0` returns the `from` point, and `1.0` returns the `to` point. | + +#### Returns + +**`GeoPoint`** + +A new `GeoPoint` object representing the interpolated point, including the calculated latitude, longitude, and altitude. + +#### Example + +```kotlin +import com.mapconductor.core.features.GeoPoint +import com.mapconductor.core.spherical.GeographicLibCalculator + +// Define two points with altitude +val startPoint = GeoPoint(latitude = 37.7749, longitude = -122.4194, altitude = 10.0) +val endPoint = GeoPoint(latitude = 34.0522, longitude = -118.2437, altitude = 110.0) + +// Find the midpoint (fraction = 0.5) +val midPoint = GeographicLibCalculator.interpolate(startPoint, endPoint, 0.5) + +println("Midpoint Latitude: ${midPoint.latitude}") +println("Midpoint Longitude: ${midPoint.longitude}") +println("Midpoint Altitude: ${midPoint.altitude}") +// Expected output: +// Midpoint Latitude: 35.9185... +// Midpoint Longitude: -120.3219... +// Midpoint Altitude: 60.0 +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/spherical/InterpolateAtMeridianGeodesic.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/spherical/InterpolateAtMeridianGeodesic.kt.md new file mode 100644 index 00000000..73577fe0 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/spherical/InterpolateAtMeridianGeodesic.kt.md @@ -0,0 +1,76 @@ +### `interpolateAtMeridianGeodesic` + +#### Signature +```kotlin +fun interpolateAtMeridianGeodesic( + from: GeoPointInterface, + to: GeoPointInterface +): GeoPoint +``` + +#### Description +Calculates the geographical point where the great circle path between two points (`from` and `to`) intersects the antimeridian (180° or -180° longitude). + +This function employs a high-precision iterative binary search algorithm to find the exact intersection point. It is particularly useful for accurately rendering geodesic paths that cross the international date line, preventing visual artifacts where a line incorrectly wraps around the globe. The final returned point will have its longitude set precisely to `180.0` or `-180.0`, with its latitude and altitude spherically interpolated. + +#### Parameters +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `from` | `GeoPointInterface` | The starting point of the geodesic path. | +| `to` | `GeoPointInterface` | The ending point of the geodesic path. | + +#### Returns +| Type | Description | +| :--- | :--- | +| `GeoPoint` | A new `GeoPoint` object representing the intersection point on the antimeridian. | + +#### Example +The following example demonstrates how to find the antimeridian crossing point for a path from a point east of the antimeridian (e.g., near Fiji) to a point west of it (e.g., near Hawaii). + +```kotlin +import com.mapconductor.core.features.GeoPoint +import com.mapconductor.core.features.GeoPointInterface +import com.mapconductor.core.spherical.Spherical // Assuming Spherical.sphericalInterpolate exists +import com.mapconductor.core.spherical.interpolateAtMeridianGeodesic + +// Mock implementations for demonstration purposes +// In a real scenario, you would use the SDK's provided classes. +object Spherical { + // A simplified mock of spherical interpolation for the example to run. + // The actual implementation is more complex. + fun sphericalInterpolate(from: GeoPointInterface, to: GeoPointInterface, fraction: Double): GeoPoint { + val lat = from.latitude + (to.latitude - from.latitude) * fraction + val lon = from.longitude + (to.longitude - from.longitude) * fraction + val alt = (from.altitude ?: 0.0) + ((to.altitude ?: 0.0) - (from.altitude ?: 0.0)) * fraction + return GeoPoint(lat, lon, alt) + } +} + +fun main() { + // Define a starting point in Fiji (east of the antimeridian) + val fiji = GeoPoint(latitude = -17.7134, longitude = 178.0650, altitude = 10.0) + + // Define an ending point in Hawaii (west of the antimeridian) + val hawaii = GeoPoint(latitude = 21.3069, longitude = -157.8583, altitude = 20.0) + + // Calculate the point where the path crosses the antimeridian + val crossingPoint = interpolateAtMeridianGeodesic(from = fiji, to = hawaii) + + println("Path from: $fiji") + println("Path to: $hawaii") + println("---") + println("Antimeridian crossing point found at:") + println("Latitude: ${crossingPoint.latitude}") + println("Longitude: ${crossingPoint.longitude}") + println("Altitude: ${crossingPoint.altitude}") +} + +// Expected Output: +// Path from: GeoPoint(latitude=-17.7134, longitude=178.065, altitude=10.0) +// Path to: GeoPoint(latitude=21.3069, longitude=-157.8583, altitude=20.0) +// --- +// Antimeridian crossing point found at: +// Latitude: -11.59... +// Longitude: 180.0 +// Altitude: 12.0... +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/spherical/InterpolateAtMeridianLinear.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/spherical/InterpolateAtMeridianLinear.kt.md new file mode 100644 index 00000000..f6bb1bbf --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/spherical/InterpolateAtMeridianLinear.kt.md @@ -0,0 +1,60 @@ +Excellent. Here is the high-quality SDK documentation for the provided code snippet. + +### `interpolateAtMeridianLinear` + +#### Signature +```kotlin +fun interpolateAtMeridianLinear( + from: GeoPointInterface, + to: GeoPointInterface +): GeoPoint +``` + +#### Description +Performs linear interpolation to find the point where a line segment between two geographical points crosses the anti-meridian (180° or -180° longitude). + +This function calculates the intersection by treating the coordinates as if they are on a simple 2D Cartesian plane (an Equirectangular projection). It determines the latitude at the meridian crossing by interpolating linearly based on the longitude difference. + +The altitude is also interpolated if both `from` and `to` points provide an altitude value. If only one point has an altitude, that value is used for the crossing point. If neither has an altitude, the altitude defaults to `0.0`. + +**Note:** This function assumes that the longitudes are "unwrapped," meaning they can extend beyond the standard `[-180, 180]` degree range to represent a continuous path. For example, a path from 170° longitude to -170° longitude should be represented with `to.longitude = 190` for the interpolation to be calculated correctly across the shortest path. + +#### Parameters +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `from` | `GeoPointInterface` | The starting point of the line segment. | +| `to` | `GeoPointInterface` | The ending point of the line segment, potentially with an "unwrapped" longitude. | + +#### Returns +**Type:** `GeoPoint` + +A new `GeoPoint` object representing the calculated intersection point on the anti-meridian. The longitude of the returned point will be either `180.0` or `-180.0`, depending on the direction of the crossing. + +#### Example +The following example demonstrates how to find the meridian crossing point for a path that goes from 170°E to 190°E (which is equivalent to -170°W). + +```kotlin +import com.mapconductor.core.features.GeoPoint +import com.mapconductor.core.features.GeoPointInterface + +// For the purpose of this example, we can assume the GeoPoint and GeoPointInterface +// from the problem description are available. + +fun main() { + // Define a starting point east of the anti-meridian. + val startPoint = GeoPoint(latitude = 50.0, longitude = 170.0, altitude = 1000.0) + + // Define an ending point west of the anti-meridian. + // Note the use of an "unwrapped" longitude (190.0) to represent a + // continuous path crossing the 180° meridian. 190° is equivalent to -170°. + val endPoint = GeoPoint(latitude = 60.0, longitude = 190.0, altitude = 2000.0) + + // Calculate the intersection point on the meridian. + val meridianCrossingPoint = interpolateAtMeridianLinear(startPoint, endPoint) + + // The function finds the point exactly halfway between the two points + // in terms of longitude, and interpolates the latitude and altitude accordingly. + println(meridianCrossingPoint) + // Expected output: GeoPoint(latitude=55.0, longitude=180.0, altitude=1500.0) +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/spherical/IsPointOnLinearLine.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/spherical/IsPointOnLinearLine.kt.md new file mode 100644 index 00000000..00e075af --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/spherical/IsPointOnLinearLine.kt.md @@ -0,0 +1,89 @@ +Of course! Here is the high-quality SDK documentation for the provided Kotlin code snippet. + +*** + +### isPointOnLinearLine + +#### Signature + +```kotlin +fun isPointOnLinearLine( + from: GeoPointInterface, + to: GeoPointInterface, + position: GeoPointInterface, + thresholdMeters: Double, +): Pair? +``` + +#### Description + +Determines if a given geographic point (`position`) is within a specified distance (`thresholdMeters`) of a straight line segment defined by `from` and `to` points. + +This function uses a planar (flat-earth) approximation for the calculation, ignoring the Earth's curvature for the path of the line segment. It correctly handles longitude wrapping by choosing the shorter path across the ±180° meridian. + +If the `position` is within the threshold, the function returns a `Pair` containing the closest point on the line segment and the exact distance in meters. Otherwise, it returns `null`. + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `from` | `GeoPointInterface` | The starting point of the line segment. | +| `to` | `GeoPointInterface` | The ending point of the line segment. | +| `position` | `GeoPointInterface` | The point to check against the line segment. | +| `thresholdMeters` | `Double` | The maximum allowed distance in meters from the line segment for the point to be considered "on" it. | + +#### Returns + +**`Pair?`** + +* A `Pair` containing the following if the `position` is within the `thresholdMeters`: + * **`first`**: A `GeoPoint` instance representing the closest point on the line segment to the `position`. Its altitude is linearly interpolated if both `from` and `to` points have altitude values. + * **`second`**: A `Double` representing the perpendicular distance in meters from the `position` to the line segment. +* Returns `null` if the `position` is outside the specified threshold. + +#### Example + +```kotlin +import com.mapconductor.core.features.GeoPoint + +fun main() { + val fromPoint = GeoPoint(latitude = 35.681236, longitude = 139.767125) // Tokyo Station + val toPoint = GeoPoint(latitude = 35.658581, longitude = 139.745433) // Tokyo Tower + + // Case 1: A point close to the line segment + val closePosition = GeoPoint(latitude = 35.670000, longitude = 139.756000) + val threshold = 500.0 // 500 meters + + val resultOnLine = isPointOnLinearLine(fromPoint, toPoint, closePosition, threshold) + + if (resultOnLine != null) { + val (closestPoint, distance) = resultOnLine + println("Point is on the line.") + println("Closest point on segment: lat=${closestPoint.latitude}, lon=${closestPoint.longitude}") + println("Distance to segment: ${"%.2f".format(distance)} meters") + } else { + println("Point is NOT on the line.") + } + // Expected Output: + // Point is on the line. + // Closest point on segment: lat=35.66931..., lon=139.75689... + // Distance to segment: 85.54 meters + + println("---") + + // Case 2: A point far from the line segment + val farPosition = GeoPoint(latitude = 35.710063, longitude = 139.8107) // Tokyo Skytree + val resultOffLine = isPointOnLinearLine(fromPoint, toPoint, farPosition, threshold) + + if (resultOffLine != null) { + val (closestPoint, distance) = resultOffLine + println("Point is on the line.") + println("Closest point on segment: lat=${closestPoint.latitude}, lon=${closestPoint.longitude}") + println("Distance to segment: ${"%.2f".format(distance)} meters") + } else { + println("Point is NOT on the line.") + } + // Expected Output: + // Point is NOT on the line. +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/spherical/IsPointOnTheGeodesicLine.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/spherical/IsPointOnTheGeodesicLine.kt.md new file mode 100644 index 00000000..c5d4820a --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/spherical/IsPointOnTheGeodesicLine.kt.md @@ -0,0 +1,92 @@ +Of course! Here is a high-quality SDK document for the provided code snippet, formatted in Markdown. + +--- + +### `isPointOnTheGeodesicLine` + +
+ +Checks if a given point is near a geodesic polyline by finding the closest point on the line and its distance. If the calculated distance is within a specified threshold, the point can be considered "on" the line. + +The function first identifies a relevant line segment and then interpolates points along it to find the closest point. It includes optional debugging parameters to visualize the process. + +#### Signature + +```kotlin +fun isPointOnTheGeodesicLine( + points: List, + position: GeoPointInterface, + threshold: Double, + debugDrawRectangle: ((GeoRectBounds, Int) -> Unit)?, + debugDrawCircle: ((GeoPointInterface, Double, Int) -> Unit)?, +): Pair? +``` + +#### Parameters + +| Parameter | Type | Description | +| -------------------- | ------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------- | +| `points` | `List` | A list of geo-points that define the vertices of the geodesic polyline. Must contain at least two points to form a line. | +| `position` | `GeoPointInterface` | The geographic point to check against the polyline. | +| `threshold` | `Double` | The tolerance in meters. This value is used to determine if the point is close enough to the line to be considered "on" it. | +| `debugDrawRectangle` | `((GeoRectBounds, Int) -> Unit)?` | An optional lambda function for debugging. If provided, it is called to draw the bounding box of the line segment being inspected. | +| `debugDrawCircle` | `((GeoPointInterface, Double, Int) -> Unit)?` | An optional lambda function for debugging. If provided, it is called to draw circles around interpolated points during the search process. | + +#### Returns + +**`Pair?`** + +* A `Pair` containing: + * `first`: The `GeoPointInterface` representing the closest point on the polyline to the `position`. + * `second`: The `Double` value representing the distance in meters from the `position` to this closest point. +* Returns `null` if the input `points` list contains fewer than two points. +* The distance in the returned pair may be `Double.MAX_VALUE` if a suitable intersection point cannot be precisely calculated. + +#### Example + +```kotlin +import com.mapconductor.core.features.GeoPoint +import com.mapconductor.core.spherical.isPointOnTheGeodesicLine + +// Define a polyline with two points (a single line segment) +val polyline = listOf( + GeoPoint(34.0522, -118.2437), // Los Angeles + GeoPoint(40.7128, -74.0060) // New York +) + +// Define a point to test +val testPosition = GeoPoint(38.8977, -94.5828) // A point near Kansas City + +// Set a threshold of 50 kilometers (50000 meters) +val distanceThreshold = 50000.0 + +// Check if the point is on the line +val result = isPointOnTheGeodesicLine( + points = polyline, + position = testPosition, + threshold = distanceThreshold, + debugDrawRectangle = null, // No debugging + debugDrawCircle = null +) + +if (result != null) { + val closestPoint = result.first + val distance = result.second + + if (distance <= distanceThreshold) { + println("Point is on the geodesic line.") + println("Closest point on line: ${closestPoint.latitude}, ${closestPoint.longitude}") + println("Distance to line: ${"%.2f".format(distance)} meters") + } else { + println("Point is not on the geodesic line (distance > threshold).") + println("Distance to line: ${"%.2f".format(distance)} meters") + } +} else { + println("Could not perform check. The polyline must have at least 2 points.") +} + +// Example Output: +// Point is on the geodesic line. +// Closest point on line: 38.9012, -94.5791 +// Distance to line: 45012.34 meters +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/spherical/LineSegmentUtils.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/spherical/LineSegmentUtils.kt.md new file mode 100644 index 00000000..5a1820f8 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/spherical/LineSegmentUtils.kt.md @@ -0,0 +1,129 @@ +# LineSegmentUtils + +The `LineSegmentUtils` object provides utility functions for working with line segments on a sphere, such as calculating bounding boxes and checking for intersections with regions. + +--- + +## createSegmentBounds + +Calculates the geographical bounding box (`GeoRectBounds`) for a line segment defined by two points. This function can compute bounds for both simple rhumb lines and more complex geodesic paths. + +### Signature + +```kotlin +fun createSegmentBounds( + point1: GeoPointInterface, + point2: GeoPointInterface, + geodesic: Boolean = false, +): GeoRectBounds +``` + +### Description + +This function creates a `GeoRectBounds` that fully encloses the line segment between `point1` and `point2`. + +- When `geodesic` is `false` (the default), the method creates a simple rectangular bounding box that contains the two endpoints. This is suitable for short distances or when a rhumb line is assumed. +- When `geodesic` is `true`, the method approximates the bounds of a great-circle path by sampling multiple points along the geodesic curve. This provides a more accurate bounding box for long segments that may curve significantly across the globe, potentially crossing the antimeridian or poles. + +### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `point1` | `GeoPointInterface` | The first endpoint of the segment. | +| `point2` | `GeoPointInterface` | The second endpoint of the segment. | +| `geodesic` | `Boolean` | **(Optional)** If `true`, calculates the bounds for a geodesic (great-circle) path. If `false` (default), it creates a simple bounding box containing only the two endpoints. | + +### Returns + +A `GeoRectBounds` object that encloses the line segment. + +### Example + +```kotlin +// Assuming GeoPoint and GeoRectBounds are implemented +val sanFrancisco = GeoPoint(-122.4194, 37.7749) +val newYork = GeoPoint(-74.0060, 40.7128) + +// Calculate simple (non-geodesic) bounds +val simpleBounds = LineSegmentUtils.createSegmentBounds(sanFrancisco, newYork) + +// Calculate more accurate geodesic bounds +val geodesicBounds = LineSegmentUtils.createSegmentBounds(sanFrancisco, newYork, geodesic = true) + +println("Simple Bounds: $simpleBounds") +println("Geodesic Bounds: $geodesicBounds") +``` + +--- + +## segmentIntersectsRegion + +Determines if a line segment potentially intersects a given rectangular region (`GeoRectBounds`). + +### Signature + +```kotlin +fun segmentIntersectsRegion( + start: GeoPointInterface, + end: GeoPointInterface, + region: GeoRectBounds, + geodesic: Boolean = false, +): Boolean +``` + +### Description + +This function performs an optimized check to see if the line segment from `start` to `end` intersects the specified `region`. It works by first calculating the bounding box of the segment (using `createSegmentBounds`) and then testing if that segment's bounding box intersects with the provided `region`. + +**Note:** This is an approximation that checks for bounding box intersection, not a precise line-polygon intersection. It may return `true` in cases where the segment's bounding box intersects the region, but the segment itself does not. However, it will never return `false` if an intersection does occur. + +### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `start` | `GeoPointInterface` | The starting point of the line segment. | +| `end` | `GeoPointInterface` | The ending point of the line segment. | +| `region` | `GeoRectBounds` | The rectangular region to check for intersection against. | +| `geodesic` | `Boolean` | **(Optional)** If `true`, the segment is treated as a geodesic path when calculating its bounds. Defaults to `false`. | + +### Returns + +Returns `true` if the segment's bounding box intersects the given `region`. Returns `false` if there is no intersection or if the input `region` is empty. + +### Example + +```kotlin +// Assuming GeoPoint and GeoRectBounds are implemented +val startPoint = GeoPoint(-122.4, 37.7) // Near San Francisco +val endPoint = GeoPoint(-74.0, 40.7) // Near New York + +// A region covering the state of Colorado +val coloradoBounds = GeoRectBounds().apply { + extend(GeoPoint(-109.0, 41.0)) + extend(GeoPoint(-102.0, 37.0)) +} + +// A region covering the state of California +val californiaBounds = GeoRectBounds().apply { + extend(GeoPoint(-124.4, 42.0)) + extend(GeoPoint(-114.1, 32.5)) +} + +// Check for intersection with Colorado (should be true for a geodesic path) +val intersectsColorado = LineSegmentUtils.segmentIntersectsRegion( + startPoint, + endPoint, + coloradoBounds, + geodesic = true +) +println("Segment intersects Colorado bounds: $intersectsColorado") // Expected: true + +// Check for intersection with California (should be true) +val intersectsCalifornia = LineSegmentUtils.segmentIntersectsRegion( + startPoint, + endPoint, + californiaBounds, + geodesic = true +) +println("Segment intersects California bounds: $intersectsCalifornia") // Expected: true +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/spherical/PointOnGeodesicSegmentOrNull.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/spherical/PointOnGeodesicSegmentOrNull.kt.md new file mode 100644 index 00000000..a60757fe --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/spherical/PointOnGeodesicSegmentOrNull.kt.md @@ -0,0 +1,98 @@ +# pointOnGeodesicSegmentOrNull + +## Signature + +```kotlin +fun pointOnGeodesicSegmentOrNull( + from: GeoPointInterface, + to: GeoPointInterface, + position: GeoPointInterface, + thresholdMeters: Double +): Pair? +``` + +## Description + +Calculates the closest point on a geodesic line segment to a given reference point. The geodesic path is determined on the WGS84 ellipsoid. + +The function identifies the point on the segment (between `from` and `to`) that is nearest to the `position`. If the distance from `position` to this nearest point is within the specified `thresholdMeters`, the function returns a `Pair` containing the calculated point and the distance in meters. + +If the minimum distance to the segment is greater than `thresholdMeters`, the function returns `null`. + +The altitude of the resulting point is linearly interpolated based on its fractional distance along the segment. If altitude is only available for one endpoint, that altitude is used. If neither endpoint has an altitude, it defaults to `0.0`. + +## Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `from` | `GeoPointInterface` | The starting point of the geodesic segment. | +| `to` | `GeoPointInterface` | The ending point of the geodesic segment. | +| `position` | `GeoPointInterface` | The reference point from which to find the closest point on the segment. | +| `thresholdMeters` | `Double` | The maximum allowed distance in meters. If the closest point is further than this value, the function returns `null`. | + +## Returns + +**`Pair?`** + +A nullable `Pair` object. +- If a point is found within the threshold, it returns a `Pair` where: + - `first`: A `GeoPointInterface` representing the closest point on the segment. + - `second`: A `Double` representing the distance in meters from `position` to the closest point. +- If the minimum distance to the segment is greater than `thresholdMeters`, it returns `null`. + +## Example + +```kotlin +import com.mapconductor.core.features.GeoPoint +import com.mapconductor.core.spherical.pointOnGeodesicSegmentOrNull + +// Define the start and end points of the geodesic segment +val startPoint = GeoPoint(latitude = 35.681236, longitude = 139.767125) // Tokyo Station +val endPoint = GeoPoint(latitude = 35.658581, longitude = 139.745433) // Tokyo Tower + +// Define a position near the segment +val testPosition = GeoPoint(latitude = 35.670000, longitude = 139.755000) + +// Set a threshold of 500 meters +val threshold = 500.0 + +// Find the closest point on the segment to our test position +val result = pointOnGeodesicSegmentOrNull( + from = startPoint, + to = endPoint, + position = testPosition, + thresholdMeters = threshold +) + +if (result != null) { + val (closestPoint, distance) = result + println("Closest point found within the threshold.") + println("Coordinates: Lat ${closestPoint.latitude}, Lon ${closestPoint.longitude}") + println("Distance: ${"%.2f".format(distance)} meters") +} else { + println("No point found on the segment within ${threshold} meters.") +} + +// Example where the position is too far +val farPosition = GeoPoint(latitude = 35.710063, longitude = 139.8107) // Tokyo Skytree +val farResult = pointOnGeodesicSegmentOrNull( + from = startPoint, + to = endPoint, + position = farPosition, + thresholdMeters = threshold +) + +if (farResult == null) { + println("\nAs expected, no point found for the far position within the threshold.") +} + +/* +Expected output: + +Closest point found within the threshold. +Coordinates: Lat 35.66893918113721, Lon 139.7572493431991 +Distance: 131.11 meters + +As expected, no point found for the far position within the threshold. +*/ +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/spherical/Spherical.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/spherical/Spherical.kt.md new file mode 100644 index 00000000..77d55597 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/spherical/Spherical.kt.md @@ -0,0 +1,428 @@ +# Spherical + +## Overview + +The `Spherical` object provides a collection of static utility functions for performing spherical geometry calculations on Earth's surface. It uses a spherical model of the Earth for all computations, which is suitable for most mapping applications. + +These functions are essential for tasks like calculating distances between two points, determining the heading from one point to another, finding a destination point given a starting point and an offset, and calculating the area or length of a path. + +This implementation is a Kotlin port of the spherical geometry utilities found in the Cordova Google Maps plugin, adapted to use the `GeoPointInterface` for coordinate representation. + +--- + +## `computeDistanceBetween` + +Calculates the great-circle distance between two geographical points using the haversine formula. + +### Signature + +```kotlin +fun computeDistanceBetween( + from: GeoPointInterface, + to: GeoPointInterface +): Double +``` + +### Description + +This function returns the distance, in meters, between two `GeoPointInterface` locations. The calculation is based on the haversine formula, which accounts for the Earth's curvature, providing an accurate distance over a sphere. + +### Parameters + +| Parameter | Type | Description | +| :-------- | :------------------ | :----------------------- | +| `from` | `GeoPointInterface` | The starting point. | +| `to` | `GeoPointInterface` | The ending point. | + +### Returns + +| Type | Description | +| :------- | :--------------------- | +| `Double` | The distance in meters. | + +### Example + +```kotlin +// Assuming GeoPoint implements GeoPointInterface +val sanFrancisco = GeoPoint(latitude = 37.7749, longitude = -122.4194) +val newYork = GeoPoint(latitude = 40.7128, longitude = -74.0060) + +val distance = Spherical.computeDistanceBetween(sanFrancisco, newYork) +// distance is approximately 4128541.9 meters +println("Distance: $distance meters") +``` + +--- + +## `computeHeading` + +Calculates the initial bearing (heading) from a starting point to a destination point. + +### Signature + +```kotlin +fun computeHeading( + from: GeoPointInterface, + to: GeoPointInterface +): Double +``` + +### Description + +This function returns the heading from one `GeoPointInterface` to another. The heading is expressed in degrees clockwise from true North and is normalized to the range `(-180, 180]`. A heading of 0 is North, 90 is East, 180 is South, and -90 is West. + +### Parameters + +| Parameter | Type | Description | +| :-------- | :------------------ | :----------------------- | +| `from` | `GeoPointInterface` | The starting point. | +| `to` | `GeoPointInterface` | The ending point. | + +### Returns + +| Type | Description | +| :------- | :------------------------------------------------ | +| `Double` | The heading in degrees within the range `(-180, 180]`. | + +### Example + +```kotlin +val sanFrancisco = GeoPoint(latitude = 37.7749, longitude = -122.4194) +val newYork = GeoPoint(latitude = 40.7128, longitude = -74.0060) + +val heading = Spherical.computeHeading(sanFrancisco, newYork) +// heading is approximately 67.2 degrees +println("Heading: $heading degrees") +``` + +--- + +## `computeOffset` + +Calculates a destination point given a starting point, a distance, and a heading. + +### Signature + +```kotlin +fun computeOffset( + origin: GeoPointInterface, + distance: Double, + heading: Double +): GeoPoint +``` + +### Description + +This function returns a new `GeoPoint` that is a specified distance and heading away from an origin point. It is useful for projecting a point on the map. + +### Parameters + +| Parameter | Type | Description | +| :--------- | :------------------ | :---------------------------------------------------- | +| `origin` | `GeoPointInterface` | The starting point. | +| `distance` | `Double` | The distance to travel in meters. | +| `heading` | `Double` | The direction to travel in degrees (0=N, 90=E, 180=S). | + +### Returns + +| Type | Description | +| :--------- | :---------------------------------------- | +| `GeoPoint` | The new `GeoPoint` at the calculated offset. | + +### Example + +```kotlin +val startPoint = GeoPoint(latitude = 40.7128, longitude = -74.0060) +val distanceMeters = 10000.0 // 10 km +val headingDegrees = 45.0 // Northeast + +val destination = Spherical.computeOffset(startPoint, distanceMeters, headingDegrees) +// destination is approximately GeoPoint(latitude=40.7763, longitude=-73.9205) +println("New Position: ${destination.latitude}, ${destination.longitude}") +``` + +--- + +## `computeOffsetOrigin` + +Calculates the origin point given a destination, the distance traveled, and the original heading. + +### Signature + +```kotlin +fun computeOffsetOrigin( + to: GeoPointInterface, + distance: Double, + heading: Double +): GeoPoint? +``` + +### Description + +This function is the inverse of `computeOffset`. It determines the starting point from which one would have traveled a certain distance at a specific heading to arrive at the destination `to`. It calculates this by reversing the heading and applying the `computeOffset` function. + +### Parameters + +| Parameter | Type | Description | +| :--------- | :------------------ | :---------------------------------------- | +| `to` | `GeoPointInterface` | The destination point. | +| `distance` | `Double` | The distance traveled in meters. | +| `heading` | `Double` | The original heading in degrees. | + +### Returns + +| Type | Description | +| :---------- | :----------------------------------------------------------------------- | +| `GeoPoint?` | The original `GeoPoint` position, or `null` if a solution is not available. | + +### Example + +```kotlin +val destination = GeoPoint(latitude = 40.7763, longitude = -73.9205) +val distanceMeters = 10000.0 +val headingDegrees = 45.0 + +val origin = Spherical.computeOffsetOrigin(destination, distanceMeters, headingDegrees) +// origin is approximately GeoPoint(latitude=40.7128, longitude=-74.0060) +if (origin != null) { + println("Original Position: ${origin.latitude}, ${origin.longitude}") +} +``` + +--- + +## `computeLength` + +Calculates the total length of a path defined by a list of points. + +### Signature + +```kotlin +fun computeLength(path: List): Double +``` + +### Description + +This function sums the great-circle distances between consecutive points in a list to calculate the total length of the path. If the path has fewer than two points, the length is 0. + +### Parameters + +| Parameter | Type | Description | +| :-------- | :---------------------- | :---------------------------------------- | +| `path` | `List` | A list of points defining the path. | + +### Returns + +| Type | Description | +| :------- | :------------------------------ | +| `Double` | The total length in meters. | + +### Example + +```kotlin +val path = listOf( + GeoPoint(latitude = 37.7749, longitude = -122.4194), // San Francisco + GeoPoint(latitude = 34.0522, longitude = -118.2437), // Los Angeles + GeoPoint(latitude = 32.7157, longitude = -117.1611) // San Diego +) + +val totalLength = Spherical.computeLength(path) +// totalLength is approximately 735,930 meters +println("Total path length: $totalLength meters") +``` + +--- + +## `computeArea` + +Calculates the non-negative area of a closed path on the Earth's surface. + +### Signature + +```kotlin +fun computeArea(path: List): Double +``` + +### Description + +This function computes the area of a closed polygon defined by a list of points. It returns the absolute (non-negative) value of the area. The path is assumed to be closed; the function does not automatically connect the last point to the first. For accurate results, the last point in the list should be the same as the first. + +### Parameters + +| Parameter | Type | Description | +| :-------- | :---------------------- | :---------------------------------------- | +| `path` | `List` | A list of points defining the polygon. | + +### Returns + +| Type | Description | +| :------- | :------------------------------ | +| `Double` | The area in square meters. | + +### Example + +```kotlin +// A square-like polygon +val polygon = listOf( + GeoPoint(latitude = 40.0, longitude = -75.0), + GeoPoint(latitude = 41.0, longitude = -75.0), + GeoPoint(latitude = 41.0, longitude = -74.0), + GeoPoint(latitude = 40.0, longitude = -74.0), + GeoPoint(latitude = 40.0, longitude = -75.0) // Closing the polygon +) + +val area = Spherical.computeArea(polygon) +println("Area: $area square meters") +``` + +--- + +## `computeSignedArea` + +Calculates the signed area of a closed path, which can be used to determine the path's orientation. + +### Signature + +```kotlin +fun computeSignedArea(path: List): Double +``` + +### Description + +This function computes the signed area of a closed polygon. The sign of the result indicates the orientation of the vertices: +- **Positive value:** The vertices are in a counter-clockwise order. +- **Negative value:** The vertices are in a clockwise order. + +The magnitude of the result is the area in square meters. The path is assumed to be closed. + +### Parameters + +| Parameter | Type | Description | +| :-------- | :---------------------- | :---------------------------------------- | +| `path` | `List` | A list of points defining the polygon. | + +### Returns + +| Type | Description | +| :------- | :----------------------------------------------------------------------- | +| `Double` | The signed area in square meters. Positive for CCW, negative for CW. | + +### Example + +```kotlin +// Counter-clockwise polygon +val ccwPolygon = listOf( + GeoPoint(latitude = 40.0, longitude = -75.0), + GeoPoint(latitude = 41.0, longitude = -75.0), + GeoPoint(latitude = 41.0, longitude = -74.0), + GeoPoint(latitude = 40.0, longitude = -74.0) +) + +val signedAreaCCW = Spherical.computeSignedArea(ccwPolygon) +// signedAreaCCW will be a positive value +println("Counter-clockwise signed area: $signedAreaCCW") + +// Clockwise polygon +val cwPolygon = ccwPolygon.reversed() +val signedAreaCW = Spherical.computeSignedArea(cwPolygon) +// signedAreaCW will be a negative value with the same magnitude +println("Clockwise signed area: $signedAreaCW") +``` + +--- + +## `sphericalInterpolate` + +Interpolates between two points along a great-circle path using Spherical Linear Interpolation (Slerp). + +### Signature + +```kotlin +fun sphericalInterpolate( + from: GeoPointInterface, + to: GeoPointInterface, + fraction: Double +): GeoPoint +``` + +### Description + +This method provides a highly accurate interpolation between two points on the globe by following the shortest path along the Earth's surface (a great-circle arc). It is ideal for interpolating over medium to long distances where Earth's curvature is significant. For very close points, it falls back to linear interpolation for stability. + +### Parameters + +| Parameter | Type | Description | +| :--------- | :------------------ | :----------------------------------------------------------------------- | +| `from` | `GeoPointInterface` | The starting point. | +| `to` | `GeoPointInterface` | The ending point. | +| `fraction` | `Double` | The interpolation fraction, from 0.0 (at `from`) to 1.0 (at `to`). | + +### Returns + +| Type | Description | +| :--------- | :---------------------------------------- | +| `GeoPoint` | The interpolated `GeoPoint` position. | + +### Example + +```kotlin +val start = GeoPoint(latitude = 37.7749, longitude = -122.4194) // San Francisco +val end = GeoPoint(latitude = 40.7128, longitude = -74.0060) // New York + +// Find the point halfway between SF and NY along the great circle +val midpoint = Spherical.sphericalInterpolate(start, end, 0.5) + +println("Midpoint: ${midpoint.latitude}, ${midpoint.longitude}") +``` + +--- + +## `linearInterpolate` + +Performs a simple linear interpolation between two points on a flat 2D plane. + +### Signature + +```kotlin +fun linearInterpolate( + from: GeoPointInterface, + to: GeoPointInterface, + fraction: Double +): GeoPoint +``` + +### Description + +This method treats latitude and longitude as coordinates on a Cartesian plane and performs a simple linear interpolation. It is computationally faster than `sphericalInterpolate` but is less accurate, especially over long distances, as it does not account for Earth's curvature. + +A key feature is its handling of longitude: it automatically interpolates along the shorter of the two paths (e.g., from 170°E to -170°W, it will cross the antimeridian instead of going the long way around). + +Use this method for short distances or when performance is more critical than geographic accuracy. + +### Parameters + +| Parameter | Type | Description | +| :--------- | :------------------ | :----------------------------------------------------------------------- | +| `from` | `GeoPointInterface` | The starting point. | +| `to` | `GeoPointInterface` | The ending point. | +| `fraction` | `Double` | The interpolation fraction, from 0.0 (at `from`) to 1.0 (at `to`). | + +### Returns + +| Type | Description | +| :--------- | :---------------------------------------- | +| `GeoPoint` | The linearly interpolated `GeoPoint` position. | + +### Example + +```kotlin +// Interpolating across the antimeridian +val start = GeoPoint(latitude = 60.0, longitude = 175.0) +val end = GeoPoint(latitude = 60.0, longitude = -175.0) + +// Find the point halfway between them (should be near 180 degrees longitude) +val midpoint = Spherical.linearInterpolate(start, end, 0.5) + +// midpoint.longitude will be approximately 180.0 (or -180.0) +println("Midpoint: ${midpoint.latitude}, ${midpoint.longitude}") +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/spherical/SplitByMeridian.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/spherical/SplitByMeridian.kt.md new file mode 100644 index 00000000..6a27c080 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/spherical/SplitByMeridian.kt.md @@ -0,0 +1,92 @@ +# `splitByMeridian` + +## Signature + +```kotlin +fun splitByMeridian( + points: List, + geodesic: Boolean, +): List> +``` + +## Description + +This function processes a list of geographic points (representing a polyline or polygon boundary) and splits it into multiple segments wherever it crosses the 180°/-180° longitude line (the antimeridian). + +When a crossing is detected between two consecutive points, the function interpolates a new point precisely on the meridian. The original segment is terminated with this new point. A new segment is then started with a corresponding point on the opposite side of the meridian (e.g., if the first segment ends at +180°, the new one starts at -180°), followed by the next point from the input list. + +This process is essential for correctly rendering geometries that wrap around the globe on a 2D map, preventing visual artifacts like long horizontal lines stretching across the map. + +## Parameters + +| Parameter | Type | Description | +| :--------- | :------------------------ | :------------------------------------------------------------------------------------------------------------------------------------- | +| `points` | `List` | The list of geographic points to process. | +| `geodesic` | `Boolean` | A flag to determine the interpolation method. If `true`, geodesic (great-circle) interpolation is used. If `false`, linear interpolation is used. | + +## Returns + +**Type:** `List>` + +Returns a list of point lists. Each inner list represents a continuous segment of the original geometry that does not cross the antimeridian. If the input list is empty, an empty list is returned. If no meridian crossings occur, a list containing a single list with all the original points is returned. + +## Example + +Here is an example of splitting a polyline that crosses the 180° meridian. + +```kotlin +// For demonstration, define the necessary data classes. +interface GeoPointInterface { + val latitude: Double + val longitude: Double +} + +data class GeoPoint( + override val latitude: Double, + override val longitude: Double +) : GeoPointInterface { + override fun toString(): String { + return "GeoPoint(lat=%.2f, lon=%.2f)".format(latitude, longitude) + } +} + +// Assume the existence of the splitByMeridian function. + +fun main() { + // Define a polyline that crosses the 180° meridian. + val polyline = listOf( + GeoPoint(latitude = 50.0, longitude = 175.0), // Point A in the Eastern Hemisphere + GeoPoint(latitude = 52.0, longitude = -170.0), // Point B in the Western Hemisphere + GeoPoint(latitude = 54.0, longitude = -165.0) // Point C in the Western Hemisphere + ) + + println("Original Polyline: $polyline\n") + + // Split the polyline using geodesic interpolation. + val splitPolylines = splitByMeridian(polyline, geodesic = true) + + // The result is a list containing two separate polylines. + println("Resulting Segments:") + splitPolylines.forEachIndexed { index, segment -> + println("Segment ${index + 1}: $segment") + } +} + +/* +Expected Output: + +Original Polyline: [GeoPoint(lat=50.00, lon=175.00), GeoPoint(lat=52.00, lon=-170.00), GeoPoint(lat=54.00, lon=-165.00)] + +Resulting Segments: +Segment 1: [GeoPoint(lat=50.00, lon=175.00), GeoPoint(lat=50.83, lon=180.00)] +Segment 2: [GeoPoint(lat=50.83, lon=-180.00), GeoPoint(lat=52.00, lon=-170.00), GeoPoint(lat=54.00, lon=-165.00)] + +*/ +``` + +### Explanation + +1. The original polyline starts at `175.0°E` and crosses the antimeridian to `-170.0°W`. +2. `splitByMeridian` detects this crossing between the first and second points. +3. **Segment 1** is created. It contains the starting point (`175.0°E`) and a new, interpolated point on the meridian (`180.0°E`). +4. **Segment 2** is created to continue the line on the other side of the map. It starts with a new point at `-180.0°W` (with the same interpolated latitude) and includes the remaining points of the original polyline. \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/spherical/WGS84Geodesic.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/spherical/WGS84Geodesic.kt.md new file mode 100644 index 00000000..bb8cc451 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/spherical/WGS84Geodesic.kt.md @@ -0,0 +1,159 @@ +Of course! Here is the high-quality SDK documentation for the provided Kotlin code snippet. + +# WGS84Geodesic + +The `WGS84Geodesic` object provides a collection of utility functions for performing geodesic calculations on the WGS84 ellipsoid, which is the reference coordinate system used by the Global Positioning System (GPS). These functions are designed for high accuracy and are compatible with standard geodesic computations, such as those used by Google Maps. + +This utility handles calculations for distance, heading (azimuth), and path interpolation between geographic coordinates. + +--- + +## `computeDistanceBetween` + +Calculates the shortest distance (geodesic) between two points on the surface of the WGS84 ellipsoid. This method implements Vincenty's inverse formula for high accuracy. + +### Signature + +```kotlin +fun computeDistanceBetween( + from: GeoPointInterface, + to: GeoPointInterface +): Double +``` + +### Description + +This function computes the geodesic distance in meters between a starting point (`from`) and an ending point (`to`). The calculation is based on Vincenty's inverse formula, which is an iterative method that is highly accurate for all distances on an ellipsoid. It is a robust alternative to the Haversine formula, which assumes a perfect sphere. + +### Parameters + +| Parameter | Type | Description | +| :-------- | :------------------ | :------------------------------- | +| `from` | `GeoPointInterface` | The starting geographical point. | +| `to` | `GeoPointInterface` | The destination geographical point. | + +### Returns + +`Double` - The geodesic distance between the two points in meters. Returns `0.0` if the points are identical or if the algorithm fails to converge. + +### Example + +```kotlin +import com.mapconductor.core.features.GeoPoint +import com.mapconductor.core.spherical.WGS84Geodesic + +fun main() { + val newYork = GeoPoint(latitude = 40.7128, longitude = -74.0060) + val london = GeoPoint(latitude = 51.5074, longitude = -0.1278) + + val distance = WGS84Geodesic.computeDistanceBetween(newYork, london) + + // The distance is approximately 5,570,224.5 meters + println("Distance between New York and London: $distance meters") +} +``` + +--- + +## `computeHeading` + +Calculates the initial bearing (forward azimuth) from a starting point to a destination point on the WGS84 ellipsoid. + +### Signature + +```kotlin +fun computeHeading( + from: GeoPointInterface, + to: GeoPointInterface +): Double +``` + +### Description + +This function determines the initial heading, in degrees, for the shortest path (geodesic) from the `from` point to the `to` point. The heading is measured clockwise from true north. + +### Parameters + +| Parameter | Type | Description | +| :-------- | :------------------ | :------------------------------- | +| `from` | `GeoPointInterface` | The starting geographical point. | +| `to` | `GeoPointInterface` | The destination geographical point. | + +### Returns + +`Double` - The initial heading in degrees, normalized to a range of `-180` to `180`. +- `0°` is North +- `90°` is East +- `180°` or `-180°` is South +- `-90°` is West + +### Example + +```kotlin +import com.mapconductor.core.features.GeoPoint +import com.mapconductor.core.spherical.WGS84Geodesic + +fun main() { + val startPoint = GeoPoint(latitude = 40.7128, longitude = -74.0060) // New York + val endPoint = GeoPoint(latitude = 51.5074, longitude = -0.1278) // London + + val heading = WGS84Geodesic.computeHeading(startPoint, endPoint) + + // The initial heading is approximately 51.2 degrees + println("Initial heading from New York to London: $heading degrees") +} +``` + +--- + +## `interpolate` + +Calculates an intermediate geographical point along the great-circle path between two points using spherical linear interpolation (Slerp). + +### Signature + +```kotlin +fun interpolate( + from: GeoPointInterface, + to: GeoPointInterface, + fraction: Double +): GeoPoint +``` + +### Description + +This function finds a `GeoPoint` that lies at a specified fraction of the distance along the path from a starting point to a destination point. It uses Spherical Linear Interpolation (Slerp), which provides a good approximation for short to medium distances by treating the Earth as a sphere. For the highest precision over long distances, Vincenty's direct formula would be required. + +Altitude is interpolated linearly. If only one of the points has an altitude, that altitude is used for the interpolated point. + +### Parameters + +| Parameter | Type | Description | +| :--------- | :------------------ | :------------------------------------------------------------------------------------------------------------------------------------- | +| `from` | `GeoPointInterface` | The starting geographical point. | +| `to` | `GeoPointInterface` | The destination geographical point. | +| `fraction` | `Double` | The fraction of the distance from the `from` point to the `to` point. Must be between `0.0` (returns `from`) and `1.0` (returns `to`). | + +### Returns + +`GeoPoint` - The new `GeoPoint` at the specified fractional distance along the path. + +### Example + +```kotlin +import com.mapconductor.core.features.GeoPoint +import com.mapconductor.core.spherical.WGS84Geodesic + +fun main() { + val startPoint = GeoPoint(latitude = 40.7128, longitude = -74.0060, altitude = 10.0) + val endPoint = GeoPoint(latitude = 51.5074, longitude = -0.1278, altitude = 20.0) + + // Find the midpoint (fraction = 0.5) + val midPoint = WGS84Geodesic.interpolate(startPoint, endPoint, 0.5) + + println("Midpoint:") + println(" Latitude: ${midPoint.latitude}") + println(" Longitude: ${midPoint.longitude}") + println(" Altitude: ${midPoint.altitude}") // Expected altitude: 15.0 +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/tileserver/LocalTileServer.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/tileserver/LocalTileServer.kt.md new file mode 100644 index 00000000..b915d6c9 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/tileserver/LocalTileServer.kt.md @@ -0,0 +1,256 @@ +# SDK Documentation: LocalTileServer + +The `LocalTileServer` class provides a lightweight, in-process HTTP server for serving map tiles. It runs locally on `127.0.0.1` with an automatically assigned port, making it ideal for dynamically generating and displaying tile data in map SDKs without needing a backend deployment or exposing a public endpoint. + +Tiles are generated on-the-fly by registering one or more `TileProviderInterface` implementations, each associated with a unique `routeId`. + +## Companion Object + +### startServer + +Creates, configures, and starts a new `LocalTileServer` instance. This is the recommended factory method for instantiating the server. + +**Signature** +```kotlin +fun startServer(forceNoStoreCache: Boolean = false): LocalTileServer +``` + +**Description** +This function initializes a `ServerSocket` on an available port, creates a `LocalTileServer` instance, and starts its request-handling thread. + +**Parameters** + +| Parameter | Type | Description | +| ------------------- | --------- | -------------------------------------------------------------------------------------------------------------------------------------- | +| `forceNoStoreCache` | `Boolean` | If `true`, all tile responses will include `Cache-Control: no-store` headers, preventing clients from caching tiles. Defaults to `false`. | + +**Returns** + +`LocalTileServer` - A running and configured instance of the tile server. + +**Example** +```kotlin +// Start the server with default caching behavior +val tileServer = LocalTileServer.startServer() + +// Start the server and force clients not to cache tiles +val noCacheTileServer = LocalTileServer.startServer(forceNoStoreCache = true) +``` + +--- + +## Properties + +### baseUrl + +The base URL of the running server. + +**Signature** +```kotlin +val baseUrl: String +``` + +**Description** +This read-only property provides the root URL for the local server, in the format `http://127.0.0.1:`. The port is chosen automatically by the system when the server is started. This URL is the foundation for all tile request URLs. + +**Example** +```kotlin +val server = LocalTileServer.startServer() +println("Server running at: ${server.baseUrl}") +// Output: Server running at: http://127.0.0.1:54321 (port will vary) +``` + +--- + +## Methods + +### setForceNoStoreCache + +Updates the server's caching behavior at runtime. + +**Signature** +```kotlin +fun setForceNoStoreCache(value: Boolean) +``` + +**Description** +This method allows you to dynamically change whether the server instructs clients to cache tiles. When set to `true`, all subsequent tile responses will be sent with `Cache-Control: no-store` headers. When `false`, responses will include headers that encourage long-term caching. + +**Parameters** + +| Parameter | Type | Description | +| --------- | --------- | -------------------------------------------------------------------------------------------------------------------------------------- | +| `value` | `Boolean` | If `true`, disables client-side caching for new requests. If `false`, enables long-term caching (the default behavior is set at creation). | + +**Example** +```kotlin +val server = LocalTileServer.startServer() + +// Initially, tiles are cacheable. +// ... + +// Now, force all new tile responses to be non-cacheable. +server.setForceNoStoreCache(true) +``` + +### register + +Registers a tile provider for a specific route. + +**Signature** +```kotlin +fun register(routeId: String, provider: TileProviderInterface) +``` + +**Description** +This method associates a `TileProviderInterface` implementation with a unique `routeId`. When the server receives a tile request URL containing this `routeId`, it will invoke the corresponding provider's `renderTile` method to generate the tile image. + +**Parameters** + +| Parameter | Type | Description | +| ---------- | ----------------------- | ------------------------------------------------------------------------------------------------------- | +| `routeId` | `String` | A unique string to identify the tile source (e.g., "weather-radar", "traffic-flow"). | +| `provider` | `TileProviderInterface` | An object that implements the logic for rendering a tile based on its coordinates (`x`, `y`, `z`). | + +**Example** +```kotlin +// Assume MyRadarProvider implements TileProviderInterface +val radarProvider = MyRadarProvider() +val server = LocalTileServer.startServer() + +server.register("radar", radarProvider) + +// The server will now handle requests to URLs like /tiles/radar/... +``` + +### unregister + +Removes a registered tile provider. + +**Signature** +```kotlin +fun unregister(routeId: String) +``` + +**Description** +This method detaches a tile provider from its `routeId`. After calling this, any requests for that route will result in a `404 Not Found` error. + +**Parameters** + +| Parameter | Type | Description | +| --------- | -------- | ----------------------------------------- | +| `routeId` | `String` | The identifier of the route to deregister. | + +**Example** +```kotlin +// Unregister the provider previously registered with the "radar" routeId +server.unregister("radar") +``` + +### urlTemplate + +Generates a URL template for a map SDK. + +**Signature** +```kotlin +fun urlTemplate(routeId: String, tileSize: Int): String +``` + +**Description** +Constructs a standard URL template that can be used as a tile source in most map SDKs. The template includes placeholders for the zoom level (`{z}`), x-coordinate (`{x}`), and y-coordinate (`{y}`). + +**Parameters** + +| Parameter | Type | Description | +| ---------- | -------- | ------------------------------------------------------------------------------------------------------- | +| `routeId` | `String` | The identifier of the registered route for which to generate the template. | +| `tileSize` | `Int` | The tile size in pixels (e.g., 256 or 512). This value is included in the URL path for informational purposes. | + +**Returns** + +`String` - A URL template string, e.g., `http://127.0.0.1:12345/tiles/my-route/256/{z}/{x}/{y}.png`. + +### urlTemplate (with cacheKey) + +Generates a URL template with a cache-busting key in the path. + +**Signature** +```kotlin +fun urlTemplate(routeId: String, tileSize: Int, cacheKey: String): String +``` + +**Description** +This overload constructs a URL template that includes an extra `cacheKey` segment in the path. This is a common technique for cache-busting. When the underlying data for a tileset changes, you can generate a new template with a new `cacheKey` (e.g., a timestamp or content hash) to force clients to refetch the tiles. + +**Parameters** + +| Parameter | Type | Description | +| ---------- | -------- | -------------------------------------------------------------------------- | +| `routeId` | `String` | The identifier of the registered route. | +| `tileSize` | `Int` | The tile size in pixels (e.g., 256 or 512). | +| `cacheKey` | `String` | A unique string to embed in the URL path for cache-busting purposes. | + +**Returns** + +`String` - A URL template string, e.g., `http://127.0.0.1:12345/tiles/my-route/256/v2_20231027/{z}/{x}/{y}.png`. + +### urlTemplateWithQueryCacheKey + +Generates a URL template with a cache-busting key as a query parameter. + +**Signature** +```kotlin +fun urlTemplateWithQueryCacheKey(routeId: String, tileSize: Int, cacheKey: String): String +``` + +**Description** +This method provides an alternative format for a cache-busting URL template. Instead of adding the `cacheKey` to the URL path, it appends it as a query parameter (e.g., `?v=some-key`). This is useful for compatibility with map SDKs that have strict parsing rules for URL templates and may not support extra path segments. + +**Parameters** + +| Parameter | Type | Description | +| ---------- | -------- | -------------------------------------------------------------------------- | +| `routeId` | `String` | The identifier of the registered route. | +| `tileSize` | `Int` | The tile size in pixels (e.g., 256 or 512). | +| `cacheKey` | `String` | A unique string to use as the value of the `v` query parameter. | + +**Returns** + +`String` - A URL template string, e.g., `http://127.0.0.1:12345/tiles/my-route/256/{z}/{x}/{y}.png?v=v2_20231027`. + +### start + +Starts the server's request-handling loop. + +**Signature** +```kotlin +fun start() +``` + +**Description** +This method starts the background thread that listens for and handles incoming HTTP requests. It is called automatically by the `startServer()` factory method, so you typically do not need to call it manually. If the server is already running, this method has no effect. + +### stop + +Stops the server and releases its resources. + +**Signature** +```kotlin +fun stop() +``` + +**Description** +This method gracefully shuts down the tile server. It closes the underlying `ServerSocket`, which interrupts the request-handling thread and causes it to terminate. Once stopped, a `LocalTileServer` instance cannot be restarted. It's important to call this method to release the network port when the server is no longer needed. + +**Example** +```kotlin +val server = LocalTileServer.startServer() +try { + // Use the server... + val url = server.urlTemplate("my-route", 256) + // myMap.addTileSource(url) +} finally { + // Ensure the server is always stopped + server.stop() +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/tileserver/TileProviderInterface.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/tileserver/TileProviderInterface.kt.md new file mode 100644 index 00000000..05800026 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/tileserver/TileProviderInterface.kt.md @@ -0,0 +1,83 @@ +# TileProviderInterface + +The `TileProviderInterface` defines a standardized contract for classes that are responsible for rendering map tiles. Any class that generates tile data must implement this interface. + +## renderTile + +Renders a single map tile based on the specifications provided in the `TileRequest` object. + +### Signature + +```kotlin +fun renderTile(request: TileRequest): ByteArray? +``` + +### Description + +This method processes a `TileRequest` to generate the corresponding map tile image. It returns the raw image data as a byte array. If the tile cannot be rendered for any reason (e.g., the requested coordinates are out of bounds, or a rendering error occurs), it returns `null`. + +### Parameters + +| Parameter | Type | Description | +| :-------- | :------------ | :--------------------------------------------------------------------------------------------------------- | +| `request` | `TileRequest` | An object containing the details for the tile to be rendered, such as zoom level and coordinates (x, y). | + +### Returns + +| Type | Description | +| :----------- | :------------------------------------------------------------------------------------------------------------------------------------- | +| `ByteArray?` | A byte array representing the rendered tile image (e.g., in PNG or JPEG format), or `null` if the tile cannot be generated for the given request. | + +### Example + +Here is an example of how to implement the `TileProviderInterface` and use it. + +```kotlin +import com.mapconductor.core.tileserver.TileProviderInterface +import com.mapconductor.core.tileserver.TileRequest + +// A dummy implementation of TileRequest for the example +data class TileRequest(val z: Int, val x: Int, val y: Int) + +/** + * An example implementation that renders a simple placeholder tile. + */ +class PngTileProvider : TileProviderInterface { + + override fun renderTile(request: TileRequest): ByteArray? { + // In a real-world scenario, you would use the request's z, x, and y + // to query data, draw features, and render a PNG or JPEG image. + println("Rendering tile for zoom=${request.z}, x=${request.x}, y=${request.y}") + + // For this example, we return a dummy byte array. + // Let's pretend this is a 1x1 transparent PNG. + val dummyPngData: ByteArray = byteArrayOf( + -119, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13, 73, 72, 68, 82, 0, 0, 0, 1, 0, 0, 0, 1, + 8, 6, 0, 0, 0, 31, 21, -60, -119, 0, 0, 0, 12, 73, 68, 65, 84, 24, -45, 99, 96, 0, 0, + 0, 0, 0, 0, -95, -11, 63, 86, 0, 0, 0, 0, 73, 69, 78, 68, -82, 66, 96, -126 + ) + + // Return null for tiles outside a certain zoom range + if (request.z > 18) { + println("Zoom level too high. Tile not rendered.") + return null + } + + return dummyPngData + } +} + +fun main() { + val tileProvider = PngTileProvider() + val tileRequest = TileRequest(z = 12, x = 1024, y = 2048) + + val tileData = tileProvider.renderTile(tileRequest) + + if (tileData != null) { + println("Successfully rendered tile. Data size: ${tileData.size} bytes.") + // Here, you would typically send the byte array in an HTTP response + } else { + println("Failed to render tile for request: $tileRequest") + } +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/tileserver/TileRequest.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/tileserver/TileRequest.kt.md new file mode 100644 index 00000000..e610c7c4 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/tileserver/TileRequest.kt.md @@ -0,0 +1,45 @@ +# TileRequest + +A data class that encapsulates the coordinates for a map tile request. + +## Signature + +```kotlin +data class TileRequest( + val x: Int, + val y: Int, + val z: Int +) +``` + +## Description + +The `TileRequest` class represents a request for a specific map tile using the standard XYZ (or "Slippy Map") tiling scheme. It is a simple data holder for the three essential coordinates: `x`, `y`, and `z` (zoom level). + +Instances of this class are typically used to query a tile server or a tile cache for a specific map tile image. + +## Parameters + +This table describes the constructor parameters for the `TileRequest` class. + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `x` | `Int` | The x-coordinate of the tile at the specified zoom level. | +| `y` | `Int` | The y-coordinate of the tile at the specified zoom level. | +| `z` | `Int` | The zoom level for the tile. | + +## Example + +Here is an example of how to create and use a `TileRequest` object. + +```kotlin +// Create a request for a tile at zoom level 12, +// with x-coordinate 1234 and y-coordinate 5678. +val tileRequest = TileRequest(x = 1234, y = 5678, z = 12) + +// You can then access the properties of the request. +println("Requesting tile at Z/X/Y: ${tileRequest.z}/${tileRequest.x}/${tileRequest.y}") + +// Expected Output: +// Requesting tile at Z/X/Y: 12/1234/5678 +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/tileserver/TileServerRegistry.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/tileserver/TileServerRegistry.kt.md new file mode 100644 index 00000000..674def3b --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/tileserver/TileServerRegistry.kt.md @@ -0,0 +1,112 @@ +# TileServerRegistry + +A singleton object that manages the lifecycle of a single `LocalTileServer` instance. It ensures that only one tile server is running within the application, providing a centralized point of access and configuration. This registry is thread-safe. + +--- + +## get + +Retrieves the singleton `LocalTileServer` instance. If the server is not already running, this method will start it and return the new instance. If a server is already running, it returns the existing instance. + +### Signature + +```kotlin +fun get(forceNoStoreCache: Boolean = this.forceNoStoreCache): LocalTileServer +``` + +### Description + +This is the primary method for accessing the `LocalTileServer`. It guarantees that only one server instance exists. The method also allows you to configure the server's caching behavior on retrieval. + +### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `forceNoStoreCache` | `Boolean` | **(Optional)** If `true`, configures the tile server to include a `Cache-Control: no-store` header in its responses, preventing downstream clients from caching tiles. Defaults to the last value set by a previous call to `get()` or `setForceNoStoreCache()`. | + +### Returns + +| Type | Description | +| :--- | :--- | +| `LocalTileServer` | The singleton `LocalTileServer` instance. | + +### Example + +```kotlin +// Get the tile server instance with default cache settings +val tileServer = TileServerRegistry.get() + +// Get the tile server and force no-store caching for its responses +val noCacheTileServer = TileServerRegistry.get(forceNoStoreCache = true) +``` + +--- + +## setForceNoStoreCache + +Updates the caching behavior for the active `LocalTileServer` instance. This allows you to dynamically enable or disable client-side caching of tiles. + +### Signature + +```kotlin +fun setForceNoStoreCache(value: Boolean) +``` + +### Description + +This method updates the `forceNoStoreCache` flag for the current `LocalTileServer` instance, if one exists. It also sets the default value for future calls to `get()`. + +### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `value` | `Boolean` | If `true`, the server will add a `Cache-Control: no-store` header to its responses. If `false`, it will not. | + +### Example + +```kotlin +// Disable client-side caching for all subsequent tile requests +TileServerRegistry.setForceNoStoreCache(true) + +// Re-enable default client-side caching behavior +TileServerRegistry.setForceNoStoreCache(false) +``` + +--- + +## warmup + +Pre-initializes the tile server on a background thread and makes a warmup HTTP request. + +### Signature + +```kotlin +fun warmup() +``` + +### Description + +Call this method early in your application's lifecycle (e.g., in `Application.onCreate()`) to reduce the initial latency when a map layer that uses the tile server is first displayed. + +The function performs the following actions on a background thread: +1. Starts the `LocalTileServer` if it's not already running. +2. Makes a dummy HTTP request to the server to establish the connection. + +This process is executed only once, even if `warmup()` is called multiple times. Any errors during warmup are logged and do not crash the application. + +### Example + +```kotlin +// In your Application class +import android.app.Application +import com.mapconductor.core.tileserver.TileServerRegistry + +class MyApp : Application() { + override fun onCreate() { + super.onCreate() + + // Warm up the tile server to reduce first-load latency for map layers. + TileServerRegistry.warmup() + } +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/zoom/AbstractZoomAltitudeConverter.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/zoom/AbstractZoomAltitudeConverter.kt.md new file mode 100644 index 00000000..52f77c0d --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/core/zoom/AbstractZoomAltitudeConverter.kt.md @@ -0,0 +1,158 @@ +# AbstractZoomAltitudeConverter + +The `AbstractZoomAltitudeConverter` is an abstract base class designed to provide a standardized interface for converting between map zoom levels and camera altitude in meters. Concrete implementations of this class define the specific mathematical model for the conversion, which can vary based on the map projection and desired behavior. + +This class is intended to be extended, not instantiated directly. + +## Constructor + +### Signature +```kotlin +abstract class AbstractZoomAltitudeConverter( + protected val zoom0Altitude: Double +) +``` + +### Description +Creates a new instance of an `AbstractZoomAltitudeConverter`. + +### Parameters +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `zoom0Altitude` | `Double` | The camera altitude in meters that corresponds to zoom level 0.0. This value serves as the baseline for all conversion calculations. | + +--- + +## Abstract Methods + +### zoomLevelToAltitude + +#### Signature +```kotlin +abstract fun zoomLevelToAltitude( + zoomLevel: Double, + latitude: Double, + tilt: Double +): Double +``` + +#### Description +Converts a given map zoom level to the corresponding camera altitude in meters. The calculation takes into account the camera's latitude and tilt to provide a more accurate altitude for a consistent viewing experience across different perspectives. + +#### Parameters +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `zoomLevel` | `Double` | The map zoom level to convert. | +| `latitude` | `Double` | The current latitude of the camera in degrees. | +| `tilt` | `Double` | The current tilt of the camera in degrees from nadir (0 = looking straight down). | + +#### Returns +| Type | Description | +| :--- | :--- | +| `Double` | The calculated camera altitude in meters. | + +--- + +### altitudeToZoomLevel + +#### Signature +```kotlin +abstract fun altitudeToZoomLevel( + altitude: Double, + latitude: Double, + tilt: Double +): Double +``` + +#### Description +Converts a given camera altitude in meters to the corresponding map zoom level. This is the inverse operation of `zoomLevelToAltitude`. The calculation also accounts for the camera's latitude and tilt. + +#### Parameters +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `altitude` | `Double` | The camera altitude in meters to convert. | +| `latitude` | `Double` | The current latitude of the camera in degrees. | +| `tilt` | `Double` | The current tilt of the camera in degrees from nadir. | + +#### Returns +| Type | Description | +| :--- | :--- | +| `Double` | The calculated map zoom level. | + +--- + +## Companion Object Constants + +These constants provide default values and constraints used within conversion calculations. + +| Constant | Value | Description | +| :--- | :--- | :--- | +| `DEFAULT_ZOOM0_ALTITUDE` | `171_319_879.0` | The default altitude in meters for zoom level 0, calibrated to approximate Google Maps' visible regions. | +| `ZOOM_FACTOR` | `2.0` | The multiplier used for zoom level calculations. Each integer zoom level change typically halves or doubles the view. | +| `MIN_ZOOM_LEVEL` | `0.0` | The minimum allowed zoom level. | +| `MAX_ZOOM_LEVEL` | `22.0` | The maximum allowed zoom level. | +| `MIN_ALTITUDE` | `100.0` | The minimum allowed camera altitude in meters. | +| `MAX_ALTITUDE` | `50_000_000.0` | The maximum allowed camera altitude in meters. | +| `MIN_COS_LAT` | `0.01` | The minimum cosine of latitude, used to prevent division by zero near the poles. | +| `MIN_COS_TILT` | `0.05` | The minimum cosine of tilt, used to prevent division by zero at extreme tilt angles. | +| `WEB_MERCATOR_INITIAL_MPP_256` | `156_543.033_928` | The initial meters-per-pixel resolution for a 256px tile at zoom level 0 in the Web Mercator projection. | + +--- + +## Example + +Since `AbstractZoomAltitudeConverter` is an abstract class, you must create a concrete implementation to use it. The following example demonstrates how to create a simple linear converter. + +```kotlin +import kotlin.math.cos +import kotlin.math.log2 +import kotlin.math.pow +import kotlin.math.max + +// A simple, hypothetical implementation of the abstract class. +class LinearZoomAltitudeConverter( + zoom0Altitude: Double = DEFAULT_ZOOM0_ALTITUDE +) : AbstractZoomAltitudeConverter(zoom0Altitude) { + + override fun zoomLevelToAltitude( + zoomLevel: Double, + latitude: Double, + tilt: Double + ): Double { + // A simplified model that ignores latitude and tilt for this example. + // A real implementation would have a more complex formula. + val altitude = zoom0Altitude / ZOOM_FACTOR.pow(zoomLevel) + return altitude.coerceIn(MIN_ALTITUDE, MAX_ALTITUDE) + } + + override fun altitudeToZoomLevel( + altitude: Double, + latitude: Double, + tilt: Double + ): Double { + // The inverse of the simplified model above. + if (altitude <= 0) return MAX_ZOOM_LEVEL + val zoomLevel = log2(zoom0Altitude / altitude) + return zoomLevel.coerceIn(MIN_ZOOM_LEVEL, MAX_ZOOM_LEVEL) + } +} + +// Usage of the concrete implementation +fun main() { + val converter = LinearZoomAltitudeConverter() + + val zoomLevel = 10.0 + val latitude = 40.7128 // New York City + val tilt = 0.0 + + // Convert zoom level to altitude + val altitude = converter.zoomLevelToAltitude(zoomLevel, latitude, tilt) + println("Zoom level $zoomLevel corresponds to an altitude of ${altitude.toInt()} meters.") + // Output: Zoom level 10.0 corresponds to an altitude of 167304 meters. + + // Convert altitude back to zoom level + val newZoomLevel = converter.altitudeToZoomLevel(altitude, latitude, tilt) + println("An altitude of ${altitude.toInt()} meters corresponds to zoom level $newZoomLevel.") + // Output: An altitude of 167304 meters corresponds to zoom level 10.0. +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/settings/Settings.kt.md b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/settings/Settings.kt.md new file mode 100644 index 00000000..17a8b7d3 --- /dev/null +++ b/experimental/api-docs/mapconductor-core/src/main/java/com/mapconductor/settings/Settings.kt.md @@ -0,0 +1,141 @@ +Here is the high-quality SDK documentation for the given code snippet. + +*** + +# Settings + +The `Settings` sealed class and related objects provide a comprehensive way to configure the appearance and behavior of map components and their interactions. + +## `Settings` + +A sealed class that encapsulates all configuration options. You can extend this class to create your own custom settings profile or use the provided `Settings.Default` object for a standard configuration. + +### Signature + +```kotlin +sealed class Settings( + val tapTolerance: Dp, + val markerDropAnimateDuration: Long, + val markerBounceAnimateDuration: Long, + val iconSize: Dp, + val iconStroke: Dp, + val composeEventDebounce: Duration, +) +``` + +### Parameters + +These are the constructor parameters for the `Settings` class. You must provide these values when creating a custom settings implementation. + +| Parameter | Type | Description | +| --------------------------- | -------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | +| `tapTolerance` | `Dp` | The distance in density-independent pixels (Dp) a touch can move before it's no longer considered a tap. Useful for distinguishing taps from drags. | +| `markerDropAnimateDuration` | `Long` | The duration in milliseconds for the marker "drop" animation when it first appears on the map. | +| `markerBounceAnimateDuration` | `Long` | The duration in milliseconds for the marker "bounce" animation, typically used to highlight a selected marker. | +| `iconSize` | `Dp` | The default size for marker icons. It is recommended to use a value from `MarkerIconSize`. | +| `iconStroke` | `Dp` | The stroke width for marker icons in density-independent pixels (Dp). | +| `composeEventDebounce` | `kotlin.time.Duration` | The debounce duration for processing frequent UI events (like map panning) to optimize performance and prevent excessive recompositions. | + +--- + +## `Settings.Default` + +A singleton object that provides a default, ready-to-use configuration. This is the recommended starting point for any map implementation. + +### Signature + +```kotlin +object Default : Settings +``` + +### Description + +The `Default` object extends `Settings` with a set of sensible default values for a typical map use case. + +### Default Values + +| Property | Default Value | +| --------------------------- | ----------------------------- | +| `tapTolerance` | `14.dp` | +| `markerDropAnimateDuration` | `300` (ms) | +| `markerBounceAnimateDuration` | `2000` (ms) | +| `iconSize` | `MarkerIconSize.Regular` (48.dp) | +| `iconStroke` | `1.dp` | +| `composeEventDebounce` | `5.milliseconds` | + +--- + +## `MarkerIconSize` + +A helper object that provides a set of predefined, standard `Dp` values for marker icon sizes. Using these constants helps maintain visual consistency across your application. + +### Signature + +```kotlin +object MarkerIconSize +``` + +### Properties + +| Property | Value | Description | +| --------- | ------- | ---------------------------- | +| `Small` | `32.dp` | A small-sized icon. | +| `Regular` | `48.dp` | A standard, medium-sized icon. | +| `Large` | `60.dp` | A large-sized icon. | + +--- + +### Example + +The following examples demonstrate how to use the default settings and how to create and apply a custom settings configuration. + +#### 1. Using Default Settings + +You can easily apply the default settings by passing `Settings.Default` to your component or by using it as a default parameter value. + +```kotlin +import com.mapconductor.settings.Settings + +// A composable that accepts a Settings object, defaulting to Settings.Default +@Composable +fun MyMapComponent(settings: Settings = Settings.Default) { + // Use settings properties to configure the map + val markerSize = settings.iconSize + // ... +} + +// Usage in your UI +@Composable +fun AppScreen() { + // This will use the default settings automatically + MyMapComponent() +} +``` + +#### 2. Creating and Using Custom Settings + +To create a custom configuration, define a new object that inherits from `Settings` and overrides the desired properties. + +```kotlin +import com.mapconductor.settings.Settings +import com.mapconductor.settings.MarkerIconSize +import androidx.compose.ui.unit.dp +import kotlin.time.Duration.Companion.milliseconds + +// Define a custom settings profile with larger icons and faster animations +object FastAndLargeSettings : Settings( + tapTolerance = 16.dp, + markerDropAnimateDuration = 150, // Faster drop animation + markerBounceAnimateDuration = 1000, // Faster bounce animation + iconSize = MarkerIconSize.Large, // Use a predefined large size + iconStroke = 1.5.dp, + composeEventDebounce = 10.milliseconds +) + +// Usage in your UI +@Composable +fun AppScreen() { + // Pass your custom settings object to the component + MyMapComponent(settings = FastAndLargeSettings) +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/ArcGISExtension.kt.md b/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/ArcGISExtension.kt.md new file mode 100644 index 00000000..2d93fe01 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/ArcGISExtension.kt.md @@ -0,0 +1,52 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +*** + +### `toArcGISColor()` + +An internal extension function that converts a Jetpack Compose `Color` object into its equivalent ArcGIS Maps SDK `Color` representation. + +### Signature + +```kotlin +internal fun Color.toArcGISColor(): com.arcgismaps.Color +``` + +### Description + +This function provides a convenient way to convert a `androidx.compose.ui.graphics.Color` instance to a `com.arcgismaps.Color`. It maps the float-based RGBA channel values (ranging from 0.0f to 1.0f) of the Compose `Color` to the integer-based RGBA values (ranging from 0 to 255) used by the ArcGIS `Color`. + +As an `internal` function, it is designed for use only within its containing module and is not part of the public API. + +### Parameters + +| Parameter | Type | Description | +|-----------|---------------------------------------|-------------------------------------------------------| +| `this` | `androidx.compose.ui.graphics.Color` | The source Jetpack Compose `Color` instance to convert. | + +### Returns + +**Type:** `com.arcgismaps.Color` + +A new `com.arcgismaps.Color` object that is visually identical to the source Compose `Color`. + +### Example + +Here is an example of how to convert a standard Compose `Color` to an ArcGIS `Color`. + +```kotlin +import androidx.compose.ui.graphics.Color +// Assuming the extension function is accessible within the current scope +// import com.mapconductor.arcgis.toArcGISColor + +// 1. Define a Jetpack Compose Color (e.g., a semi-transparent blue) +val composeBlue = Color(red = 0.0f, green = 0.0f, blue = 1.0f, alpha = 0.5f) + +// 2. Convert it to an ArcGIS Color using the extension function +val arcGisBlue = composeBlue.toArcGISColor() + +// The resulting arcGisBlue is a com.arcgismaps.Color instance +// with integer RGBA values: R=0, G=0, B=255, A=127. +println("ArcGIS Color: R=${arcGisBlue.red}, G=${arcGisBlue.green}, B=${arcGisBlue.blue}, A=${arcGisBlue.alpha}") +// Output: ArcGIS Color: R=0, G=0, B=255, A=127 +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/ArcGISMapDesignType.kt.md b/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/ArcGISMapDesignType.kt.md new file mode 100644 index 00000000..83bc24ba --- /dev/null +++ b/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/ArcGISMapDesignType.kt.md @@ -0,0 +1,196 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +--- + +# ArcGISDesign + +The `ArcGISDesign` class and its companion object provide a structured way to define and manage map styles for an ArcGIS map. It encapsulates predefined basemap styles from both ArcGIS and OpenStreetMap (OSM), and allows for customization with elevation data. + +## `ArcGISDesign` Class + +A data class that represents a specific ArcGIS or OSM basemap style. It holds the unique identifier for the style and a list of associated elevation sources. + +### Signature + +```kotlin +data class ArcGISDesign( + override val id: String, + override val elevationSources: List = emptyList(), +) : ArcGISDesignTypeInterface +``` + +### Properties + +| Property | Type | Description | +| :---------------- | :------------------ | :-------------------------------------------------------------------------- | +| `id` | `String` | The unique string identifier for the map design (e.g., `"arc_gis_streets"`). | +| `elevationSources`| `List` | A list of URLs for elevation data sources to be applied to the map. | + +### Methods + +#### `withElevationSources` + +Creates a new `ArcGISDesign` instance by copying the current design and applying a new list of elevation sources. This method follows the immutable object pattern. + +**Signature** +```kotlin +fun withElevationSources(sources: List): ArcGISDesign +``` + +**Parameters** +| Parameter | Type | Description | +| :-------- | :------------- | :--------------------------------------------- | +| `sources` | `List` | A list of URLs for the new elevation sources. | + +**Returns** +| Type | Description | +| :-------------- | :----------------------------------------------------------------------- | +| `ArcGISDesign` | A new `ArcGISDesign` object with the updated `elevationSources`. | + +#### `getValue` + +Retrieves the unique identifier of the map design. + +**Signature** +```kotlin +override fun getValue(): String +``` + +**Returns** +| Type | Description | +| :--------- | :---------------------------------------- | +| `String` | The `id` of the `ArcGISDesign` instance. | + +--- + +## `ArcGISDesign.Companion` Object + +The companion object for `ArcGISDesign` serves as a factory and utility provider. It contains a comprehensive list of predefined map designs and helper functions to create designs and convert them to ArcGIS-native types. + +### Predefined Designs + +The companion object includes numerous predefined static properties for common ArcGIS and OSM basemap styles. These provide a convenient and type-safe way to select a map style. + +**Examples of Predefined Designs:** +* `ArcGISDesign.Streets` +* `ArcGISDesign.Imagery` +* `ArcGISDesign.NavigationNight` +* `ArcGISDesign.Topographic` +* `ArcGISDesign.OsmStandard` +* `ArcGISDesign.OsmStreets` + +### Companion Object Functions + +#### `Create` + +A factory function that retrieves a predefined `ArcGISDesign` instance based on its unique string ID. + +**Signature** +```kotlin +fun Create( + id: String, + sources: List = emptyList(), +): ArcGISDesign +``` + +**Description** +This function looks up a predefined `ArcGISDesign` from the provided `id`. If a matching design is found, it is returned. Note that the `sources` parameter is currently not used to modify the returned predefined instance. Throws a `Throwable` if the `id` does not correspond to any known design. + +**Parameters** +| Parameter | Type | Description | +| :-------- | :------------- | :----------------------------------------------------------------------- | +| `id` | `String` | The unique identifier of the desired map design. | +| `sources` | `List` | An optional list of elevation sources. Defaults to an empty list. | + +**Returns** +| Type | Description | +| :------------- | :----------------------------------------------- | +| `ArcGISDesign` | The corresponding predefined `ArcGISDesign` instance. | + +#### `toBasemapStyle` + +Converts an `ArcGISDesignTypeInterface` implementation (like `ArcGISDesign`) into the corresponding `BasemapStyle` enum required by the ArcGIS Maps SDK for Kotlin. + +**Signature** +```kotlin +fun toBasemapStyle(designType: ArcGISDesignTypeInterface): BasemapStyle +``` + +**Description** +This utility function acts as a bridge between the `ArcGISDesign` system and the underlying ArcGIS SDK, translating the design's ID into the appropriate `BasemapStyle` enum value. Throws a `Throwable` if the design type's ID is not recognized. + +**Parameters** +| Parameter | Type | Description | +| :----------- | :-------------------------- | :---------------------------------------- | +| `designType` | `ArcGISDesignTypeInterface` | The map design instance to convert. | + +**Returns** +| Type | Description | +| :------------- | :----------------------------------------------------------------------- | +| `BasemapStyle` | The equivalent `BasemapStyle` enum from the `com.arcgismaps.mapping` package. | + +--- + +## `ArcGISDesignTypeInterface` Interface + +An interface that defines the basic contract for an ArcGIS map design type. + +### Signature +```kotlin +interface ArcGISDesignTypeInterface : MapDesignTypeInterface { + val elevationSources: List +} +``` + +### Properties +| Property | Type | Description | +| :---------------- | :------------- | :---------------------------------------- | +| `elevationSources`| `List` | A list of URLs for elevation data sources. | + +--- + +## Example + +Here's how you can use `ArcGISDesign` to configure a map. + +```kotlin +import com.arcgismaps.mapping.Basemap +import com.arcgismaps.mapping.BasemapStyle +import com.mapconductor.arcgis.map.ArcGISDesign + +fun main() { + // 1. Use a predefined map design directly + val streetDesign = ArcGISDesign.Streets + println("Selected design ID: ${streetDesign.getValue()}") + + // 2. Add elevation sources to an existing design + val elevationSourceUrl = "https://elevation3d.arcgis.com/arcgis/rest/services/WorldElevation3D/Terrain3D/ImageServer" + val streetDesignWithElevation = streetDesign.withElevationSources(listOf(elevationSourceUrl)) + println("Elevation sources: ${streetDesignWithElevation.elevationSources}") + + // 3. Create a design from a string ID using the factory function + try { + val imageryDesign = ArcGISDesign.Create("arc_gis_imagery") + println("Successfully created design from ID: ${imageryDesign.id}") + } catch (e: Throwable) { + println(e.message) + } + + // 4. Convert an ArcGISDesign to the ArcGIS SDK's BasemapStyle enum + // This is typically done when creating a Basemap object for the map view. + val basemapStyle: BasemapStyle = ArcGISDesign.toBasemapStyle(streetDesign) + val basemap = Basemap(basemapStyle) + println("Converted to ArcGIS BasemapStyle: $basemapStyle") + + // Example of using the created basemap (conceptual) + // mapView.map = ArcGISMap(basemap) +} +``` + +### Output of the Example: +``` +Selected design ID: arc_gis_streets +Elevation sources: [https://elevation3d.arcgis.com/arcgis/rest/services/WorldElevation3D/Terrain3D/ImageServer] +Successfully created design from ID: arc_gis_imagery +Converted to ArcGIS BasemapStyle: ArcGISStreets +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/ArcGISMapView.kt.md b/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/ArcGISMapView.kt.md new file mode 100644 index 00000000..f114b44a --- /dev/null +++ b/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/ArcGISMapView.kt.md @@ -0,0 +1,145 @@ +Of course! Here is the high-quality SDK documentation for the provided `ArcGISMapView` composable. + +--- + +### `ArcGISMapView` + +A Jetpack Compose composable that displays an interactive 3D ArcGIS map. This component is built upon the ArcGIS Maps SDK for Android `SceneView` and provides a declarative, Compose-native way to manage map state, handle user interactions, and render various overlays. + +The `ArcGISMapView` is the root component for all ArcGIS map-related UI. You can add children composables like `Marker`, `Polygon`, `Polyline`, etc., within its `content` lambda to render objects on the map. + +### Signature + +```kotlin +@Composable +fun ArcGISMapView( + state: ArcGISMapViewState, + modifier: Modifier = Modifier, + markerTiling: MarkerTilingOptions? = null, + sdkInitialize: (suspend (android.content.Context) -> Boolean)? = null, + onMapLoaded: OnMapLoadedHandler? = null, + onCameraMoveStart: OnCameraMoveHandler? = null, + onCameraMove: OnCameraMoveHandler? = null, + onCameraMoveEnd: OnCameraMoveHandler? = null, + onMapClick: OnMapEventHandler? = null, + content: (@Composable ArcGISMapViewScope.() -> Unit)? = null, +) +``` + +### Description + +This composable function renders an ArcGIS map and manages its lifecycle within a Compose application. It takes a `ArcGISMapViewState` to control properties like camera position and map style. It also provides callbacks for various map events, such as map loading and camera movement. + +Map overlays and other UI elements are added declaratively within the trailing `content` lambda, which provides an `ArcGISMapViewScope`. + +### Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| `state` | `ArcGISMapViewState` | The state object that holds map configuration, such as camera position and map design. It also controls the map's lifecycle and provides an interface to interact with the map controller. | +| `modifier` | `Modifier` | The `Modifier` to be applied to the map view layout. | +| `markerTiling` | `MarkerTilingOptions?` | Optional configuration for marker tiling to optimize performance with a large number of markers. If `null`, `MarkerTilingOptions.Default` is used. | +| `sdkInitialize` | `(suspend (Context) -> Boolean)?` | An optional suspend lambda to perform custom initialization of the ArcGIS SDK. If not provided, a default initializer is used which attempts to set the API key from the app's `AndroidManifest.xml`. Return `true` on success and `false` on failure. | +| `onMapLoaded` | `OnMapLoadedHandler?` | A callback invoked once when the base map has successfully loaded and is ready for interaction. | +| `onCameraMoveStart` | `OnCameraMoveHandler?` | A callback invoked when the map camera starts moving, either due to user gesture or programmatic animation. It receives the current `MapCameraPosition`. | +| `onCameraMove` | `OnCameraMoveHandler?` | A callback invoked continuously while the map camera is moving. It receives the current `MapCameraPosition`. | +| `onCameraMoveEnd` | `OnCameraMoveHandler?` | A callback invoked when the map camera finishes moving. It receives the final `MapCameraPosition`. | +| `onMapClick` | `OnMapEventHandler?` | A callback invoked when the user clicks on a point on the map that is not an overlay. It receives the `LatLng` of the click location. | +| `content` | `(@Composable ArcGISMapViewScope.() -> Unit)?` | A composable lambda within the `ArcGISMapViewScope` where you can declaratively add map overlays like `Marker`, `Polygon`, `Polyline`, `Circle`, etc. | + +### Returns + +This is a `@Composable` function and does not have a return value. It emits the ArcGIS map view UI into the composition. + +### Deprecated Overload + +An older version of `ArcGISMapView` exists that accepts click handlers for individual overlay types (`onMarkerClick`, `onPolygonClick`, etc.). This overload is deprecated. + +```kotlin +@Deprecated("Use CircleState/PolylineState/PolygonState onClick instead.") +@Composable +fun ArcGISMapView( + // ... other parameters + onMarkerClick: OnMarkerEventHandler?, + onCircleClick: OnCircleEventHandler?, + onPolylineClick: OnPolylineEventHandler?, + onPolygonClick: OnPolygonEventHandler?, + // ... +) +``` + +**Reason for Deprecation:** The modern approach is to handle click events directly on the state object associated with each overlay (e.g., `rememberMarkerState(onClick = { ... })`). This provides a more granular and idiomatic Compose API, associating the event handling logic directly with the state of the UI element. + +### Example + +Here is a complete example of how to use `ArcGISMapView` in a Jetpack Compose screen. + +```kotlin +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import android.util.Log +import com.mapconductor.arcgis.map.ArcGISMapView +import com.mapconductor.arcgis.map.rememberArcGISMapViewState +import com.mapconductor.arcgis.marker.Marker +import com.mapconductor.arcgis.marker.rememberMarkerState +import com.mapconductor.arcgis.polygon.Polygon +import com.mapconductor.arcgis.polygon.rememberPolygonState +import com.mapconductor.core.map.MapCameraPosition +import com.mapconductor.core.types.LatLng + +@Composable +fun MyMapScreen() { + // 1. Remember the map view state to control the map. + val mapState = rememberArcGISMapViewState( + initialCameraPosition = MapCameraPosition( + target = LatLng(34.0522, -118.2437), // Los Angeles + zoom = 12.0 + ) + ) + + // 2. Use the ArcGISMapView composable. + ArcGISMapView( + state = mapState, + modifier = Modifier.fillMaxSize(), + onMapLoaded = { + Log.d("MyMapScreen", "ArcGIS map has loaded.") + }, + onCameraMoveEnd = { newPosition -> + Log.d("MyMapScreen", "Camera stopped at: ${newPosition.target}") + } + ) { + // 3. Add overlays declaratively within the content lambda. + + // Add a marker + val markerState = rememberMarkerState( + position = LatLng(34.0522, -118.2437) + ) + Marker( + state = markerState, + title = "Los Angeles City Hall", + snippet = "A historic landmark." + ) + + // Add a polygon with a click listener + val polygonState = rememberPolygonState( + points = listOf( + LatLng(34.06, -118.24), + LatLng(34.06, -118.25), + LatLng(34.05, -118.25), + LatLng(34.05, -118.24) + ), + onClick = { polygonId -> + Log.d("MyMapScreen", "Polygon $polygonId was clicked!") + } + ) + Polygon( + state = polygonState, + fillColor = Color.Blue.copy(alpha = 0.3f), + strokeColor = Color.Blue, + strokeWidth = 2f + ) + } +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/ArcGISMapViewController.kt.md b/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/ArcGISMapViewController.kt.md new file mode 100644 index 00000000..e73b5428 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/ArcGISMapViewController.kt.md @@ -0,0 +1,415 @@ +# ArcGISMapViewController + +The `ArcGISMapViewController` class is the primary controller for managing the ArcGIS map view. It orchestrates interactions between the map, camera, and various data overlays like markers, polylines, and polygons. This controller handles user input events (taps, drags), manages the lifecycle of map features, and provides an interface for programmatic camera control. + +## Signature + +```kotlin +class ArcGISMapViewController( + override val holder: ArcGISMapViewHolder, + private val markerController: ArcGISMarkerController, + private val polylineController: ArcGISPolylineOverlayController, + private val polygonController: ArcGISPolygonOverlayController, + private val circleController: ArcGISCircleOverlayController, + private val groundImageController: ArcGISGroundImageController, + private val rasterLayerController: ArcGISRasterLayerController, + override val coroutine: CoroutineScope = CoroutineScope(Dispatchers.Default), +) : BaseMapViewController(), ArcGISMapViewControllerInterface +``` + +## Constructor + +Initializes a new instance of the `ArcGISMapViewController`. + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `holder` | `ArcGISMapViewHolder` | The view holder that contains the ArcGIS `MapView` instance. | +| `markerController` | `ArcGISMarkerController` | The controller responsible for managing marker overlays. | +| `polylineController` | `ArcGISPolylineOverlayController` | The controller for managing polyline overlays. | +| `polygonController` | `ArcGISPolygonOverlayController` | The controller for managing polygon overlays. | +| `circleController` | `ArcGISCircleOverlayController` | The controller for managing circle overlays. | +| `groundImageController` | `ArcGISGroundImageController` | The controller for managing ground image overlays. | +| `rasterLayerController` | `ArcGISRasterLayerController` | The controller for managing raster layer overlays. | +| `coroutine` | `CoroutineScope` | The coroutine scope used for managing asynchronous operations. Defaults to `CoroutineScope(Dispatchers.Default)`. | + +## Properties + +These properties are inherited from `BaseMapViewController` and are used to set callbacks for various map events. + +| Property | Type | Description | +| :--- | :--- | :--- | +| `mapClickCallback` | `((GeoPoint) -> Unit)?` | A callback invoked when the user taps on the map at a location where no other overlay was tapped. The `GeoPoint` represents the coordinate of the tap. | +| `mapLongClickCallback` | `((GeoPoint) -> Unit)?` | A callback invoked when the user long-presses on the map. The `GeoPoint` represents the coordinate of the long-press. | +| `cameraMoveStartCallback` | `((MapCameraPosition) -> Unit)?` | A callback invoked when the map camera starts moving. | +| `cameraMoveCallback` | `((MapCameraPosition) -> Unit)?` | A callback invoked continuously while the map camera is moving. | +| `cameraMoveEndCallback` | `((MapCameraPosition) -> Unit)?` | A callback invoked when the map camera has finished moving. This is debounced to prevent rapid firing. | +| `mapLoadedCallback` | `(() -> Unit)?` | A one-time callback invoked when the map has finished its initial load. It is set to `null` after being called. | + +## Methods + +### Camera Control + +#### moveCamera + +Instantly moves the map camera to a specified position without animation. + +**Signature** +```kotlin +fun moveCamera(position: MapCameraPosition) +``` + +**Parameters** +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `position` | `MapCameraPosition` | The target camera position, including location, zoom, bearing, and tilt. | + +--- + +#### animateCamera + +Animates the map camera from its current position to a specified position over a given duration. + +**Signature** +```kotlin +suspend fun animateCamera( + position: MapCameraPosition, + duration: Long +) +``` + +**Parameters** +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `position` | `MapCameraPosition` | The target camera position to animate to. | +| `duration` | `Long` | The duration of the animation in milliseconds. | + +--- + +### Overlay Management + +#### clearOverlays + +Removes all overlays (markers, polylines, polygons, circles, ground images, and raster layers) from the map. + +**Signature** +```kotlin +suspend fun clearOverlays() +``` + +--- + +#### compositionMarkers + +Adds a list of new markers to the map. + +**Signature** +```kotlin +suspend fun compositionMarkers(data: List) +``` + +**Parameters** +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `data` | `List` | A list of `MarkerState` objects to be added to the map. | + +--- + +#### updateMarker + +Updates the state of an existing marker on the map. + +**Signature** +```kotlin +suspend fun updateMarker(state: MarkerState) +``` + +**Parameters** +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `state` | `MarkerState` | The new state for the marker to be updated. | + +--- + +#### compositionPolylines + +Adds a list of new polylines to the map. + +**Signature** +```kotlin +suspend fun compositionPolylines(data: List) +``` + +**Parameters** +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `data` | `List` | A list of `PolylineState` objects to be added. | + +--- + +#### updatePolyline + +Updates the state of an existing polyline on the map. + +**Signature** +```kotlin +suspend fun updatePolyline(state: PolylineState) +``` + +**Parameters** +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `state` | `PolylineState` | The new state for the polyline to be updated. | + +--- + +#### compositionPolygons + +Adds a list of new polygons to the map. + +**Signature** +```kotlin +suspend fun compositionPolygons(data: List) +``` + +**Parameters** +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `data` | `List` | A list of `PolygonState` objects to be added. | + +--- + +#### updatePolygon + +Updates the state of an existing polygon on the map. + +**Signature** +```kotlin +suspend fun updatePolygon(state: PolygonState) +``` + +**Parameters** +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `state` | `PolygonState` | The new state for the polygon to be updated. | + +--- + +#### compositionCircles + +Adds a list of new circles to the map. + +**Signature** +```kotlin +suspend fun compositionCircles(data: List) +``` + +**Parameters** +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `data` | `List` | A list of `CircleState` objects to be added. | + +--- + +#### updateCircle + +Updates the state of an existing circle on the map. + +**Signature** +```kotlin +suspend fun updateCircle(state: CircleState) +``` + +**Parameters** +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `state` | `CircleState` | The new state for the circle to be updated. | + +--- + +#### compositionGroundImages + +Adds a list of new ground images to the map. + +**Signature** +```kotlin +suspend fun compositionGroundImages(data: List) +``` + +**Parameters** +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `data` | `List` | A list of `GroundImageState` objects to be added. | + +--- + +#### updateGroundImage + +Updates the state of an existing ground image on the map. + +**Signature** +```kotlin +suspend fun updateGroundImage(state: GroundImageState) +``` + +**Parameters** +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `state` | `GroundImageState` | The new state for the ground image to be updated. | + +--- + +#### compositionRasterLayers + +Adds a list of new raster layers to the map. + +**Signature** +```kotlin +suspend fun compositionRasterLayers(data: List) +``` + +**Parameters** +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `data` | `List` | A list of `RasterLayerState` objects to be added. | + +--- + +#### updateRasterLayer + +Updates the state of an existing raster layer on the map. + +**Signature** +```kotlin +suspend fun updateRasterLayer(state: RasterLayerState) +``` + +**Parameters** +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `state` | `RasterLayerState` | The new state for the raster layer to be updated. | + +--- + +### Overlay Checks + +These methods check for the existence of a specific overlay entity on the map. + +| Method | Description | Returns | +| :--- | :--- | :--- | +| `hasMarker(state: MarkerState)` | Checks if a marker with the given state's ID exists. | `Boolean` | +| `hasPolyline(state: PolylineState)` | Checks if a polyline with the given state's ID exists. | `Boolean` | +| `hasPolygon(state: PolygonState)` | Checks if a polygon with the given state's ID exists. | `Boolean` | +| `hasCircle(state: CircleState)` | Checks if a circle with the given state's ID exists. | `Boolean` | +| `hasGroundImage(state: GroundImageState)` | Checks if a ground image with the given state's ID exists. | `Boolean` | +| `hasRasterLayer(state: RasterLayerState)` | Checks if a raster layer with the given state's ID exists. | `Boolean` | + +--- + +### Map Configuration + +#### setMapDesignType + +Sets the visual style (basemap) of the map. + +**Signature** +```kotlin +fun setMapDesignType(value: ArcGISDesignTypeInterface) +``` + +**Parameters** +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `value` | `ArcGISDesignTypeInterface` | The desired map design type (e.g., `ArcGISDesign.Streets`). | + +--- + +#### setMapDesignTypeChangeListener + +Registers a listener that is invoked when the map's design type changes. The listener is also immediately called with the current design type upon registration. + +**Signature** +```kotlin +fun setMapDesignTypeChangeListener(listener: ArcGISDesignTypeChangeHandler) +``` + +**Parameters** +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `listener` | `ArcGISDesignTypeChangeHandler` | The callback to be invoked with the map design type. | + +--- + +### Initialization + +#### sendInitialCameraUpdate + +Manually triggers a camera position update. This is useful for notifying listeners of the initial camera state after the map view is ready. + +**Signature** +```kotlin +fun sendInitialCameraUpdate() +``` + +--- + +### Deprecated Event Listeners + +These methods are deprecated. It is recommended to set event handlers directly on the `State` object for each overlay (e.g., `MarkerState.onClick`, `PolylineState.onClick`). + +| Method | Deprecation Reason | +| :--- | :--- | +| `setOnCircleClickListener(listener: OnCircleEventHandler?)` | Use `CircleState.onClick` instead. | +| `setOnGroundImageClickListener(listener: OnGroundImageEventHandler?)` | Use `GroundImageState.onClick` instead. | +| `setOnMarkerDragStart(listener: OnMarkerEventHandler?)` | Use `MarkerState.onDragStart` instead. | +| `setOnMarkerDrag(listener: OnMarkerEventHandler?)` | Use `MarkerState.onDrag` instead. | +| `setOnMarkerDragEnd(listener: OnMarkerEventHandler?)` | Use `MarkerState.onDragEnd` instead. | +| `setOnMarkerAnimateStart(listener: OnMarkerEventHandler?)` | Use `MarkerState.onAnimateStart` instead. | +| `setOnMarkerAnimateEnd(listener: OnMarkerEventHandler?)` | Use `MarkerState.onAnimateEnd` instead. | +| `setOnMarkerClickListener(listener: OnMarkerEventHandler?)` | Use `MarkerState.onClick` instead. | +| `setOnPolylineClickListener(listener: OnPolylineEventHandler?)` | Use `PolylineState.onClick` instead. | +| `setOnPolygonClickListener(listener: OnPolygonEventHandler?)` | Use `PolygonState.onClick` instead. | + +## Example + +Here is an example of how to initialize the `ArcGISMapViewController`, add a marker, and handle map click events. + +```kotlin +import com.mapconductor.core.features.GeoPoint +import com.mapconductor.core.marker.MarkerState +import kotlinx.coroutines.launch + +// Assume 'mapViewController' is an initialized instance of ArcGISMapViewController + +// 1. Set a callback for map clicks +mapViewController.mapClickCallback = { geoPoint -> + println("Map clicked at: ${geoPoint.latitude}, ${geoPoint.longitude}") +} + +// 2. Define a marker +val tokyoStation = GeoPoint.fromLongLat(139.7671, 35.6812) +val marker = MarkerState( + id = "tokyo-station-marker", + position = tokyoStation, + title = "Tokyo Station", + draggable = true +).apply { + // Set an onClick listener directly on the marker state + onClick = { event -> + println("Marker '${event.state.title}' clicked!") + // Return true to indicate the event was handled + true + } +} + +// 3. Add the marker to the map within a coroutine scope +mapViewController.coroutine.launch { + mapViewController.compositionMarkers(listOf(marker)) +} + +// 4. Move the camera to the marker's location +val cameraPosition = MapCameraPosition( + position = tokyoStation, + zoom = 15.0 +) +mapViewController.moveCamera(cameraPosition) +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/ArcGISMapViewControllerInterface.kt.md b/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/ArcGISMapViewControllerInterface.kt.md new file mode 100644 index 00000000..64e97033 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/ArcGISMapViewControllerInterface.kt.md @@ -0,0 +1,121 @@ +Of course! Here is the high-quality SDK documentation for the provided Kotlin code snippet. + +--- + +# ArcGISMapViewControllerInterface + +The primary interface for controlling and interacting with an ArcGIS map view. + +## Description + +`ArcGISMapViewControllerInterface` serves as the main controller for an ArcGIS map instance. It extends the generic `MapViewControllerInterface` and incorporates a comprehensive set of capabilities for managing various map features and overlays. + +This interface allows you to: +- Manage the lifecycle and core properties of the map view. +- Add, remove, and update markers, polylines, polygons, circles, ground images, and raster layers. +- Control ArcGIS-specific features, such as the map's design type (basemap). +- Listen for changes to the map's design type. + +It inherits functionality from the following interfaces: +- `MapViewControllerInterface` +- `MarkerCapableInterface` +- `PolylineCapableInterface` +- `PolygonCapableInterface` +- `CircleCapableInterface` +- `GroundImageCapableInterface` +- `RasterLayerCapableInterface` + +--- + +## Type Aliases + +### ArcGISDesignTypeChangeHandler + +A function type that defines the signature for a listener that is invoked when the map's design type changes. + +**Signature** +```kotlin +typealias ArcGISDesignTypeChangeHandler = (ArcGISDesignTypeInterface) -> Unit +``` + +**Parameters** + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `designType` | `ArcGISDesignTypeInterface` | The new design type that has been applied to the map. | + +--- + +## Functions + +### setMapDesignType + +Sets the visual design (basemap) of the map. + +**Signature** +```kotlin +fun setMapDesignType(value: ArcGISDesignTypeInterface) +``` + +**Description** + +This function updates the current basemap of the ArcGIS map to the specified design type. Use this to dynamically change the map's appearance, for example, switching between satellite, street, and topographic views. + +**Parameters** + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `value` | `ArcGISDesignTypeInterface` | The new map design type to apply. | + +**Returns** + +This method does not return a value. + +**Example** + +```kotlin +// Assuming 'mapController' is an instance of ArcGISMapViewControllerInterface +// and 'ArcGISTopographicDesign' implements ArcGISDesignTypeInterface. + +val newDesign = ArcGISTopographicDesign() +mapController.setMapDesignType(newDesign) +``` + +--- + +### setMapDesignTypeChangeListener + +Registers a listener to be notified when the map's design type changes. + +**Signature** +```kotlin +fun setMapDesignTypeChangeListener(listener: ArcGISDesignTypeChangeHandler) +``` + +**Description** + +This function sets a callback that will be executed whenever the map's design type is updated, either programmatically via `setMapDesignType` or through other UI interactions. This is useful for reacting to basemap changes within your application. + +**Parameters** + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `listener` | `ArcGISDesignTypeChangeHandler` | A lambda or function reference that will be invoked when the map design changes. The new `ArcGISDesignTypeInterface` is passed as an argument to this listener. | + +**Returns** + +This method does not return a value. + +**Example** + +```kotlin +// Assuming 'mapController' is an instance of ArcGISMapViewControllerInterface. + +mapController.setMapDesignTypeChangeListener { newDesignType -> + // Log the change or update the UI to reflect the new basemap. + println("Map design changed to: ${newDesignType::class.simpleName}") + + // Example: Update a UI label with the name of the new design. + updateBasemapLabel(newDesignType.name) +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/ArcGISMapViewHolderImpl.kt.md b/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/ArcGISMapViewHolderImpl.kt.md new file mode 100644 index 00000000..3a8c65e7 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/ArcGISMapViewHolderImpl.kt.md @@ -0,0 +1,254 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet, formatted in Markdown. + +*** + +## WrapSceneView + +A custom `FrameLayout` that serves as a wrapper for the ArcGIS `SceneView`. This class is designed to integrate the `SceneView` into a view hierarchy and manage its lifecycle. + +The class provides standard Android `View` constructors and delegates lifecycle events from a `LifecycleOwner` (like an `Activity` or `Fragment`) to the underlying `SceneView`. + +### Lifecycle Management + +The following methods must be called from the corresponding lifecycle callbacks of the hosting `Activity` or `Fragment` to ensure the proper functioning of the `SceneView`. + +#### onCreate(owner) + +Forwards the `onCreate` lifecycle event to the `SceneView`. This should be called within the `onCreate` method of the hosting `Activity` or `Fragment`. + +**Signature** +```kotlin +fun onCreate(owner: LifecycleOwner) +``` + +**Description** +Initializes the `SceneView` and prepares it for use. + +**Parameters** +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `owner` | `LifecycleOwner` | The lifecycle owner (e.g., `Activity`, `Fragment`) whose state is being managed. | + +--- + +#### onPause(owner) + +Forwards the `onPause` lifecycle event to the `SceneView`. This should be called within the `onPause` method of the hosting `Activity` or `Fragment`. + +**Signature** +```kotlin +fun onPause(owner: LifecycleOwner) +``` + +**Description** +Pauses the `SceneView`, stopping rendering and other active processes to conserve resources when the view is not in the foreground. + +**Parameters** +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `owner` | `LifecycleOwner` | The lifecycle owner whose state is being managed. | + +--- + +#### onResume(owner) + +Forwards the `onResume` lifecycle event to the `SceneView`. This should be called within the `onResume` method of the hosting `Activity` or `Fragment`. + +**Signature** +```kotlin +fun onResume(owner: LifecycleOwner) +``` + +**Description** +Resumes the `SceneView` after it has been paused, restarting rendering and other processes. + +**Parameters** +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `owner` | `LifecycleOwner` | The lifecycle owner whose state is being managed. | + +--- + +#### onStop(owner) + +Forwards the `onStop` lifecycle event to the `SceneView`. This should be called within the `onStop` method of the hosting `Activity` or `Fragment`. + +**Signature** +```kotlin +fun onStop(owner: LifecycleOwner) +``` + +**Description** +Stops the `SceneView` when it is no longer visible to the user. + +**Parameters** +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `owner` | `LifecycleOwner` | The lifecycle owner whose state is being managed. | + +--- + +#### onDestroy(owner) + +Forwards the `onDestroy` lifecycle event to the `SceneView`. This should be called within the `onDestroy` method of the hosting `Activity` or `Fragment`. + +**Signature** +```kotlin +fun onDestroy(owner: LifecycleOwner) +``` + +**Description** +Cleans up and releases all resources used by the `SceneView`. This is a final cleanup step. + +**Parameters** +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `owner` | `LifecycleOwner` | The lifecycle owner whose state is being managed. | + +*** + +## ArcGISMapViewHolder + +An adapter class that implements the `MapViewHolderInterface`. It acts as a bridge between a generic map interface and the specific ArcGIS `SceneView` implementation, providing methods for coordinate transformations. + +### toScreenOffset(position) + +Converts a geographic coordinate (`GeoPointInterface`) to a screen coordinate (`Offset`). + +**Signature** +```kotlin +fun toScreenOffset(position: GeoPointInterface): Offset? +``` + +**Description** +This function takes a geographic point and projects it onto the screen space of the `SceneView`, returning the corresponding pixel coordinates. + +**Parameters** +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `position` | `GeoPointInterface` | The geographic point (latitude, longitude, and optional altitude) to convert. | + +**Returns** +`Offset?` — The corresponding screen `Offset` (x, y coordinates), or `null` if the conversion is not possible (e.g., the point is not visible on the screen). + +--- + +### fromScreenOffset(offset) + +Asynchronously converts a screen coordinate (`Offset`) to a geographic coordinate (`GeoPoint`). + +**Signature** +```kotlin +suspend fun fromScreenOffset(offset: Offset): GeoPoint? +``` + +**Description** +This suspend function takes a screen pixel coordinate and performs a reverse projection to find the corresponding geographic coordinate on the 3D scene. Because this can be a computationally intensive operation, it is performed asynchronously. + +**Parameters** +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `offset` | `Offset` | The screen offset (x, y pixel coordinates) to convert. | + +**Returns** +`GeoPoint?` — The corresponding `GeoPoint`, or `null` if the screen coordinate does not map to a location on the scene. + +--- + +### fromScreenOffsetSync(offset) + +Synchronously converts a screen coordinate (`Offset`) to a geographic coordinate (`GeoPoint`). + +**Signature** +```kotlin +fun fromScreenOffsetSync(offset: Offset): GeoPoint? +``` + +**Description** +This function is a synchronous wrapper around `fromScreenOffset`. It blocks the calling thread until the conversion is complete. It is useful when you need the result immediately and are not on the main thread. **Warning:** Calling this on the main UI thread will cause it to freeze and may lead to an "Application Not Responding" (ANR) error. + +**Parameters** +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `offset` | `Offset` | The screen offset (x, y pixel coordinates) to convert. | + +**Returns** +`GeoPoint?` — The corresponding `GeoPoint`, or `null` if the screen coordinate does not map to a location on the scene. + +### Example + +```kotlin +// Assuming 'mapViewHolder' is an instance of ArcGISMapViewHolder +// and this code is run within a coroutine scope. + +// 1. Convert a geographic point to a screen offset +val geoPoint = GeoPoint(latitude = 34.0562, longitude = -117.1956) // A point in Redlands, CA +val screenOffset = mapViewHolder.toScreenOffset(geoPoint) + +screenOffset?.let { + println("Screen coordinates: x=${it.x}, y=${it.y}") +} + +// 2. Convert a screen offset back to a geographic point +val someScreenOffset = Offset(500f, 800f) +val newGeoPoint = mapViewHolder.fromScreenOffset(someScreenOffset) + +newGeoPoint?.let { + println("Geographic coordinates: lat=${it.latitude}, lon=${it.longitude}") +} +``` + +*** + +## getArcGisApiKey() + +An internal extension function on `Context` that retrieves the ArcGIS API key from the application's manifest metadata. + +**Signature** +```kotlin +internal fun Context.getArcGisApiKey(): String? +``` + +**Description** +This utility function simplifies retrieving the ArcGIS API key required for using ArcGIS services. It looks for a `` tag with the name `ARCGIS_API_KEY` within the `` tag of your `AndroidManifest.xml`. + +**Returns** +`String?` — The ArcGIS API key if found in the manifest's metadata, otherwise `null`. + +### Example + +To use this function, you must first add your API key to the `AndroidManifest.xml` file. + +**1. Add API Key to `AndroidManifest.xml`** + +Place the following `` tag inside the `` tag. + +```xml + + + + + + ... + +``` + +**2. Retrieve the Key in Code** + +You can then call the function on a `Context` instance. + +```kotlin +// Inside an Activity, Fragment, or any class with access to a Context +val apiKey = context.getArcGisApiKey() + +if (apiKey != null) { + // Use the API key to set up ArcGIS services + ArcGISRuntimeEnvironment.setApiKey(apiKey) +} else { + // Handle the case where the API key is not found + Log.e("ArcGISSetup", "ARCGIS_API_KEY not found in AndroidManifest.xml") +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/ArcGISMapViewInitOptions.kt.md b/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/ArcGISMapViewInitOptions.kt.md new file mode 100644 index 00000000..116d1773 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/ArcGISMapViewInitOptions.kt.md @@ -0,0 +1,39 @@ +# ArcGISMapViewInitOptions + +A data class that holds configuration options for initializing an `ArcGISMapView`. It allows for the customization of the map's initial appearance and 3D capabilities, such as the basemap style and elevation data sources. + +## Signature + +```kotlin +data class ArcGISMapViewInitOptions( + val basemapStyle: BasemapStyle, + val elevationSources: List, +) +``` + +## Parameters + +| Parameter | Type | Description | +| :----------------- | :-------------------- | :------------------------------------------------------------------------------------------------------------------------------------- | +| `basemapStyle` | `BasemapStyle` | The initial style for the map's basemap. This determines the visual theme, such as imagery, streets, or topography. | +| `elevationSources` | `List` | A list of URLs pointing to elevation services. These sources are used to create a 3D surface for the map, enabling terrain visualization. | + +## Example + +The following example demonstrates how to create an instance of `ArcGISMapViewInitOptions` to configure a map with an imagery basemap and a 3D terrain surface. + +```kotlin +import com.arcgismaps.mapping.BasemapStyle +import com.mapconductor.arcgis.map.ArcGISMapViewInitOptions + +// Define the URL for the elevation data source +val elevationSourceUrl = "https://elevation3d.arcgis.com/arcgis/rest/services/WorldElevation3D/Terrain3D/ImageServer" + +// Create an instance of ArcGISMapViewInitOptions +val mapViewOptions = ArcGISMapViewInitOptions( + basemapStyle = BasemapStyle.ARCGIS_IMAGERY, + elevationSources = listOf(elevationSourceUrl) +) + +// These options can then be passed to an ArcGISMapView component during its initialization. +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/ArcGISMapViewScope.kt.md b/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/ArcGISMapViewScope.kt.md new file mode 100644 index 00000000..6d798aa1 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/ArcGISMapViewScope.kt.md @@ -0,0 +1,64 @@ +Of course. Here is the high-quality SDK documentation for the given Kotlin code snippet, incorporating the feedback provided. + +--- + +# ArcGISMapViewScope + +## Signature +```kotlin +class ArcGISMapViewScope : MapViewScope() +``` + +## Description +`ArcGISMapViewScope` is a specialized scope class designed for configuring an ArcGIS map view. It extends the base `MapViewScope`, inheriting all its common map configuration capabilities, and adds functionalities exclusive to the ArcGIS Maps SDK. + +This class provides a context-specific DSL (Domain-Specific Language) for map setup. When you configure an ArcGIS map, you operate within this scope, gaining access to both shared map properties (like camera position) and ArcGIS-specific features (like basemap styles or 3D settings). + +## Properties +This class inherits all properties from its parent, `MapViewScope`. It also serves as the designated place for properties unique to the ArcGIS implementation. + +| Property (Example) | Type | Description | +| :--- | :--- | :--- | +| `basemapStyle` | `ArcGISBasemapStyle` | Gets or sets the visual style of the ArcGIS basemap (e.g., Streets, Imagery, Topographic). | +| `is3DModeEnabled` | `Boolean` | A flag to enable or disable the 3D scene view for the map. | + +*Note: The properties listed above are examples of ArcGIS-specific functionalities that would be defined in this class.* + +## Methods +This class inherits all methods from `MapViewScope`. ArcGIS-specific methods for controlling the map's behavior are defined here. + +## Example +The `ArcGISMapViewScope` is typically used within a map configuration block, such as a Jetpack Compose `ArcGISMap` composable. The lambda provided to the composable has `ArcGISMapViewScope` as its receiver, giving you direct access to its properties and methods. + +```kotlin +import androidx.compose.runtime.Composable +import com.mapconductor.arcgis.map.ArcGISMap +import com.mapconductor.arcgis.map.model.ArcGISBasemapStyle +import com.mapconductor.core.model.LatLng +import com.mapconductor.core.model.CameraPosition + +@Composable +fun MyMapScreen() { + // The lambda block for ArcGISMap operates within the ArcGISMapViewScope. + ArcGISMap( + modifier = Modifier.fillMaxSize() + ) { + // --- Inherited from MapViewScope --- + // Set the initial camera position using a method from the base scope. + setInitialCameraPosition( + CameraPosition( + target = LatLng(34.0522, -118.2437), // Los Angeles + zoom = 12.0 + ) + ) + + // --- Specific to ArcGISMapViewScope --- + // Set an ArcGIS-specific basemap style. + // This property or method would not be available in other map SDK scopes. + basemapStyle = ArcGISBasemapStyle.ARCGIS_NAVIGATION + + // Enable 3D view, another hypothetical ArcGIS-specific feature. + is3DModeEnabled = true + } +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/ArcGISMapViewStateImpl.kt.md b/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/ArcGISMapViewStateImpl.kt.md new file mode 100644 index 00000000..7b595e6a --- /dev/null +++ b/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/ArcGISMapViewStateImpl.kt.md @@ -0,0 +1,140 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet, formatted in Markdown. + +*** + +# ArcGIS Map State SDK + +This document provides detailed documentation for the state management components of the ArcGIS Map SDK for Jetpack Compose. These components are essential for creating, remembering, and programmatically controlling the state of an ArcGIS map within your application. + +## `rememberArcGISMapViewState` + +This composable function is the primary entry point for creating and remembering the state of an ArcGIS map view. It leverages `rememberSaveable` to ensure that the map's state, including camera position and map design, is preserved across configuration changes (like screen rotation) and process recreation. + +### Signature +```kotlin +@Composable +fun rememberArcGISMapViewState( + mapDesign: ArcGISDesign = ArcGISDesign.Streets, + cameraPosition: MapCameraPositionInterface = MapCameraPosition.Default, +): ArcGISMapViewState +``` + +### Description +Creates an `ArcGISMapViewState` instance that is remembered across recompositions. The state is automatically saved and restored, making it robust against configuration changes. The returned `ArcGISMapViewState` object is the main handle used to interact with and control the map programmatically. + +### Parameters +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `mapDesign` | `ArcGISDesign` | The initial base map style to be applied to the map. Defaults to `ArcGISDesign.Streets`. | +| `cameraPosition` | `MapCameraPositionInterface` | The initial camera position of the map, including location, zoom, tilt, and heading. Defaults to `MapCameraPosition.Default`. | + +### Returns +An `ArcGISMapViewState` instance that is remembered across recompositions and saved across configuration changes. + +### Example +Here's how to create and use `rememberArcGISMapViewState` within a composable screen. + +```kotlin +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import com.mapconductor.arcgis.map.ArcGISDesign +import com.mapconductor.arcgis.map.rememberArcGISMapViewState +import com.mapconductor.core.features.GeoPoint +import com.mapconductor.core.map.MapCameraPosition + +@Composable +fun MyMapScreen() { + // Create and remember the map view state + val mapViewState = rememberArcGISMapViewState( + mapDesign = ArcGISDesign.Topographic, + cameraPosition = MapCameraPosition( + position = GeoPoint(latitude = 34.0562, longitude = -117.1956), // ESRI Headquarters + zoom = 12.0 + ) + ) + + // Assuming an ArcGISMapView composable exists that accepts this state + ArcGISMapView( + state = mapViewState, + modifier = Modifier.fillMaxSize() + ) +} +``` + +--- + +## `ArcGISMapViewState` Class + +A stateful class that holds and manages the properties of an ArcGIS map, such as its camera position and visual style. It serves as the primary interface for programmatically controlling the map's behavior and appearance. An instance of this class is typically created using the `rememberArcGISMapViewState` composable. + +### Key Properties + +| Property | Type | Description | +| :--- | :--- | :--- | +| `id` | `String` | A unique identifier for the map state instance. | +| `cameraPosition` | `MapCameraPosition` | (Read-only) The current position of the map's camera. This property is updated as the user interacts with the map or when camera movements are initiated programmatically. | +| `mapDesignType` | `ArcGISDesignTypeInterface` | The current visual style (basemap) of the map. This property can be set to a new `ArcGISDesignTypeInterface` to dynamically change the map's appearance. | +| `padding` | `StateFlow` | A `StateFlow` representing the padding applied to the map view. This is useful for informing the map about UI elements that overlay it, ensuring that features like the compass or attribution are not obscured. | + +### Methods + +#### `moveCameraTo(cameraPosition, durationMillis)` +Moves the map's camera to a specified `MapCameraPosition`. + +##### Signature +```kotlin +fun moveCameraTo( + cameraPosition: MapCameraPosition, + durationMillis: Long? +) +``` + +##### Description +Moves the map's camera to a specified `MapCameraPosition`. If a `durationMillis` greater than zero is provided, the camera animates to the new position over the specified duration. Otherwise, the camera moves instantly. + +##### Parameters +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `cameraPosition` | `MapCameraPosition` | The target camera position, including location, zoom, tilt, and heading. | +| `durationMillis` | `Long?` | The duration of the camera animation in milliseconds. If `null` or `0`, the camera moves instantly. | + +--- + +#### `moveCameraTo(position, durationMillis)` +A convenience function to move the map's camera to a new geographical coordinate (`GeoPoint`). + +##### Signature +```kotlin +fun moveCameraTo( + position: GeoPoint, + durationMillis: Long? +) +``` + +##### Description +Moves the map's camera to center on a new geographical coordinate (`GeoPoint`) while preserving the current zoom, tilt, and heading. + +##### Parameters +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `position` | `GeoPoint` | The target geographical coordinate to center the map on. | +| `durationMillis` | `Long?` | The duration of the camera animation in milliseconds. If `null` or `0`, the camera moves instantly. | + +--- + +## `ArcGISMapViewStateInterface` Interface + +An interface that defines the public contract for an ArcGIS map view state object. + +### Description +This interface extends the generic `MapViewStateInterface` and specifies `ArcGISDesignTypeInterface` as the design type. It ensures a consistent API for map state management across different map providers within the MapConductor ecosystem. + +--- + +## `ArcGISMapViewSaver` Class + +A `Saver` implementation for `ArcGISMapViewState`. + +### Description +This class is used internally by `rememberArcGISMapViewState` to save and restore the map's state, allowing it to survive configuration changes and process death. Developers typically do not need to interact with this class directly, as its functionality is automatically handled by the `rememberArcGISMapViewState` composable. \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/ArcGISTypeAlias.kt.md b/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/ArcGISTypeAlias.kt.md new file mode 100644 index 00000000..b2cdf4d9 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/ArcGISTypeAlias.kt.md @@ -0,0 +1,65 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +# Type Aliases + +This document outlines the core type aliases used within the `com.mapconductor.arcgis` package. These aliases provide a consistent and abstracted interface over the underlying ArcGIS Maps SDK for Kotlin types, simplifying development and improving code readability. They map common map concepts (like markers, polylines, etc.) to their specific ArcGIS implementation classes. + +--- + +### `ArcGISActualMarker` + +**Signature** +```kotlin +typealias ArcGISActualMarker = com.arcgismaps.mapping.view.Graphic +``` + +**Description** +An alias for the `com.arcgismaps.mapping.view.Graphic` class. This type is used to represent a marker or a point graphic on the map. Using this alias helps abstract the specific ArcGIS implementation for markers, providing a more consistent API within the MapConductor SDK. + +--- + +### `ArcGISActualCircle` + +**Signature** +```kotlin +typealias ArcGISActualCircle = com.arcgismaps.mapping.view.Graphic +``` + +**Description** +An alias for the `com.arcgismaps.mapping.view.Graphic` class. This type represents a circle shape drawn on the map. In the ArcGIS SDK, shapes like circles are typically implemented as `Graphic` objects with a specific geometry. This alias provides a standardized "Circle" type within the MapConductor SDK. + +--- + +### `ArcGISActualPolyline` + +**Signature** +```kotlin +typealias ArcGISActualPolyline = com.arcgismaps.mapping.view.Graphic +``` + +**Description** +An alias for the `com.arcgismaps.mapping.view.Graphic` class. It is used to represent a polyline, which consists of a series of connected line segments, on the map. + +--- + +### `ArcGISActualPolygon` + +**Signature** +```kotlin +typealias ArcGISActualPolygon = com.arcgismaps.mapping.view.Graphic +``` + +**Description** +An alias for the `com.arcgismaps.mapping.view.Graphic` class. It is used to represent a polygon, which is a closed shape defined by a series of connected vertices, on the map. + +--- + +### `ArcGISActualGroundImage` + +**Signature** +```kotlin +typealias ArcGISActualGroundImage = com.mapconductor.arcgis.groundimage.ArcGISGroundImageHandle +``` + +**Description** +An alias for the `com.mapconductor.arcgis.groundimage.ArcGISGroundImageHandle` class. This type represents a handle or a wrapper object for managing a ground image overlay on the map. It abstracts the specific implementation details required to display and manage ground imagery within the ArcGIS environment. \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/ArcGISViewControllerStore.kt.md b/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/ArcGISViewControllerStore.kt.md new file mode 100644 index 00000000..f042deba --- /dev/null +++ b/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/ArcGISViewControllerStore.kt.md @@ -0,0 +1,91 @@ +Of course! Here is the high-quality SDK documentation for the provided Kotlin code snippet. + +--- + +# ArcGIS Module Utilities + +This document provides details on the utility components available in the `com.mapconductor.arcgis` package, including a specialized type alias and a singleton controller store. + +## ArcGISMapViewHolderInterface + +A type alias for a `MapViewHolderInterface` that is pre-configured for use with ArcGIS 3D scenes. + +### Signature + +```kotlin +typealias ArcGISMapViewHolderInterface = MapViewHolderInterface +``` + +### Description + +`ArcGISMapViewHolderInterface` simplifies the implementation of map view holders for ArcGIS 3D maps. It is an alias for the generic `MapViewHolderInterface`, with its type parameters specialized to `WrapSceneView` and `SceneView`. + +This allows developers to work with a consistent interface for managing views that contain an ArcGIS `SceneView`, abstracting away the more complex generic signature. + +- **`WrapSceneView`**: The MapConductor wrapper view that contains the ArcGIS scene. +- **`SceneView`**: The native ArcGIS SDK view for displaying 3D scenes. + +### Example + +By using this type alias, you can create cleaner and more readable view holder classes. + +```kotlin +import com.mapconductor.arcgis.ArcGISMapViewHolderInterface + +// Implement the interface using the type alias +class MyCustomSceneViewHolder : ArcGISMapViewHolderInterface { + + override fun onMapReady(mapView: WrapSceneView, nativeMapView: SceneView) { + // The map and native scene view are ready to be used. + println("SceneView is ready for interaction.") + nativeMapView.arcGISScene?.let { scene -> + println("Scene loaded: ${scene.basemap?.name}") + } + } + + override fun onMapDestroyed() { + // Clean up resources when the view is destroyed. + println("SceneView is being destroyed.") + } +} +``` + +## ArcGISViewControllerStore + +A singleton object that provides global access to an `ArcGISMapViewController` instance. + +### Signature + +```kotlin +object ArcGISViewControllerStore : StaticHolder() +``` + +### Description + +`ArcGISViewControllerStore` acts as a centralized, static repository for a single `ArcGISMapViewController` instance. It inherits from `StaticHolder`, which manages the storage and lifecycle of the object. + +The primary purpose of this store is to provide a convenient, application-wide access point to the main map view controller, eliminating the need to pass the controller instance through multiple layers of the application. + +### Example + +You can set and retrieve the `ArcGISMapViewController` instance from anywhere in your application. + +```kotlin +import com.mapconductor.arcgis.ArcGISViewControllerStore +import com.mapconductor.arcgis.map.ArcGISMapViewController + +// --- In your Activity or Fragment where the controller is created --- + +// Create and store the controller instance +val mapViewController = ArcGISMapViewController(context) +ArcGISViewControllerStore.instance = mapViewController + + +// --- In another part of your application --- + +// Retrieve the globally accessible controller instance +val controller = ArcGISViewControllerStore.instance + +// Use the controller to interact with the map +controller?.panToLocation(someLocation) +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/GeoPoint.kt.md b/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/GeoPoint.kt.md new file mode 100644 index 00000000..e5808533 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/GeoPoint.kt.md @@ -0,0 +1,175 @@ +Of course! Here is the high-quality SDK documentation for the provided Kotlin code snippet, formatted in Markdown. + +# ArcGIS Geometry Converters + +This document provides detailed information on a set of Kotlin extension and factory functions designed for seamless conversion between a custom `GeoPoint` type and the ArcGIS Maps SDK for Kotlin `Point` type. + +--- + +## `GeoPoint.toPoint()` + +Converts a `GeoPoint` instance into an ArcGIS `Point` object. + +### Signature +```kotlin +fun GeoPoint.toPoint(spatialReference: SpatialReference? = null): Point +``` + +### Description +This extension function transforms a `GeoPoint` into an ArcGIS `Point`. It maps the `longitude`, `latitude`, and `altitude` properties of the `GeoPoint` to the `x`, `y`, and `z` coordinates of the resulting `Point`, respectively. You can optionally assign a `SpatialReference` to the new `Point`. + +### Parameters +| Parameter | Type | Description | +|--------------------|-----------------------|--------------------------------------------------------------| +| `spatialReference` | `SpatialReference?` | (Optional) The spatial reference to assign to the created `Point`. Defaults to `null`. | + +### Returns +| Type | Description | +|---------|-------------------------------------------| +| `Point` | An ArcGIS `Point` object. | + +### Example +```kotlin +import com.arcgismaps.geometry.Point +import com.arcgismaps.geometry.SpatialReference +import com.mapconductor.core.features.GeoPoint + +// Assuming GeoPoint is defined as: +// data class GeoPoint(val latitude: Double, val longitude: Double, val altitude: Double) + +val geoPoint = GeoPoint(latitude = 34.0522, longitude = -118.2437, altitude = 71.0) +val wgs84 = SpatialReference.wgs84() + +// Convert GeoPoint to an ArcGIS Point with a spatial reference +val arcgisPoint = geoPoint.toPoint(spatialReference = wgs84) + +println("ArcGIS Point: x=${arcgisPoint.x}, y=${arcgisPoint.y}, z=${arcgisPoint.z}") +// Expected output: ArcGIS Point: x=-118.2437, y=34.0522, z=71.0 +``` + +--- + +## `GeoPoint.Companion.fromLatLongAltitude()` + +A factory method to create a `GeoPoint` instance. + +### Signature +```kotlin +fun GeoPoint.Companion.fromLatLongAltitude( + latitude: Double, + longitude: Double, + altitude: Double +): GeoPoint +``` + +### Description +Creates a new `GeoPoint` instance from the provided latitude, longitude, and altitude values. This is a convenience factory method that follows the standard `latitude, longitude` ordering. + +### Parameters +| Parameter | Type | Description | +|-------------|----------|----------------------------| +| `latitude` | `Double` | The latitude coordinate. | +| `longitude` | `Double` | The longitude coordinate. | +| `altitude` | `Double` | The altitude value. | + +### Returns +| Type | Description | +|------------|-------------------------------------------| +| `GeoPoint` | A new `GeoPoint` instance. | + +### Example +```kotlin +val geoPoint = GeoPoint.fromLatLongAltitude( + latitude = 40.7128, + longitude = -74.0060, + altitude = 10.0 +) + +println("Created GeoPoint: lat=${geoPoint.latitude}, lon=${geoPoint.longitude}, alt=${geoPoint.altitude}") +// Expected output: Created GeoPoint: lat=40.7128, lon=-74.0060, alt=10.0 +``` + +--- + +## `GeoPoint.Companion.fromLongLat()` + +A factory method to create a `GeoPoint` instance. + +### Signature +```kotlin +fun GeoPoint.Companion.fromLongLat( + longitude: Double, + latitude: Double, + altitude: Double +): GeoPoint +``` + +### Description +Creates a new `GeoPoint` instance from the provided longitude, latitude, and altitude values. This is a convenience factory method for cases where coordinates are provided in `longitude, latitude` order. + +### Parameters +| Parameter | Type | Description | +|-------------|----------|----------------------------| +| `longitude` | `Double` | The longitude coordinate. | +| `latitude` | `Double` | The latitude coordinate. | +| `altitude` | `Double` | The altitude value. | + +### Returns +| Type | Description | +|------------|-------------------------------------------| +| `GeoPoint` | A new `GeoPoint` instance. | + +### Example +```kotlin +val geoPoint = GeoPoint.fromLongLat( + longitude = -74.0060, + latitude = 40.7128, + altitude = 10.0 +) + +println("Created GeoPoint: lat=${geoPoint.latitude}, lon=${geoPoint.longitude}, alt=${geoPoint.altitude}") +// Expected output: Created GeoPoint: lat=40.7128, lon=-74.0060, alt=10.0 +``` + +--- + +## `Point.toGeoPoint()` + +Converts an ArcGIS `Point` object into a `GeoPoint`. + +### Signature +```kotlin +fun Point.toGeoPoint(): GeoPoint +``` + +### Description +This extension function transforms an ArcGIS `Point` into a `GeoPoint`. It directly maps the `x`, `y`, and `z` coordinates of the `Point` to the `longitude`, `latitude`, and `altitude` properties of the `GeoPoint`. If the `Point`'s `z` value is `null` (i.e., it is a 2D point), the resulting `GeoPoint`'s altitude will be set to `0.0`. + +**Note:** This function performs a direct coordinate mapping (`x` -> `longitude`, `y` -> `latitude`). It does **not** perform any spatial reference projection. For accurate conversion, ensure the source `Point` is in a geographic coordinate system (like WGS84) where the x-coordinate represents longitude and the y-coordinate represents latitude. + +### Returns +| Type | Description | +|------------|-------------------------------------------| +| `GeoPoint` | A `GeoPoint` instance. | + +### Example +```kotlin +import com.arcgismaps.geometry.Point +import com.arcgismaps.geometry.SpatialReference + +// Create a 3D ArcGIS Point in WGS84 +val arcgisPoint3D = Point(x = -122.4194, y = 37.7749, z = 52.0, spatialReference = SpatialReference.wgs84()) + +// Convert the ArcGIS Point to a GeoPoint +val geoPoint3D = arcgisPoint3D.toGeoPoint() + +println("Converted 3D GeoPoint: lat=${geoPoint3D.latitude}, lon=${geoPoint3D.longitude}, alt=${geoPoint3D.altitude}") +// Expected output: Converted 3D GeoPoint: lat=37.7749, lon=-122.4194, alt=52.0 + +// Create a 2D ArcGIS Point (null z-value) +val arcgisPoint2D = Point(x = -0.1278, y = 51.5074, spatialReference = SpatialReference.wgs84()) +val geoPoint2D = arcgisPoint2D.toGeoPoint() + +println("Converted 2D GeoPoint: lat=${geoPoint2D.latitude}, lon=${geoPoint2D.longitude}, alt=${geoPoint2D.altitude}") +// Expected output: Converted 2D GeoPoint: lat=51.5074, lon=-0.1278, alt=0.0 +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/MapCameraPosition.kt.md b/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/MapCameraPosition.kt.md new file mode 100644 index 00000000..90220418 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/MapCameraPosition.kt.md @@ -0,0 +1,421 @@ +This document provides detailed documentation for the ArcGIS MapConductor SDK extensions, which facilitate interoperability between MapConductor's core mapping abstractions and the ArcGIS Maps SDK for Kotlin. + +## Constants + +### `ZOOM0_ALTITUDE` + +A constant representing the camera altitude in meters corresponding to zoom level 0. + +#### Signature + +```kotlin +const val ZOOM0_ALTITUDE = 5_000_000.0 +``` + +#### Description + +This value serves as the baseline altitude for a fully zoomed-out map view (zoom level 0). It is a fundamental constant used in the conversion calculations between zoom levels and camera altitudes, ensuring a consistent scale reference. + +--- + +## Extension Functions + +### `MapCameraPosition.getAltitudeForArcGIS()` + +Calculates the appropriate camera altitude for the ArcGIS map view based on the `MapCameraPosition` properties. + +#### Signature + +```kotlin +fun MapCameraPosition.getAltitudeForArcGIS(): Double +``` + +#### Description + +This function converts a platform-agnostic `MapCameraPosition` into an ArcGIS-specific altitude. It takes into account the zoom level, latitude, and tilt to produce an altitude value that results in a visually similar map scale on ArcGIS as on other platforms like Google Maps, compensating for differences in camera Field of View (FOV). + +#### Returns + +| Type | Description | +| :------- | :---------------------------------------- | +| `Double` | The calculated camera altitude in meters. | + +#### Example + +```kotlin +// Assuming a MapCameraPosition instance +val mapPosition = MapCameraPosition( + position = GeoPoint.fromLatLong(latitude = 34.0522, longitude = -118.2437), + zoom = 12.0, + tilt = 45.0, + bearing = 0.0 +) + +// Calculate the specific altitude needed for an ArcGIS camera +val arcgisAltitude = mapPosition.getAltitudeForArcGIS() +println("Calculated ArcGIS Altitude: $arcgisAltitude meters") +// Example Output: Calculated ArcGIS Altitude: 1907.5 meters (value is illustrative) +``` + +--- + +### `MapCameraPosition.toCamera()` + +Converts a `MapCameraPosition` object to an ArcGIS `Camera` object. + +#### Signature + +```kotlin +fun MapCameraPosition.toCamera(): Camera +``` + +#### Description + +This function provides a direct conversion from the abstract `MapCameraPosition` to a concrete `com.arcgismaps.mapping.view.Camera` instance. It translates properties like zoom, bearing, and tilt into the corresponding ArcGIS camera parameters (altitude, heading, and pitch), enabling the ArcGIS map to reflect the state of the `MapCameraPosition`. + +#### Returns + +| Type | Description an ArcGIS `Camera` object configured to match the `MapCameraPosition`. | +| :--------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | + +#### Example + +```kotlin +val mapPosition = MapCameraPosition( + position = GeoPoint.fromLatLong(latitude = 48.8584, longitude = 2.2945), // Paris + zoom = 15.0, + tilt = 30.0, + bearing = 90.0 +) + +// Convert to an ArcGIS Camera +val arcgisCamera = mapPosition.toCamera() + +println("ArcGIS Camera:") +println(" - Location: ${arcgisCamera.location.y}, ${arcgisCamera.location.x}") +println(" - Altitude: ${arcgisCamera.location.z}") +println(" - Heading: ${arcgisCamera.heading}") +println(" - Pitch: ${arcgisCamera.pitch}") +``` + +--- + +### `Camera.getZoomLevel()` + +Calculates the "Google-like" zoom level from the properties of an ArcGIS `Camera`. + +#### Signature + +```kotlin +fun Camera.getZoomLevel(): Double +``` + +#### Description + +This function performs the reverse operation of `getAltitudeForArcGIS`. It takes an ArcGIS `Camera`'s altitude, latitude, and pitch to calculate an equivalent zoom level that is consistent with the scaling used by providers like Google Maps. This is useful for synchronizing state or displaying a zoom level value to the user. + +#### Returns + +| Type | Description - | +| `Double` | The calculated zoom level, typically ranging from 0 to 22. - | + +#### Example + +```kotlin +// An existing ArcGIS Camera instance +val camera = Camera( + latitude = 34.0522, + longitude = -118.2437, + altitude = 2000.0, + heading = 0.0, + pitch = 45.0, + roll = 0.0 +) + +// Calculate the equivalent zoom level +val zoomLevel = camera.getZoomLevel() +println("Calculated Zoom Level: $zoomLevel") +// Example Output: Calculated Zoom Level: 11.85 (value is illustrative) +``` + +--- + +### `Camera.withZoomLevel()` + +Creates a new `Camera` instance by applying a new zoom level to an existing camera. + +#### Signature + +```kotlin +fun Camera.withZoomLevel(zoomLevel: Double): Camera +``` + +#### Description + +This function provides an immutable way to change a camera's zoom level. It calculates the new altitude required to match the specified `zoomLevel` while preserving the camera's current latitude, longitude, heading, pitch, and roll. It returns a new `Camera` object with the updated altitude. + +#### Parameters + +| Parameter | Type | Description - | +| :------------ | :------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `zoomLevel` | `Double` | The target zoom level to apply to the new camera. - | + +#### Returns + +| Type | Description - | +| :--------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `Camera` | A new `Camera` instance with the same properties as the original, except for the altitude, which is adjusted for the new zoom level. | + +#### Example + +```kotlin +val initialCamera = Camera( + latitude = 34.0522, + longitude = -118.2437, + altitude = 8000.0, // Corresponds to some initial zoom level + heading = 0.0, + pitch = 0.0, + roll = 0.0 +) + +// Create a new camera with a closer zoom level +val zoomedCamera = initialCamera.withZoomLevel(14.0) + +println("Initial Altitude: ${initialCamera.location.z}") +println("New Altitude for Zoom 14: ${zoomedCamera.location.z}") +// Example Output: +// Initial Altitude: 8000.0 +// New Altitude for Zoom 14: 953.7... (value is illustrative) +``` + +--- + +### `Camera.toMapCameraPosition()` + +Converts an ArcGIS `Camera` object to a `MapCameraPosition`. + +#### Signature + +```kotlin +fun Camera.toMapCameraPosition(): MapCameraPosition +``` + +#### Description + +This function translates the properties of a platform-specific ArcGIS `Camera` into the platform-agnostic `MapCameraPosition` format. It extracts the location, calculates the zoom level from the altitude, and normalizes the heading to a bearing value (0-360 degrees). This allows the state of the ArcGIS map to be represented in a common format that can be used across different parts of an application. + +#### Returns + +| Type | Description - | +| :-------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `MapCameraPosition` | A new `MapCameraPosition` instance representing the state of the `Camera`. | + +#### Example + +```kotlin +val camera = Camera( + latitude = 40.7128, + longitude = -74.0060, // New York City + altitude = 5000.0, + heading = 45.0, + pitch = 60.0, + roll = 0.0 +) + +// Convert the ArcGIS camera to a MapCameraPosition +val mapPosition = camera.toMapCameraPosition() + +println("MapCameraPosition:") +println(" - Position: ${mapPosition.position.latitude}, ${mapPosition.position.longitude}") +println(" - Zoom: ${mapPosition.zoom}") +println(" - Bearing: ${mapPosition.bearing}") +println(" - Tilt: ${mapPosition.tilt}") +``` + +--- + +## Utility Functions + +### `calculateDestinationPoint()` + +Calculates the geographic coordinates of a destination point given a starting point, bearing, and distance. + +#### Signature + +```kotlin +fun calculateDestinationPoint( + lat: Double, + lon: Double, + bearing: Double, + distance: Double +): GeoPoint +``` + +#### Description + +This utility function determines a new geographical point by projecting a specified distance along a given bearing (azimuth) from a starting latitude and longitude. It is based on a spherical model of the Earth and is useful for geospatial calculations, such as determining a camera's location relative to its target. + +#### Parameters + +| Parameter | Type | Description - | +| `lat` | `Double` | The latitude of the starting point, in degrees. - | +| `lon` | `Double` | The longitude of the starting point, in degrees. - | +| `bearing` | `Double` | The initial bearing (or azimuth) in degrees, where 0 is North, 90 is East, 180 is South, and 270 is West. | +| `distance` | `Double` | The distance to travel from the starting point, in meters. - | + +#### Returns + +| Type | Description - | +| :---------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `GeoPoint` | A new `GeoPoint` object representing the calculated destination coordinates (latitude and longitude). | + +#### Example + +```kotlin +// Starting point: Los Angeles, CA +val startLat = 34.0522 +val startLon = -118.2437 + +// Travel 100km to the East (bearing = 90 degrees) +val bearing = 90.0 +val distance = 100_000.0 // 100 km in meters + +val destination = calculateDestinationPoint(startLat, startLon, bearing, distance) + +println("Starting Point: $startLat, $startLon") +println("Destination Point: ${destination.latitude}, ${destination.longitude}") +// Example Output: +// Starting Point: 34.0522, -118.2437 +// Destination Point: 34.05218, -117.1443... +``` + +--- + +### `calculateCameraForOrbitParameters()` + +Calculates the camera position and orientation for an `OrbitLocationCameraController`. + +#### Signature + +```kotlin +fun calculateCameraForOrbitParameters( + targetPoint: com.arcgismaps.geometry.Point, + distance: Double, + cameraHeadingOffset: Double, + cameraPitchOffset: Double +): Camera +``` + +#### Description + +This function is designed to compute the properties of a `Camera` that orbits a central `targetPoint`. It positions the camera at a specified `distance` from the target, with a given heading and pitch. This is essential for implementing "orbit" or "look-at" camera behaviors where the camera always faces a point of interest. + +#### Parameters + +| Parameter | Type | Description - | +| :-------------------- | :---------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `targetPoint` | `com.arcgismaps.geometry.Point` | The central point that the camera should orbit or look at. - | +| `distance` | `Double` | The straight-line distance in meters from the camera to the `targetPoint`. - | +| `cameraHeadingOffset` | `Double` | The heading of the camera relative to the target, in degrees. A value of 0 places the camera due North of the target. | +| `cameraPitchOffset` | `Double` | The pitch (tilt) of the camera in degrees. 0 is a top-down view, and 90 is a view from the horizon. - | + +#### Returns + +| Type | Description - | +| :--------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `Camera` | A new `Camera` object positioned and oriented according to the specified orbit parameters. | + +#### Example + +```kotlin +// Import Point for clarity +import com.arcgismaps.geometry.Point + +// Define the target for the camera to look at (e.g., Eiffel Tower) +val target = Point(x = 2.2945, y = 48.8584) + +// Define orbit parameters +val orbitDistance = 1500.0 // 1.5 km away +val headingOffset = 225.0 // From the southwest +val pitchOffset = 75.0 // Steep angle + +// Calculate the camera position +val camera = calculateCameraForOrbitParameters( + targetPoint = target, + distance = orbitDistance, + cameraHeadingOffset = headingOffset, + cameraPitchOffset = pitchOffset +) + +println("Calculated Camera for Orbit:") +println(" - Location: ${camera.location.y}, ${camera.location.x}") +println(" - Altitude: ${camera.location.z}") +println(" - Heading: ${camera.heading}") +println(" - Pitch: ${camera.pitch}") +``` + +--- + +## Companion Object Functions + +### `MapCameraPosition.Companion.from()` + +A factory function that creates a `MapCameraPosition` from any object that implements the `MapCameraPositionInterface`. + +#### Signature + +```kotlin +fun MapCameraPosition.Companion.from( + position: MapCameraPositionInterface +): MapCameraPosition +``` + +#### Description + +This function acts as a safe and convenient constructor. If the provided `position` is already a `MapCameraPosition`, it is returned directly. Otherwise, it constructs a new `MapCameraPosition` by extracting the necessary properties from the `MapCameraPositionInterface` object, ensuring a consistent object type for further processing. + +#### Parameters + +| Parameter | Type | Description - | +| :--------- | :--------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `position` | `MapCameraPositionInterface` | An object conforming to the `MapCameraPositionInterface`. | + +#### Returns + +| Type | Description - | +| :-------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `MapCameraPosition` | A `MapCameraPosition` instance. If the input was already a `MapCameraPosition`, it is returned directly; otherwise, a new instance is created. | + +#### Example + +```kotlin +// 1. Example with a custom implementation of MapCameraPositionInterface +// A mock interface implementation for demonstration +val customPosition = object : MapCameraPositionInterface { + override val position = GeoPoint.fromLatLong(latitude = 51.5074, longitude = -0.1278) // London + override val zoom = 10.0 + override val bearing = 0.0 + override val tilt = 0.0 + override val paddings = MapPaddings.Zeros + override val visibleRegion = null +} + +val mapPositionFromInterface = MapCameraPosition.from(customPosition) +println("Created from interface: ${mapPositionFromInterface.zoom}") + +// 2. Example with an existing MapCameraPosition instance +val existingMapPosition = MapCameraPosition( + position = GeoPoint.fromLatLong(latitude = 35.6895, longitude = 139.6917), // Tokyo + zoom = 12.0 +) + +val mapPositionFromSelf = MapCameraPosition.from(existingMapPosition) +println("Passed through: ${mapPositionFromSelf.zoom}") +// Check if the instance is the same +println("Instances are identical: ${existingMapPosition === mapPositionFromSelf}") + +// Example Output: +// Created from interface: 10.0 +// Passed through: 12.0 +// Instances are identical: true +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/ZoomAltitudeConverterDeprecated.kt.md b/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/ZoomAltitudeConverterDeprecated.kt.md new file mode 100644 index 00000000..fefb3c25 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/ZoomAltitudeConverterDeprecated.kt.md @@ -0,0 +1,297 @@ +Of course! Here is the high-quality SDK documentation for the provided Kotlin code snippet. + +--- + +# ZoomAltitudeConverterDeprecated + +## Description + +The `ZoomAltitudeConverterDeprecated` class provides methods to convert between map zoom levels and camera altitude. This implementation is designed to be compatible with ArcGIS-style map rendering, where altitude is affected by latitude and camera tilt. + +**Note:** As the name suggests, this is a deprecated implementation. It is provided for backward compatibility. For new development, consider using the latest recommended converter if available. + +This class extends `AbstractZoomAltitudeConverter`. + +## Constructor + +### `ZoomAltitudeConverterDeprecated(zoom0Altitude: Double)` + +Initializes a new instance of the `ZoomAltitudeConverterDeprecated`. + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `zoom0Altitude` | `Double` | The camera altitude in meters that corresponds to zoom level 0 at the equator (0° latitude) with no camera tilt. Defaults to `DEFAULT_ZOOM0_ALTITUDE`. It is highly recommended to calculate this value using the `computeZoom0DistanceForView` utility function. | + +## Companion Object Methods + +These are static-like utility methods available on the `ZoomAltitudeConverterDeprecated` class. + +### `verticalFovFromHorizontal()` + +Calculates the vertical field of view (FOV) based on a given horizontal FOV and the viewport's aspect ratio. + +#### Signature + +```kotlin +fun verticalFovFromHorizontal( + horizontalFovDeg: Double, + aspectRatio: Double +): Double +``` + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `horizontalFovDeg` | `Double` | The horizontal field of view in degrees. | +| `aspectRatio` | `Double` | The aspect ratio of the viewport (width / height). | + +#### Returns + +`Double` - The calculated vertical field of view in degrees. + +### `computeZoom0DistanceForView()` + +A utility function to compute the ideal `zoom0Altitude` value for initializing the converter. This value is based on the viewport's dimensions and the camera's vertical field of view. + +#### Signature + +```kotlin +fun computeZoom0DistanceForView( + viewportHeightPx: Int, + verticalFovDeg: Double, + initialMetersPerPixel: Double = WEB_MERCATOR_INITIAL_MPP_256 +): Double +``` + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `viewportHeightPx` | `Int` | The height of the map viewport in pixels. | +| `verticalFovDeg` | `Double` | The vertical field of view of the camera in degrees. | +| `initialMetersPerPixel` | `Double` | *(Optional)* The map resolution (meters per pixel) at zoom level 0 for a standard 256x256 tile. Defaults to the Web Mercator value `156_543.033_928`. | + +#### Returns + +`Double` - The calculated distance for zoom level 0, which should be used as the `zoom0Altitude` parameter when constructing the converter. + +## Methods + +### `zoomLevelToAltitude()` + +Converts a map zoom level to the corresponding camera altitude, taking into account the current latitude and camera tilt. + +#### Signature + +```kotlin +override fun zoomLevelToAltitude( + zoomLevel: Double, + latitude: Double, + tilt: Double +): Double +``` + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `zoomLevel` | `Double` | The map zoom level to convert. | +| `latitude` | `Double` | The current latitude of the map center in degrees. | +| `tilt` | `Double` | The current camera tilt (pitch) in degrees, where 0 is looking straight down. | + +#### Returns + +`Double` - The calculated camera altitude in meters. + +### `altitudeToZoomLevel()` + +Converts a camera altitude to the corresponding map zoom level, taking into account the current latitude and camera tilt. + +#### Signature + +```kotlin +override fun altitudeToZoomLevel( + altitude: Double, + latitude: Double, + tilt: Double +): Double +``` + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `altitude` | `Double` | The camera altitude in meters. | +| `latitude` | `Double` | The current latitude of the map center in degrees. | +| `tilt` | `Double` | The current camera tilt (pitch) in degrees, where 0 is looking straight down. | + +#### Returns + +`Double` - The calculated map zoom level. + +## Legacy Methods + +These methods are provided for backward compatibility and may not account for all view parameters like camera tilt. + +### `zoomLevelToAltitude(zoomLevel: Double)` + +Converts a zoom level to altitude, assuming 0° latitude and 0° tilt. + +#### Signature + +```kotlin +fun zoomLevelToAltitude(zoomLevel: Double): Double +``` + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `zoomLevel` | `Double` | The map zoom level to convert. | + +#### Returns + +`Double` - The calculated camera altitude in meters. + +### `zoomLevelToAltitude(zoomLevel: Double, latitude: Double)` + +Converts a zoom level to altitude, accounting for latitude but assuming 0° tilt. + +#### Signature + +```kotlin +fun zoomLevelToAltitude( + zoomLevel: Double, + latitude: Double +): Double +``` + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `zoomLevel` | `Double` | The map zoom level to convert. | +| `latitude` | `Double` | The current latitude of the map center in degrees. | + +#### Returns + +`Double` - The calculated camera altitude in meters. + +### `zoomLevelToDistance(zoomLevel: Double, latitude: Double)` + +Converts a zoom level to the camera's distance from the ground along the view center axis, accounting for latitude. Note that "distance" differs from "altitude" when the camera is tilted. + +#### Signature + +```kotlin +fun zoomLevelToDistance( + zoomLevel: Double, + latitude: Double +): Double +``` + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `zoomLevel` | `Double` | The map zoom level to convert. | +| `latitude` | `Double` | The current latitude of the map center in degrees. | + +#### Returns + +`Double` - The calculated camera distance in meters. + +### `altitudeToZoomLevel(altitude: Double)` + +Converts an altitude to a zoom level, assuming 0° latitude and 0° tilt. + +#### Signature + +```kotlin +fun altitudeToZoomLevel(altitude: Double): Double +``` + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `altitude` | `Double` | The camera altitude in meters. | + +#### Returns + +`Double` - The calculated map zoom level. + +### `altitudeToZoomLevel(altitude: Double, latitude: Double)` + +Converts an altitude to a zoom level, accounting for latitude but assuming 0° tilt. + +#### Signature + +```kotlin +fun altitudeToZoomLevel( + altitude: Double, + latitude: Double +): Double +``` + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `altitude` | `Double` | The camera altitude in meters. | +| `latitude` | `Double` | The current latitude of the map center in degrees. | + +#### Returns + +`Double` - The calculated map zoom level. + +## Example + +Here is an example of how to initialize and use the `ZoomAltitudeConverterDeprecated`. + +```kotlin +import com.mapconductor.arcgis.ZoomAltitudeConverterDeprecated + +fun main() { + // Viewport and camera parameters + val viewportHeight = 1080 // pixels + val verticalFov = 60.0 // degrees + val currentLatitude = 40.7128 // New York City + val currentTilt = 45.0 // degrees + + // 1. Calculate the required zoom0Altitude for our specific view setup + val zoom0Altitude = ZoomAltitudeConverterDeprecated.computeZoom0DistanceForView( + viewportHeightPx = viewportHeight, + verticalFovDeg = verticalFov + ) + println("Calculated zoom0Altitude: $zoom0Altitude") + + // 2. Create an instance of the converter with the calculated value + val converter = ZoomAltitudeConverterDeprecated(zoom0Altitude) + + // 3. Convert a zoom level to altitude + val zoomLevel = 15.0 + val altitude = converter.zoomLevelToAltitude( + zoomLevel = zoomLevel, + latitude = currentLatitude, + tilt = currentTilt + ) + println("For zoom level $zoomLevel at latitude $currentLatitude and tilt $currentTilt, altitude is $altitude meters.") + + // 4. Convert an altitude back to a zoom level + val newZoomLevel = converter.altitudeToZoomLevel( + altitude = altitude, + latitude = currentLatitude, + tilt = currentTilt + ) + println("For altitude $altitude at latitude $currentLatitude and tilt $currentTilt, zoom level is $newZoomLevel.") +} + +// Example Output: +// Calculated zoom0Altitude: 4.818844949903386E7 +// For zoom level 15.0 at latitude 40.7128 and tilt 45.0, altitude is 1048.099119129931 meters. +// For altitude 1048.099119129931 at latitude 40.7128 and tilt 45.0, zoom level is 15.0. +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/authentication/ArcGISAuthenticationHelpers.kt.md b/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/authentication/ArcGISAuthenticationHelpers.kt.md new file mode 100644 index 00000000..96347f9a --- /dev/null +++ b/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/authentication/ArcGISAuthenticationHelpers.kt.md @@ -0,0 +1,221 @@ +Of course! Here is the high-quality SDK documentation for the provided Kotlin code snippets. + +*** + +# ArcGIS Authentication SDK + +This document provides detailed documentation for a set of helper functions designed to simplify OAuth authentication with the ArcGIS Maps SDK for Kotlin. + +## `arcGISOAuthApplicationInitialize` + +Initializes ArcGIS authentication using OAuth Application Credentials. This method performs app-level authentication using a Client ID and Client Secret, which does not require a user login dialog. It is ideal for accessing content that is shared with the organization or publicly. The function is asynchronous and waits for the authentication process to complete. + +### Signature + +```kotlin +suspend fun arcGISOAuthApplicationInitialize( + portalUrl: String, + clientId: String, + clientSecret: String, + authenticatorState: AuthenticatorState? = null, +): Boolean +``` + +### Description + +This function configures the `ArcGISEnvironment` to use OAuth with application credentials. It first clears any existing API key, then creates and adds an `OAuthApplicationCredential` to the global credential store. To verify the authentication, it attempts to load a `Portal` object. + +An optional `authenticatorState` can be provided to serve as a fallback mechanism, allowing the application to challenge the user for credentials if the application-level authentication is insufficient for accessing a specific resource. + +### Parameters + +| Parameter | Type | Description | +| ------------------ | -------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | +| `portalUrl` | `String` | The URL of the ArcGIS Portal (e.g., `"https://www.arcgis.com/"`). | +| `clientId` | `String` | The OAuth Client ID for your application. | +| `clientSecret` | `String` | The OAuth Client Secret for your application. | +| `authenticatorState` | `AuthenticatorState?`| (Optional) An `AuthenticatorState` instance to handle user authentication challenges as a fallback. Defaults to `null`. | + +### Returns + +`Boolean` - Returns `true` if the application credential is created and the portal is loaded successfully, indicating successful authentication. Returns `false` otherwise. + +### Example + +```kotlin +import androidx.lifecycle.viewModelScope +import kotlinx.coroutines.launch + +// In your ViewModel or another coroutine scope +fun initializeArcGIS() { + viewModelScope.launch { + val portalUrl = "https://www.arcgis.com" + val clientId = "YOUR_CLIENT_ID" + val clientSecret = "YOUR_CLIENT_SECRET" + + val isAuthenticated = arcGISOAuthApplicationInitialize( + portalUrl = portalUrl, + clientId = clientId, + clientSecret = clientSecret + ) + + if (isAuthenticated) { + println("ArcGIS Application Authentication Successful!") + // Now you can load secured layers or portal items + } else { + println("ArcGIS Application Authentication Failed.") + } + } +} +``` + +--- + +## `arcGISOAuthUserInitialize` + +Initializes ArcGIS authentication using OAuth User Credentials. This method is used when a user needs to log in to access secured content. It leverages an `AuthenticatorState` to manage and display the login user interface. The function is asynchronous and waits for the user to complete the login process. + +### Signature + +```kotlin +suspend fun arcGISOAuthUserInitialize( + authenticatorState: AuthenticatorState, + portalUrl: String, + clientId: String, + redirectUrl: String, +): Boolean +``` + +### Description + +This function configures the `ArcGISEnvironment` for a user-centric OAuth flow. It sets up an `OAuthUserConfiguration` on the provided `authenticatorState` and registers the state as the global challenge handler. When a secured resource is requested, the `authenticatorState` will trigger the display of a login dialog. The function confirms a successful login by attempting to load a `Portal` object, which initiates the authentication challenge. + +### Parameters + +| Parameter | Type | Description | +| ------------------ | -------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | +| `authenticatorState` | `AuthenticatorState` | The state manager that handles the authentication UI and flow. This is typically observed in your UI to show a login prompt. | +| `portalUrl` | `String` | The URL of the ArcGIS Portal (e.g., `"https://www.arcgis.com/"`). | +| `clientId` | `String` | The OAuth Client ID for your application. | +| `redirectUrl` | `String` | The OAuth Redirect URI configured for your application (e.g., `"urn:ietf:wg:oauth:2.0:oob"`). | + +### Returns + +`Boolean` - Returns `true` if the user successfully authenticates and the portal is loaded. Returns `false` if the authentication fails or is canceled. + +### Example + +```kotlin +import androidx.lifecycle.viewModelScope +import com.arcgismaps.toolkit.authentication.AuthenticatorState +import kotlinx.coroutines.launch + +// In your ViewModel +val authenticatorState = AuthenticatorState() + +fun initializeArcGISForUser() { + viewModelScope.launch { + val portalUrl = "https://www.arcgis.com" + val clientId = "YOUR_CLIENT_ID" + val redirectUrl = "urn:ietf:wg:oauth:2.0:oob" + + val isAuthenticated = arcGISOAuthUserInitialize( + authenticatorState = authenticatorState, + portalUrl = portalUrl, + clientId = clientId, + redirectUrl = redirectUrl + ) + + if (isAuthenticated) { + println("ArcGIS User Authentication Successful!") + // The user is logged in. + } else { + println("ArcGIS User Authentication Failed.") + } + } +} + +// In your Composable UI, you would use the Authenticator composable +// Authenticator(authenticatorState = viewModel.authenticatorState) { +// // Your main app content, e.g., a MapView +// } +``` + +--- + +## `ArcGISOAuthHybridInitialize` + +Initializes a hybrid authentication flow that first attempts app-level authentication and automatically falls back to user-level authentication if required. This function is asynchronous and waits for an authentication method to succeed. + +### Signature + +```kotlin +suspend fun ArcGISOAuthHybridInitialize( + authenticatorState: AuthenticatorState, + portalUrl: String, + redirectUrl: String, + clientId: String, + clientSecret: String? = null, +): Boolean +``` + +### Description + +This function provides a seamless authentication experience by first trying to authenticate silently with application credentials (if a `clientSecret` is provided). If this method is insufficient to access a resource, or if no `clientSecret` is given, it automatically falls back to the user authentication flow, prompting the user to log in via the `authenticatorState`. + +The function configures both authentication methods and then verifies success by attempting to load a `Portal` object. + +### Parameters + +| Parameter | Type | Description | +| ------------------ | -------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | +| `authenticatorState` | `AuthenticatorState` | The state manager that handles the authentication UI for the user login fallback. | +| `portalUrl` | `String` | The URL of the ArcGIS Portal. | +| `redirectUrl` | `String` | The OAuth Redirect URI for the user authentication fallback. | +| `clientId` | `String` | The OAuth Client ID for your application. | +| `clientSecret` | `String?` | (Optional) The OAuth Client Secret. If provided, application-level authentication is attempted first. Defaults to `null`. | + +### Returns + +`Boolean` - Returns `true` if authentication succeeds (either via application or user credentials). Returns `false` if all authentication attempts fail. + +### Example + +```kotlin +import androidx.lifecycle.viewModelScope +import com.arcgismaps.toolkit.authentication.AuthenticatorState +import kotlinx.coroutines.launch + +// In your ViewModel +val authenticatorState = AuthenticatorState() + +fun initializeHybridAuth() { + viewModelScope.launch { + val portalUrl = "https://www.arcgis.com" + val clientId = "YOUR_CLIENT_ID" + val clientSecret = "YOUR_CLIENT_SECRET" // Can be null + val redirectUrl = "urn:ietf:wg:oauth:2.0:oob" + + val isAuthenticated = ArcGISOAuthHybridInitialize( + authenticatorState = authenticatorState, + portalUrl = portalUrl, + redirectUrl = redirectUrl, + clientId = clientId, + clientSecret = clientSecret + ) + + if (isAuthenticated) { + println("ArcGIS Hybrid Authentication Successful!") + // App is authenticated via app or user credentials. + } else { + println("ArcGIS Hybrid Authentication Failed.") + } + } +} + +// In your Composable UI, you would still need the Authenticator +// to handle the potential user login fallback. +// Authenticator(authenticatorState = viewModel.authenticatorState) { +// // Your main app content +// } +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/circle/ArcGISCircleOverlayController.kt.md b/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/circle/ArcGISCircleOverlayController.kt.md new file mode 100644 index 00000000..79f4b900 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/circle/ArcGISCircleOverlayController.kt.md @@ -0,0 +1,69 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +--- + +### `ArcGISCircleOverlayController` + +The `ArcGISCircleOverlayController` is a specialized controller responsible for managing and rendering circle overlays on an ArcGIS map. It integrates the generic circle management logic from `CircleController` with the specific rendering capabilities of `ArcGISCircleOverlayRenderer`. + +This class serves as the primary entry point for developers to programmatically add, remove, and update circles on the map. + +### Signature + +```kotlin +class ArcGISCircleOverlayController( + circleManager: CircleManagerInterface = CircleManager(), + override val renderer: ArcGISCircleOverlayRenderer +) : CircleController(circleManager, renderer) +``` + +### Parameters + +This class is initialized with the following parameters: + +| Parameter | Type | Description | Default | +|---|---|---|---| +| `circleManager` | `CircleManagerInterface` | The manager responsible for handling the lifecycle and state of all circle objects. It tracks additions, removals, and updates. | `CircleManager()` | +| `renderer` | `ArcGISCircleOverlayRenderer` | The renderer responsible for drawing the circles on the ArcGIS map view. It translates circle data into visual representations. | - | + +### Example + +The following example demonstrates how to initialize the `ArcGISCircleOverlayController` and use it to add a circle to an ArcGIS map. + +```kotlin +import com.esri.arcgisruntime.geometry.Point +import com.esri.arcgisruntime.geometry.SpatialReferences +import com.esri.arcgisruntime.mapping.view.MapView +import android.graphics.Color + +// Assume 'mapView' is an instance of an ArcGIS MapView from your layout. +// Assume 'ArcGISCircleOverlayRenderer' and 'CircleOptions' are available in your project. + +// 1. Initialize the renderer and add its overlay to the map. +val circleRenderer = ArcGISCircleOverlayRenderer() +mapView.graphicsOverlays.add(circleRenderer.overlay) + +// 2. Initialize the controller with the specific renderer. +val circleController = ArcGISCircleOverlayController(renderer = circleRenderer) + +// 3. Define the properties for a new circle. +// Note: The 'CircleOptions' class is used here for illustrative purposes. +val circleOptions = CircleOptions( + center = Point(-122.4194, 37.7749, SpatialReferences.getWgs84()), // San Francisco + radius = 1500.0, // in meters + fillColor = Color.argb(100, 255, 102, 0), // Semi-transparent orange + strokeWidth = 2.5f, + strokeColor = Color.rgb(204, 82, 0) // Solid orange +) + +// 4. Add the circle to the map using the controller. +// The controller delegates this to the circleManager and renderer. +val myCircle = circleController.add(circleOptions) + +// You can later update the circle's properties +myCircle.radius = 2000.0 +myCircle.fillColor = Color.argb(100, 0, 0, 255) // Change to semi-transparent blue + +// To remove the circle from the map +circleController.remove(myCircle) +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/circle/ArcGISCircleOverlayRenderer.kt.md b/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/circle/ArcGISCircleOverlayRenderer.kt.md new file mode 100644 index 00000000..6b6fd7f7 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/circle/ArcGISCircleOverlayRenderer.kt.md @@ -0,0 +1,166 @@ +# ArcGISCircleOverlayRenderer + +The `ArcGISCircleOverlayRenderer` class is a concrete implementation for rendering and managing circle graphics on an ArcGIS map. It extends `AbstractCircleOverlayRenderer` and is responsible for translating abstract `CircleState` data into tangible ArcGIS `Graphic` objects on a specified `GraphicsOverlay`. + +This renderer handles the entire lifecycle of a circle graphic, including its creation, removal, and property updates. It supports both geodesic (more accurate on a globe) and planar (simpler, faster) circles. + +**Note:** The type `ArcGISActualCircle` is an alias for the ArcGIS `Graphic` class (`com.arcgismaps.mapping.view.Graphic`). + +## Constructor + +### Signature + +```kotlin +class ArcGISCircleOverlayRenderer( + val circleLayer: GraphicsOverlay, + override val holder: ArcGISMapViewHolder, + override val coroutine: CoroutineScope = CoroutineScope(Dispatchers.Main), +) : AbstractCircleOverlayRenderer() +``` + +### Description + +Initializes a new instance of the `ArcGISCircleOverlayRenderer`. + +### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `circleLayer` | `GraphicsOverlay` | The ArcGIS graphics layer where the circle graphics will be added and managed. | +| `holder` | `ArcGISMapViewHolder` | A view holder that provides access to the map view and its context, such as the spatial reference. | +| `coroutine` | `CoroutineScope` | The coroutine scope used for executing asynchronous operations. Defaults to `CoroutineScope(Dispatchers.Main)`. | + +--- + +## Methods + +### createCircle + +#### Signature + +```kotlin +override suspend fun createCircle(state: CircleState): ArcGISActualCircle? +``` + +#### Description + +Creates a new circle graphic based on the provided `CircleState` and adds it to the `GraphicsOverlay`. This function can create either a geodesic or a planar circle. The geometry is generated using `GeometryEngine.bufferGeodeticOrNull` for geodesic circles or `GeometryEngine.bufferOrNull` for planar circles. + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `state` | `CircleState` | The state object defining the circle's properties, including its center, radius, colors, and whether it is geodesic. | + +#### Returns + +| Type | Description | +| :--- | :--- | +| `ArcGISActualCircle?` | The newly created ArcGIS `Graphic` object representing the circle, or `null` if the creation process fails. | + +--- + +### removeCircle + +#### Signature + +```kotlin +override suspend fun removeCircle(entity: CircleEntityInterface) +``` + +#### Description + +Removes a specified circle graphic from the `GraphicsOverlay`. This operation is performed asynchronously on the provided coroutine scope. + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `entity` | `CircleEntityInterface` | The circle entity that contains the `Graphic` to be removed from the map. | + +#### Returns + +This method does not return a value. + +--- + +### updateCircleProperties + +#### Signature + +```kotlin +override suspend fun updateCircleProperties( + circle: ArcGISActualCircle, + current: CircleEntityInterface, + prev: CircleEntityInterface, +): ArcGISActualCircle? +``` + +#### Description + +Updates the properties of an existing circle graphic. This method efficiently checks for changes between the `current` and `prev` states using their fingerprints. It updates the geometry (center, radius, geodesic type) or the symbol properties (fill color, stroke color, stroke width) only if they have changed, minimizing unnecessary rendering operations. + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `circle` | `ArcGISActualCircle` | The actual ArcGIS `Graphic` object that needs to be updated. | +| `current` | `CircleEntityInterface` | The entity representing the new, updated state of the circle. | +| `prev` | `CircleEntityInterface` | The entity representing the previous state of the circle, used for comparison to detect changes. | + +#### Returns + +| Type | Description | +| :--- | :--- | +| `ArcGISActualCircle?` | The updated `Graphic` object, or `null` if the update fails. | + +--- + +## Example + +Here is an example of how to instantiate `ArcGISCircleOverlayRenderer` and use it to draw a circle on the map. + +```kotlin +import com.arcgismaps.mapping.view.GraphicsOverlay +import com.mapconductor.arcgis.circle.ArcGISCircleOverlayRenderer +import com.mapconductor.core.circle.CircleState +import com.mapconductor.core.features.GeoPoint +import com.mapconductor.core.units.Distance +import com.mapconductor.core.units.DistanceUnit +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch + +// Assuming you have an ArcGISMapViewHolder instance named 'mapViewHolder' +val mapViewHolder: ArcGISMapViewHolder = /* ... initialize ... */ + +// 1. Create a GraphicsOverlay to hold the circles +val circleGraphicsOverlay = GraphicsOverlay() +mapViewHolder.mapView.graphicsOverlays.add(circleGraphicsOverlay) + +// 2. Instantiate the renderer +val circleRenderer = ArcGISCircleOverlayRenderer( + circleLayer = circleGraphicsOverlay, + holder = mapViewHolder +) + +// 3. Define the state for a new circle +val circleState = CircleState( + center = GeoPoint(latitude = 34.0522, longitude = -118.2437), // Los Angeles + radiusMeters = 1000.0, + strokeWidth = Distance(2f, DistanceUnit.PX), + strokeColor = Color.BLUE, + fillColor = Color.argb(128, 0, 0, 255), // Semi-transparent blue + geodesic = true +) + +// 4. Create the circle on the map within a coroutine +CoroutineScope(Dispatchers.Main).launch { + val circleGraphic = circleRenderer.createCircle(circleState) + if (circleGraphic != null) { + println("Circle created successfully!") + } else { + println("Failed to create circle.") + } +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/groundimage/ArcGISGroundImageController.kt.md b/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/groundimage/ArcGISGroundImageController.kt.md new file mode 100644 index 00000000..5680a442 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/groundimage/ArcGISGroundImageController.kt.md @@ -0,0 +1,71 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +--- + +### `ArcGISGroundImageController` + +#### Signature + +```kotlin +class ArcGISGroundImageController( + renderer: ArcGISGroundImageOverlayRenderer +) : GroundImageController +``` + +#### Description + +The `ArcGISGroundImageController` is the primary class for managing and displaying ground-level imagery on an ArcGIS map. It serves as the main entry point for developers to add, remove, and control ground images within an ArcGIS environment. + +This controller acts as a bridge, connecting the generic `GroundImageManager` (which handles the core logic and state of all ground images) with the platform-specific `ArcGISGroundImageOverlayRenderer` (which handles the actual drawing of images on the map). It is specialized to work with `ArcGISGroundImageHandle` objects, which represent individual ground images added to the map. + +#### Parameters + +This section describes the parameters for the `ArcGISGroundImageController` constructor. + +| Parameter | Type | Description | +| :--------- | :--------------------------------- | :------------------------------------------------------------------------------------------------------ | +| `renderer` | `ArcGISGroundImageOverlayRenderer` | The renderer instance responsible for drawing the ground image overlays onto the ArcGIS map. This object handles all platform-specific rendering tasks. | + +#### Example + +The following example demonstrates how to initialize the `ArcGISGroundImageController` and use it to add a ground image to an ArcGIS map. + +```kotlin +import android.graphics.Bitmap +import com.mapconductor.arcgis.groundimage.ArcGISGroundImageController +import com.mapconductor.arcgis.groundimage.ArcGISGroundImageHandle +import com.mapconductor.arcgis.groundimage.ArcGISGroundImageOverlayRenderer +import com.mapconductor.core.groundimage.GroundImage +import com.mapconductor.core.types.LatLng + +// 1. Assume you have an ArcGISMapView instance from your layout +val mapView: ArcGISMapView = findViewById(R.id.mapView) + +// 2. Create the specific renderer for the ArcGIS map +val groundImageRenderer = ArcGISGroundImageOverlayRenderer(mapView) + +// 3. Instantiate the controller with the renderer +val groundImageController = ArcGISGroundImageController(renderer = groundImageRenderer) + +// 4. Define the properties for the ground image you want to display +// (This assumes you have a Bitmap loaded and a LatLng class for coordinates) +val imageBitmap: Bitmap = // ... load your image bitmap from assets or network +val imageLocation = LatLng(34.0522, -118.2437) // Example: Los Angeles, CA +val imageWidthInMeters = 75.0 +val imageBearing = 45.0 // Orientation in degrees from North + +val myGroundImage = GroundImage( + bitmap = imageBitmap, + position = imageLocation, + width = imageWidthInMeters, + bearing = imageBearing +) + +// 5. Add the ground image to the map via the controller. +// The controller returns a handle that can be used to manage this specific image later. +val imageHandle: ArcGISGroundImageHandle = groundImageController.add(myGroundImage) + +// You can now use the handle to update or remove the image +// For example, to remove the image from the map: +// groundImageController.remove(imageHandle) +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/groundimage/ArcGISGroundImageHandle.kt.md b/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/groundimage/ArcGISGroundImageHandle.kt.md new file mode 100644 index 00000000..c99cdfef --- /dev/null +++ b/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/groundimage/ArcGISGroundImageHandle.kt.md @@ -0,0 +1,55 @@ +# ArcGISGroundImageHandle + +## Signature + +```kotlin +data class ArcGISGroundImageHandle( + val routeId: String, + val generation: Long, + val cacheKey: String, + val tileProvider: GroundImageTileProvider, + val layer: WebTiledLayer, +) +``` + +## Description + +The `ArcGISGroundImageHandle` is a data class that serves as a container for all the components and metadata associated with a specific ground imagery layer displayed on an ArcGIS map. + +This handle conveniently groups the identifying information (such as `routeId` and `generation`), the caching key, the custom tile provider, and the actual ArcGIS `WebTiledLayer` object. It is typically returned when a ground imagery layer is created and added to the map, allowing for easy management and reference to the layer and its related resources. + +## Parameters + +| Parameter | Type | Description | +| :------------- | :------------------------ | :--------------------------------------------------------------------------------------------------------- | +| `routeId` | `String` | The unique identifier for the route to which the ground imagery belongs. | +| `generation` | `Long` | A version number for the imagery data. This can be used to manage updates and invalidate caches. | +| `cacheKey` | `String` | A unique key used for caching the ground imagery tiles, ensuring efficient retrieval. | +| `tileProvider` | `GroundImageTileProvider` | The custom tile provider instance responsible for fetching and supplying the ground image tiles. | +| `layer` | `WebTiledLayer` | The ArcGIS `WebTiledLayer` instance that is added to the map to render the ground imagery. | + +## Example + +The following example demonstrates how to create an instance of `ArcGISGroundImageHandle`. + +```kotlin +import com.arcgismaps.mapping.layers.WebTiledLayer +import com.mapconductor.core.groundimage.GroundImageTileProvider + +// Assume these are initialized elsewhere in your application +val myTileProvider = GroundImageTileProvider(/*...*/) +val myWebTiledLayer = WebTiledLayer(/*...*/) + +// Create an instance of ArcGISGroundImageHandle +val groundImageHandle = ArcGISGroundImageHandle( + routeId = "route-12345", + generation = 1678886400L, + cacheKey = "route-12345-g1678886400", + tileProvider = myTileProvider, + layer = myWebTiledLayer +) + +// You can now use the handle to access properties of the ground image layer +println("Managing layer for route: ${groundImageHandle.routeId}") +// Example: map.operationalLayers.add(groundImageHandle.layer) +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/groundimage/ArcGISGroundImageOverlayRenderer.kt.md b/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/groundimage/ArcGISGroundImageOverlayRenderer.kt.md new file mode 100644 index 00000000..8c53cf4b --- /dev/null +++ b/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/groundimage/ArcGISGroundImageOverlayRenderer.kt.md @@ -0,0 +1,211 @@ +# ArcGISGroundImageOverlayRenderer + +Manages the lifecycle of ground image overlays on an ArcGIS map. This class is a concrete implementation of `AbstractGroundImageOverlayRenderer` tailored for the ArcGIS Maps SDK for Kotlin. It translates abstract `GroundImageState` objects into tangible `WebTiledLayer` instances on the map, handling their creation, updates, and removal by interacting with a `LocalTileServer`. + +## Constructor + +### Signature + +```kotlin +class ArcGISGroundImageOverlayRenderer( + override val holder: ArcGISMapViewHolder, + private val tileServer: LocalTileServer, + override val coroutine: CoroutineScope = CoroutineScope(Dispatchers.Default), +) : AbstractGroundImageOverlayRenderer() +``` + +### Description + +Initializes a new instance of the `ArcGISGroundImageOverlayRenderer`. + +### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `holder` | `ArcGISMapViewHolder` | The view holder that manages the ArcGIS map instance where overlays will be rendered. | +| `tileServer` | `LocalTileServer` | The local tile server responsible for generating and serving image tiles for the ground overlays. | +| `coroutine` | `CoroutineScope` | The coroutine scope for executing asynchronous operations. Defaults to `CoroutineScope(Dispatchers.Default)`. | + +--- + +## Methods + +### createGroundImage + +#### Signature + +```kotlin +override suspend fun createGroundImage(state: GroundImageState): ArcGISGroundImageHandle? +``` + +#### Description + +Asynchronously creates and displays a new ground image overlay on the map based on the provided state. + +This function generates tiles for the image, registers them with the `LocalTileServer`, creates an ArcGIS `WebTiledLayer`, and adds it to the map's operational layers. Opacity is handled by the native `WebTiledLayer.opacity` property for efficient updates, rather than being baked into the image tiles. + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `state` | `GroundImageState` | An object defining the properties of the ground image, such as the image bitmap, geographical bounds, tile size, and opacity. | + +#### Returns + +| Type | Description | +| :--- | :--- | +| `ArcGISGroundImageHandle?` | A handle to the newly created ground image overlay, or `null` if creation fails (e.g., the map scene is not available or the layer fails to load). | + +--- + +### updateGroundImageProperties + +#### Signature + +```kotlin +override suspend fun updateGroundImageProperties( + groundImage: ArcGISGroundImageHandle, + current: GroundImageEntityInterface, + prev: GroundImageEntityInterface, +): ArcGISGroundImageHandle? +``` + +#### Description + +Asynchronously updates an existing ground image overlay by comparing the `current` and `prev` states. + +The method performs efficient updates: +- If only the opacity has changed, it updates the `opacity` property on the existing `WebTiledLayer` directly. +- If the image, bounds, or tile size have changed, it regenerates the underlying tile set, creates a new `WebTiledLayer` to ensure the cache is invalidated, and seamlessly swaps it with the old layer on the map. + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `groundImage` | `ArcGISGroundImageHandle` | The handle for the existing ground image overlay to be updated. | +| `current` | `GroundImageEntityInterface` | An entity wrapper for the new, desired state of the ground image. | +| `prev` | `GroundImageEntityInterface` | An entity wrapper for the previous state, used to determine which properties have changed. | + +#### Returns + +| Type | Description | +| :--- | :--- | +| `ArcGISGroundImageHandle?` | A handle representing the updated overlay. This may be a new instance if the underlying tiles were regenerated. Returns `null` if the update fails. | + +--- + +### removeGroundImage + +#### Signature + +```kotlin +override suspend fun removeGroundImage(entity: GroundImageEntityInterface) +``` + +#### Description + +Asynchronously removes a ground image overlay from the map. It removes the corresponding `WebTiledLayer` from the scene's operational layers and unregisters its tile provider from the `LocalTileServer` to free up resources. + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `entity` | `GroundImageEntityInterface` | The entity containing the handle of the ground image to remove. | + +#### Returns + +This function does not return a value. + +--- + +## Example + +The following example demonstrates the full lifecycle (create, update, remove) of a ground image using `ArcGISGroundImageOverlayRenderer`. + +```kotlin +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch + +// Assume these instances are already configured and available in your application +val mapViewHolder: ArcGISMapViewHolder = getMapViewHolder() +val localTileServer: LocalTileServer = getLocalTileServer() +val coroutineScope = CoroutineScope(Dispatchers.Main) + +// A placeholder entity class for the example +data class GroundImageEntity( + override val state: GroundImageState, + override var groundImage: T +) : GroundImageEntityInterface { + override val fingerPrint: GroundImageFingerprint + get() = state.fingerPrint +} + +suspend fun manageGroundImageLifecycle() { + // 1. Initialize the renderer + val groundImageRenderer = ArcGISGroundImageOverlayRenderer( + holder = mapViewHolder, + tileServer = localTileServer, + coroutine = coroutineScope + ) + + // 2. Define the state for a new ground image + val imageBitmap: Bitmap = loadBitmapFromAssets("campus_map.png") + val imageBounds = Envelope( + -122.4194, 37.7749, -122.4150, 37.7780, + SpatialReference.wgs84() + ) + val initialState = GroundImageState( + id = "campus-overlay", + image = imageBitmap, + bounds = imageBounds, + opacity = 0.9f, + tileSize = 256 + ) + + // 3. Create the ground image on the map + var groundImageHandle = groundImageRenderer.createGroundImage(initialState) + if (groundImageHandle != null) { + println("Ground image created successfully.") + } else { + println("Failed to create ground image.") + return + } + + // --- Later, to update the image's opacity --- + + // 4. Define the updated state + val updatedState = initialState.copy(opacity = 0.5f) + + // Create entities required by the update method + val currentEntity = GroundImageEntity(state = updatedState, groundImage = groundImageHandle) + val prevEntity = GroundImageEntity(state = initialState, groundImage = groundImageHandle) + + // 5. Update the ground image + val updatedHandle = groundImageRenderer.updateGroundImageProperties( + groundImage = groundImageHandle, + current = currentEntity, + prev = prevEntity + ) + if (updatedHandle != null) { + groundImageHandle = updatedHandle // Store the new handle + println("Ground image updated successfully.") + } else { + println("Failed to update ground image.") + } + + // --- Later, to remove the image --- + + // 6. Create an entity to identify the image to be removed + val entityToRemove = GroundImageEntity(state = updatedState, groundImage = groundImageHandle) + + // 7. Remove the ground image + groundImageRenderer.removeGroundImage(entityToRemove) + println("Ground image removed.") +} + +// Execute the function within a coroutine +coroutineScope.launch { + manageGroundImageLifecycle() +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/marker/ArcGISMarkerController.kt.md b/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/marker/ArcGISMarkerController.kt.md new file mode 100644 index 00000000..e3c203ed --- /dev/null +++ b/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/marker/ArcGISMarkerController.kt.md @@ -0,0 +1,164 @@ +Excellent. Here is the high-quality SDK documentation for the provided code snippet. + +*** + +# ArcGISMarkerController + +The `ArcGISMarkerController` class is responsible for managing the lifecycle of markers on an ArcGIS map. It extends `AbstractMarkerController` and provides a concrete implementation for the ArcGIS Maps SDK. + +This controller handles adding, updating, removing, and finding markers. A key feature is its performance optimization strategy: it can dynamically switch between rendering markers as individual `Graphic` objects and rendering them as a single, server-generated tiled raster layer. This tiling mechanism is automatically engaged when the number of markers exceeds a configurable threshold, significantly improving performance for large datasets. + +## Creating an Instance + +The `ArcGISMarkerController` is instantiated using the `create` factory method. + +### create + +```kotlin +fun create( + holder: ArcGISMapViewHolder, + markerTiling: MarkerTilingOptions = MarkerTilingOptions.Default, +): ArcGISMarkerController +``` + +#### Description + +A static factory method that creates and initializes a new instance of `ArcGISMarkerController`. It sets up the required `GraphicsOverlay` for individual markers and configures the controller with the specified tiling options. + +#### Parameters + +| Name | Type | Description | +| :--- | :--- | :--- | +| `holder` | `ArcGISMapViewHolder` | The view holder that manages the ArcGIS map view instance. | +| `markerTiling` | `MarkerTilingOptions` | (Optional) Configuration for the marker tiling feature. If omitted, `MarkerTilingOptions.Default` is used. | + +#### Returns + +| Type | Description | +| :--- | :--- | +| `ArcGISMarkerController` | A new, fully configured instance of the controller. | + +#### Example + +```kotlin +// Define tiling options (optional) +val tilingOptions = MarkerTilingOptions( + enabled = true, + minMarkerCount = 500, + // ... other options +) + +// Create the controller +val markerController = ArcGISMarkerController.create( + holder = myArcGISMapViewHolder, + markerTiling = tilingOptions +) +``` + +## Methods + +### setRasterLayerCallback + +```kotlin +fun setRasterLayerCallback(callback: MarkerTileRasterLayerCallback?) +``` + +#### Description + +Sets a callback that is invoked when the raster layer for tiled markers is created, updated, or removed. When the controller switches to tiling mode, it generates a `RasterLayerState`. The host application must use this callback to receive the state and add the corresponding `RasterLayer` to the map. When the tile layer is no longer needed, the callback is invoked with `null`. + +#### Parameters + +| Name | Type | Description | +| :--- | :--- | :--- | +| `callback` | `MarkerTileRasterLayerCallback?` | The callback to handle raster layer state changes. Pass `null` to remove a previously set callback. | + +### find + +```kotlin +override fun find(position: GeoPointInterface): MarkerEntityInterface? +``` + +#### Description + +Finds the nearest marker entity to a given geographic position based on screen space. This method calculates if the touch point falls within the tappable area of a marker's icon, which includes the icon's dimensions, anchor point, and a predefined tolerance. + +#### Parameters + +| Name | Type | Description | +| :--- | :--- | :--- | +| `position` | `GeoPointInterface` | The geographic coordinate (latitude/longitude) to search near. | + +#### Returns + +| Type | Description | +| :--- | :--- | +| `MarkerEntityInterface?` | The found marker entity, or `null` if no marker is within the tap tolerance of the given position. | + +### add + +```kotlin +override suspend fun add(data: List) +``` + +#### Description + +Adds a list of new markers to the map. The controller's ingestion engine processes the markers. If marker tiling is enabled and the total number of markers meets the `minMarkerCount`, eligible markers (i.e., those that are not draggable and have no animations) are rendered as part of a tiled raster layer. All other markers are rendered as individual `Graphic` objects. + +#### Parameters + +| Name | Type | Description | +| :--- | :--- | :--- | +| `data` | `List` | A list of `MarkerState` objects, each representing a marker to be added. | + +### update + +```kotlin +override suspend fun update(state: MarkerState) +``` + +#### Description + +Updates an existing marker with a new state. The method checks if the marker's state has actually changed before applying updates. It intelligently handles transitions between rendering modes. For example, if a marker that was part of the tile layer is updated to be draggable, it will be removed from the tiles and rendered as an individual `Graphic`. + +#### Parameters + +| Name | Type | Description | +| :--- | :--- | :--- | +| `state` | `MarkerState` | The new state for the marker. The `id` within the state must match an existing marker. | + +### clear + +```kotlin +override suspend fun clear() +``` + +#### Description + +Removes all markers from the map. This operation clears both individual `Graphic` markers and removes the tiled raster layer if it is active. + +### onCameraChanged + +```kotlin +override suspend fun onCameraChanged(mapCameraPosition: MapCameraPosition) +``` + +#### Description + +A lifecycle method that should be called when the map's camera position (e.g., zoom, pan) changes. The controller uses this information to track the current zoom level, which is essential for managing the tiled marker layer. This method is typically invoked by the map framework, not directly by the developer. + +#### Parameters + +| Name | Type | Description | +| :--- | :--- | :--- | +| `mapCameraPosition` | `MapCameraPosition` | The new camera position of the map. | + +### destroy + +```kotlin +override fun destroy() +``` + +#### Description + +Cleans up and releases all resources used by the controller. This includes unregistering the tile server, clearing raster layer callbacks, and removing any remaining markers. This method must be called when the controller is no longer needed to prevent memory leaks and other resource issues. \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/marker/ArcGISMarkerEventController.kt.md b/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/marker/ArcGISMarkerEventController.kt.md new file mode 100644 index 00000000..e3709805 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/marker/ArcGISMarkerEventController.kt.md @@ -0,0 +1,331 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet, formatted in Markdown. + +# ArcGIS Marker Event Controllers + +This document provides detailed documentation for the marker event controller interfaces and their implementations within the ArcGIS module. These controllers are responsible for handling and dispatching user interactions with markers on the map, such as clicks and drags. + +**Note:** The components documented here are marked as `internal`, suggesting they are intended for use within the library's ecosystem. + +## `ArcGISMarkerEventControllerInterface` + +An interface that defines the contract for handling marker-related events in an ArcGIS map context. It extends the generic `MarkerEventControllerInterface` and adds functionalities specific to the ArcGIS environment. + +### Methods + +#### **find** + +Finds a marker entity at a specific geographic position. + +**Signature** +```kotlin +fun find(position: GeoPoint): MarkerEntityInterface? +``` + +**Description** +Searches for a marker entity that corresponds to the given geographic coordinates. + +**Parameters** +| Parameter | Type | Description | +|------------|------------|--------------------------------------------------| +| `position` | `GeoPoint` | The geographic coordinate to search for a marker. | + +**Returns** +| Type | Description | +|------------------------------------------------|--------------------------------------------------------------------------| +| `MarkerEntityInterface?` | The found marker entity, or `null` if no marker exists at the given position. | + +--- + +#### **getSelectedState** + +Retrieves the state of the currently selected marker. + +**Signature** +```kotlin +fun getSelectedState(): MarkerState? +``` + +**Description** +Returns the `MarkerState` of the marker that is currently selected, typically during a drag operation. + +**Returns** +| Type | Description | +|---------------|------------------------------------------------------------------| +| `MarkerState?`| The state of the selected marker, or `null` if no marker is selected. | + +--- + +#### **startDrag** + +Initiates a drag operation for a specific marker entity. + +**Signature** +```kotlin +fun startDrag(entity: MarkerEntityInterface) +``` + +**Description** +Marks the beginning of a drag sequence for the given marker entity. This method sets the provided entity as the currently selected marker for subsequent drag updates. + +**Parameters** +| Parameter | Type | Description | +|-----------|----------------------------------------------|--------------------------------------| +| `entity` | `MarkerEntityInterface` | The marker entity to start dragging. | + +--- + +#### **updateDrag** + +Updates the position of the marker currently being dragged. + +**Signature** +```kotlin +fun updateDrag(point: Point, position: GeoPoint) +``` + +**Description** +This method is called continuously during a drag gesture to update the visual position of the marker on the map and its underlying geographic coordinates. + +**Parameters** +| Parameter | Type | Description | +|------------|------------|--------------------------------------------------------------------------| +| `point` | `Point` | The new map coordinate (`com.arcgismaps.geometry.Point`) of the marker. | +| `position` | `GeoPoint` | The new geographic coordinate (`com.mapconductor.core.features.GeoPoint`). | + +--- + +#### **endDrag** + +Finalizes the drag operation for the currently selected marker. + +**Signature** +```kotlin +fun endDrag(point: Point, position: GeoPoint) +``` + +**Description** +This method is called at the end of a drag gesture. It sets the marker's final position and clears the "selected" state, concluding the drag operation. + +**Parameters** +| Parameter | Type | Description | +|------------|------------|--------------------------------------------------------------------------| +| `point` | `Point` | The final map coordinate (`com.arcgismaps.geometry.Point`) of the marker. | +| `position` | `GeoPoint` | The final geographic coordinate (`com.mapconductor.core.features.GeoPoint`). | + +--- + +#### **dispatchClick** + +Dispatches a click event to the registered listener. + +**Signature** +```kotlin +fun dispatchClick(state: MarkerState) +``` + +**Description** +Invokes the `onMarkerClick` handler if a click listener has been set. + +**Parameters** +| Parameter | Type | Description | +|-----------|---------------|--------------------------------------| +| `state` | `MarkerState` | The state of the marker that was clicked. | + +--- + +#### **dispatchDragStart** + +Dispatches a drag start event to the registered listener. + +**Signature** +```kotlin +fun dispatchDragStart(state: MarkerState) +``` + +**Description** +Invokes the `onMarkerDragStart` handler if a drag start listener has been set. + +**Parameters** +| Parameter | Type | Description | +|-----------|---------------|---------------------------------------------------| +| `state` | `MarkerState` | The state of the marker at the start of the drag. | + +--- + +#### **dispatchDrag** + +Dispatches a drag event to the registered listener. + +**Signature** +```kotlin +fun dispatchDrag(state: MarkerState) +``` + +**Description** +Invokes the `onMarkerDrag` handler if a drag listener has been set. This is typically called repeatedly as the marker is being moved. + +**Parameters** +| Parameter | Type | Description | +|-----------|---------------|---------------------------------------------| +| `state` | `MarkerState` | The current state of the marker being dragged. | + +--- + +#### **dispatchDragEnd** + +Dispatches a drag end event to the registered listener. + +**Signature** +```kotlin +fun dispatchDragEnd(state: MarkerState) +``` + +**Description** +Invokes the `onMarkerDragEnd` handler if a drag end listener has been set. + +**Parameters** +| Parameter | Type | Description | +|-----------|---------------|-----------------------------------------------| +| `state` | `MarkerState` | The final state of the marker after dragging. | + +--- + +#### **setClickListener** + +Sets the event handler for marker click events. + +**Signature** +```kotlin +fun setClickListener(listener: OnMarkerEventHandler?) +``` + +**Parameters** +| Parameter | Type | Description | +|------------|------------------------|--------------------------------------------------------------------------| +| `listener` | `OnMarkerEventHandler?`| The listener to handle click events, or `null` to remove the current listener. | + +--- + +#### **setDragStartListener** + +Sets the event handler for marker drag start events. + +**Signature** +```kotlin +fun setDragStartListener(listener: OnMarkerEventHandler?) +``` + +**Parameters** +| Parameter | Type | Description | +|------------|------------------------|--------------------------------------------------------------------------------| +| `listener` | `OnMarkerEventHandler?`| The listener to handle drag start events, or `null` to remove the current listener. | + +--- + +#### **setDragListener** + +Sets the event handler for marker drag events. + +**Signature** +```kotlin +fun setDragListener(listener: OnMarkerEventHandler?) +``` + +**Parameters** +| Parameter | Type | Description | +|------------|------------------------|--------------------------------------------------------------------------| +| `listener` | `OnMarkerEventHandler?`| The listener to handle drag events, or `null` to remove the current listener. | + +--- + +#### **setDragEndListener** + +Sets the event handler for marker drag end events. + +**Signature** +```kotlin +fun setDragEndListener(listener: OnMarkerEventHandler?) +``` + +**Parameters** +| Parameter | Type | Description | +|------------|------------------------|------------------------------------------------------------------------------| +| `listener` | `OnMarkerEventHandler?`| The listener to handle drag end events, or `null` to remove the current listener. | + +--- + +#### **setAnimateStartListener** + +Sets the event handler for marker animation start events. + +**Signature** +```kotlin +fun setAnimateStartListener(listener: OnMarkerEventHandler?) +``` + +**Parameters** +| Parameter | Type | Description | +|------------|------------------------|------------------------------------------------------------------------------------| +| `listener` | `OnMarkerEventHandler?`| The listener to handle animation start events, or `null` to remove the current listener. | + +--- + +#### **setAnimateEndListener** + +Sets the event handler for marker animation end events. + +**Signature** +```kotlin +fun setAnimateEndListener(listener: OnMarkerEventHandler?) +``` + +**Parameters** +| Parameter | Type | Description | +|------------|------------------------|----------------------------------------------------------------------------------| +| `listener` | `OnMarkerEventHandler?`| The listener to handle animation end events, or `null` to remove the current listener. | + +
+ +## `DefaultArcGISMarkerEventController` + +A default implementation of `ArcGISMarkerEventControllerInterface`. It acts as a proxy, delegating event handling and state management logic to an underlying `ArcGISMarkerController`. This class bridges raw map interactions with the core marker management system. + +### Constructor + +**Signature** +```kotlin +DefaultArcGISMarkerEventController( + controller: ArcGISMarkerController +) +``` + +**Description** +Creates an instance of `DefaultArcGISMarkerEventController`. + +**Parameters** +| Parameter | Type | Description | +|--------------|------------------------|--------------------------------------------------------------------------| +| `controller` | `ArcGISMarkerController` | The main marker controller that manages marker state and dispatches events. | + +
+ +## `StrategyArcGISMarkerEventController` + +An alternative implementation of `ArcGISMarkerEventControllerInterface` designed to work with a `StrategyMarkerController`. Unlike `DefaultArcGISMarkerEventController`, this class manages the state of the selected marker internally during a drag operation. It delegates event dispatching and marker lookups to the provided `StrategyMarkerController`. + +### Constructor + +**Signature** +```kotlin +StrategyArcGISMarkerEventController( + controller: StrategyMarkerController +) +``` + +**Description** +Creates an instance of `StrategyArcGISMarkerEventController`. + +**Parameters** +| Parameter | Type | Description | +|--------------|-----------------------------------------------|----------------------------------------------------------------------------------| +| `controller` | `StrategyMarkerController`| The strategy-based marker controller for event dispatching and marker lookups. | \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/marker/ArcGISMarkerRenderer.kt.md b/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/marker/ArcGISMarkerRenderer.kt.md new file mode 100644 index 00000000..6fe2d7aa --- /dev/null +++ b/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/marker/ArcGISMarkerRenderer.kt.md @@ -0,0 +1,182 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +--- + +# ArcGISMarkerRenderer + +## Signature + +```kotlin +class ArcGISMarkerRenderer( + val markerLayer: GraphicsOverlay, + holder: ArcGISMapViewHolder, + coroutine: CoroutineScope = CoroutineScope(Dispatchers.Main), +) : AbstractMarkerOverlayRenderer( + holder = holder, + coroutine = coroutine, + ) +``` + +## Description + +The `ArcGISMarkerRenderer` is responsible for rendering and managing marker objects on an ArcGIS map. It handles the complete lifecycle of markers—including creation, removal, and updates—by manipulating `Graphic` objects within a specified `GraphicsOverlay`. This class acts as a concrete implementation for the MapConductor framework's marker rendering system on the ArcGIS platform. + +Operations are performed asynchronously using coroutines to ensure the UI remains responsive. + +## Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `markerLayer` | `GraphicsOverlay` | The ArcGIS graphics layer where all markers will be drawn and managed. | +| `holder` | `ArcGISMapViewHolder` | A view holder that provides access to the map, map view, and other context-related ArcGIS components. | +| `coroutine` | `CoroutineScope` | The coroutine scope used to launch asynchronous operations. Defaults to `CoroutineScope(Dispatchers.Main)`. | + +--- + +## Methods + +### setMarkerPosition + +#### Signature + +```kotlin +override fun setMarkerPosition( + markerEntity: MarkerEntityInterface, + position: GeoPoint, +) +``` + +#### Description + +Asynchronously updates the geographical position of a single marker graphic on the map. + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `markerEntity` | `MarkerEntityInterface` | The marker entity containing the `Graphic` whose position needs to be updated. | +| `position` | `GeoPoint` | The new geographical coordinates for the marker. | + +--- + +### onAdd + +#### Signature + +```kotlin +override suspend fun onAdd( + data: List +): List +``` + +#### Description + +Asynchronously creates and adds a batch of new markers to the `markerLayer`. For each item in the `data` list, it constructs a `PictureMarkerSymbol` from the provided bitmap icon and creates a corresponding `Graphic` object at the specified location. The new graphics are then added to the map. + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `data` | `List` | A list of parameter objects, where each object contains the necessary information (e.g., icon, position, state) to create a new marker. | + +#### Returns + +| Type | Description | +| :--- | :--- | +| `List` | A list containing the newly created ArcGIS `Graphic` objects. | + +--- + +### onRemove + +#### Signature + +```kotlin +override suspend fun onRemove(data: List>) +``` + +#### Description + +Asynchronously removes a batch of specified marker graphics from the `markerLayer`. + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `data` | `List>` | A list of marker entities to be removed from the map. | + +--- + +### onPostProcess + +#### Signature + +```kotlin +override suspend fun onPostProcess() +``` + +#### Description + +A lifecycle method intended for post-processing tasks after a batch of updates. In this implementation, this method performs no operations. + +--- + +### onChange + +#### Signature + +```kotlin +override suspend fun onChange( + data: List> +): List +``` + +#### Description + +Asynchronously processes a batch of changes for existing markers. This method efficiently updates marker properties such as icon, position, and visibility by modifying the existing `Graphic` object whenever possible, avoiding unnecessary object recreation. + +If a `Graphic` does not yet exist for a marker entity, it will be created. The method compares the fingerprints of the previous and current states to determine if the icon needs to be updated. + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `data` | `List>` | A list of parameter objects, each containing the previous and current state of a marker to be updated. | + +#### Returns + +| Type | Description | +| :--- | :--- | +| `List` | A list containing the updated marker instances. | + +--- + +## Example + +The `ArcGISMarkerRenderer` is typically instantiated and used within the MapConductor framework. The following example shows how you would create an instance of this class. + +```kotlin +import com.arcgismaps.mapping.view.GraphicsOverlay +import com.mapconductor.arcgis.marker.ArcGISMarkerRenderer +import com.mapconductor.arcgis.map.ArcGISMapViewHolder +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers + +// Assuming 'mapViewHolder' is an initialized instance of ArcGISMapViewHolder + +// 1. Create a GraphicsOverlay to hold the markers. +val markerGraphicsOverlay = GraphicsOverlay() + +// 2. Add the overlay to the map view. +mapViewHolder.mapView.graphicsOverlays.add(markerGraphicsOverlay) + +// 3. Instantiate the ArcGISMarkerRenderer. +val markerRenderer = ArcGISMarkerRenderer( + markerLayer = markerGraphicsOverlay, + holder = mapViewHolder, + coroutine = CoroutineScope(Dispatchers.Main) // Optional: Defaults to Main scope +) + +// The MapConductor framework will now use 'markerRenderer' to manage markers +// by calling its onAdd, onRemove, and onChange methods internally. +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/polygon/ArcGISPolygonOverlayController.kt.md b/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/polygon/ArcGISPolygonOverlayController.kt.md new file mode 100644 index 00000000..9b528724 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/polygon/ArcGISPolygonOverlayController.kt.md @@ -0,0 +1,59 @@ +# ArcGISPolygonOverlayController + +### Signature +```kotlin +class ArcGISPolygonOverlayController( + polygonManager: PolygonManagerInterface = PolygonManager(), + override val renderer: ArcGISPolygonOverlayRenderer, +) : PolygonController(polygonManager, renderer) +``` + +### Description +The `ArcGISPolygonOverlayController` is a specialized controller responsible for managing and rendering polygon overlays on an ArcGIS map. It acts as a bridge between the generic polygon management logic provided by `PolygonController` and the specific rendering implementation for ArcGIS maps, `ArcGISPolygonOverlayRenderer`. + +This class coordinates the state of polygons (e.g., adding, removing, updating) with their visual representation on the map, leveraging a `PolygonManager` for state handling and an `ArcGISPolygonOverlayRenderer` for drawing. + +### Parameters +| Parameter | Type | Description | Default | +|-----------|------|-------------|---------| +| `polygonManager` | `PolygonManagerInterface` | The manager responsible for handling the lifecycle and state of `ArcGISActualPolygon` objects. | `PolygonManager()` | +| `renderer` | `ArcGISPolygonOverlayRenderer` | The renderer responsible for drawing the polygons onto the ArcGIS map view. This is a required parameter. | None | + +### Example +Here is an example of how to instantiate and use the `ArcGISPolygonOverlayController`. + +```kotlin +import com.mapconductor.arcgis.polygon.ArcGISPolygonOverlayController +import com.mapconductor.arcgis.polygon.ArcGISPolygonOverlayRenderer +import com.mapconductor.core.polygon.PolygonManager + +// Assume you have an instance of your ArcGIS MapView and a renderer class. +// val arcgisMapView = getYourMapView() +// class ArcGISPolygonOverlayRenderer(mapView: Any) { /* ... rendering logic ... */ } + +// 1. Initialize the renderer for your ArcGIS map +val polygonRenderer = ArcGISPolygonOverlayRenderer(arcgisMapView) + +// 2. Instantiate the controller with the specific renderer. +// The default PolygonManager will be used. +val polygonController = ArcGISPolygonOverlayController( + renderer = polygonRenderer +) + +// You can now use the controller to manage polygons on the map. +// For example: +// val newPolygon = createArcGisPolygon() +// polygonController.addPolygon(newPolygon) + + +// --- Optional: Using a custom Polygon Manager --- + +// If you have a custom implementation of PolygonManagerInterface, +// you can provide it during instantiation. +val customManager = CustomPolygonManager() // Your custom implementation + +val customPolygonController = ArcGISPolygonOverlayController( + polygonManager = customManager, + renderer = polygonRenderer +) +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/polygon/ArcGISPolygonOverlayRenderer.kt.md b/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/polygon/ArcGISPolygonOverlayRenderer.kt.md new file mode 100644 index 00000000..92b7a817 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/polygon/ArcGISPolygonOverlayRenderer.kt.md @@ -0,0 +1,226 @@ +Of course! Here is the high-quality SDK documentation for the provided Kotlin code snippet. + +--- + +# ArcGISPolygonOverlayRenderer + +## Description + +The `ArcGISPolygonOverlayRenderer` class is a concrete implementation of `AbstractPolygonOverlayRenderer` designed to render and manage polygon graphics on an ArcGIS map. It is responsible for translating abstract `PolygonState` objects into visible ArcGIS `Graphic` objects on a specified `GraphicsOverlay`. + +A key feature of this renderer is its advanced handling of polygons with holes. For simple polygons, it creates a standard `SimpleFillSymbol`. However, for complex polygons containing one or more holes, it employs a raster masking technique. It renders the outer polygon boundary with a transparent fill and dynamically generates a separate raster tile layer underneath. This raster layer provides the visible fill color, effectively "masking out" the hole areas, which allows for the correct visual representation of complex shapes. + +The renderer also manages polygon properties such as fill color, stroke color, stroke width, and Z-index, ensuring that graphics are updated efficiently and drawn in the correct order. + +## Constructor + +### Signature + +```kotlin +class ArcGISPolygonOverlayRenderer( + val polygonLayer: GraphicsOverlay, + override val holder: ArcGISMapViewHolder, + private val rasterLayerController: ArcGISRasterLayerController, + private val tileServer: LocalTileServer = TileServerRegistry.get(forceNoStoreCache = true), + override val coroutine: CoroutineScope = CoroutineScope(Dispatchers.Default), +) : AbstractPolygonOverlayRenderer() +``` + +### Parameters + +| Parameter | Type | Description | +| :-------------------- | :-------------------------- | :------------------------------------------------------------------------------------------------------------------------------------- | +| `polygonLayer` | `GraphicsOverlay` | The ArcGIS `GraphicsOverlay` where the polygon graphics will be added and managed. | +| `holder` | `ArcGISMapViewHolder` | The view holder that provides context and access to the map instance. | +| `rasterLayerController` | `ArcGISRasterLayerController` | The controller used to manage the raster mask layers, which are required for rendering polygons with holes. | +| `tileServer` | `LocalTileServer` | *(Optional)* The local tile server instance used to generate and serve raster tiles for the polygon hole masks. Defaults to a new instance. | +| `coroutine` | `CoroutineScope` | *(Optional)* The coroutine scope for executing asynchronous operations. Defaults to `CoroutineScope(Dispatchers.Default)`. | + +--- + +## Methods + +### createPolygon + +Creates and displays a new polygon graphic on the map based on the provided state. + +#### Signature + +```kotlin +override suspend fun createPolygon(state: PolygonState): ArcGISActualPolygon? +``` + +#### Description + +This function translates a `PolygonState` object into an ArcGIS `Graphic`. It configures the polygon's geometry, fill, and stroke properties. If the `state` includes holes, this method will also trigger the creation of a corresponding raster mask layer to render the fill correctly. The resulting graphic is added to the `polygonLayer`. + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :------------- | :------------------------------------------------- | +| `state` | `PolygonState` | The state object defining the polygon's properties. | + +#### Returns + +| Type | Description | +| :-------------------- | :----------------------------------------------------------------------- | +| `ArcGISActualPolygon?` | The created ArcGIS `Graphic` object, or `null` if creation failed. `ArcGISActualPolygon` is a type alias for `Graphic`. | + +--- + +### updatePolygonProperties + +Updates the properties of an existing polygon graphic. + +#### Signature + +```kotlin +override suspend fun updatePolygonProperties( + polygon: ArcGISActualPolygon, + current: PolygonEntityInterface, + prev: PolygonEntityInterface, +): ArcGISActualPolygon? +``` + +#### Description + +This function efficiently updates an existing polygon graphic by comparing the `current` and `prev` states. It modifies properties such as geometry, fill color, stroke, and Z-index only if they have changed. If holes are added to a polygon that previously had none, it will create the necessary raster mask layer. Conversely, if all holes are removed, it will clean up the mask layer. + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :------------------------------------------ | :----------------------------------------------------------------------- | +| `polygon` | `ArcGISActualPolygon` | The actual ArcGIS `Graphic` object to be updated. | +| `current` | `PolygonEntityInterface` | The wrapper entity containing the new state and the polygon graphic. | +| `prev` | `PolygonEntityInterface` | The wrapper entity containing the previous state for comparison. | + +#### Returns + +| Type | Description | +| :-------------------- | :----------------------------------------------------------------------- | +| `ArcGISActualPolygon?` | The updated ArcGIS `Graphic` object, or `null` if the update failed. | + +--- + +### removePolygon + +Removes a polygon graphic from the map. + +#### Signature + +```kotlin +override suspend fun removePolygon(entity: PolygonEntityInterface) +``` + +#### Description + +This function removes the specified polygon graphic from the `polygonLayer`. If the polygon had an associated raster mask layer (for rendering holes), that layer and its resources are also removed and cleaned up. + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :------------------------------------------ | :----------------------------------------------------------------- | +| `entity` | `PolygonEntityInterface` | The wrapper entity containing the polygon graphic to be removed. | + +--- + +### onPostProcess + +A lifecycle method called after a batch of updates has been processed. + +#### Signature + +```kotlin +override suspend fun onPostProcess() +``` + +#### Description + +This function is called after all create, update, and remove operations in a given cycle are complete. It ensures the correct visual stacking order of polygons by sorting the graphics in the `polygonLayer` based on their `zIndex` attribute. Polygons with higher `zIndex` values will be rendered on top of those with lower values. + +--- + +## Example + +The following example demonstrates how to instantiate `ArcGISPolygonOverlayRenderer` and use it to create, update, and remove polygons. + +```kotlin +import com.arcgismaps.mapping.view.GraphicsOverlay +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch + +// Assume these dependencies are initialized elsewhere in your application +val graphicsOverlay = GraphicsOverlay() +val mapHolder: ArcGISMapViewHolder = getMapViewHolder() +val rasterController: ArcGISRasterLayerController = getRasterLayerController() +val coroutineScope = CoroutineScope(Dispatchers.Main) + +// 1. Initialize the renderer +val polygonRenderer = ArcGISPolygonOverlayRenderer( + polygonLayer = graphicsOverlay, + holder = mapHolder, + rasterLayerController = rasterController, + coroutine = coroutineScope +) + +// Add the graphics overlay to the map +mapHolder.mapView.graphicsOverlays.add(graphicsOverlay) + +coroutineScope.launch { + // 2. Define state for a simple polygon + val simplePolygonState = PolygonState( + id = "simple-polygon-1", + points = listOf( + GeoPoint(34.0, -118.0), + GeoPoint(34.0, -118.5), + GeoPoint(34.5, -118.5), + GeoPoint(34.5, -118.0) + ), + fillColor = Color.Blue.copy(alpha = 0.5f), + strokeColor = Color.Blue, + strokeWidth = 2.dp, + zIndex = 10 + ) + + // Create the simple polygon + val simplePolygonGraphic = polygonRenderer.createPolygon(simplePolygonState) + println("Created simple polygon: ${simplePolygonGraphic?.attributes?.get("id")}") + + // 3. Define state for a polygon with a hole + val polygonWithHoleState = PolygonState( + id = "polygon-with-hole-1", + points = listOf( + GeoPoint(35.0, -119.0), + GeoPoint(35.0, -120.0), + GeoPoint(36.0, -120.0), + GeoPoint(36.0, -119.0) + ), + holes = listOf( + listOf( // Inner ring defining the hole + GeoPoint(35.2, -119.2), + GeoPoint(35.2, -119.8), + GeoPoint(35.8, -119.8), + GeoPoint(35.8, -119.2) + ) + ), + fillColor = Color.Red.copy(alpha = 0.7f), + strokeColor = Color.Red, + strokeWidth = 3.dp, + zIndex = 20 + ) + + // Create the polygon with a hole. This will also create a raster mask layer. + val complexPolygonGraphic = polygonRenderer.createPolygon(polygonWithHoleState) + println("Created polygon with hole: ${complexPolygonGraphic?.attributes?.get("id")}") + + // 4. After processing, sort the graphics by zIndex + polygonRenderer.onPostProcess() + + // 5. To remove a polygon + // (Assuming you have the PolygonEntityInterface instance) + val entityToRemove = PolygonEntity(polygonWithHoleState, complexPolygonGraphic!!) + polygonRenderer.removePolygon(entityToRemove) + println("Removed polygon with hole.") +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/polyline/ArcGISPolylineOverlayController.kt.md b/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/polyline/ArcGISPolylineOverlayController.kt.md new file mode 100644 index 00000000..f2b454ff --- /dev/null +++ b/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/polyline/ArcGISPolylineOverlayController.kt.md @@ -0,0 +1,52 @@ +# ArcGISPolylineOverlayController + +The `ArcGISPolylineOverlayController` is a specialized controller that manages the display and lifecycle of polyline overlays on an ArcGIS map. + +## Signature + +```kotlin +class ArcGISPolylineOverlayController( + polylineManager: PolylineManagerInterface = PolylineManager(), + override val renderer: ArcGISPolylineOverlayRenderer, +) : PolylineController(polylineManager, renderer) +``` + +## Description + +This class acts as the primary component for managing polyline overlays within an ArcGIS environment. It connects the core polyline management logic (from `PolylineManager`) with the specific rendering implementation required for ArcGIS maps (`ArcGISPolylineOverlayRenderer`). + +The controller orchestrates the state of polyline data and delegates the responsibility of drawing the polylines on the map to the provided `renderer`. It inherits from the generic `PolylineController`, specializing it for `ArcGISActualPolyline` objects. + +## Parameters + +| Parameter | Type | Description | Default | +| :--- | :--- | :--- | :--- | +| `polylineManager` | `PolylineManagerInterface` | The manager responsible for handling the state and lifecycle of polyline data. It tracks all added, removed, and updated polylines. | `PolylineManager()` | +| `renderer` | `ArcGISPolylineOverlayRenderer` | The renderer responsible for drawing the polylines onto the ArcGIS map view. This object handles the platform-specific drawing operations. | - | + +## Example + +The following example demonstrates how to initialize the `ArcGISPolylineOverlayController` to manage polylines on an ArcGIS map. + +```kotlin +import com.esri.arcgisruntime.mapping.view.MapView +import com.mapconductor.arcgis.polyline.ArcGISPolylineOverlayController +import com.mapconductor.arcgis.polyline.ArcGISPolylineOverlayRenderer + +// 1. Assume you have an instance of an ArcGIS MapView +val mapView: MapView = getMyArcGISMapView() + +// 2. Create the specific renderer for ArcGIS polyline overlays, +// passing the MapView instance to it. +val polylineRenderer = ArcGISPolylineOverlayRenderer(mapView) + +// 3. Instantiate the controller with the renderer. +// The default PolylineManager will be used automatically. +val polylineController = ArcGISPolylineOverlayController(renderer = polylineRenderer) + +// 4. The controller is now set up. You can use it to manage polylines +// on the map. For example, adding a new polyline: +// +// val polylineOptions = PolylineOptions(...) +// val newPolyline = polylineController.addPolyline(polylineOptions) +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/polyline/ArcGISPolylineOverlayRenderer.kt.md b/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/polyline/ArcGISPolylineOverlayRenderer.kt.md new file mode 100644 index 00000000..de534a79 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/polyline/ArcGISPolylineOverlayRenderer.kt.md @@ -0,0 +1,151 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +*** + +# class `ArcGISPolylineOverlayRenderer` + +## Description + +Manages the rendering lifecycle of polylines on an ArcGIS map. This class acts as a concrete implementation of `AbstractPolylineOverlayRenderer` for the ArcGIS Maps SDK. + +It is responsible for creating, updating, and removing polyline graphics from a specified `GraphicsOverlay`. The renderer translates abstract `PolylineState` objects, which define the properties of a polyline, into tangible ArcGIS `Graphic` objects that are displayed on the map. + +A key feature of this renderer is its handling of polyline geometry based on the `geodesic` property: +- **Geodesic (`true`):** Renders a true geodesic curve, which represents the shortest path between two points on the surface of the Earth. +- **Non-geodesic (`false`):** Renders a straight line on the 2D map projection (a rhumb line) by performing linear interpolation between the provided vertices. This results in a visually straight line on a flat map view. + +## Signature + +```kotlin +class ArcGISPolylineOverlayRenderer( + val polylineLayer: GraphicsOverlay, + override val holder: ArcGISMapViewHolder, + override val coroutine: CoroutineScope = CoroutineScope(Dispatchers.Default), +) : AbstractPolylineOverlayRenderer() +``` + +## Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `polylineLayer` | `GraphicsOverlay` | The ArcGIS graphics layer where the polyline graphics will be drawn. | +| `holder` | `ArcGISMapViewHolder` | A view holder that provides access to the map context. | +| `coroutine` | `CoroutineScope` | The coroutine scope used to execute asynchronous rendering operations. Defaults to `CoroutineScope(Dispatchers.Default)`. | + +--- + +## Methods + +### `createPolyline` + +Creates a new polyline graphic from a `PolylineState` object and adds it to the map's `polylineLayer`. + +#### Signature + +```kotlin +override suspend fun createPolyline(state: PolylineState): ArcGISActualPolyline? +``` + +#### Description + +This function constructs the geometry and symbol for a new polyline based on the provided state. It sets the polyline's path, color, and width, and then adds the resulting `Graphic` to the graphics overlay. The geometry is created as either geodesic or non-geodesic based on the `state.geodesic` flag. + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `state` | `PolylineState` | An object containing the properties for the new polyline, including its vertices (`points`), `strokeColor`, `strokeWidth`, and `geodesic` flag. | + +#### Returns + +| Type | Description | +| :--- | :--- | +| `ArcGISActualPolyline?` | The newly created ArcGIS `Graphic` (type-aliased as `ArcGISActualPolyline`), or `null` if creation fails. | + +#### Example + +```kotlin +// Assuming 'renderer' is an instance of ArcGISPolylineOverlayRenderer +// and 'polylineState' is a valid PolylineState object. + +viewModelScope.launch { + val newPolylineGraphic = renderer.createPolyline(polylineState) + if (newPolylineGraphic != null) { + println("Polyline created and added to the map.") + } else { + println("Failed to create polyline.") + } +} +``` + +--- + +### `updatePolylineProperties` + +Efficiently updates the properties of an existing polyline graphic by comparing its current and previous states. + +#### Signature + +```kotlin +override suspend fun updatePolylineProperties( + polyline: ArcGISActualPolyline, + current: PolylineEntityInterface, + prev: PolylineEntityInterface, +): ArcGISActualPolyline? +``` + +#### Description + +This function checks for differences between the `current` and `prev` states. It applies changes to the `Graphic`'s geometry (`points`, `geodesic`), color (`strokeColor`), or width (`strokeWidth`) only if they have been modified. This avoids unnecessary redraws and improves performance. + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `polyline` | `ArcGISActualPolyline` | The existing ArcGIS `Graphic` to be updated. | +| `current` | `PolylineEntityInterface` | The entity representing the new, updated state of the polyline. | +| `prev` | `PolylineEntityInterface` | The entity representing the previous state, used for comparison to detect changes. | + +#### Returns + +| Type | Description | +| :--- | :--- | +| `ArcGISActualPolyline?` | The updated `Graphic` object. | + +--- + +### `removePolyline` + +Asynchronously removes a polyline graphic from the map. + +#### Signature + +```kotlin +override suspend fun removePolyline(entity: PolylineEntityInterface) +``` + +#### Description + +This function launches a coroutine to remove the specified polyline's `Graphic` from the `polylineLayer`, effectively deleting it from the map view. + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `entity` | `PolylineEntityInterface` | The polyline entity containing the graphic to be removed. | + +#### Returns + +This function does not return a value. + +#### Example + +```kotlin +// Assuming 'renderer' is an instance of ArcGISPolylineOverlayRenderer +// and 'polylineEntity' is the entity to be removed. + +viewModelScope.launch { + renderer.removePolyline(polylineEntity) + println("Polyline removal has been initiated.") +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/raster/ArcGISRasterLayerController.kt.md b/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/raster/ArcGISRasterLayerController.kt.md new file mode 100644 index 00000000..6973aab3 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/raster/ArcGISRasterLayerController.kt.md @@ -0,0 +1,62 @@ +# ArcGISRasterLayerController + +### Signature + +```kotlin +class ArcGISRasterLayerController( + rasterLayerManager: RasterLayerManagerInterface = RasterLayerManager(), + renderer: ArcGISRasterLayerOverlayRenderer, +) : RasterLayerController(rasterLayerManager, renderer) +``` + +### Description + +The `ArcGISRasterLayerController` is a specialized controller responsible for managing and displaying raster data as layers within an ArcGIS map environment. It extends the generic `RasterLayerController`, bridging the core raster layer management logic with an ArcGIS-specific rendering implementation (`ArcGISRasterLayerOverlayRenderer`). + +This controller coordinates the state of raster layers, managed by the `rasterLayerManager`, with their visual representation on the map, handled by the `renderer`. It is the primary component for integrating raster layer functionality into an ArcGIS map application. + +### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `rasterLayerManager` | `RasterLayerManagerInterface` | The manager responsible for handling the collection of raster layers. If not provided, a default `RasterLayerManager` instance is created. (Optional) | +| `renderer` | `ArcGISRasterLayerOverlayRenderer` | The ArcGIS-specific renderer responsible for drawing the raster layers onto the map view. (Required) | + +### Example + +The following example demonstrates how to create an instance of `ArcGISRasterLayerController` and associate it with an ArcGIS `MapView`. + +```kotlin +import com.arcgismaps.mapping.view.MapView +import com.mapconductor.arcgis.raster.ArcGISRasterLayerController +import com.mapconductor.arcgis.raster.ArcGISRasterLayerOverlayRenderer +import com.mapconductor.core.raster.RasterLayerManager + +// Assuming you have an ArcGIS MapView instance from your layout +val mapView: MapView = findViewById(R.id.mapView) + +// 1. Create the ArcGIS-specific renderer. +// This renderer typically interacts with the map view's graphics overlays to display raster data. +val arcGisRenderer = ArcGISRasterLayerOverlayRenderer(mapView) + +// 2. Instantiate the controller with the required renderer. +// This example uses the default RasterLayerManager. +val rasterController = ArcGISRasterLayerController(renderer = arcGisRenderer) + +// Now you can use the rasterController to add, remove, and manage raster layers. +// For example: +// val myRaster = // ... create or load an ArcGIS Raster +// val myRasterLayer = RasterLayer(myRaster) +// rasterController.addLayer(myRasterLayer) + + +// --- Alternative Example with a custom manager --- + +// You can also provide a custom or shared RasterLayerManager instance. +// This is useful if you need to share layer state between different components. +val customManager = RasterLayerManager() +val rasterControllerWithCustomManager = ArcGISRasterLayerController( + rasterLayerManager = customManager, + renderer = arcGisRenderer +) +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/raster/ArcGISRasterLayerOverlayRenderer.kt.md b/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/raster/ArcGISRasterLayerOverlayRenderer.kt.md new file mode 100644 index 00000000..bfdbe1f7 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/raster/ArcGISRasterLayerOverlayRenderer.kt.md @@ -0,0 +1,183 @@ +# ArcGISRasterLayerOverlayRenderer + +The `ArcGISRasterLayerOverlayRenderer` class is responsible for managing and rendering raster layer overlays on an ArcGIS map. It implements the `RasterLayerOverlayRendererInterface` and handles the lifecycle of raster layers, including adding, updating, and removing them from the map scene. + +This renderer supports raster data from `ArcGisService` and `UrlTemplate` sources. It does not support `TileJson` sources. + +## Constructor + +### Signature + +```kotlin +ArcGISRasterLayerOverlayRenderer( + holder: ArcGISMapViewHolder, + coroutine: CoroutineScope = CoroutineScope(Dispatchers.Main) +) +``` + +### Description + +Creates a new instance of the `ArcGISRasterLayerOverlayRenderer`. This renderer will add, update, and remove layers on the map provided by the `ArcGISMapViewHolder`. + +### Parameters + +| Parameter | Type | Description | +|---|---|---| +| `holder` | `ArcGISMapViewHolder` | The view holder that contains the ArcGIS map instance where layers will be rendered. | +| `coroutine` | `CoroutineScope` | The coroutine scope for running asynchronous operations. Defaults to `CoroutineScope(Dispatchers.Main)`. | + +## Methods + +### onAdd + +#### Signature + +```kotlin +override suspend fun onAdd( + data: List +): List +``` + +#### Description + +Adds new raster layers to the map based on the provided state data. For each item in the input list, it creates a corresponding ArcGIS `Layer` and adds it to the map's operational layers. + +Supported sources are `RasterLayerSource.ArcGisService` and `RasterLayerSource.UrlTemplate`. `RasterLayerSource.TileJson` is not supported and will result in a `null` entry in the returned list. The renderer waits for each layer to load successfully before adding it to the map to ensure its properties are fully initialized. + +#### Parameters + +| Parameter | Type | Description | +|---|---|---| +| `data` | `List` | A list of parameters, each containing the `RasterLayerState` for a new layer to be added. | + +#### Returns + +A `List` containing the newly created ArcGIS `Layer` instances, corresponding in order to the input data. An element will be `null` if the layer could not be created (e.g., unsupported source type or a loading error). + +--- + +### onChange + +#### Signature + +```kotlin +override suspend fun onChange( + data: List> +): List +``` + +#### Description + +Processes changes to existing raster layers. + +- If a layer's source (`RasterLayerState.source`) has changed, the old layer is removed, and a new one is created and added to the map to reflect the new source. +- If only other properties like `opacity` or `visible` have changed, the existing layer is updated in place for better performance. + +#### Parameters + +| Parameter | Type | Description | +|---|---|---| +| `data` | `List>` | A list of change parameters, each containing the previous and current state of a layer. | + +#### Returns + +A `List` containing the updated or newly created ArcGIS `Layer` instances corresponding to the input data. + +--- + +### onRemove + +#### Signature + +```kotlin +override suspend fun onRemove(data: List>) +``` + +#### Description + +Removes the specified raster layers from the map's operational layers. + +#### Parameters + +| Parameter | Type | Description | +|---|---|---| +| `data` | `List>` | A list of layer entities to be removed from the map. | + +#### Returns + +This method does not return a value. + +--- + +### onPostProcess + +#### Signature + +```kotlin +override suspend fun onPostProcess() +``` + +#### Description + +A lifecycle method called after all add, change, and remove operations in a batch are processed. In this implementation, this method is empty and performs no action. + +#### Parameters + +This method has no parameters. + +#### Returns + +This method does not return a value. + +## Example + +The following example demonstrates how to initialize the `ArcGISRasterLayerOverlayRenderer` and use it to add a new raster layer from a URL template (OpenStreetMap) to the map. + +```kotlin +import com.mapconductor.arcgis.raster.ArcGISRasterLayerOverlayRenderer +import com.mapconductor.core.raster.RasterLayerState +import com.mapconductor.core.raster.RasterLayerSource +import com.mapconductor.core.raster.TileScheme +import com.mapconductor.core.raster.RasterLayerOverlayRendererInterface +import kotlinx.coroutines.launch +import kotlinx.coroutines.CoroutineScope + +// Assume the following setup exists: +// - An `ArcGISMapViewHolder` instance named `mapViewHolder` is available. +// - A `CoroutineScope` instance named `coroutineScope` is available (e.g., viewModelScope). + +// Define a simple implementation of AddParamsInterface for the example +data class AddParams( + override val state: RasterLayerState +) : RasterLayerOverlayRendererInterface.AddParamsInterface + +// 1. Initialize the renderer with the map holder +val renderer = ArcGISRasterLayerOverlayRenderer(mapViewHolder) + +// 2. Define the state for a new raster layer from a URL template +val openStreetMapState = RasterLayerState( + id = "osm-layer", + source = RasterLayerSource.UrlTemplate( + template = "https://a.tile.openstreetmap.org/{z}/{x}/{y}.png", + tileSize = 256, + scheme = TileScheme.XYZ + ), + opacity = 0.8f, + visible = true +) + +// 3. Create the parameters for the onAdd call +val addData = listOf( + AddParams(state = openStreetMapState) +) + +// 4. Add the layer to the map asynchronously +coroutineScope.launch { + val addedLayers = renderer.onAdd(addData) + if (addedLayers.isNotEmpty() && addedLayers[0] != null) { + println("Successfully added OpenStreetMap layer to the map.") + } else { + println("Failed to add OpenStreetMap layer.") + } +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/zoom/ZoomAltitudeConverter.kt.md b/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/zoom/ZoomAltitudeConverter.kt.md new file mode 100644 index 00000000..02a67283 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/zoom/ZoomAltitudeConverter.kt.md @@ -0,0 +1,432 @@ +Of course. Here is a high-quality SDK document for the provided code snippet, meticulously crafted to be clear, complete, and developer-friendly, while addressing all previous feedback. + +--- + +# ZoomAltitudeConverter + +## Class: `ZoomAltitudeConverter` + +### Signature +```kotlin +class ZoomAltitudeConverter( + zoom0Altitude: Double = ARCGIS_OPTIMIZED_ZOOM0_ALTITUDE +) : AbstractZoomAltitudeConverter(zoom0Altitude) +``` + +### Description +The `ZoomAltitudeConverter` class provides a sophisticated mechanism for converting between abstract "zoom levels" (as used in map APIs like Google Maps) and camera "altitude" in meters (as used in ArcGIS `SceneView`). + +This converter is specifically calibrated for ArcGIS and incorporates several factors to ensure a visually consistent and accurate mapping experience: + +- **Latitude Correction:** Adjusts for the visual distortion caused by the Mercator projection, where geographic areas appear larger further from the equator. +- **Tilt Correction:** Accounts for the camera's tilt angle, converting the straight-line "distance" to the target into vertical "altitude". +- **Viewport Scaling:** Dynamically scales the altitude based on the map view's height. This mimics the behavior of 2D tiled maps (like Google Maps), where a larger screen displays a larger geographic area at the same zoom level. + +The class offers a range of methods, from comprehensive conversions that use all available context (latitude, tilt, viewport size) to simpler legacy methods for basic use cases or backward compatibility. + +--- + +## Constructor + +### Signature +```kotlin +ZoomAltitudeConverter(zoom0Altitude: Double = ARCGIS_OPTIMIZED_ZOOM0_ALTITUDE) +``` + +### Description +Initializes a new instance of the `ZoomAltitudeConverter`. + +### Parameters +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `zoom0Altitude` | `Double` | **Optional.** The camera altitude in meters that corresponds to zoom level 0.0. Defaults to `ARCGIS_OPTIMIZED_ZOOM0_ALTITUDE`. | + +--- + +## Companion Object + +### `ARCGIS_OPTIMIZED_ZOOM0_ALTITUDE` + +#### Signature +```kotlin +const val ARCGIS_OPTIMIZED_ZOOM0_ALTITUDE = 124000000.0 +``` + +#### Description +A calibrated constant representing the default altitude in meters for zoom level 0. This value has been empirically determined to provide an optimal starting point for conversions in an ArcGIS environment. + +--- + +## Core Conversion Methods + +These methods provide the most accurate conversions by incorporating latitude, tilt, and viewport dimensions. It is highly recommended to use these methods whenever the context is available. + +### `zoomLevelToAltitude` + +#### Signature +```kotlin +fun zoomLevelToAltitude( + zoomLevel: Double, + latitude: Double, + tilt: Double, + viewportWidthPx: Int, + viewportHeightPx: Int +): Double +``` + +#### Description +Converts a map zoom level to the corresponding camera altitude in meters, applying corrections for latitude, camera tilt, and viewport dimensions. This is the most accurate method for conversion. + +#### Parameters +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `zoomLevel` | `Double` | The abstract map zoom level to convert. | +| `latitude` | `Double` | The current latitude in degrees of the camera's target. | +| `tilt` | `Double` | The current camera tilt angle in degrees (0 is looking straight down). | +| `viewportWidthPx` | `Int` | The width of the map view in pixels. | +| `viewportHeightPx` | `Int` | The height of the map view in pixels. | + +#### Returns +`Double` - The calculated camera altitude in meters, clamped within a valid range. + +#### Example +```kotlin +val converter = ZoomAltitudeConverter() +val zoomLevel = 15.5 +val latitude = 34.0522 // Los Angeles +val tilt = 45.0 +val viewportWidth = 1080 +val viewportHeight = 1920 + +val altitude = converter.zoomLevelToAltitude( + zoomLevel, + latitude, + tilt, + viewportWidth, + viewportHeight +) +// e.g., altitude might be ~2450.0 meters +``` + +### `altitudeToZoomLevel` + +#### Signature +```kotlin +fun altitudeToZoomLevel( + altitude: Double, + latitude: Double, + tilt: Double, + viewportWidthPx: Int, + viewportHeightPx: Int +): Double +``` + +#### Description +Converts a camera altitude in meters to the corresponding map zoom level, applying corrections for latitude, camera tilt, and viewport dimensions. This is the most accurate method for reverse conversion. + +#### Parameters +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `altitude` | `Double` | The camera altitude in meters to convert. | +| `latitude` | `Double` | The current latitude in degrees of the camera's target. | +| `tilt` | `Double` | The current camera tilt angle in degrees. | +| `viewportWidthPx` | `Int` | The width of the map view in pixels. | +| `viewportHeightPx` | `Int` | The height of the map view in pixels. | + +#### Returns +`Double` - The calculated map zoom level, clamped within a valid range. + +#### Example +```kotlin +val converter = ZoomAltitudeConverter() +val altitude = 2450.0 +val latitude = 34.0522 // Los Angeles +val tilt = 45.0 +val viewportWidth = 1080 +val viewportHeight = 1920 + +val zoomLevel = converter.altitudeToZoomLevel( + altitude, + latitude, + tilt, + viewportWidth, + viewportHeight +) +// e.g., zoomLevel might be ~15.5 +``` + +--- + +## Simplified Conversion Methods (Overrides) + +These methods are overrides from the `AbstractZoomAltitudeConverter` and provide conversions without viewport scaling. They are useful when viewport dimensions are not available. + +### `zoomLevelToAltitude` + +#### Signature +```kotlin +override fun zoomLevelToAltitude( + zoomLevel: Double, + latitude: Double, + tilt: Double +): Double +``` + +#### Description +Converts a map zoom level to camera altitude, applying corrections for latitude and tilt. This version does not use viewport scaling. + +#### Parameters +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `zoomLevel` | `Double` | The abstract map zoom level to convert. | +| `latitude` | `Double` | The current latitude in degrees of the camera's target. | +| `tilt` | `Double` | The current camera tilt angle in degrees. | + +#### Returns +`Double` - The calculated camera altitude in meters. + +#### Example +```kotlin +val converter = ZoomAltitudeConverter() +val zoomLevel = 12.0 +val latitude = 48.8566 // Paris +val tilt = 30.0 + +val altitude = converter.zoomLevelToAltitude(zoomLevel, latitude, tilt) +// e.g., altitude might be ~14500.0 meters +``` + +### `altitudeToZoomLevel` + +#### Signature +```kotlin +override fun altitudeToZoomLevel( + altitude: Double, + latitude: Double, + tilt: Double +): Double +``` + +#### Description +Converts a camera altitude to a map zoom level, applying corrections for latitude and tilt. This version does not use viewport scaling. + +#### Parameters +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `altitude` | `Double` | The camera altitude in meters to convert. | +| `latitude` | `Double` | The current latitude in degrees of the camera's target. | +| `tilt` | `Double` | The current camera tilt angle in degrees. | + +#### Returns +`Double` - The calculated map zoom level. + +#### Example +```kotlin +val converter = ZoomAltitudeConverter() +val altitude = 14500.0 +val latitude = 48.8566 // Paris +val tilt = 30.0 + +val zoomLevel = converter.altitudeToZoomLevel(altitude, latitude, tilt) +// e.g., zoomLevel might be ~12.0 +``` + +--- + +## Distance Conversion Methods + +These methods convert a zoom level to the straight-line "distance" from the camera to the point on the ground. This value does not account for camera tilt. + +### `zoomLevelToDistance` (without viewport scaling) + +#### Signature +```kotlin +fun zoomLevelToDistance( + zoomLevel: Double, + latitude: Double +): Double +``` + +#### Description +Converts a map zoom level to the straight-line camera distance in meters, applying a correction for latitude. This method does not account for camera tilt or viewport size. + +#### Parameters +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `zoomLevel` | `Double` | The abstract map zoom level to convert. | +| `latitude` | `Double` | The current latitude in degrees of the camera's target. | + +#### Returns +`Double` - The calculated camera distance in meters. + +#### Example +```kotlin +val converter = ZoomAltitudeConverter() +val zoomLevel = 10.0 +val latitude = 51.5072 // London + +val distance = converter.zoomLevelToDistance(zoomLevel, latitude) +// e.g., distance might be ~76000.0 meters +``` + +### `zoomLevelToDistance` (with viewport scaling) + +#### Signature +```kotlin +fun zoomLevelToDistance( + zoomLevel: Double, + latitude: Double, + viewportWidthPx: Int, + viewportHeightPx: Int +): Double +``` + +#### Description +Converts a map zoom level to the straight-line camera distance in meters, applying corrections for both latitude and viewport dimensions for higher accuracy across different screen sizes. + +#### Parameters +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `zoomLevel` | `Double` | The abstract map zoom level to convert. | +| `latitude` | `Double` | The current latitude in degrees of the camera's target. | +| `viewportWidthPx` | `Int` | The width of the map view in pixels. | +| `viewportHeightPx` | `Int` | The height of the map view in pixels. | + +#### Returns +`Double` - The calculated camera distance in meters. + +#### Example +```kotlin +val converter = ZoomAltitudeConverter() +val zoomLevel = 10.0 +val latitude = 51.5072 // London +val viewportWidth = 1440 +val viewportHeight = 2560 + +val distance = converter.zoomLevelToDistance( + zoomLevel, + latitude, + viewportWidth, + viewportHeight +) +// e.g., distance might be ~105000.0 meters (higher due to larger viewport) +``` + +--- + +## Legacy Conversion Methods + +These methods are provided for backward compatibility or for very simple use cases where context like latitude, tilt, or viewport size is unavailable. Their accuracy is limited. + +### `zoomLevelToAltitude` (basic) + +#### Signature +```kotlin +fun zoomLevelToAltitude(zoomLevel: Double): Double +``` + +#### Description +Performs a basic conversion from zoom level to altitude without any corrections for latitude, tilt, or viewport size. + +#### Parameters +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `zoomLevel` | `Double` | The abstract map zoom level to convert. | + +#### Returns +`Double` - The calculated camera altitude in meters. + +#### Example +```kotlin +val converter = ZoomAltitudeConverter() +val altitude = converter.zoomLevelToAltitude(10.0) +// altitude will be 121093.75 +``` + +### `zoomLevelToAltitude` (with latitude) + +#### Signature +```kotlin +fun zoomLevelToAltitude( + zoomLevel: Double, + latitude: Double +): Double +``` + +#### Description +Converts a zoom level to altitude, applying a correction for latitude. It does not account for tilt or viewport size. + +#### Parameters +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `zoomLevel` | `Double` | The abstract map zoom level to convert. | +| `latitude` | `Double` | The current latitude in degrees. | + +#### Returns +`Double` - The calculated camera altitude in meters. + +#### Example +```kotlin +val converter = ZoomAltitudeConverter() +// At the equator (latitude 0), cos(0) = 1, so result is same as basic method. +val altitudeAtEquator = converter.zoomLevelToAltitude(10.0, 0.0) +// altitudeAtEquator will be 121093.75 + +// At 60 degrees latitude, cos(60) = 0.5, so altitude is halved. +val altitudeAt60 = converter.zoomLevelToAltitude(10.0, 60.0) +// altitudeAt60 will be 60546.875 +``` + +### `altitudeToZoomLevel` (basic) + +#### Signature +```kotlin +fun altitudeToZoomLevel(altitude: Double): Double +``` + +#### Description +Performs a basic conversion from altitude to zoom level without any corrections. + +#### Parameters +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `altitude` | `Double` | The camera altitude in meters to convert. | + +#### Returns +`Double` - The calculated map zoom level. + +#### Example +```kotlin +val converter = ZoomAltitudeConverter() +val zoomLevel = converter.altitudeToZoomLevel(121093.75) +// zoomLevel will be 10.0 +``` + +### `altitudeToZoomLevel` (with latitude) + +#### Signature +```kotlin +fun altitudeToZoomLevel( + altitude: Double, + latitude: Double +): Double +``` + +#### Description +Converts an altitude to a zoom level, applying a correction for latitude. + +#### Parameters +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `altitude` | `Double` | The camera altitude in meters to convert. | +| `latitude` | `Double` | The current latitude in degrees. | + +#### Returns +`Double` - The calculated map zoom level. + +#### Example +```kotlin +val converter = ZoomAltitudeConverter() +val zoomLevel = converter.altitudeToZoomLevel(60546.875, 60.0) +// zoomLevel will be 10.0 +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/AdaptiveInterpolation.kt.md b/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/AdaptiveInterpolation.kt.md new file mode 100644 index 00000000..747df920 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/AdaptiveInterpolation.kt.md @@ -0,0 +1,241 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +*** + +# Google Maps Utilities + +This document outlines a collection of internal utility classes and objects used for optimizing polyline rendering on Google Maps. These helpers manage adaptive interpolation calculations and caching to balance performance and visual quality. + +## `AdaptiveInterpolation` + +A utility object that provides functions for calculating parameters needed for adaptive polyline interpolation. Adaptive interpolation adjusts the density of points in a polyline based on the map's zoom level, ensuring that lines look smooth without using an excessive number of vertices, which helps optimize rendering performance. + +--- + +### `maxSegmentLengthMeters` + +Calculates the maximum length for a polyline segment in meters. This length is adaptive, based on the current map zoom level and latitude. The calculation aims to maintain a consistent visual segment length on the screen, breaking down long polylines into smaller, visually appropriate segments. The result is clamped to predefined minimum and maximum values to ensure sensibility. + +#### Signature + +```kotlin +fun maxSegmentLengthMeters( + zoom: Float, + latitude: Double +): Double +``` + +#### Description + +This function determines the ideal segment length by calculating the meters-per-pixel ratio at a given latitude and zoom level. It then multiplies this by a target pixel length (`TARGET_SEGMENT_PIXELS`) to find the desired segment length in meters. + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `zoom` | `Float` | The current zoom level of the map. | +| `latitude` | `Double` | The latitude for which the calculation is being performed. The meters-per-pixel ratio changes with latitude. | + +#### Returns + +| Type | Description | +| :--- | :--- | +| `Double` | The calculated maximum segment length in meters, clamped between 50.0 and 100,000.0. | + +--- + +### `pointsHash` + +Generates a stable 64-bit hash for a list of geographic points. This function is designed to be resilient to minor floating-point inaccuracies by first quantizing the latitude and longitude values. + +#### Signature + +```kotlin +fun pointsHash(points: List): Long +``` + +#### Description + +This function uses the 64-bit FNV-1a hashing algorithm. It iterates over each point, quantizes its coordinates to an integer representation, and incorporates them into the hash. The total number of points is also factored into the final hash value, ensuring that lists with different lengths but similar starting points produce unique hashes. This is primarily used for creating reliable cache keys. + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `points` | `List` | The list of geographic points to hash. | + +#### Returns + +| Type | Description | +| :--- | :--- | +| `Long` | A 64-bit FNV-1a hash of the input points. | + +--- + +### `cacheKey` + +Creates a unique string key for caching interpolated polyline data. + +#### Signature + +```kotlin +fun cacheKey( + pointsHash: Long, + maxSegmentLengthMeters: Double +): String +``` + +#### Description + +This function combines the hash of an original polyline with the interpolation segment length used to process it. This ensures that if either the source points or the interpolation detail (segment length) changes, a new cache key is generated, preventing cache collisions. + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `pointsHash` | `Long` | The hash of the original list of points, typically from `pointsHash()`. | +| `maxSegmentLengthMeters` | `Double` | The segment length used for interpolation, typically from `maxSegmentLengthMeters()`. | + +#### Returns + +| Type | Description | +| :--- | :--- | +| `String` | A unique string suitable for use as a cache key. | + +## `LatLngInterpolationCache` + +An internal class that provides a memory cache for storing the results of polyline interpolations (which are lists of `LatLng`). It is a wrapper around Android's `LruCache`, implementing a "Least Recently Used" eviction policy. + +#### Signature + +```kotlin +internal class LatLngInterpolationCache(maxEntries: Int) +``` + +#### Description + +When the cache reaches its maximum configured size, it automatically discards the least recently accessed items to make room for new ones. This is useful for storing computationally expensive interpolation results that are likely to be reused. + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `maxEntries` | `Int` | The maximum number of entries the cache can hold before items are evicted. | + +--- + +### `get` + +Retrieves an interpolated list of `LatLng` points from the cache. + +#### Signature + +```kotlin +fun get(key: String): List? +``` + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `key` | `String` | The key associated with the cached data. | + +#### Returns + +| Type | Description | +| :--- | :--- | +| `List?` | The cached list of `LatLng` points if the key is found, otherwise `null`. | + +--- + +### `put` + +Adds a list of `LatLng` points to the cache with its corresponding key. + +#### Signature + +```kotlin +fun put( + key: String, + value: List +) +``` + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `key` | `String` | The key to store the data under. | +| `value` | `List` | The list of `LatLng` points to cache. | + +#### Returns + +This method does not return a value. + +--- + +### Example + +The following example demonstrates the typical workflow for using `AdaptiveInterpolation` and `LatLngInterpolationCache` together. + +```kotlin +import com.google.android.gms.maps.model.LatLng +import com.mapconductor.core.features.GeoPointInterface + +// A simple implementation of GeoPointInterface for the example +data class MyGeoPoint(override val latitude: Double, override val longitude: Double) : GeoPointInterface + +fun main() { + // 1. Define the original points of a polyline + val originalPoints = listOf( + MyGeoPoint(40.7128, -74.0060), // New York + MyGeoPoint(34.0522, -118.2437) // Los Angeles + ) + + // 2. Define map state + val currentZoom = 8.0f + val currentLatitude = 39.8283 // Approx. center of US + + // 3. Calculate the adaptive segment length for the current view + val segmentLength = AdaptiveInterpolation.maxSegmentLengthMeters(currentZoom, currentLatitude) + println("Calculated max segment length: ${segmentLength.toLong()} meters") + + // 4. Generate a hash for the original points + val hash = AdaptiveInterpolation.pointsHash(originalPoints) + println("Points hash: $hash") + + // 5. Create a unique cache key + val key = AdaptiveInterpolation.cacheKey(hash, segmentLength) + println("Cache key: $key") + + // 6. Initialize the cache + val interpolationCache = LatLngInterpolationCache(maxEntries = 100) + + // 7. Assume we have performed interpolation and have a result + val interpolatedResult = listOf( + LatLng(40.7128, -74.0060), + // ... many more interpolated points ... + LatLng(34.0522, -118.2437) + ) + + // 8. Store the result in the cache + interpolationCache.put(key, interpolatedResult) + println("Stored ${interpolatedResult.size} points in cache.") + + // 9. Later, retrieve the data from the cache + val cachedData = interpolationCache.get(key) + + if (cachedData != null) { + println("Successfully retrieved ${cachedData.size} points from cache.") + } else { + println("Cache miss. Must re-calculate interpolation.") + } +} + +// Example Output: +// Calculated max segment length: 100000 meters +// Points hash: -544593387939339395 +// Cache key: -544593387939339395_100000 +// Stored 2 points in cache. +// Successfully retrieved 2 points from cache. +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/GeoPoint.kt.md b/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/GeoPoint.kt.md new file mode 100644 index 00000000..0ace5ec0 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/GeoPoint.kt.md @@ -0,0 +1,129 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +# GeoPoint and LatLng Interoperability + +This document outlines a set of Kotlin extension functions designed to facilitate seamless conversion between the `com.mapconductor.core.features.GeoPoint` and the Google Maps `com.google.android.gms.maps.model.LatLng` types. These utilities simplify the process of working with location data across different parts of an application that may use these distinct types. + +--- + +## `GeoPoint.toLatLng()` + +Converts a `GeoPoint` object into a Google Maps `LatLng` object. This is useful for passing `GeoPoint` data to Google Maps SDK components that require a `LatLng`. + +### Signature + +```kotlin +fun GeoPoint.toLatLng(): LatLng +``` + +### Description + +This extension function is called on an instance of `GeoPoint`. It creates and returns a new `LatLng` object using the latitude and longitude from the source `GeoPoint`. + +### Returns + +| Type | Description | +|---|---| +| `LatLng` | A `LatLng` object with the same latitude and longitude as the source `GeoPoint`. | + +### Example + +```kotlin +import com.mapconductor.core.features.GeoPoint +import com.mapconductor.googlemaps.toLatLng +import com.google.android.gms.maps.model.MarkerOptions + +// 1. Create a GeoPoint instance +val geoPoint = GeoPoint(latitude = 40.7128, longitude = -74.0060) + +// 2. Convert it to a LatLng object +val latLng = geoPoint.toLatLng() + +// 3. Use the LatLng object with the Google Maps SDK +// googleMap.addMarker(MarkerOptions().position(latLng).title("New York City")) +``` + +--- + +## `GeoPoint.Companion.from()` + +Creates a `GeoPoint` instance from a Google Maps `LatLng` object. This serves as a factory method for converting Google Maps data into the `GeoPoint` type. + +### Signature + +```kotlin +fun GeoPoint.Companion.from(latLng: LatLng): GeoPoint +``` + +### Description + +This extension function is called on the `GeoPoint` companion object. It takes a `LatLng` object and constructs a new `GeoPoint` instance with the corresponding latitude and longitude. + +### Parameters + +| Parameter | Type | Description | +|---|---|---| +| `latLng` | `LatLng` | The Google Maps `LatLng` object to convert. | + +### Returns + +| Type | Description | +|---|---| +| `GeoPoint` | A new `GeoPoint` instance with the latitude and longitude from the provided `LatLng` object. | + +### Example + +```kotlin +import com.mapconductor.core.features.GeoPoint +import com.mapconductor.googlemaps.from +import com.google.android.gms.maps.model.LatLng + +// 1. Assume you have a LatLng object from the Google Maps SDK +val latLng = LatLng(34.0522, -118.2437) + +// 2. Create a GeoPoint from the LatLng object +val geoPoint = GeoPoint.from(latLng) + +// Now you can use the geoPoint object in your application's core logic +// assert(geoPoint.latitude == 34.0522) +// assert(geoPoint.longitude == -118.2437) +``` + +--- + +## `LatLng.toGeoPoint()` + +Converts a Google Maps `LatLng` object into a `GeoPoint` object. This is a convenient way to handle location data returned from the Google Maps SDK and use it within your application's core logic. + +### Signature + +```kotlin +fun LatLng.toGeoPoint(): GeoPoint +``` + +### Description + +This extension function is called on an instance of `LatLng`. It creates and returns a new `GeoPoint` object using the latitude and longitude from the source `LatLng`. + +### Returns + +| Type | Description | +|---|---| +| `GeoPoint` | A `GeoPoint` object with the same latitude and longitude as the source `LatLng`. | + +### Example + +```kotlin +import com.mapconductor.core.features.GeoPoint +import com.mapconductor.googlemaps.toGeoPoint +import com.google.android.gms.maps.model.LatLng + +// 1. Assume you receive a LatLng from a map click event +val mapClickPosition = LatLng(48.8566, 2.3522) + +// 2. Convert the LatLng to a GeoPoint +val geoPoint = mapClickPosition.toGeoPoint() + +// 3. Use the GeoPoint for other operations, like saving to a database +// saveLocation(geoPoint) +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/GeoRectBounds.kt.md b/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/GeoRectBounds.kt.md new file mode 100644 index 00000000..7ca8f770 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/GeoRectBounds.kt.md @@ -0,0 +1,100 @@ +Of course! Here is the high-quality SDK documentation for the provided Kotlin code snippet. + +# Google Maps Type Conversions + +This document provides details on the extension functions used for converting between MapConductor Core and Google Maps data types. These utilities facilitate seamless interoperability between the two libraries. + +--- + +## `toLatLngBounds()` + +Converts a `GeoRectBounds` object from the MapConductor Core library into a `LatLngBounds` object for the Google Maps SDK. + +### Signature + +```kotlin +fun GeoRectBounds.toLatLngBounds(): LatLngBounds? +``` + +### Description + +This extension function transforms a `GeoRectBounds` instance into its equivalent `LatLngBounds` representation. The conversion relies on the `southWest` and `northEast` corners of the `GeoRectBounds`. If either of these corners is `null`, the function will return `null` to prevent potential runtime errors. + +### Parameters + +| Parameter | Type | Description | +|-----------|-------------------|-------------------------------------------| +| `this` | `GeoRectBounds` | The `GeoRectBounds` instance to convert. | + +### Returns + +A `LatLngBounds` object that represents the same geographical area, or `null` if the source `GeoRectBounds` has a `null` `southWest` or `northEast` property. + +### Example + +```kotlin +import com.mapconductor.core.features.GeoPoint +import com.mapconductor.core.features.GeoRectBounds +import com.google.android.gms.maps.model.LatLngBounds + +// Define the source GeoRectBounds +val geoRect = GeoRectBounds( + southWest = GeoPoint(latitude = 34.0522, longitude = -118.2437), // Los Angeles + northEast = GeoPoint(latitude = 40.7128, longitude = -74.0060) // New York City +) + +// Perform the conversion +val latLngBounds: LatLngBounds? = geoRect.toLatLngBounds() + +// The result is a Google Maps LatLngBounds object +if (latLngBounds != null) { + println("Conversion successful: $latLngBounds") +} else { + println("Conversion failed because one of the corners was null.") +} +``` + +--- + +## `toGeoRectBounds()` + +Converts a `LatLngBounds` object from the Google Maps SDK into a `GeoRectBounds` object for the MapConductor Core library. + +### Signature + +```kotlin +fun LatLngBounds.toGeoRectBounds(): GeoRectBounds +``` + +### Description + +This extension function transforms a Google Maps `LatLngBounds` instance into its equivalent `GeoRectBounds` representation. It maps the `southwest` and `northeast` properties of the `LatLngBounds` to create a new `GeoRectBounds` object. This conversion is non-nullable and always returns a valid `GeoRectBounds` instance. + +### Parameters + +| Parameter | Type | Description | +|-----------|-------------------|-------------------------------------------| +| `this` | `LatLngBounds` | The `LatLngBounds` instance to convert. | + +### Returns + +A non-null `GeoRectBounds` object that represents the same geographical area. + +### Example + +```kotlin +import com.mapconductor.core.features.GeoRectBounds +import com.google.android.gms.maps.model.LatLng +import com.google.android.gms.maps.model.LatLngBounds + +// Define the source LatLngBounds +val southwestCorner = LatLng(34.0522, -118.2437) // Los Angeles +val northeastCorner = LatLng(40.7128, -74.0060) // New York City +val latLngBounds = LatLngBounds(southwestCorner, northeastCorner) + +// Perform the conversion +val geoRect: GeoRectBounds = latLngBounds.toGeoRectBounds() + +// The result is a MapConductor GeoRectBounds object +println("Conversion successful: $geoRect") +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/GoogleMapDesign.kt.md b/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/GoogleMapDesign.kt.md new file mode 100644 index 00000000..9e4559dc --- /dev/null +++ b/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/GoogleMapDesign.kt.md @@ -0,0 +1,167 @@ +Of course! Here is the high-quality SDK documentation for the provided Kotlin code snippet. + +--- + +# GoogleMapDesign + +The `GoogleMapDesign` sealed class represents the various base map tile types available in the Google Maps SDK. It provides a type-safe way to manage and use Google's predefined map type constants. + +This class implements the `GoogleMapDesignType` interface, which is a type alias for `MapDesignTypeInterface`. + +```kotlin +sealed class GoogleMapDesign( + override val id: Int, +) : GoogleMapDesignType +``` + +## Map Design Objects + +The `GoogleMapDesign` class contains singleton objects that correspond to the map types defined in `com.google.android.gms.maps.GoogleMap`. + +| Object | Google Maps Constant | Description | +| :---------- | :---------------------- | :----------------------------------------------------------------------- | +| `Normal` | `MAP_TYPE_NORMAL` | Typical road map with streets, labels, and some points of interest. | +| `Satellite` | `MAP_TYPE_SATELLITE` | Satellite imagery of the Earth without map labels. | +| `Hybrid` | `MAP_TYPE_HYBRID` | A combination of satellite imagery and the normal map layer (roads, labels). | +| `Terrain` | `MAP_TYPE_TERRAIN` | Topographic map showing elevation and land contours. | +| `None` | `MAP_TYPE_NONE` | No base map tiles. Useful for displaying custom tile overlays exclusively. | + +--- + +## Functions + +### getValue() + +Retrieves the underlying integer constant for the map type, as defined in the `GoogleMap` class. + +#### Signature + +```kotlin +fun getValue(): Int +``` + +#### Description + +This method returns the integer ID associated with a `GoogleMapDesign` instance. This ID corresponds directly to one of the `MAP_TYPE_*` constants from the Google Maps SDK. + +#### Returns + +| Type | Description | +| :---- | :---------------------------------------- | +| `Int` | The integer constant for the map design type. | + +#### Example + +```kotlin +import com.google.android.gms.maps.GoogleMap + +// Get the integer value for the Satellite map type +val satelliteTypeId = GoogleMapDesign.Satellite.getValue() + +// satelliteTypeId is now equal to GoogleMap.MAP_TYPE_SATELLITE +println(satelliteTypeId == GoogleMap.MAP_TYPE_SATELLITE) // true +``` + +--- + +## Companion Object Functions + +The companion object provides factory methods for creating `GoogleMapDesign` instances from integer IDs. + +### Create() + +Creates a `GoogleMapDesign` instance from a given integer map type ID. + +#### Signature + +```kotlin +fun Create(id: Int): GoogleMapDesign +``` + +#### Description + +This factory method looks up the appropriate `GoogleMapDesign` object based on the provided integer `id`. If the `id` does not match any of the known map types, it defaults to `GoogleMapDesign.None`. + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :---- | :----------------------------------------------------------------------- | +| `id` | `Int` | The integer ID of the map type (e.g., `GoogleMap.MAP_TYPE_NORMAL`). | + +#### Returns + +| Type | Description | +| :---------------- | :----------------------------------------------------------------------------- | +| `GoogleMapDesign` | The corresponding `GoogleMapDesign` object, or `GoogleMapDesign.None` if the ID is not recognized. | + +#### Example + +```kotlin +import com.google.android.gms.maps.GoogleMap + +// Create a GoogleMapDesign instance from a constant +val mapDesign = GoogleMapDesign.Create(GoogleMap.MAP_TYPE_TERRAIN) + +// mapDesign is now GoogleMapDesign.Terrain +println(mapDesign is GoogleMapDesign.Terrain) // true + +// Create from an unknown ID +val unknownDesign = GoogleMapDesign.Create(99) + +// unknownDesign is now GoogleMapDesign.None +println(unknownDesign is GoogleMapDesign.None) // true +``` + +### toMapDesignType() + +Creates an instance that conforms to the `GoogleMapDesignType` interface from a given integer map type ID. + +#### Signature + +```kotlin +fun toMapDesignType(id: Int): GoogleMapDesignType +``` + +#### Description + +This function is similar to `Create()`, but it returns the result as the `GoogleMapDesignType` interface. This is useful for maintaining abstraction in your code. It defaults to `GoogleMapDesign.None` for unrecognized IDs. + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :---- | :----------------------------------------------------------------------- | +| `id` | `Int` | The integer ID of the map type (e.g., `GoogleMap.MAP_TYPE_HYBRID`). | + +#### Returns + +| Type | Description | +| :-------------------- | :------------------------------------------------------------------------------------------------------ | +| `GoogleMapDesignType` | An object implementing the `GoogleMapDesignType` interface, or `GoogleMapDesign.None` if the ID is not recognized. | + +#### Example + +```kotlin +import com.google.android.gms.maps.GoogleMap + +// Get the map design type for Hybrid +val designType: GoogleMapDesignType = GoogleMapDesign.toMapDesignType(GoogleMap.MAP_TYPE_HYBRID) + +// Use the returned value +val mapId = designType.getValue() // Returns GoogleMap.MAP_TYPE_HYBRID +``` + +--- + +# GoogleMapDesignType + +A type alias for `MapDesignTypeInterface`, representing a generic interface for a map design type where the underlying value is an `Int`. + +#### Signature + +```kotlin +typealias GoogleMapDesignType = MapDesignTypeInterface +``` + +#### Description + +This type alias provides a clear and specific name for the map design interface used within the context of Google Maps, enhancing code readability. All `GoogleMapDesign` objects conform to this interface. \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/GoogleMapTypeAlias.kt.md b/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/GoogleMapTypeAlias.kt.md new file mode 100644 index 00000000..7be4c9d1 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/GoogleMapTypeAlias.kt.md @@ -0,0 +1,20 @@ +# Google Maps Type Aliases + +This document provides a reference for the type aliases used to map abstract `mapconductor` concepts to the concrete classes provided by the Google Maps Android SDK. These aliases are part of an abstraction layer, allowing for consistent type usage within the `mapconductor` ecosystem while internally utilizing the Google Maps implementation. + +### Description + +The following `typealias` declarations create convenient, descriptive names for the underlying Google Maps SDK objects. This helps in maintaining clean, readable, and provider-agnostic code in the upper layers of the application. + +### Type Aliases + +The table below lists each type alias, the Google Maps SDK class it maps to, and a brief description of its purpose. + +| Alias Name | Underlying Google Maps Type | Description | +| :--- | :--- | :--- | +| `GoogleMapActualMarker` | `com.google.android.gms.maps.model.Marker` | An alias for a map marker object used to indicate a single location on the map. | +| `GoogleMapActualCircle` | `com.google.android.gms.maps.model.Polygon` | An alias for a circle shape. It uses a `Polygon` implementation to support geodesic-correct circles, which the standard `Circle` object does not. | +| `GoogleMapActualPolyline` | `com.google.android.gms.maps.model.Polyline` | An alias for a polyline object, which represents a series of connected line segments on the map. | +| `GoogleMapActualPolygon` | `com.google.android.gms.maps.model.Polygon` | An alias for a polygon object, which represents an enclosed, fillable area on the map. | +| `GoogleMapActualGroundImage` | `com.google.android.gms.maps.model.GroundOverlay` | An alias for a ground overlay, which is an image that is fixed to a specific geographical location on the map. | +| `GoogleMapActualRasterLayer` | `com.google.android.gms.maps.model.TileOverlay` | An alias for a tile overlay, used for adding a custom set of raster images (tiles) on top of the base map. | \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/GoogleMapView.kt.md b/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/GoogleMapView.kt.md new file mode 100644 index 00000000..95d47728 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/GoogleMapView.kt.md @@ -0,0 +1,146 @@ +Of course! Here is a high-quality SDK document for the `GoogleMapView` composable function, formatted in Markdown. + +--- + +# GoogleMapView + +`GoogleMapView` is a Jetpack Compose composable that displays a Google Map. It provides a declarative way to manage the map's state, handle user interactions, and draw overlays such as markers, polylines, and polygons. The component is lifecycle-aware and integrates seamlessly with the Compose UI framework. + +It offers two overloads: a comprehensive version with callbacks for all map and overlay events, and a simplified version for use cases that don't require detailed marker, polyline, or polygon event handling. + +### Signature + +```kotlin +// Full signature +@Composable +fun GoogleMapView( + state: GoogleMapViewState, + modifier: Modifier = Modifier, + markerTiling: MarkerTilingOptions? = null, + sdkInitialize: (suspend (android.content.Context) -> Boolean)? = null, + onMapLoaded: OnMapLoadedHandler? = null, + onMapClick: OnMapEventHandler? = null, + onCameraMoveStart: OnCameraMoveHandler? = null, + onCameraMove: OnCameraMoveHandler? = null, + onCameraMoveEnd: OnCameraMoveHandler? = null, + onMarkerClick: OnMarkerEventHandler?, + onMarkerDragStart: OnMarkerEventHandler? = null, + onMarkerDrag: OnMarkerEventHandler? = null, + onMarkerDragEnd: OnMarkerEventHandler? = null, + onMarkerAnimateStart: OnMarkerEventHandler? = null, + onMarkerAnimateEnd: OnMarkerEventHandler? = null, + onCircleClick: OnCircleEventHandler? = null, + onPolylineClick: OnPolylineEventHandler? = null, + onPolygonClick: OnPolygonEventHandler? = null, + onGroundImageClick: OnGroundImageEventHandler? = null, + content: (@Composable GoogleMapViewScope.() -> Unit)? = null, +) + +// Simplified signature +@Composable +fun GoogleMapView( + state: GoogleMapViewState, + modifier: Modifier = Modifier, + markerTiling: MarkerTilingOptions? = null, + sdkInitialize: (suspend (android.content.Context) -> Boolean)? = null, + onMapLoaded: OnMapLoadedHandler? = null, + onMapClick: OnMapEventHandler? = null, + onCameraMoveStart: OnCameraMoveHandler? = null, + onCameraMove: OnCameraMoveHandler? = null, + onCameraMoveEnd: OnCameraMoveHandler? = null, + onGroundImageClick: OnGroundImageEventHandler? = null, + content: (@Composable GoogleMapViewScope.() -> Unit)? = null, +) +``` + +### Description + +This composable function renders a Google Map view and manages its lifecycle. It serves as the root container for all map-related UI, including overlays and controls. You can interact with the map and listen to events through various callback parameters. Map overlays like markers and shapes are added declaratively within the `content` lambda. + +### Parameters + +This section describes the parameters for the comprehensive `GoogleMapView` function. + +| Parameter | Type | Description | +|---|---|---| +| `state` | `GoogleMapViewState` | **Required.** Manages the map's state, including camera position and map design type. Use `rememberGoogleMapViewState()` to create and remember an instance. | +| `modifier` | `Modifier` | An optional `Modifier` to be applied to the map container. | +| `markerTiling` | `MarkerTilingOptions?` | Optional configuration for marker tiling (clustering). If `null`, default options are used. | +| `sdkInitialize` | `(suspend (Context) -> Boolean)?` | An optional asynchronous lambda to initialize the Google Maps SDK. It should return `true` on success. If not provided, a default initialization is assumed. | +| `onMapLoaded` | `OnMapLoadedHandler?` | A callback invoked once the map has finished loading and is ready for interaction. | +| `onMapClick` | `OnMapEventHandler?` | A callback invoked when the user clicks on the map. Returns the `GeoPoint` of the click location. | +| `onCameraMoveStart` | `OnCameraMoveHandler?` | A callback invoked when the map camera starts moving. Provides the current `MapCameraPositionInterface`. | +| `onCameraMove` | `OnCameraMoveHandler?` | A callback invoked repeatedly while the map camera is moving. Provides the current `MapCameraPositionInterface`. | +| `onCameraMoveEnd` | `OnCameraMoveHandler?` | A callback invoked when the map camera finishes moving. Provides the final `MapCameraPositionInterface`. | +| `onMarkerClick` | `OnMarkerEventHandler?` | A callback invoked when a marker is clicked. Return `true` to indicate the event has been consumed. | +| `onMarkerDragStart` | `OnMarkerEventHandler?` | A callback invoked when the user starts dragging a marker. | +| `onMarkerDrag` | `OnMarkerEventHandler?` | A callback invoked repeatedly while a marker is being dragged. | +| `onMarkerDragEnd` | `OnMarkerEventHandler?` | A callback invoked when the user finishes dragging a marker. | +| `onMarkerAnimateStart`| `OnMarkerEventHandler?` | A callback invoked when a marker animation begins. | +| `onMarkerAnimateEnd` | `OnMarkerEventHandler?` | A callback invoked when a marker animation ends. | +| `onCircleClick` | `OnCircleEventHandler?` | A callback invoked when a circle overlay is clicked. | +| `onPolylineClick` | `OnPolylineEventHandler?` | A callback invoked when a polyline is clicked. | +| `onPolygonClick` | `OnPolygonEventHandler?` | A callback invoked when a polygon is clicked. | +| `onGroundImageClick`| `OnGroundImageEventHandler?` | A callback invoked when a ground image overlay is clicked. | +| `content` | `@Composable GoogleMapViewScope.() -> Unit` | A composable lambda within the `GoogleMapViewScope` where you can declaratively add map overlays like `Marker`, `Polyline`, `Polygon`, etc. | + +### Returns + +This is a `@Composable` function and does not have a return value. It emits the Google Map view into the composition. + +### Example + +Here is an example of how to use `GoogleMapView` in a Composable screen. + +```kotlin +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import com.mapconductor.core.features.GeoPoint +import com.mapconductor.googlemaps.GoogleMapView +import com.mapconductor.googlemaps.marker.Marker +import com.mapconductor.googlemaps.state.GoogleMapViewState +import com.mapconductor.googlemaps.state.rememberGoogleMapViewState + +@Composable +fun MyMapScreen() { + // 1. Remember the map's state. + // This will survive recompositions and configuration changes. + val mapState: GoogleMapViewState = rememberGoogleMapViewState( + initialPosition = GeoPoint(40.7128, -74.0060), // New York City + initialZoom = 12.0 + ) + + // 2. Add the GoogleMapView to your composition. + GoogleMapView( + state = mapState, + modifier = Modifier.fillMaxSize(), + onMapClick = { geoPoint -> + println("Map clicked at: ${geoPoint.latitude}, ${geoPoint.longitude}") + }, + onMarkerClick = { marker -> + println("Marker clicked: ${marker.id}") + // Return true to indicate the event was consumed and prevent default behavior. + true + } + ) { + // 3. Add overlays declaratively within the content lambda. + // This Marker will be displayed on the map. + Marker( + id = "nyc-marker", + position = GeoPoint(40.7128, -74.0060), + title = "New York City", + snippet = "The Big Apple" + ) + } +} + +@Preview +@Composable +fun MyMapScreenPreview() { + // Note: Map previews may not render in the Android Studio editor + // without special configuration. Run on an emulator or device. + MyMapScreen() +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/GoogleMapViewController.kt.md b/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/GoogleMapViewController.kt.md new file mode 100644 index 00000000..53bfc06a --- /dev/null +++ b/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/GoogleMapViewController.kt.md @@ -0,0 +1,368 @@ +Of course! Here is the high-quality SDK documentation for the provided `GoogleMapViewController` class. + +--- + +# GoogleMapViewController + +The `GoogleMapViewController` is the primary controller for managing and interacting with a `GoogleMap` instance. It serves as the main bridge between the `MapConductor` core logic and the Google Maps SDK for Android. + +This class is responsible for: +- Managing the lifecycle and rendering of map overlays, including markers, polylines, polygons, circles, ground images, and raster layers. +- Handling user interactions such as map clicks and marker drags. +- Controlling camera movements, both immediate and animated. +- Listening to and broadcasting map state changes, like camera position and map load status. + +## Class Signature + +```kotlin +class GoogleMapViewController( + override val holder: GoogleMapViewHolder, + private val markerController: GoogleMapMarkerController, + // ... other controllers + override val coroutine: CoroutineScope = CoroutineScope(Dispatchers.Main), + val backCoroutine: CoroutineScope = CoroutineScope(Dispatchers.Default) +) : BaseMapViewController(), GoogleMapViewControllerInterface, /* ... other listeners */ +``` + +## Constructor + +Initializes a new instance of the `GoogleMapViewController`. This typically occurs during the setup of your map view. + +### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `holder` | `GoogleMapViewHolder` | The view holder containing the `GoogleMap` and `MapView` instances. | +| `markerController` | `GoogleMapMarkerController` | The controller responsible for managing markers. | +| `polylineController` | `GoogleMapPolylineController` | The controller responsible for managing polylines. | +| `polygonController` | `GoogleMapPolygonController` | The controller responsible for managing polygons. | +| `groundImageController` | `GoogleMapGroundImageController` | The controller responsible for managing ground images. | +| `circleController` | `GoogleMapCircleController` | The controller responsible for managing circles. | +| `rasterLayerController` | `GoogleMapRasterLayerController` | The controller responsible for managing raster tile layers. | +| `coroutine` | `CoroutineScope` | The coroutine scope for main thread operations. Defaults to `CoroutineScope(Dispatchers.Main)`. | +| `backCoroutine` | `CoroutineScope` | The coroutine scope for background thread operations. Defaults to `CoroutineScope(Dispatchers.Default)`. | + +## Properties + +### mapLoadedState + +A `StateFlow` that emits the loading status of the map. It emits `true` once the map has been fully loaded and is ready for interaction. You can collect this flow to perform actions after the map is ready. + +**Signature** +```kotlin +val mapLoadedState: StateFlow +``` + +**Example** +```kotlin +coroutineScope.launch { + mapViewController.mapLoadedState.collect { isLoaded -> + if (isLoaded) { + println("Map is now loaded and ready!") + // Perform actions that require the map to be loaded + } + } +} +``` + +## Camera Methods + +### moveCamera + +Instantly moves the map camera to a specified position without animation. + +**Signature** +```kotlin +override fun moveCamera(position: MapCameraPosition) +``` + +**Parameters** + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `position` | `MapCameraPosition` | The target camera position, including location, zoom, tilt, and bearing. | + +### animateCamera + +Animates the camera's movement from its current position to a new specified position over a given duration. + +**Signature** +```kotlin +override fun animateCamera(position: MapCameraPosition, duration: Long) +``` + +**Parameters** + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `position` | `MapCameraPosition` | The target camera position to animate to. | +| `duration` | `Long` | The duration of the animation in milliseconds. | + +## Overlay Management + +### clearOverlays + +Removes all overlays (markers, polylines, polygons, etc.) from the map. + +**Signature** +```kotlin +override suspend fun clearOverlays() +``` + +### compositionMarkers + +Adds a list of markers to the map. If a marker with the same ID already exists, it will be updated. + +**Signature** +```kotlin +override suspend fun compositionMarkers(data: List) +``` + +**Parameters** + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `data` | `List` | A list of `MarkerState` objects to be rendered on the map. | + +### updateMarker + +Updates a single existing marker on the map based on its state. + +**Signature** +```kotlin +override suspend fun updateMarker(state: MarkerState) +``` + +**Parameters** + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `state` | `MarkerState` | The updated state for the marker. The marker is identified by `state.id`. | + +### hasMarker + +Checks if a marker with the given state's ID exists on the map. + +**Signature** +```kotlin +override fun hasMarker(state: MarkerState): Boolean +``` + +**Parameters** + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `state` | `MarkerState` | The state of the marker to check for. The check is based on `state.id`. | + +**Returns** + +`Boolean` - `true` if the marker exists, `false` otherwise. + +--- +*Note: The `composition`, `update`, and `has` methods for `Circles`, `Polygons`, `Polylines`, `GroundImages`, and `RasterLayers` follow the same pattern as the `Marker` methods shown above.* +--- + +## Event Listeners (Deprecated) + +The following methods for setting global event listeners are deprecated. It is recommended to set event handlers directly on the individual `State` objects (e.g., `MarkerState.onClick`, `CircleState.onClick`). + +### setOnMarkerClickListener + +**[Deprecated]** Sets a global click listener for all markers. + +**Signature** +```kotlin +@Deprecated("Use MarkerState.onClick instead.") +override fun setOnMarkerClickListener(listener: OnMarkerEventHandler?) +``` + +**Recommendation**: Assign a lambda to the `onClick` property of the `MarkerState` object when creating it. + +**Example (Recommended)** +```kotlin +val marker = MarkerState( + id = "marker1", + position = GeoPoint(34.05, -118.24), + onClick = { markerState -> + println("Marker ${markerState.id} was clicked!") + } +) +compositionMarkers(listOf(marker)) +``` + +### setOnMarkerDragStart + +**[Deprecated]** Sets a global listener for the start of a marker drag event. + +**Signature** +```kotlin +@Deprecated("Use MarkerState.onDragStart instead.") +override fun setOnMarkerDragStart(listener: OnMarkerEventHandler?) +``` + +**Recommendation**: Use the `onDragStart` property on `MarkerState`. + +### setOnMarkerDrag + +**[Deprecated]** Sets a global listener for marker drag events. + +**Signature** +```kotlin +@Deprecated("Use MarkerState.onDrag instead.") +override fun setOnMarkerDrag(listener: OnMarkerEventHandler?) +``` + +**Recommendation**: Use the `onDrag` property on `MarkerState`. + +### setOnMarkerDragEnd + +**[Deprecated]** Sets a global listener for the end of a marker drag event. + +**Signature** +```kotlin +@Deprecated("Use MarkerState.onDragEnd instead.") +override fun setOnMarkerDragEnd(listener: OnMarkerEventHandler?) +``` + +**Recommendation**: Use the `onDragEnd` property on `MarkerState`. + +### setOnCircleClickListener + +**[Deprecated]** Sets a global click listener for all circles. + +**Signature** +```kotlin +@Deprecated("Use CircleState.onClick instead.") +override fun setOnCircleClickListener(listener: OnCircleEventHandler?) +``` + +**Recommendation**: Use the `onClick` property on `CircleState`. + +### setOnPolygonClickListener + +**[Deprecated]** Sets a global click listener for all polygons. + +**Signature** +```kotlin +@Deprecated("Use PolygonState.onClick instead.") +override fun setOnPolygonClickListener(listener: OnPolygonEventHandler?) +``` + +**Recommendation**: Use the `onClick` property on `PolygonState`. + +### setOnPolylineClickListener + +**[Deprecated]** Sets a global click listener for all polylines. + +**Signature** +```kotlin +@Deprecated("Use PolylineState.onClick instead.") +override fun setOnPolylineClickListener(listener: OnPolylineEventHandler?) +``` + +**Recommendation**: Use the `onClick` property on `PolylineState`. + +### setOnGroundImageClickListener + +**[Deprecated]** Sets a global click listener for all ground images. + +**Signature** +```kotlin +@Deprecated("Use GroundImageState.onClick instead.") +override fun setOnGroundImageClickListener(listener: OnGroundImageEventHandler?) +``` + +**Recommendation**: Use the `onClick` property on `GroundImageState`. + +## Map Configuration + +### setMapDesignType + +Sets the visual design or type of the map (e.g., Normal, Satellite). + +**Signature** +```kotlin +override fun setMapDesignType(value: GoogleMapDesignType) +``` + +**Parameters** + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `value` | `GoogleMapDesignType` | The desired map design, such as `GoogleMapDesign.Normal` or `GoogleMapDesign.Satellite`. | + +### setMapDesignTypeChangeListener + +Registers a listener that is invoked whenever the map's design type changes. The listener is also immediately called with the current map design type upon registration. + +**Signature** +```kotlin +override fun setMapDesignTypeChangeListener(listener: GoogleMapDesignTypeChangeHandler) +``` + +**Parameters** + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `listener` | `GoogleMapDesignTypeChangeHandler` | A lambda or function that will be called with the new `GoogleMapDesignType`. | + +## Advanced Marker Rendering + +### createMarkerRenderer + +Creates a renderer for markers that use a custom rendering strategy. + +**Signature** +```kotlin +fun createMarkerRenderer( + strategy: MarkerRenderingStrategyInterface +): MarkerOverlayRendererInterface +``` + +**Parameters** + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `strategy` | `MarkerRenderingStrategyInterface` | The custom strategy for rendering markers. | + +**Returns** + +`MarkerOverlayRendererInterface` - A new marker renderer instance. + +### createMarkerEventController + +Creates an event controller for markers managed by a custom strategy. + +**Signature** +```kotlin +fun createMarkerEventController( + controller: StrategyMarkerController, + renderer: MarkerOverlayRendererInterface +): MarkerEventControllerInterface +``` + +**Parameters** + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `controller` | `StrategyMarkerController` | The strategy-based controller for the markers. | +| `renderer` | `MarkerOverlayRendererInterface` | The renderer associated with the markers. | + +**Returns** + +`MarkerEventControllerInterface` - A new marker event controller. + +### registerMarkerEventController + +Registers a custom marker event controller to handle marker interactions. This allows for extending or overriding the default marker event handling. + +**Signature** +```kotlin +fun registerMarkerEventController(controller: MarkerEventControllerInterface) +``` + +**Parameters** + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `controller` | `MarkerEventControllerInterface` | The custom event controller to register. | \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/GoogleMapViewControllerInterface.kt.md b/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/GoogleMapViewControllerInterface.kt.md new file mode 100644 index 00000000..806b2d2b --- /dev/null +++ b/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/GoogleMapViewControllerInterface.kt.md @@ -0,0 +1,92 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +# GoogleMapViewControllerInterface + +## Description + +The `GoogleMapViewControllerInterface` serves as the primary controller for a Google Map view. It provides a comprehensive API for interacting with the map, consolidating functionalities for managing various map objects and controlling the map's visual appearance. + +This interface aggregates capabilities from several other interfaces, making it a central point for managing: +- Markers (`MarkerCapableInterface`) +- Polygons (`PolygonCapableInterface`) +- Polylines (`PolylineCapableInterface`) +- Circles (`CircleCapableInterface`) +- Ground Images (`GroundImageCapableInterface`) +- Raster Layers (`RasterLayerCapableInterface`) + +In addition to these object management capabilities, it provides specific methods for controlling the Google Map's design type (e.g., standard, satellite, hybrid). + +## Methods + +### setMapDesignType + +Sets the visual style or theme of the map. + +**Signature** +```kotlin +fun setMapDesignType(value: GoogleMapDesignType) +``` + +**Parameters** + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `value` | `GoogleMapDesignType` | The new design type to apply to the map. | + +
+ +### setMapDesignTypeChangeListener + +Registers a listener that is invoked whenever the map's design type changes. This allows you to react to style changes, for example, by updating UI elements. + +**Signature** +```kotlin +fun setMapDesignTypeChangeListener(listener: GoogleMapDesignTypeChangeHandler) +``` + +**Parameters** + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `listener` | `GoogleMapDesignTypeChangeHandler` | A callback function that will be executed with the new `GoogleMapDesignType` when the map style changes. | + +## Type Aliases + +### GoogleMapDesignTypeChangeHandler + +A function type definition for a listener that handles map design type change events. + +**Signature** +```kotlin +typealias GoogleMapDesignTypeChangeHandler = (GoogleMapDesignType) -> Unit +``` + +**Description** + +This is a lambda or function that accepts a single parameter: +- `GoogleMapDesignType`: The new design type that has been applied to the map. + +## Example + +The following example demonstrates how to set a listener for map design changes and then trigger it by changing the map's design type. + +```kotlin +// Assume 'mapController' is an available instance of GoogleMapViewControllerInterface +// and 'GoogleMapDesignType' is an enum with values like STANDARD, SATELLITE, etc. + +// 1. Set up a listener to react to map design changes. +// The lambda will be called every time the design type is updated. +mapController.setMapDesignTypeChangeListener { newDesignType -> + println("Map design type has been updated to: $newDesignType") + // You can update other UI components or trigger logic based on the new style. +} + +// 2. Change the map's design type to SATELLITE. +// This action will invoke the listener defined above. +println("Attempting to set map design to SATELLITE...") +mapController.setMapDesignType(GoogleMapDesignType.SATELLITE) + +// Expected console output: +// Attempting to set map design to SATELLITE... +// Map design type has been updated to: SATELLITE +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/GoogleMapViewControllerStore.kt.md b/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/GoogleMapViewControllerStore.kt.md new file mode 100644 index 00000000..241f7ed9 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/GoogleMapViewControllerStore.kt.md @@ -0,0 +1,102 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +--- + +### `GoogleMapViewHolderInterface` + +A type alias for `MapViewHolderInterface` that is specialized for the Google Maps SDK. + +#### Signature +```kotlin +typealias GoogleMapViewHolderInterface = MapViewHolderInterface +``` + +#### Description +This type alias simplifies the declaration of view holders that work with Google Maps. It pre-defines the generic types for `MapViewHolderInterface` to be `MapView` (the view component) and `GoogleMap` (the map controller object). + +#### Example +Using this alias makes class definitions cleaner and more readable. + +```kotlin +// Without the type alias +class MyMapViewHolder : MapViewHolderInterface { + // ... implementation +} + +// With the type alias +class MyMapViewHolder : GoogleMapViewHolderInterface { + // ... implementation +} +``` + +--- + +### `GoogleMapViewControllerStore` + +A singleton object that provides a static holder for a `GoogleMapViewController` instance. + +#### Signature +```kotlin +object GoogleMapViewControllerStore : StaticHolder() +``` + +#### Description +`GoogleMapViewControllerStore` serves as a centralized, globally accessible container for a single `GoogleMapViewController` instance. This is useful for persisting the controller across configuration changes (like screen rotations) or sharing it between different components of an application without passing it through intent extras or constructor parameters. + +#### Example +```kotlin +// Create and store a map view controller instance +val myController = GoogleMapViewController(context) +GoogleMapViewControllerStore.instance = myController + +// Retrieve the stored instance from another part of the app +val storedController = GoogleMapViewControllerStore.instance +if (storedController != null) { + // Use the controller + storedController.setZoom(15.0) +} +``` + +--- + +### `findActivity()` + +An internal extension function on `Context` that finds the hosting `Activity` from a `Context` instance. + +#### Signature +```kotlin +internal fun Context.findActivity(): Activity? +``` + +#### Description +This utility function traverses the `ContextWrapper` chain to locate the base `Activity`. It's useful when you have a `Context` (e.g., from a `View`) and need a reference to the `Activity` that hosts it, which is often required for dialogs, permissions, or starting new activities. + +#### Receiver +| Parameter | Type | Description | +| :-------- | :-------- | :------------------------------------------- | +| `this` | `Context` | The context from which to find the Activity. | + +#### Returns +| Type | Description | +| :-------- | :-------------------------------------------------------------------------- | +| `Activity?` | The `Activity` instance if one is found in the context chain; otherwise, `null`. | + +#### Example +```kotlin +// In a custom view or any class with access to a Context +fun showDialog(context: Context) { + val activity = context.findActivity() + + if (activity != null) { + // Now you can use the activity context to show a dialog + AlertDialog.Builder(activity) + .setTitle("Success") + .setMessage("Activity found!") + .setPositiveButton("OK", null) + .show() + } else { + // Handle the case where no activity was found + Log.e("MyApp", "Could not find Activity from the provided context.") + } +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/GoogleMapViewHolder.kt.md b/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/GoogleMapViewHolder.kt.md new file mode 100644 index 00000000..c6144b2d --- /dev/null +++ b/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/GoogleMapViewHolder.kt.md @@ -0,0 +1,122 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +--- + +# GoogleMapViewHolder + +A concrete implementation of `MapViewHolderInterface` that wraps the Google Maps SDK (`MapView` and `GoogleMap`). It provides essential utility functions for converting between geographical coordinates and screen pixel coordinates. + +This class acts as an adapter, allowing the core map logic to interact with the Google Maps Android API in a standardized way. + +## Constructor + +### Signature + +```kotlin +class GoogleMapViewHolder( + override val mapView: MapView, + override val map: GoogleMap, +) : MapViewHolderInterface +``` + +### Description + +Creates an instance of `GoogleMapViewHolder`, which holds references to the `MapView` and the `GoogleMap` object. + +### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `mapView` | `MapView` | The Android `MapView` instance being controlled. | +| `map` | `GoogleMap` | The `GoogleMap` object used for map interactions and projections. | + +## Methods + +### toScreenOffset + +Converts a geographical coordinate (`GeoPointInterface`) into a screen pixel coordinate (`Offset`) relative to the map view's top-left corner. + +#### Signature + +```kotlin +override fun toScreenOffset(position: GeoPointInterface): Offset? +``` + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `position` | `GeoPointInterface` | The geographical coordinate to convert. | + +#### Returns + +**`Offset?`** + +A Compose `Offset` object representing the x and y pixel coordinates on the screen. Returns `null` if the projection is not available or fails. + +### fromScreenOffset + +Converts a screen pixel coordinate (`Offset`) into a geographical coordinate (`GeoPoint`). This is a `suspend` function, as the underlying projection operation can be asynchronous. + +#### Signature + +```kotlin +override suspend fun fromScreenOffset(offset: Offset): GeoPoint? +``` + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `offset` | `Offset` | The screen pixel coordinate (relative to the map view's top-left corner) to convert. | + +#### Returns + +**`GeoPoint?`** + +A `GeoPoint` object representing the geographical location at the given screen offset. Returns `null` if the conversion is not possible. + +## Example + +The following example demonstrates how to create a `GoogleMapViewHolder` and use its conversion methods within a coroutine scope. + +```kotlin +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.geometry.Offset +import com.google.android.gms.maps.GoogleMap +import com.google.android.gms.maps.MapView +import com.mapconductor.core.features.GeoPoint +import kotlinx.coroutines.launch + +// Assume you have a MapView and a GoogleMap instance from your map setup +lateinit var mapView: MapView +lateinit var googleMap: GoogleMap + +// In your Composable or setup code: +val mapViewHolder = GoogleMapViewHolder(mapView, googleMap) +val coroutineScope = rememberCoroutineScope() + +// --- Example 1: Convert a GeoPoint to a screen Offset --- +val newYorkCity = GeoPoint(latitude = 40.7128, longitude = -74.0060) +val screenOffset = mapViewHolder.toScreenOffset(newYorkCity) + +if (screenOffset != null) { + println("Screen coordinates for NYC: x=${screenOffset.x}, y=${screenOffset.y}") +} else { + println("Could not project GeoPoint to screen.") +} + +// --- Example 2: Convert a screen Offset to a GeoPoint --- +// This must be called from a coroutine +coroutineScope.launch { + // An offset representing a tap on the screen, e.g., the center of a 1080x1920 display + val touchOffset = Offset(x = 540f, y = 960f) + val locationAtOffset = mapViewHolder.fromScreenOffset(touchOffset) + + if (locationAtOffset != null) { + println("Geographical coordinates at offset: lat=${locationAtOffset.latitude}, lon=${locationAtOffset.longitude}") + } else { + println("Could not project screen offset to a location.") + } +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/GoogleMapViewScope.kt.md b/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/GoogleMapViewScope.kt.md new file mode 100644 index 00000000..a0ed8d81 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/GoogleMapViewScope.kt.md @@ -0,0 +1,38 @@ +# GoogleMapViewScope + +## Signature + +```kotlin +class GoogleMapViewScope : MapViewScope() +``` + +## Description + +`GoogleMapViewScope` is a specialized scope class for the Google Maps implementation within the MapConductor framework. It extends the base `MapViewScope`, inheriting all common map functionalities, while also providing access to features unique to the Google Maps SDK. + +This scope is the context for map configuration and control when using Google Maps as the provider. Use it to access Google-specific functionalities that are not part of the common map abstraction layer, such as Street View. + +## Example + +The `GoogleMapViewScope` is provided as the receiver (`this`) within the map setup block. You can call common methods from the parent `MapViewScope` as well as Google-specific methods defined in this class. + +```kotlin +// Assume 'mapFragment' is a Fragment containing the map +mapFragment.getMapAsync { + // 'this' is an instance of GoogleMapViewScope + + // Example of calling a common function from the parent MapViewScope + setCameraPosition( + latitude = 35.681236, + longitude = 139.767125, + zoom = 15.0 + ) + + // Example of calling a hypothetical Google Maps-specific function + // that would be defined in this class. + launchStreetView( + latitude = 35.681236, + longitude = 139.767125 + ) +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/GoogleMapViewStateImpl.kt.md b/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/GoogleMapViewStateImpl.kt.md new file mode 100644 index 00000000..59c360d1 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/GoogleMapViewStateImpl.kt.md @@ -0,0 +1,141 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet, formatted in Markdown. + +--- + +# Google Maps State Management for Compose + +This document provides detailed documentation for the state management components used with the Google Maps SDK in a Jetpack Compose environment. The primary entry point for developers is the `rememberGoogleMapViewState` composable function. + +## `rememberGoogleMapViewState` + +A composable function that creates and remembers an instance of `GoogleMapViewState`. This is the recommended way to manage the state of a map view within a Jetpack Compose application. It ensures that the map's state, such as camera position and map style, is preserved across recompositions, configuration changes, and process death. + +### Signature +```kotlin +@Composable +fun rememberGoogleMapViewState( + mapDesign: GoogleMapDesign = GoogleMapDesign.Normal, + cameraPosition: MapCameraPositionInterface = MapCameraPosition.Default, +): GoogleMapViewState +``` + +### Description +This function acts as a state holder factory for your map. It leverages `rememberSaveable` with a custom `GoogleMapViewSaver` to automatically save and restore the essential map state. You should create one instance of this state per map view in your UI. + +### Parameters +| Parameter | Type | Description | Default | +|---|---|---|---| +| `mapDesign` | `GoogleMapDesign` | The initial visual style of the map (e.g., Normal, Satellite, Terrain). | `GoogleMapDesign.Normal` | +| `cameraPosition` | `MapCameraPositionInterface` | The initial position and configuration of the map's camera, including location, zoom, tilt, and bearing. | `MapCameraPosition.Default` | + +### Returns +| Type | Description | +|---|---| +| `GoogleMapViewState` | A stable `GoogleMapViewState` object that can be passed to a `GoogleMapView` composable and used to programmatically control the map. | + +### Example +Here's how to create and use `rememberGoogleMapViewState` in your composable screen. + +```kotlin +import androidx.compose.runtime.Composable +import com.mapconductor.core.features.GeoPoint +import com.mapconductor.core.map.MapCameraPosition +import com.mapconductor.googlemaps.GoogleMapDesign +import com.mapconductor.googlemaps.rememberGoogleMapViewState + +@Composable +fun MyMapScreen() { + // Create and remember the map state + val mapState = rememberGoogleMapViewState( + mapDesign = GoogleMapDesign.Satellite, + cameraPosition = MapCameraPosition( + position = GeoPoint(40.7128, -74.0060), // New York City + zoom = 12.0f + ) + ) + + // The mapState can now be passed to your map view composable + // and used to control the map. + // + // GoogleMapView( + // modifier = Modifier.fillMaxSize(), + // state = mapState + // ) +} +``` + +--- + +## `GoogleMapViewState` + +A state-holder class that manages all state-related information for a Google Map view, such as camera position, map design, and padding. It provides properties to read the current state and methods to modify it. + +### Description +An instance of `GoogleMapViewState` is the single source of truth for your map's UI. It is created and managed by the `rememberGoogleMapViewState` function. You can use this object to read the map's current camera position or programmatically update the map's camera and design. + +### Properties +| Property | Type | Description | +|---|---|---| +| `id` | `String` | A unique, read-only identifier for the map state instance. | +| `cameraPosition` | `MapCameraPosition` | A read-only property representing the current camera position, including target coordinates, zoom, tilt, and bearing. This value is updated automatically as the user interacts with the map. | +| `padding` | `StateFlow` | A `StateFlow` that emits the current padding applied to the map. This is useful for ensuring UI elements don't obscure map controls or content. | +| `mapDesignType` | `GoogleMapDesignType` | A mutable property to get or set the current map design. Setting this property will dynamically update the map's visual style. | + +### Methods + +#### `moveCameraTo` +Moves the map camera to a new position. This method has two overloads for convenience. + +**Overload 1: Move to a `GeoPoint`** + +Moves the camera to a specific geographical coordinate, keeping the current zoom, tilt, and bearing. + +##### Signature +```kotlin +fun moveCameraTo( + position: GeoPoint, + durationMillis: Long? = null, +) +``` +##### Parameters +| Parameter | Type | Description | +|---|---|---| +| `position` | `GeoPoint` | The target geographical coordinates (latitude and longitude) to center the map on. | +| `durationMillis` | `Long?` | The duration of the camera animation in milliseconds. If `null` or `0`, the camera moves instantly. | + +**Overload 2: Move to a `MapCameraPosition`** + +Moves the camera to a detailed camera position, allowing you to specify coordinates, zoom, tilt, and bearing simultaneously. + +##### Signature +```kotlin +fun moveCameraTo( + cameraPosition: MapCameraPosition, + durationMillis: Long? = null, +) +``` +##### Parameters +| Parameter | Type | Description | +|---|---|---| +| `cameraPosition` | `MapCameraPosition` | The complete target camera configuration. | +| `durationMillis` | `Long?` | The duration of the camera animation in milliseconds. If `null` or `0`, the camera moves instantly. | + +##### Example +```kotlin +// Assuming mapState is obtained from rememberGoogleMapViewState +val mapState: GoogleMapViewState = ... + +// Animate camera to a new position over 1 second +val sanFrancisco = GeoPoint(37.7749, -122.4194) +mapState.moveCameraTo( + position = sanFrancisco, + durationMillis = 1000L +) + +// Instantly move camera with a new zoom level +val newCameraPosition = MapCameraPosition( + position = sanFrancisco, + zoom = 15.0f +) +mapState.moveCameraTo(newCameraPosition) +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/MapCameraPosition.kt.md b/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/MapCameraPosition.kt.md new file mode 100644 index 00000000..685df0d3 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/MapCameraPosition.kt.md @@ -0,0 +1,157 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +--- + +This document provides details on a set of Kotlin extension functions designed to facilitate conversion between the platform-agnostic `MapCameraPosition` and the Google Maps specific `CameraPosition`. These utilities are essential for interoperability within the MapConductor framework when using the Google Maps provider. + +## `toCameraPosition` + +### Signature + +```kotlin +fun MapCameraPosition.toCameraPosition(): CameraPosition +``` + +### Description + +Converts a platform-agnostic `MapCameraPosition` object into a Google Maps `CameraPosition` object. This is useful when you need to apply a camera state defined in the core library to an actual Google Map view. + +The function maps the `position`, `zoom`, `tilt`, and `bearing` from the `MapCameraPosition` to the corresponding fields in the `CameraPosition.Builder`. + +### Returns + +| Type | Description | +| :--- | :--- | +| `CameraPosition` | A new `CameraPosition` instance configured with the properties of the source `MapCameraPosition`. | + +### Example + +```kotlin +import com.mapconductor.core.features.GeoPoint +import com.mapconductor.core.map.MapCameraPosition +import com.mapconductor.googlemaps.toCameraPosition + +// 1. Create a platform-agnostic MapCameraPosition +val mapCameraPos = MapCameraPosition( + position = GeoPoint(latitude = 40.7128, longitude = -74.0060), + zoom = 12.0, + tilt = 30.0, + bearing = 45.0 +) + +// 2. Convert it to a Google Maps specific CameraPosition +val googleCameraPosition = mapCameraPos.toCameraPosition() + +// googleCameraPosition can now be used with a GoogleMap instance, for example: +// googleMap.moveCamera(CameraUpdateFactory.newCameraPosition(googleCameraPosition)) + +println("Zoom: ${googleCameraPosition.zoom}") // Outputs: Zoom: 12.0 +println("Tilt: ${googleCameraPosition.tilt}") // Outputs: Tilt: 30.0 +println("Bearing: ${googleCameraPosition.bearing}") // Outputs: Bearing: 45.0 +``` + +--- + +## `MapCameraPosition.from` + +### Signature + +```kotlin +fun MapCameraPosition.Companion.from(position: MapCameraPositionInterface): MapCameraPosition +``` + +### Description + +A factory function that creates a concrete `MapCameraPosition` instance from any object that implements the `MapCameraPositionInterface`. + +If the provided `position` is already a `MapCameraPosition`, it is returned directly. Otherwise, a new `MapCameraPosition` is constructed by copying the properties from the source interface. This ensures type safety and provides a consistent object to work with. + +### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `position` | `MapCameraPositionInterface` | The source camera position object to convert from. | + +### Returns + +| Type | Description | +| :--- | :--- | +| `MapCameraPosition` | A concrete `MapCameraPosition` instance. | + +### Example + +```kotlin +import com.mapconductor.core.features.GeoPoint +import com.mapconductor.core.map.MapCameraPosition +import com.mapconductor.core.map.MapCameraPositionInterface +import com.mapconductor.googlemaps.from + +// Assume you have a custom implementation of MapCameraPositionInterface +class CustomCameraPosition : MapCameraPositionInterface { + override val position: GeoPoint = GeoPoint(34.0522, -118.2437) + override val zoom: Double = 10.0 + override val bearing: Double = 0.0 + override val tilt: Double = 0.0 + // ... other properties +} + +val customPosition = CustomCameraPosition() + +// Create a concrete MapCameraPosition from the interface +val mapCameraPosition = MapCameraPosition.from(customPosition) + +println(mapCameraPosition.position.latitude) // Outputs: 34.0522 +println(mapCameraPosition.zoom) // Outputs: 10.0 +``` + +--- + +## `toMapCameraPosition` + +### Signature + +```kotlin +fun CameraPosition.toMapCameraPosition(paddings: MapPaddingsInterface = MapPaddings.Zeros): MapCameraPosition +``` + +### Description + +Converts a Google Maps `CameraPosition` object into a platform-agnostic `MapCameraPosition` object. This is typically used when capturing the current state of the map view to be used in the core, platform-independent logic. + +This function performs a key calculation: it converts the Google Maps `zoom` level into an `altitude` value for the `GeoPoint` in the resulting `MapCameraPosition`. The conversion takes into account the zoom level, latitude, and tilt. The `visibleRegion` property of the returned object is set to `null`. + +### Parameters + +| Parameter | Type | Default Value | Description | +| :--- | :--- | :--- | :--- | +| `paddings` | `MapPaddingsInterface` | `MapPaddings.Zeros` | Optional map paddings to associate with the resulting `MapCameraPosition`. | + +### Returns + +| Type | Description | +| :--- | :--- | +| `MapCameraPosition` | A new `MapCameraPosition` instance representing the state of the source `CameraPosition`. | + +### Example + +```kotlin +import com.google.android.gms.maps.model.CameraPosition +import com.google.android.gms.maps.model.LatLng +import com.mapconductor.googlemaps.toMapCameraPosition + +// 1. Create a Google Maps CameraPosition (e.g., from a map listener) +val googleCameraPosition = CameraPosition.Builder() + .target(LatLng(48.8566, 2.3522)) + .zoom(15f) + .tilt(45f) + .bearing(90f) + .build() + +// 2. Convert it to a platform-agnostic MapCameraPosition +val mapCameraPos = googleCameraPosition.toMapCameraPosition() + +// The resulting object can be used in platform-agnostic business logic +println("Latitude: ${mapCameraPos.position.latitude}") // Outputs: Latitude: 48.8566 +println("Zoom: ${mapCameraPos.zoom}") // Outputs: Zoom: 15.0 +println("Altitude: ${mapCameraPos.position.altitude}") // Outputs a calculated altitude, e.g., 385.0 +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/circle/CirclePolygonHelper.kt.md b/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/circle/CirclePolygonHelper.kt.md new file mode 100644 index 00000000..4be8985a --- /dev/null +++ b/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/circle/CirclePolygonHelper.kt.md @@ -0,0 +1,82 @@ +# SDK Documentation + +## `CirclePolygonHelper` + +A helper object for generating polygon vertices that approximate a circle on a map. This is useful for drawing circular shapes using polygon-based map APIs, such as Google Maps Polygons. + +The helper supports two types of circle calculations: +- **Geodesic**: A true circle on the Earth's spherical surface (a great circle path). This is accurate for all distances and latitudes. +- **Non-geodesic**: A circle drawn on a flat (Mercator) projection. This is computationally simpler but can appear distorted, especially over large distances or at high latitudes. + +--- + +### `generateCirclePoints` + +Generates a list of `LatLng` points that form the vertices of a polygon approximating a circle. + +#### Signature + +```kotlin +fun generateCirclePoints( + center: LatLng, + radiusMeters: Double, + geodesic: Boolean +): List +``` + +#### Description + +This function calculates a series of geographical coordinates (`LatLng` points) that, when connected, form a polygon that approximates a circle. You can specify the center, radius, and whether the circle should be calculated as a true geodesic circle or a simpler non-geodesic projection. The resulting list of points is ready to be used with map APIs to draw a polygon. + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `center` | `LatLng` | The geographical center point of the circle. | +| `radiusMeters` | `Double` | The radius of the circle, specified in meters. | +| `geodesic` | `Boolean` | Determines the calculation method.
- `true`: Generates a geodesic circle. Recommended for accuracy over large distances and at high latitudes.
- `false`: Generates a non-geodesic circle using a simpler planar projection. Suitable for small radii where the Earth's curvature is negligible. | + +#### Returns + +| Type | Description | +| :--- | :--- | +| `List` | A list of `LatLng` points representing the vertices of the polygon that approximates the circle. The polygon is formed by connecting these points in order. | + +#### Example + +The following example demonstrates how to generate points for both a geodesic and a non-geodesic circle. + +```kotlin +import com.google.android.gms.maps.model.LatLng +import com.mapconductor.googlemaps.circle.CirclePolygonHelper + +fun main() { + // Define the center and radius for our circle + val centerPoint = LatLng(34.0522, -118.2437) // Los Angeles + val radiusInMeters = 10000.0 // 10 kilometers + + // 1. Generate points for a geodesic circle (more accurate) + // This is the recommended approach for most use cases. + val geodesicCirclePoints: List = CirclePolygonHelper.generateCirclePoints( + center = centerPoint, + radiusMeters = radiusInMeters, + geodesic = true + ) + + println("Generated ${geodesicCirclePoints.size} points for a geodesic circle.") + // These points can now be used to add a Polygon to a Google Map. + // e.g., googleMap.addPolygon(PolygonOptions().addAll(geodesicCirclePoints)) + + + // 2. Generate points for a non-geodesic circle + // Suitable for small circles where performance is critical and accuracy is less of a concern. + val nonGeodesicCirclePoints: List = CirclePolygonHelper.generateCirclePoints( + center = centerPoint, + radiusMeters = radiusInMeters, + geodesic = false + ) + + println("Generated ${nonGeodesicCirclePoints.size} points for a non-geodesic circle.") + // These points can also be used to add a Polygon to a map. +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/circle/GoogleMapCircleController.kt.md b/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/circle/GoogleMapCircleController.kt.md new file mode 100644 index 00000000..66d6d716 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/circle/GoogleMapCircleController.kt.md @@ -0,0 +1,77 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +--- + +# GoogleMapCircleController + +### Signature + +```kotlin +class GoogleMapCircleController( + circleManager: CircleManagerInterface = CircleManager(), + renderer: GoogleMapCircleOverlayRenderer, +) : CircleController(circleManager, renderer) +``` + +### Description + +The `GoogleMapCircleController` is a specialized controller responsible for managing and rendering circle overlays on a Google Map. It acts as a bridge between the abstract circle management logic provided by a `CircleManager` and the platform-specific rendering handled by the `GoogleMapCircleOverlayRenderer`. + +This controller extends the generic `CircleController`, providing a concrete implementation for the Google Maps SDK. It orchestrates the data layer (`circleManager`) and the view layer (`renderer`), ensuring that the visual representation of circles on the map stays in sync with the underlying data model. When circles are added, updated, or removed via the controller, it delegates the state management to the `circleManager` and triggers the `renderer` to apply the changes to the map view. + +### Parameters + +This documentation describes the parameters for the `GoogleMapCircleController` constructor. + +| Parameter | Type | Description | Default | +| :--- | :--- | :--- | :--- | +| `circleManager` | `CircleManagerInterface` | The manager responsible for handling the lifecycle and state of circle data. It tracks all the circles to be displayed. | `CircleManager()` | +| `renderer` | `GoogleMapCircleOverlayRenderer` | The platform-specific renderer that draws the circles onto the Google Map view. | *None* | + +### Example + +The following example demonstrates how to initialize and use the `GoogleMapCircleController` within a typical Android application that uses Google Maps. This code would typically reside in your `Activity` or `Fragment` where you handle the `onMapReady` callback. + +```kotlin +import android.graphics.Color +import com.google.android.gms.maps.GoogleMap +import com.google.android.gms.maps.OnMapReadyCallback +import com.google.android.gms.maps.model.LatLng +import com.mapconductor.core.circle.CircleManager +import com.mapconductor.core.model.MapCircle // Assuming a data class for circle properties + +// Assume this class implements OnMapReadyCallback +class MapsActivity : AppCompatActivity(), OnMapReadyCallback { + + private lateinit var circleController: GoogleMapCircleController + + override fun onMapReady(googleMap: GoogleMap) { + // 1. Initialize the platform-specific renderer with the GoogleMap instance. + val circleRenderer = GoogleMapCircleOverlayRenderer(googleMap) + + // 2. (Optional) Initialize a custom circle manager. + // The controller uses a default one if not provided. + val circleManager = CircleManager() + + // 3. Create the controller instance, passing the manager and renderer. + circleController = GoogleMapCircleController( + circleManager = circleManager, + renderer = circleRenderer + ) + + // 4. Use the controller to add a new circle to the map. + val sfCircle = MapCircle( + id = "circle-sf", + center = LatLng(37.7749, -122.4194), // San Francisco + radius = 1000.0, // Radius in meters + strokeWidth = 10f, + strokeColor = Color.DKGRAY, + fillColor = Color.argb(70, 100, 150, 250) // Semi-transparent blue + ) + + // The controller's `add` method (inherited from CircleController) + // updates the manager and triggers the renderer to draw the circle. + circleController.add(sfCircle) + } +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/circle/GoogleMapCircleOverlayRenderer.kt.md b/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/circle/GoogleMapCircleOverlayRenderer.kt.md new file mode 100644 index 00000000..0d594d56 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/circle/GoogleMapCircleOverlayRenderer.kt.md @@ -0,0 +1,171 @@ +Excellent! Here is the high-quality SDK documentation for the provided Kotlin code snippet. + +*** + +# GoogleMapCircleOverlayRenderer + +## Class Signature + +```kotlin +class GoogleMapCircleOverlayRenderer( + override val holder: GoogleMapViewHolder, + override val coroutine: CoroutineScope = CoroutineScope(Dispatchers.Main), +) : AbstractCircleOverlayRenderer() +``` + +## Description + +The `GoogleMapCircleOverlayRenderer` is responsible for rendering, updating, and removing circle overlays on a Google Map. + +This renderer implements circle drawing by creating `Polygon` objects rather than using the native `Circle` object from the Google Maps SDK. This approach allows for more precise control and consistent visual behavior, especially for geodesic circles and stroke width rendering. The radius is adjusted internally to account for the stroke width, ensuring the outer edge of the circle's stroke aligns with the specified radius. + +All map operations are executed on the main thread via the provided `CoroutineScope`. + +### Constructor + +```kotlin +GoogleMapCircleOverlayRenderer( + holder: GoogleMapViewHolder, + coroutine: CoroutineScope = CoroutineScope(Dispatchers.Main) +) +``` + +Initializes a new instance of the `GoogleMapCircleOverlayRenderer`. + +#### Parameters + +| Parameter | Type | Description | +| :---------- | :------------------ | :------------------------------------------------------------------------------------------------------ | +| `holder` | `GoogleMapViewHolder` | The view holder that contains the `GoogleMap` instance on which the circles will be rendered. | +| `coroutine` | `CoroutineScope` | The coroutine scope used to execute map operations. Defaults to `CoroutineScope(Dispatchers.Main)`. | + +--- + +## Methods + +### createCircle + +#### Signature + +```kotlin +override suspend fun createCircle(state: CircleState): GoogleMapActualCircle? +``` + +#### Description + +Creates and draws a new circle on the map based on the provided state. This method generates a polygon representation of the circle, configures its visual properties (colors, stroke, etc.), and adds it to the map. + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :------------ | :------------------------------------------------------------------------------------------------------ | +| `state` | `CircleState` | An object containing all configuration details for the circle, such as center, radius, colors, and style. | + +#### Returns + +| Type | Description | +| :---------------------- | :------------------------------------------------------------------------------------------------------ | +| `GoogleMapActualCircle?` | The created `Polygon` object (aliased as `GoogleMapActualCircle`) that represents the circle, or `null` if creation fails. | + +--- + +### removeCircle + +#### Signature + +```kotlin +override suspend fun removeCircle(entity: CircleEntityInterface) +``` + +#### Description + +Removes a specified circle polygon from the map. + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :-------------------------------------------- | :----------------------------------------------------------------------- | +| `entity` | `CircleEntityInterface` | The entity wrapper that contains the circle polygon instance to be removed. | + +--- + +### updateCircleProperties + +#### Signature + +```kotlin +override suspend fun updateCircleProperties( + circle: GoogleMapActualCircle, + current: CircleEntityInterface, + prev: CircleEntityInterface, +): GoogleMapActualCircle? +``` + +#### Description + +Updates the properties of an existing circle polygon on the map. This method performs an efficient update by comparing the `current` and `prev` states and only applying the properties that have changed. + +If the center, radius, geodesic, or stroke width properties have changed, the polygon's points are regenerated to reflect the new geometry. Otherwise, only the visual properties (e.g., colors, z-index) are updated. + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :-------------------------------------------- | :------------------------------------------------------------------------------------------------------ | +| `circle` | `GoogleMapActualCircle` | The actual `Polygon` object on the map that needs to be updated. | +| `current` | `CircleEntityInterface` | The entity wrapper containing the new, updated state for the circle. | +| `prev` | `CircleEntityInterface` | The entity wrapper containing the previous state of the circle, used for diffing to determine what changed. | + +#### Returns + +| Type | Description | +| :---------------------- | :---------------------------------------- | +| `GoogleMapActualCircle?` | The updated `Polygon` object. | + +--- + +## Example + +Here is a conceptual example of how to use `GoogleMapCircleOverlayRenderer` to draw a circle on a map. + +```kotlin +import androidx.compose.ui.graphics.Color +import com.mapconductor.core.circle.CircleState +import com.mapconductor.core.types.latitude +import com.mapconductor.core.types.longitude +import com.mapconductor.googlemaps.GoogleMapViewHolder +import com.mapconductor.googlemaps.circle.GoogleMapCircleOverlayRenderer +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch + +// Assume you have a GoogleMap instance and a CoroutineScope +val googleMap: GoogleMap = // ... get your GoogleMap instance +val coroutineScope = CoroutineScope(Dispatchers.Main) + +// 1. Create a GoogleMapViewHolder +val mapViewHolder = GoogleMapViewHolder(googleMap) + +// 2. Instantiate the renderer +val circleRenderer = GoogleMapCircleOverlayRenderer(mapViewHolder, coroutineScope) + +// 3. Define the state for the circle +val circleState = CircleState( + id = "my-unique-circle-id", + center = GeoPoint(latitude = 37.7749.latitude, longitude = (-122.4194).longitude), + radiusMeters = 1000.0, + strokeColor = Color.Blue, + strokeWidth = 10f, // in dp + fillColor = Color(0x880000FF), // Blue with 50% transparency + geodesic = true +) + +// 4. Launch a coroutine to create the circle on the map +coroutineScope.launch { + val createdCircle = circleRenderer.createCircle(circleState) + if (createdCircle != null) { + println("Circle successfully created on the map with tag: ${createdCircle.tag}") + } else { + println("Failed to create circle.") + } +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/groundimage/GoogleMapGroundImageController.kt.md b/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/groundimage/GoogleMapGroundImageController.kt.md new file mode 100644 index 00000000..5bd9a5e2 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/groundimage/GoogleMapGroundImageController.kt.md @@ -0,0 +1,67 @@ +# GoogleMapGroundImageController + +### Signature + +```kotlin +class GoogleMapGroundImageController( + groundImageManager: GroundImageManagerInterface = GroundImageManager(), + renderer: GoogleMapGroundImageOverlayRenderer, +) : GroundImageController(groundImageManager, renderer) +``` + +### Description + +The `GoogleMapGroundImageController` is a specialized controller responsible for managing and displaying ground image overlays on a Google Map instance. It acts as a bridge between the generic `GroundImageController` logic and the platform-specific rendering provided by `GoogleMapGroundImageOverlayRenderer`. + +This controller handles the entire lifecycle of ground images—including adding, updating, and removing them from the map—by coordinating the `GroundImageManager` and the `renderer`. + +### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `groundImageManager` | `GroundImageManagerInterface` | (Optional) The manager responsible for handling the state and lifecycle of ground images. Defaults to a new `GroundImageManager()` instance. | +| `renderer` | `GoogleMapGroundImageOverlayRenderer` | The renderer responsible for drawing and managing the ground image overlays on the `GoogleMap` object. | + +### Example + +The following example demonstrates how to initialize the `GoogleMapGroundImageController` and use it to add a ground image overlay to a map. + +```kotlin +import com.google.android.gms.maps.GoogleMap +import com.google.android.gms.maps.model.BitmapDescriptorFactory +import com.google.android.gms.maps.model.LatLng +import com.google.android.gms.maps.model.LatLngBounds +import com.mapconductor.core.groundimage.GroundImage // Assuming this core data class exists + +// Assume you have a GoogleMap instance from the onMapReady callback +// val googleMap: GoogleMap = ... + +// 1. Create a renderer for the Google Map +val groundImageRenderer = GoogleMapGroundImageOverlayRenderer(googleMap) + +// 2. Instantiate the controller with the renderer +val groundImageController = GoogleMapGroundImageController(renderer = groundImageRenderer) + +// 3. Define the properties for a new ground image +val imageBounds = LatLngBounds( + LatLng(40.7122, -74.2265), // Southwest corner + LatLng(40.7739, -74.1254) // Northeast corner +) +val overlayImage = BitmapDescriptorFactory.fromResource(R.drawable.overlay_image) + +// 4. Create a GroundImage object +val groundImage = GroundImage( + id = "unique-overlay-id-1", + image = overlayImage, + bounds = imageBounds, + transparency = 0.5f, + zIndex = 5f +) + +// 5. Add the ground image to the map via the controller +// The controller inherits the `add` method from GroundImageController +groundImageController.add(listOf(groundImage)) + +// To remove the ground image later by its ID +// groundImageController.remove(listOf("unique-overlay-id-1")) +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/groundimage/GoogleMapGroundImageOverlayRenderer.kt.md b/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/groundimage/GoogleMapGroundImageOverlayRenderer.kt.md new file mode 100644 index 00000000..1400736c --- /dev/null +++ b/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/groundimage/GoogleMapGroundImageOverlayRenderer.kt.md @@ -0,0 +1,146 @@ +# GoogleMapGroundImageOverlayRenderer + +The `GoogleMapGroundImageOverlayRenderer` is a concrete implementation of `AbstractGroundImageOverlayRenderer` designed for the Google Maps SDK. It manages the lifecycle of ground image overlays on a `GoogleMap` instance, including their creation, removal, and property updates. This class translates abstract `GroundImageState` objects into tangible `GroundOverlay` objects on the map. + +`GoogleMapActualGroundImage` is a type alias for the Google Maps SDK's `GroundOverlay`. + +## Signature + +```kotlin +class GoogleMapGroundImageOverlayRenderer( + override val holder: GoogleMapViewHolder, + override val coroutine: CoroutineScope = CoroutineScope(Dispatchers.Main), +) : AbstractGroundImageOverlayRenderer() +``` + +## Parameters + +| Parameter | Type | Description | +|-----------|------|-------------| +| `holder` | `GoogleMapViewHolder` | The view holder containing the `GoogleMap` instance where overlays will be rendered. | +| `coroutine` | `CoroutineScope` | The coroutine scope for executing asynchronous operations. Defaults to `CoroutineScope(Dispatchers.Main)`. | + +--- + +## Methods + +### createGroundImage + +Asynchronously creates and adds a new ground overlay to the map based on the provided state. It configures the overlay's image, bounds, and opacity. + +#### Signature + +```kotlin +override suspend fun createGroundImage(state: GroundImageState): GoogleMapActualGroundImage? +``` + +#### Parameters + +| Parameter | Type | Description | +|-----------|------|-------------| +| `state` | `GroundImageState` | An object containing the desired properties for the new ground overlay, such as the image, geographic bounds, and opacity. | + +#### Returns + +| Type | Description | +|------|-------------| +| `GoogleMapActualGroundImage?` | The newly created `GroundOverlay` instance if successful, or `null` if the creation fails (e.g., due to invalid bounds). | + +--- + +### removeGroundImage + +Asynchronously removes a specified ground overlay from the map. + +#### Signature + +```kotlin +override suspend fun removeGroundImage(entity: GroundImageEntityInterface) +``` + +#### Parameters + +| Parameter | Type | Description | +|-----------|------|-------------| +| `entity` | `GroundImageEntityInterface` | The entity wrapper containing the `GroundOverlay` instance to be removed. | + +#### Returns + +This method does not return a value. + +--- + +### updateGroundImageProperties + +Asynchronously updates the properties of an existing ground overlay. It efficiently compares the previous and current states and applies only the changed properties (bounds, image, or opacity) to the `GroundOverlay` on the map. + +#### Signature + +```kotlin +override suspend fun updateGroundImageProperties( + groundImage: GoogleMapActualGroundImage, + current: GroundImageEntityInterface, + prev: GroundImageEntityInterface, +): GoogleMapActualGroundImage? +``` + +#### Parameters + +| Parameter | Type | Description | +|-----------|------|-------------| +| `groundImage` | `GoogleMapActualGroundImage` | The actual `GroundOverlay` object on the map to be updated. | +| `current` | `GroundImageEntityInterface` | The entity representing the new, desired state of the ground overlay. | +| `prev` | `GroundImageEntityInterface` | The entity representing the previous state of the ground overlay, used for comparison. | + +#### Returns + +| Type | Description | +|------|-------------| +| `GoogleMapActualGroundImage?` | The updated `GroundOverlay` instance. | + +--- + +## Example + +Here is an example of how to instantiate the renderer and use it to create a ground overlay. + +```kotlin +import com.google.android.gms.maps.GoogleMap +import com.mapconductor.core.groundimage.GroundImageState +import com.mapconductor.core.math.LatLonBounds +import com.mapconductor.googlemaps.GoogleMapViewHolder +import kotlinx.coroutines.runBlocking + +// Assume you have a GoogleMap instance and a Drawable resource +// val googleMap: GoogleMap = ... +// val imageDrawable: Drawable = ContextCompat.getDrawable(context, R.drawable.newark_nj_1922)!! + +// 1. Set up the GoogleMapViewHolder +val mapViewHolder = GoogleMapViewHolder(googleMap) + +// 2. Instantiate the renderer +val groundImageRenderer = GoogleMapGroundImageOverlayRenderer(mapViewHolder) + +// 3. Define the state for the ground overlay +val imageState = GroundImageState( + id = "newark-1922-map", + image = imageDrawable, + bounds = LatLonBounds( + north = 40.785, + south = 40.685, + east = -74.118, + west = -74.258 + ), + opacity = 0.75f +) + +// 4. Use the renderer to create the ground overlay on the map +runBlocking { + val groundOverlay = groundImageRenderer.createGroundImage(imageState) + if (groundOverlay != null) { + println("Ground overlay created with tag: ${groundOverlay.tag}") + } else { + println("Failed to create ground overlay.") + } +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/marker/BitmapDescriptorCache.kt.md b/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/marker/BitmapDescriptorCache.kt.md new file mode 100644 index 00000000..fd256e5f --- /dev/null +++ b/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/marker/BitmapDescriptorCache.kt.md @@ -0,0 +1,82 @@ +# BitmapDescriptorCache + +A utility object that provides a cache for `BitmapDescriptor` instances. This helps improve performance and reduce memory churn by avoiding the recreation of `BitmapDescriptor` objects for identical bitmaps. It uses the `hashCode` of the input `Bitmap` as the key for efficient cache lookups. + +This object is implemented as a thread-safe singleton using `ConcurrentHashMap`. + +--- + +## Methods + +### fromBitmap + +Retrieves a `BitmapDescriptor` for the given `Bitmap`. It first checks the internal cache for an existing instance corresponding to the bitmap's hash code. If a cached instance is found, it is returned immediately. Otherwise, a new `BitmapDescriptor` is created, stored in the cache, and then returned. + +#### Signature +```kotlin +fun fromBitmap(bitmap: Bitmap): BitmapDescriptor +``` + +#### Parameters +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `bitmap` | `Bitmap` | The `Bitmap` object to convert into a `BitmapDescriptor`. | + +#### Returns +`BitmapDescriptor` - The cached or newly created `BitmapDescriptor` instance. + +#### Example +```kotlin +// Assume 'myCustomBitmap' is a valid Bitmap object +val descriptor1 = BitmapDescriptorCache.fromBitmap(myCustomBitmap) + +// This call will hit the cache and return the same instance as descriptor1 +// without creating a new object. +val descriptor2 = BitmapDescriptorCache.fromBitmap(myCustomBitmap) + +// Use the descriptor to add a marker to the map +googleMap.addMarker( + MarkerOptions() + .position(CLEVELAND) + .title("Cached Marker") + .icon(descriptor1) +) +``` + +--- + +### clearCache + +Removes all entries from the `BitmapDescriptor` cache. This can be useful in low-memory situations or when you need to ensure that all descriptors are recreated on their next request. + +#### Signature +```kotlin +fun clearCache() +``` + +#### Example +```kotlin +// Clear all cached BitmapDescriptors to free up memory. +BitmapDescriptorCache.clearCache() +``` + +--- + +### getCacheSize + +Returns the current number of `BitmapDescriptor` instances stored in the cache. This method is primarily intended for debugging and monitoring purposes. + +#### Signature +```kotlin +fun getCacheSize(): Int +``` + +#### Returns +`Int` - The total number of items currently in the cache. + +#### Example +```kotlin +// Get the number of items in the cache +val currentCacheSize = BitmapDescriptorCache.getCacheSize() +Log.d("CacheInfo", "Current BitmapDescriptorCache size: $currentCacheSize") +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/marker/GoogleMapMarkerController.kt.md b/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/marker/GoogleMapMarkerController.kt.md new file mode 100644 index 00000000..40ac37f6 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/marker/GoogleMapMarkerController.kt.md @@ -0,0 +1,341 @@ +Of course! Here is the high-quality SDK documentation for the provided `GoogleMapMarkerController` code. + +# GoogleMapMarkerController + +Manages the lifecycle of markers on a Google Map. This controller handles adding, updating, removing, and finding markers. It includes an advanced performance optimization feature that automatically switches to rendering markers as raster tiles when the marker count is high, a process known as marker tiling. + +An instance of this controller is created using the companion object's `create` factory method. + +```kotlin +class GoogleMapMarkerController private constructor(...) : AbstractMarkerController(...) +``` + +--- + +## Companion Object + +### create + +Factory method to create a new instance of `GoogleMapMarkerController`. This is the primary entry point for instantiating the controller. + +**Signature** +```kotlin +fun create( + holder: GoogleMapViewHolder, + markerTiling: MarkerTilingOptions = MarkerTilingOptions.Default +): GoogleMapMarkerController +``` + +**Description** +This method initializes the marker controller with the necessary map components and tiling configuration. + +**Parameters** + +| Parameter | Type | Description | +| :------------- | :-------------------- | :------------------------------------------------------------------------------------------------------ | +| `holder` | `GoogleMapViewHolder` | The view holder that contains the `GoogleMap` instance. | +| `markerTiling` | `MarkerTilingOptions` | *(Optional)* Configuration for the marker tiling feature. Defaults to `MarkerTilingOptions.Default`. | + +**Returns** + +`GoogleMapMarkerController` - A new instance of the controller. + +**Example** + +```kotlin +// Assuming 'mapViewHolder' is an instance of GoogleMapViewHolder +val markerTilingOptions = MarkerTilingOptions(enabled = true, minMarkerCount = 100) + +val markerController = GoogleMapMarkerController.create( + holder = mapViewHolder, + markerTiling = markerTilingOptions +) +``` + +--- + +## Methods + +### setRasterLayerCallback + +Sets a callback to manage the underlying `RasterLayer` used for tiled marker rendering. + +**Signature** +```kotlin +fun setRasterLayerCallback(callback: MarkerTileRasterLayerCallback?) +``` + +**Description** +This is essential for the marker tiling feature to function. It allows the `MarkerController` to request additions, updates, or removals of the raster tile layer from a `RasterLayerController`. This method must be called before adding markers if tiling is enabled. + +**Parameters** + +| Parameter | Type | Description | +| :--------- | :------------------------------ | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `callback` | `MarkerTileRasterLayerCallback?` | The callback to be invoked for raster layer updates. Pass `null` to remove the callback. See [MarkerTileRasterLayerCallback](#markertilerasterlayercallback) for details. | + +**Example** + +```kotlin +// Assuming 'rasterLayerController' is available +markerController.setRasterLayerCallback { rasterLayerState -> + // The controller will pass a state when it needs to add/update the tile layer, + // or null when it needs to be removed. + if (rasterLayerState != null) { + rasterLayerController.addOrUpdate(rasterLayerState) + } else { + // The ID of the raster layer is consistent, find and remove it. + // (Implementation depends on the RasterLayerController API) + } +} +``` + +--- + +### add + +Asynchronously adds a list of markers to the map. + +**Signature** +```kotlin +suspend fun add(data: List) +``` + +**Description** +The controller intelligently decides whether to render markers individually or as a tiled layer based on the number of markers and the `MarkerTilingOptions` configuration. If tiling is enabled and the marker count exceeds `minMarkerCount`, markers that are not draggable and have no animation will be rendered as part of a raster tile overlay for improved performance. + +**Parameters** + +| Parameter | Type | Description | +| :-------- | :------------------ | :-------------------------------------------------------------- | +| `data` | `List` | A list of `MarkerState` objects, each defining a marker to add. | + +**Example** + +```kotlin +val markerStates = listOf( + MarkerState(id = "marker1", position = GeoPoint(40.7128, -74.0060)), + MarkerState(id = "marker2", position = GeoPoint(34.0522, -118.2437)) +) + +// Add markers within a coroutine scope +coroutineScope.launch { + markerController.add(markerStates) +} +``` + +--- + +### update + +Asynchronously updates the state of an existing marker. + +**Signature** +```kotlin +suspend fun update(state: MarkerState) +``` + +**Description** +This method handles transitions between individual and tiled rendering. For example, if a marker becomes draggable, it will be transitioned from the tile layer to an individual marker, and vice-versa. If the marker's state has not changed (based on its `fingerPrint`), the operation is skipped to avoid unnecessary work. + +**Parameters** + +| Parameter | Type | Description | +| :-------- | :------------ | :--------------------------------------------------------------- | +| `state` | `MarkerState` | The new state for the marker, which is identified by its `id`. | + +**Example** + +```kotlin +val updatedState = MarkerState( + id = "marker1", + position = GeoPoint(40.7128, -74.0060), + draggable = true // Update the marker to be draggable +) + +coroutineScope.launch { + markerController.update(updatedState) +} +``` + +--- + +### clear + +Asynchronously removes all markers from the map. + +**Signature** +```kotlin +suspend fun clear() +``` + +**Description** +This method removes all individual markers and clears any active raster tile overlays being managed by the controller. + +**Example** + +```kotlin +coroutineScope.launch { + markerController.clear() +} +``` + +--- + +### find + +Finds the nearest marker to a given geographic position within a specified tap tolerance. + +**Signature** +```kotlin +fun find( + position: GeoPointInterface, + zoom: Double +): MarkerEntityInterface? +``` + +**Description** +This method is useful for implementing tap listeners on the map. The tolerance is calculated based on the screen density and the current map zoom level to provide a consistent user experience. + +**Parameters** + +| Parameter | Type | Description | +| :--------- | :------------------ | :----------------------------------------------------------------------- | +| `position` | `GeoPointInterface` | The geographic coordinates (latitude, longitude) to search around. | +| `zoom` | `Double` | The current zoom level of the map, used to calculate the search radius. | + +**Returns** + +`MarkerEntityInterface?` - The nearest marker entity if one is found within the tolerance radius, otherwise `null`. + +**Example** + +```kotlin +googleMap.setOnMapClickListener { latLng -> + val clickedPosition = GeoPoint(latLng.latitude, latLng.longitude) + val currentZoom = googleMap.cameraPosition.zoom.toDouble() + + val foundMarker = markerController.find( + position = clickedPosition, + zoom = currentZoom + ) + + if (foundMarker != null) { + // A marker was tapped + println("Tapped marker with ID: ${foundMarker.state.id}") + } +} +``` + +--- + +### find (Convenience Override) + +Finds the nearest marker to a given geographic position using the last known zoom level of the map. + +**Signature** +```kotlin +override fun find(position: GeoPointInterface): MarkerEntityInterface? +``` + +**Description** +This is a convenience override that calls the more specific `find` method with the controller's internally stored `lastKnownZoom`. + +**Parameters** + +| Parameter | Type | Description | +| :--------- | :------------------ | :----------------------------------------------------------- | +| `position` | `GeoPointInterface` | The geographic coordinates (latitude, longitude) to search around. | + +**Returns** + +`MarkerEntityInterface?` - The nearest marker entity if one is found within the tolerance radius, otherwise `null`. + +--- + +### onCameraChanged + +Callback method that should be invoked whenever the map's camera position changes. + +**Signature** +```kotlin +override suspend fun onCameraChanged(mapCameraPosition: MapCameraPosition) +``` + +**Description** +The controller uses this to update its internal state, such as the last known zoom level, which is crucial for the `find` operation and tiling logic. + +**Parameters** + +| Parameter | Type | Description | +| :---------------- | :------------------ | :------------------------------------ | +| `mapCameraPosition` | `MapCameraPosition` | The new camera position of the map. | + +**Example** + +```kotlin +// In your map setup +googleMap.setOnCameraIdleListener { + val position = googleMap.cameraPosition + val mapCameraPosition = MapCameraPosition( + target = GeoPoint(position.target.latitude, position.target.longitude), + zoom = position.zoom.toDouble(), + // ... other camera properties + ) + + coroutineScope.launch { + markerController.onCameraChanged(mapCameraPosition) + } +} +``` + +--- + +### destroy + +Cleans up all resources used by the controller. + +**Signature** +```kotlin +override fun destroy() +``` + +**Description** +This method unregisters tile servers and removes any raster layers from the map. It should be called when the controller is no longer needed (e.g., in a `Fragment.onDestroyView()` or `Activity.onDestroy()` lifecycle event) to prevent memory leaks. + +**Example** + +```kotlin +override fun onDestroyView() { + super.onDestroyView() + markerController.destroy() +} +``` + +--- + +## Interfaces + +### MarkerTileRasterLayerCallback + +A functional (SAM) interface used to decouple the `GoogleMapMarkerController` from a `RasterLayerController`. It provides a single method to notify a listener about required changes to the raster layer used for marker tiling. + +**Signature** +```kotlin +fun interface MarkerTileRasterLayerCallback +``` + +#### onRasterLayerUpdate + +Called when the raster layer for marker tiles needs to be added, updated, or removed. + +**Signature** +```kotlin +suspend fun onRasterLayerUpdate(state: RasterLayerState?) +``` + +**Parameters** + +| Parameter | Type | Description | +| :-------- | :----------------- | :------------------------------------------------------------------------------------------------------------------------------------- | +| `state` | `RasterLayerState?` | The new state for the raster layer. If `null`, the layer should be removed. Otherwise, the layer should be added or updated to this state. | \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/marker/GoogleMapMarkerEventController.kt.md b/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/marker/GoogleMapMarkerEventController.kt.md new file mode 100644 index 00000000..239017c1 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/marker/GoogleMapMarkerEventController.kt.md @@ -0,0 +1,275 @@ +# SDK Documentation + +This document provides a detailed overview of the Marker Event Controllers for Google Maps, including an interface and its implementations. These components are responsible for managing and dispatching marker-related events. + +## `GoogleMapMarkerEventControllerInterface` + +An interface that defines the contract for controlling and handling events related to Google Map markers. It extends the core `MarkerEventControllerInterface` and provides methods to set event listeners and programmatically dispatch events. + +--- + +### `getEntity` + +Retrieves a marker entity by its unique identifier. + +#### Signature + +```kotlin +fun getEntity(id: String): MarkerEntityInterface? +``` + +#### Description + +This function allows you to fetch a specific `MarkerEntityInterface` instance from the controller's internal manager using its ID. This is useful when you need to interact with a specific marker that is already on the map. + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :----- | :---------------------------------------- | +| `id` | String | The unique identifier of the marker entity. | + +#### Returns + +| Type | Description | +| :------------------------------------------------ | :------------------------------------------------------------- | +| `MarkerEntityInterface?` | The corresponding marker entity, or `null` if no marker with the specified `id` is found. | + +--- + +### `dispatchClick` + +Programmatically dispatches a click event for a marker. + +#### Signature + +```kotlin +fun dispatchClick(state: MarkerState) +``` + +#### Description + +This function manually triggers the `onMarkerClick` event handler, if one is set. It simulates a user clicking on a marker. + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :------------ | :--------------------------------------------------------- | +| `state` | `MarkerState` | The state of the marker at the time the event is dispatched. | + +--- + +### `dispatchDragStart` + +Programmatically dispatches a drag start event for a marker. + +#### Signature + +```kotlin +fun dispatchDragStart(state: MarkerState) +``` + +#### Description + +This function manually triggers the `onMarkerDragStart` event handler, simulating the start of a drag operation on a marker. + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :------------ | :--------------------------------------------------------- | +| `state` | `MarkerState` | The state of the marker at the time the event is dispatched. | + +--- + +### `dispatchDrag` + +Programmatically dispatches a drag event for a marker. + +#### Signature + +```kotlin +fun dispatchDrag(state: MarkerState) +``` + +#### Description + +This function manually triggers the `onMarkerDrag` event handler, simulating an ongoing drag operation. + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :------------ | :--------------------------------------------------------- | +| `state` | `MarkerState` | The state of the marker at the time the event is dispatched. | + +--- + +### `dispatchDragEnd` + +Programmatically dispatches a drag end event for a marker. + +#### Signature + +```kotlin +fun dispatchDragEnd(state: MarkerState) +``` + +#### Description + +This function manually triggers the `onMarkerDragEnd` event handler, simulating the completion of a drag operation. + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :------------ | :--------------------------------------------------------- | +| `state` | `MarkerState` | The state of the marker at the time the event is dispatched. | + +--- + +### `setClickListener` + +Sets or clears the event handler for marker click events. + +#### Signature + +```kotlin +fun setClickListener(listener: OnMarkerEventHandler?) +``` + +#### Parameters + +| Parameter | Type | Description | +| :--------- | :--------------------- | :------------------------------------------------------------------------------------------------------ | +| `listener` | `OnMarkerEventHandler?` | The handler to be invoked on a marker click. The handler receives a `MarkerState` object. Pass `null` to remove the current listener. | + +--- + +### `setDragStartListener` + +Sets or clears the event handler for marker drag start events. + +#### Signature + +```kotlin +fun setDragStartListener(listener: OnMarkerEventHandler?) +``` + +#### Parameters + +| Parameter | Type | Description | +| :--------- | :--------------------- | :------------------------------------------------------------------------------------------------------ | +| `listener` | `OnMarkerEventHandler?` | The handler to be invoked when a marker drag starts. The handler receives a `MarkerState` object. Pass `null` to remove the current listener. | + +--- + +### `setDragListener` + +Sets or clears the event handler for marker drag events. + +#### Signature + +```kotlin +fun setDragListener(listener: OnMarkerEventHandler?) +``` + +#### Parameters + +| Parameter | Type | Description | +| :--------- | :--------------------- | :------------------------------------------------------------------------------------------------------ | +| `listener` | `OnMarkerEventHandler?` | The handler to be invoked during a marker drag. The handler receives a `MarkerState` object. Pass `null` to remove the current listener. | + +--- + +### `setDragEndListener` + +Sets or clears the event handler for marker drag end events. + +#### Signature + +```kotlin +fun setDragEndListener(listener: OnMarkerEventHandler?) +``` + +#### Parameters + +| Parameter | Type | Description | +| :--------- | :--------------------- | :------------------------------------------------------------------------------------------------------ | +| `listener` | `OnMarkerEventHandler?` | The handler to be invoked when a marker drag ends. The handler receives a `MarkerState` object. Pass `null` to remove the current listener. | + +--- + +### `setAnimateStartListener` + +Sets or clears the event handler for marker animation start events. + +#### Signature + +```kotlin +fun setAnimateStartListener(listener: OnMarkerEventHandler?) +``` + +#### Parameters + +| Parameter | Type | Description | +| :--------- | :--------------------- | :------------------------------------------------------------------------------------------------------ | +| `listener` | `OnMarkerEventHandler?` | The handler to be invoked when a marker animation starts. The handler receives a `MarkerState` object. Pass `null` to remove the current listener. | + +--- + +### `setAnimateEndListener` + +Sets or clears the event handler for marker animation end events. + +#### Signature + +```kotlin +fun setAnimateEndListener(listener: OnMarkerEventHandler?) +``` + +#### Parameters + +| Parameter | Type | Description | +| :--------- | :--------------------- | :------------------------------------------------------------------------------------------------------ | +| `listener` | `OnMarkerEventHandler?` | The handler to be invoked when a marker animation ends. The handler receives a `MarkerState` object. Pass `null` to remove the current listener. | + +
+ +## `DefaultGoogleMapMarkerEventController` + +A default implementation of `GoogleMapMarkerEventControllerInterface`. It delegates all event handling and listener management calls to an underlying `GoogleMapMarkerController` instance. This class provides a straightforward way to connect marker events to the main marker controller. + +### Constructor + +#### Signature + +```kotlin +DefaultGoogleMapMarkerEventController( + private val controller: GoogleMapMarkerController +) +``` + +#### Parameters + +| Parameter | Type | Description | +| :----------- | :-------------------------- | :----------------------------------------------------------------------- | +| `controller` | `GoogleMapMarkerController` | The main marker controller that will manage the markers and their events. | + +
+ +## `StrategyGoogleMapMarkerEventController` + +An implementation of `GoogleMapMarkerEventControllerInterface` that uses a `StrategyMarkerController`. This class is designed to work within a strategy pattern, where the logic for handling marker events is encapsulated in a separate strategy controller. It delegates all method calls to the provided `StrategyMarkerController`. + +### Constructor + +#### Signature + +```kotlin +StrategyGoogleMapMarkerEventController( + private val controller: StrategyMarkerController +) +``` + +#### Parameters + +| Parameter | Type | Description | +| :----------- | :-------------------------------------------------- | :----------------------------------------------------------------- | +| `controller` | `StrategyMarkerController` | The strategy controller responsible for implementing the event logic. | \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/marker/GoogleMapMarkerRenderer.kt.md b/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/marker/GoogleMapMarkerRenderer.kt.md new file mode 100644 index 00000000..cc13a8c7 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/marker/GoogleMapMarkerRenderer.kt.md @@ -0,0 +1,151 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +*** + +# class GoogleMapMarkerRenderer + +A renderer class responsible for managing and displaying `Marker` objects on a Google Map. + +This class is a concrete implementation of `AbstractMarkerOverlayRenderer` tailored for the Google Maps SDK. It handles the complete lifecycle of markers on the map, including their creation (`onAdd`), modification (`onChange`), and deletion (`onRemove`). It uses Kotlin Coroutines to ensure that all interactions with the Google Map instance are performed on the correct thread. + +**Note:** The type `GoogleMapActualMarker` is a type alias for `com.google.android.gms.maps.model.Marker`. + +## Constructor + +### Signature + +```kotlin +GoogleMapMarkerRenderer( + holder: GoogleMapViewHolder, + coroutine: CoroutineScope = CoroutineScope(Dispatchers.Main), +) +``` + +### Description + +Creates a new instance of the `GoogleMapMarkerRenderer`. + +### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `holder` | `GoogleMapViewHolder` | The view holder that contains the `GoogleMap` instance where markers will be rendered. | +| `coroutine` | `CoroutineScope` | (Optional) The coroutine scope used to execute map operations. Defaults to `CoroutineScope(Dispatchers.Main)`. | + +--- + +## Methods + +### setMarkerPosition + +#### Signature + +```kotlin +override fun setMarkerPosition( + markerEntity: MarkerEntityInterface, + position: GeoPoint, +) +``` + +#### Description + +Asynchronously updates the geographical position of a single, existing marker on the map. + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `markerEntity` | `MarkerEntityInterface` | The marker entity whose position is being updated. | +| `position` | `GeoPoint` | The new geographical coordinates for the marker. | + +--- + +### onAdd + +#### Signature + +```kotlin +override suspend fun onAdd( + data: List +): List +``` + +#### Description + +Asynchronously adds a batch of new markers to the map. For each item in the `data` list, it constructs and displays a new Google Maps `Marker` with the specified properties (position, icon, anchor, etc.). + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `data` | `List` | A list of parameter objects, each defining the properties for a new marker to be added. | + +#### Returns + +| Type | Description | +| :--- | :--- | +| `List` | A list containing the newly created `Marker` objects. An element in the list will be `null` if the corresponding marker failed to be created. | + +--- + +### onRemove + +#### Signature + +```kotlin +override suspend fun onRemove( + data: List> +) +``` + +#### Description + +Asynchronously removes a batch of markers from the map. + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `data` | `List>` | A list of marker entities that should be removed from the map. | + +--- + +### onPostProcess + +#### Signature + +```kotlin +override suspend fun onPostProcess() +``` + +#### Description + +A lifecycle callback executed after all other rendering operations (add, remove, change) for a frame are complete. In this specific implementation, this method performs no action. + +--- + +### onChange + +#### Signature + +```kotlin +override suspend fun onChange( + data: List> +): List +``` + +#### Description + +Asynchronously processes a batch of changes for existing markers. It efficiently updates marker properties such as icon, position, visibility, and z-index based on the provided previous and current states. If a marker to be changed does not yet exist on the map (e.g., `params.prev.marker` is null), it will be created and added. + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `data` | `List>` | A list of change parameter objects. Each object contains the previous and current state of a marker, enabling efficient updates. | + +#### Returns + +| Type | Description | +| :--- | :--- | +| `List` | A list of the updated or newly created `Marker` instances. An element can be `null` if the marker update or creation failed. | \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/polygon/GoogleMapPolygonController.kt.md b/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/polygon/GoogleMapPolygonController.kt.md new file mode 100644 index 00000000..7407ad6a --- /dev/null +++ b/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/polygon/GoogleMapPolygonController.kt.md @@ -0,0 +1,58 @@ +# GoogleMapPolygonController + +### Signature + +```kotlin +class GoogleMapPolygonController( + polygonManager: PolygonManagerInterface = PolygonManager(), + renderer: GoogleMapPolygonOverlayRenderer, +) : PolygonController(polygonManager, renderer) +``` + +### Description + +The `GoogleMapPolygonController` is a specialized controller responsible for managing and rendering polygon overlays on a Google Map. It extends the generic `PolygonController` to provide a concrete implementation tailored for the Google Maps platform. + +This class acts as a bridge, connecting the abstract polygon data management logic (handled by `polygonManager`) with the platform-specific rendering logic (handled by `renderer`). It orchestrates the process of adding, updating, and removing polygons from the map view. + +### Parameters + +| Parameter | Type | Description | Optional | +| :--- | :--- | :--- | :--- | +| `polygonManager` | `PolygonManagerInterface` | The manager responsible for handling the lifecycle and state of polygon data. It defaults to a new `PolygonManager` instance. | Yes | +| `renderer` | `GoogleMapPolygonOverlayRenderer` | The renderer responsible for drawing and updating the polygon visuals on the Google Map instance. | No | + +### Example + +This example demonstrates how to set up and instantiate the `GoogleMapPolygonController`. + +```kotlin +// Assuming 'googleMap' is an instance of the GoogleMap object from the Maps SDK +// and 'context' is an available Android Context. + +// 1. Create a renderer for Google Maps polygons. +// This renderer needs the GoogleMap object to draw on. +val polygonRenderer = GoogleMapPolygonOverlayRenderer(googleMap, context) + +// 2. Instantiate the controller with the specific renderer. +// This example uses the default PolygonManager. +val polygonController = GoogleMapPolygonController( + renderer = polygonRenderer +) + +// The controller is now ready to manage polygons on the map. +// You can use methods inherited from PolygonController to add, update, or remove polygons. + +// For example, to add a new polygon: +val polygonToAdd = MapPolygon( + id = "unique-polygon-id-1", + options = PolygonOptions() + .add(LatLng(40.7128, -74.0060)) // New York + .add(LatLng(34.0522, -118.2437)) // Los Angeles + .add(LatLng(41.8781, -87.6298)) // Chicago + .strokeColor(Color.RED) + .fillColor(Color.argb(100, 255, 0, 0)) +) + +polygonController.add(listOf(polygonToAdd)) +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/polygon/GoogleMapPolygonOverlayRenderer.kt.md b/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/polygon/GoogleMapPolygonOverlayRenderer.kt.md new file mode 100644 index 00000000..f25a4760 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/polygon/GoogleMapPolygonOverlayRenderer.kt.md @@ -0,0 +1,161 @@ +Excellent. Here is the high-quality SDK documentation for the provided code snippet. + +*** + +# class GoogleMapPolygonOverlayRenderer + +## Description + +The `GoogleMapPolygonOverlayRenderer` is a specialized class responsible for rendering, updating, and removing polygon overlays on a Google Map. It extends `AbstractPolygonOverlayRenderer` and is tailored for the Google Maps SDK environment. + +A key feature of this renderer is its advanced handling of polygons with holes. For simple polygons (without holes), it uses the native Google Maps `Polygon` object. However, for polygons with holes, it employs a sophisticated technique: +1. It renders the outer boundary of the polygon with a transparent fill color. +2. It dynamically generates and serves a raster tile overlay that precisely fills the area between the outer boundary and the holes. + +This approach ensures that complex polygons are rendered correctly and efficiently. The class also implements adaptive geodesic interpolation, which adds intermediate points to polygon edges based on the current map zoom level. This results in smooth, geographically accurate curves that adapt to the user's perspective, enhancing visual quality while optimizing performance. + +## Constructor + +### GoogleMapPolygonOverlayRenderer(...) + +Creates an instance of the `GoogleMapPolygonOverlayRenderer`. + +```kotlin +class GoogleMapPolygonOverlayRenderer( + override val holder: GoogleMapViewHolder, + private val rasterLayerController: GoogleMapRasterLayerController, + private val tileServer: LocalTileServer = TileServerRegistry.get(forceNoStoreCache = true), + override val coroutine: CoroutineScope = CoroutineScope(Dispatchers.Main), +) : AbstractPolygonOverlayRenderer() +``` + +### Parameters + +| Parameter | Type | Description | +| :-------------------- | :--------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------- | +| `holder` | `GoogleMapViewHolder` | The view holder that provides access to the `GoogleMap` instance. | +| `rasterLayerController` | `GoogleMapRasterLayerController` | The controller used to manage the raster tile layers required for rendering the fill of polygons with holes. | +| `tileServer` | `LocalTileServer` | **Optional**. The local tile server for serving the raster mask tiles. If not provided, a default instance is used. | +| `coroutine` | `CoroutineScope` | **Optional**. The coroutine scope for managing asynchronous rendering operations. Defaults to `CoroutineScope(Dispatchers.Main)`. | + +## Functions + +### createPolygon + +Asynchronously creates and adds a new polygon to the map based on the provided state. If the polygon contains holes, this method also sets up the necessary raster tile layer to render the fill correctly. + +**Signature** +```kotlin +override suspend fun createPolygon(state: PolygonState): GoogleMapActualPolygon? +``` + +**Parameters** +| Parameter | Type | Description | +| :-------- | :------------- | :--------------------------------------------------------------------------------------------------------- | +| `state` | `PolygonState` | An object defining all properties of the polygon, including points, holes, colors, stroke width, and z-index. | + +**Returns** +| Type | Description | +| :----------------------- | :------------------------------------------------------------------------------------------------------------------------------------- | +| `GoogleMapActualPolygon?` | The created Google Maps `Polygon` object (`GoogleMapActualPolygon` is a type alias) or `null` if the polygon could not be added to the map. | + +### updatePolygonProperties + +Asynchronously updates the properties of an existing polygon on the map. It intelligently compares the previous and current states to apply only the necessary changes, optimizing performance. This includes updating geometry (points, holes), appearance (colors, stroke), and z-index. It also manages the underlying raster mask layer if the polygon's hole configuration changes. + +**Signature** +```kotlin +override suspend fun updatePolygonProperties( + polygon: GoogleMapActualPolygon, + current: PolygonEntityInterface, + prev: PolygonEntityInterface, +): GoogleMapActualPolygon? +``` + +**Parameters** +| Parameter | Type | Description | +| :-------- | :--------------------------------------------- | :----------------------------------------------------------------------------- | +| `polygon` | `GoogleMapActualPolygon` | The native Google Maps `Polygon` object to be updated. | +| `current` | `PolygonEntityInterface` | The entity representing the new, updated state of the polygon. | +| `prev` | `PolygonEntityInterface` | The entity representing the previous state of the polygon, used for comparison. | + +**Returns** +| Type | Description | +| :----------------------- | :------------------------------- | +| `GoogleMapActualPolygon?` | The updated `Polygon` object. | + +### removePolygon + +Asynchronously removes a polygon from the map. If the polygon had an associated raster mask layer (for rendering holes), that layer is also removed and its resources are cleaned up. + +**Signature** +```kotlin +override suspend fun removePolygon(entity: PolygonEntityInterface) +``` + +**Parameters** +| Parameter | Type | Description | +| :-------- | :--------------------------------------------- | :------------------------------------ | +| `entity` | `PolygonEntityInterface` | The polygon entity to be removed. | + +## Example + +Here is an example of how to instantiate and use the `GoogleMapPolygonOverlayRenderer` to draw a complex polygon with a hole. + +```kotlin +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import androidx.compose.ui.graphics.Color +import com.mapconductor.core.features.GeoPoint +import com.mapconductor.core.polygon.PolygonState + +// Assume these are provided by your application's setup +val googleMap: GoogleMap = getGoogleMap() +val mapViewHolder = GoogleMapViewHolder(googleMap) +val rasterController = GoogleMapRasterLayerController(mapViewHolder) +val coroutineScope = CoroutineScope(Dispatchers.Main) + +// 1. Instantiate the renderer +val polygonRenderer = GoogleMapPolygonOverlayRenderer( + holder = mapViewHolder, + rasterLayerController = rasterController, + coroutine = coroutineScope +) + +// 2. Define the state for a polygon with a hole +val outerRing = listOf( + GeoPoint(40.7128, -74.0060), // NYC + GeoPoint(34.0522, -118.2437), // LA + GeoPoint(29.7604, -95.3698) // Houston +) + +val innerHole = listOf( + GeoPoint(39.9526, -75.1652), // Philadelphia + GeoPoint(38.9072, -77.0369), // Washington D.C. + GeoPoint(39.2904, -76.6122) // Baltimore +) + +val polygonState = PolygonState( + id = "complex-polygon-1", + points = outerRing, + holes = listOf(innerHole), + fillColor = Color(0x8800FF00), // Semi-transparent green + strokeColor = Color.Black, + strokeWidth = 2.0, // in dp + zIndex = 10, + geodesic = true +) + +// 3. Use a coroutine to create the polygon on the map +coroutineScope.launch { + val polygonEntity = polygonRenderer.createPolygon(polygonState) + + if (polygonEntity != null) { + println("Polygon created successfully.") + // The renderer automatically handles creating the raster layer for the fill. + } else { + println("Failed to create polygon.") + } +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/polyline/GoogleMapPolylineController.kt.md b/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/polyline/GoogleMapPolylineController.kt.md new file mode 100644 index 00000000..af3e4a5a --- /dev/null +++ b/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/polyline/GoogleMapPolylineController.kt.md @@ -0,0 +1,52 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +*** + +# GoogleMapPolylineController + +### Signature +```kotlin +class GoogleMapPolylineController( + polylineManager: PolylineManagerInterface = PolylineManager(), + renderer: GoogleMapPolylineOverlayRenderer, +) : PolylineController(polylineManager, renderer) +``` + +### Description +The `GoogleMapPolylineController` is the primary controller for managing and rendering polylines on a Google Map. It serves as the main entry point for all polyline-related operations within the Google Maps implementation of the MapConductor framework. + +This class orchestrates the core logic from a `PolylineManager` and the visual representation from a `GoogleMapPolylineOverlayRenderer`. It extends the base `PolylineController` to provide a concrete implementation tailored specifically for the Google Maps SDK, handling the lifecycle and drawing of `GoogleMapActualPolyline` objects. + +### Parameters +The constructor accepts the following parameters: + +| Parameter | Type | Description | +|---|---|---| +| `polylineManager` | `PolylineManagerInterface` | The manager responsible for the lifecycle and state of polyline data. It handles adding, removing, and updating polylines. **Optional**: If not provided, a default `PolylineManager` instance is created. | +| `renderer` | `GoogleMapPolylineOverlayRenderer` | The platform-specific renderer that draws the polylines onto the `GoogleMap` instance. This parameter is **required**. | + +### Example +The following example demonstrates how to initialize the `GoogleMapPolylineController` to manage polylines on a `GoogleMap` instance. + +```kotlin +import android.content.Context +import com.google.android.gms.maps.GoogleMap +import com.mapconductor.googlemaps.polyline.GoogleMapPolylineController +import com.mapconductor.googlemaps.polyline.GoogleMapPolylineOverlayRenderer + +// Assuming you have a GoogleMap object from a MapFragment or MapView +lateinit var googleMap: GoogleMap +lateinit var context: Context + +// 1. Create an instance of the renderer, which requires the GoogleMap object and a Context. +val polylineRenderer = GoogleMapPolylineOverlayRenderer(googleMap, context) + +// 2. Instantiate the controller, passing the required renderer. +// The polylineManager is optional and will be created with its default implementation. +val polylineController = GoogleMapPolylineController(renderer = polylineRenderer) + +// Now you can use the polylineController to add, remove, and manage polylines. +// For example, to add a new polyline: +// val newPolyline = Polyline(...) +// polylineController.add(listOf(newPolyline)) +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/polyline/GoogleMapPolylineOverlayRenderer.kt.md b/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/polyline/GoogleMapPolylineOverlayRenderer.kt.md new file mode 100644 index 00000000..d581e792 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/polyline/GoogleMapPolylineOverlayRenderer.kt.md @@ -0,0 +1,163 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +--- + +# GoogleMapPolylineOverlayRenderer + +The `GoogleMapPolylineOverlayRenderer` is a concrete implementation of `AbstractPolylineOverlayRenderer` designed specifically for Google Maps. It is responsible for rendering and managing `Polyline` objects on the map. + +This class handles the entire lifecycle of a polyline, including its creation, property updates (e.g., color, width, points), and removal. It translates an abstract `PolylineState` into a tangible `Polyline` on the Google Map, ensuring that the visual representation stays in sync with the state. + +A key feature of this renderer is its use of adaptive interpolation for geodesic polylines. It dynamically calculates the density of points based on the current map zoom level, ensuring a visually smooth curve without sacrificing performance. It also caches these interpolated points to optimize rendering during panning and zooming. + +## Constructor + +### Signature + +```kotlin +class GoogleMapPolylineOverlayRenderer( + override val holder: GoogleMapViewHolder, + override val coroutine: CoroutineScope = CoroutineScope(Dispatchers.Main), +) : AbstractPolylineOverlayRenderer() +``` + +### Description + +Initializes a new instance of the `GoogleMapPolylineOverlayRenderer`. + +### Parameters + +| Parameter | Type | Description | +|-------------|-----------------------|------------------------------------------------------------------------------------------------------------| +| `holder` | `GoogleMapViewHolder` | The view holder that provides access to the native `GoogleMap` instance. | +| `coroutine` | `CoroutineScope` | The coroutine scope used to execute map operations, typically on the main thread. Defaults to `Dispatchers.Main`. | + +## Public Methods + +### createPolyline + +#### Signature + +```kotlin +override suspend fun createPolyline(state: PolylineState): GoogleMapActualPolyline? +``` + +#### Description + +Asynchronously creates a new `Polyline` on the map based on the provided `PolylineState`. It configures the polyline's points, color, width, z-index, and geodesic property. For geodesic polylines, it uses an adaptive interpolation strategy to ensure a smooth curve at various zoom levels. The new polyline's tag is set to the `state.id` for identification. + +#### Parameters + +| Parameter | Type | Description | +|-----------|----------------|--------------------------------------------------------------------------| +| `state` | `PolylineState`| The state object containing all the properties for the new polyline. | + +#### Returns + +`GoogleMapActualPolyline?` — The newly created Google Maps `Polyline` object (`GoogleMapActualPolyline` is a type alias for `com.google.android.gms.maps.model.Polyline`), or `null` if creation fails. + +--- + +### updatePolylineProperties + +#### Signature + +```kotlin +override suspend fun updatePolylineProperties( + polyline: GoogleMapActualPolyline, + current: PolylineEntityInterface, + prev: PolylineEntityInterface, +): Polyline? +``` + +#### Description + +Asynchronously updates the properties of an existing `Polyline` on the map. It efficiently compares the `current` and `prev` states to determine which properties have changed and applies only the necessary updates. This prevents unnecessary redraws and improves performance. + +#### Parameters + +| Parameter | Type | Description | +|------------|-------------------------------------------------------|----------------------------------------------------------------------------------| +| `polyline` | `GoogleMapActualPolyline` | The native `Polyline` object on the map that needs to be updated. | +| `current` | `PolylineEntityInterface` | The entity representing the current, updated state of the polyline. | +| `prev` | `PolylineEntityInterface` | The entity representing the previous state of the polyline, used for comparison. | + +#### Returns + +`Polyline?` — The updated `Polyline` object. + +--- + +### removePolyline + +#### Signature + +```kotlin +override suspend fun removePolyline(entity: PolylineEntityInterface) +``` + +#### Description + +Asynchronously removes a specified polyline from the map. + +#### Parameters + +| Parameter | Type | Description | +|-----------|----------------------------------------------------|--------------------------------------------------------------------------| +| `entity` | `PolylineEntityInterface` | The polyline entity to be removed. The underlying `Polyline` is accessed from this entity. | + +#### Returns + +This function does not return a value. + +## Example + +Here is an example of how to instantiate the `GoogleMapPolylineOverlayRenderer` and use it to create a polyline on the map. + +```kotlin +import androidx.compose.ui.graphics.Color +import com.google.android.gms.maps.GoogleMap +import com.mapconductor.core.features.GeoPoint +import com.mapconductor.core.polyline.PolylineState +import com.mapconductor.googlemaps.GoogleMapViewHolder +import com.mapconductor.googlemaps.polyline.GoogleMapPolylineOverlayRenderer +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch + +// Assume you have a GoogleMap instance and a CoroutineScope +val googleMap: GoogleMap = getGoogleMap() // Placeholder for obtaining the map instance +val mainScope = CoroutineScope(Dispatchers.Main) + +// 1. Create a GoogleMapViewHolder +val mapViewHolder = GoogleMapViewHolder(googleMap) + +// 2. Instantiate the renderer +val polylineRenderer = GoogleMapPolylineOverlayRenderer( + holder = mapViewHolder, + coroutine = mainScope +) + +// 3. Define the state for a new polyline +val polylineState = PolylineState( + id = "route-42", + points = listOf( + GeoPoint(40.7128, -74.0060), // New York + GeoPoint(34.0522, -118.2437) // Los Angeles + ), + strokeColor = Color.Blue, + strokeWidth = 10f, + geodesic = true, + zIndex = 10 +) + +// 4. Use the renderer to create the polyline on the map +mainScope.launch { + val nativePolyline = polylineRenderer.createPolyline(polylineState) + if (nativePolyline != null) { + println("Polyline with ID ${nativePolyline.tag} created successfully.") + } else { + println("Failed to create polyline.") + } +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/raster/GoogleMapRasterLayerController.kt.md b/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/raster/GoogleMapRasterLayerController.kt.md new file mode 100644 index 00000000..54f242ec --- /dev/null +++ b/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/raster/GoogleMapRasterLayerController.kt.md @@ -0,0 +1,80 @@ +# GoogleMapRasterLayerController + +## Signature + +```kotlin +class GoogleMapRasterLayerController( + rasterLayerManager: RasterLayerManagerInterface = RasterLayerManager(), + renderer: GoogleMapRasterLayerOverlayRenderer, +) : RasterLayerController(rasterLayerManager, renderer) +``` + +## Description + +The `GoogleMapRasterLayerController` is a specialized controller responsible for managing and displaying raster tile layers on a Google Map. It serves as a concrete implementation of the abstract `RasterLayerController`, tailored for the Google Maps SDK for Android. + +This class orchestrates the interaction between the generic layer management logic provided by `RasterLayerManager` and the platform-specific rendering handled by `GoogleMapRasterLayerOverlayRenderer`. It uses `TileOverlay` as the native map object for representing raster layers. By using this controller, you can easily add, remove, and manage various raster data sources (such as XYZ, WMS, or WMTS) on a Google Map. + +## Parameters + +This class is instantiated through its primary constructor. + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `rasterLayerManager` | `RasterLayerManagerInterface` | An optional manager for the collection of raster layers. It handles the lifecycle and state of each layer. If not provided, a default `RasterLayerManager` instance is created. | +| `renderer` | `GoogleMapRasterLayerOverlayRenderer` | A required renderer that handles the creation and display of `TileOverlay` objects on the associated Google Map. This object bridges the controller's logic with the map view. | + +## Returns + +An instance of `GoogleMapRasterLayerController`, which can be used to manage raster layers on a Google Map. + +## Example + +The following example demonstrates how to initialize and use the `GoogleMapRasterLayerController` to add an OpenStreetMap tile layer to a Google Map. This code would typically be placed within the `onMapReady` callback where the `GoogleMap` object is available. + +```kotlin +import com.google.android.gms.maps.GoogleMap +import com.google.android.gms.maps.OnMapReadyCallback +import com.google.android.gms.maps.SupportMapFragment +import com.mapconductor.core.raster.XyzRasterLayer +import com.mapconductor.googlemaps.raster.GoogleMapRasterLayerController +import com.mapconductor.googlemaps.raster.GoogleMapRasterLayerOverlayRenderer + +class MyMapActivity : AppCompatActivity(), OnMapReadyCallback { + + private lateinit var rasterLayerController: GoogleMapRasterLayerController + private lateinit var googleMap: GoogleMap + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_map) + + val mapFragment = supportFragmentManager + .findFragmentById(R.id.map) as SupportMapFragment + mapFragment.getMapAsync(this) + } + + override fun onMapReady(map: GoogleMap) { + googleMap = map + + // 1. Create a renderer for the Google Map. + // This class is responsible for drawing the tile overlays on the map. + val renderer = GoogleMapRasterLayerOverlayRenderer(googleMap) + + // 2. Instantiate the controller with the renderer. + // We will use the default RasterLayerManager provided by the constructor. + rasterLayerController = GoogleMapRasterLayerController(renderer = renderer) + + // 3. Define a raster layer to add (e.g., an XYZ tile layer). + val openStreetMapLayer = XyzRasterLayer( + id = "osm-standard-layer", + url = "https://a.tile.openstreetmap.org/{z}/{x}/{y}.png", + displayName = "OpenStreetMap" + ) + + // 4. Use the controller to add the layer to the map. + // The controller will use the renderer to create and display the TileOverlay. + rasterLayerController.addLayer(openStreetMapLayer) + } +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/raster/GoogleMapRasterLayerOverlayRenderer.kt.md b/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/raster/GoogleMapRasterLayerOverlayRenderer.kt.md new file mode 100644 index 00000000..f97b60a4 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/raster/GoogleMapRasterLayerOverlayRenderer.kt.md @@ -0,0 +1,201 @@ +Excellent. Here is the high-quality SDK documentation for the provided `GoogleMapRasterLayerOverlayRenderer` class. + +--- + +# GoogleMapRasterLayerOverlayRenderer + +## Class Description + +The `GoogleMapRasterLayerOverlayRenderer` class is a concrete implementation of the `RasterLayerOverlayRendererInterface` for the Google Maps SDK. It is responsible for rendering and managing the lifecycle of raster tile layers on a `GoogleMap` instance. + +This renderer handles the addition, modification, and removal of `TileOverlay` objects based on a provided `RasterLayerState`. It supports fetching tiles from various sources, including URL templates and ArcGIS map services. Key features include setting opacity, visibility, z-index, and custom request headers. It also provides a powerful debugging feature that overlays tile coordinates and other metadata directly onto the tile images. + +## Constructor + +### Signature + +```kotlin +class GoogleMapRasterLayerOverlayRenderer( + private val holder: GoogleMapViewHolder, + private val okHttpClient: OkHttpClient, + override val coroutine: CoroutineScope = CoroutineScope(Dispatchers.Main), +) : RasterLayerOverlayRendererInterface +``` + +### Description + +Creates a new instance of `GoogleMapRasterLayerOverlayRenderer`. + +### Parameters + +| Parameter | Type | Description | +| :------------- | :------------------- | :------------------------------------------------------------------------------------------------------ | +| `holder` | `GoogleMapViewHolder`| The view holder that provides access to the `GoogleMap` and `MapView` instances. | +| `okHttpClient` | `OkHttpClient` | The `OkHttpClient` instance to be used for all network requests to fetch tile images. | +| `coroutine` | `CoroutineScope` | The coroutine scope used for executing asynchronous operations. Defaults to `CoroutineScope(Dispatchers.Main)`. | + +## Methods + +### onAdd + +#### Signature + +```kotlin +override suspend fun onAdd( + data: List +): List +``` + +#### Description + +Asynchronously adds a list of new raster layers to the map. For each layer state provided in the `data` list, this method creates a corresponding `TileOverlay` and adds it to the Google Map. + +The method configures a `TileProvider` for each layer to fetch tile images based on the layer's source (`UrlTemplate` or `ArcGisService`). It handles coordinate scheme transformations (TMS vs. XYZ) and sets custom HTTP headers for the requests. If a layer's `debug` flag is set to `true`, it will render a debug overlay on each tile. + +**Note:** `TileJson` sources are not supported and will be ignored. + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :------------------------------------------------------------ | :------------------------------------------------------------------------------------------------------ | +| `data` | `List`| A list of `AddParamsInterface` objects, where each object contains the `RasterLayerState` for a new layer. | + +#### Returns + +A `List` containing the newly created `TileOverlay` for each corresponding input state, or `null` if a layer could not be added (e.g., due to an unsupported source type). + +--- + +### onChange + +#### Signature + +```kotlin +override suspend fun onChange( + data: List> +): List +``` + +#### Description + +Asynchronously processes updates for a list of existing raster layers. It intelligently compares the previous and next state of each layer. + +- If the `source` or `debug` properties have changed, the existing `TileOverlay` is removed and a completely new one is created to reflect the fundamental change. +- If only other properties like `opacity`, `visible`, or `zIndex` have changed, the existing `TileOverlay` is updated in-place for better performance. + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :------------------------------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------- | +| `data` | `List>` | A list of `ChangeParamsInterface` objects, each containing the previous `RasterLayerEntityInterface` and the new `RasterLayerState`. | + +#### Returns + +A `List` containing the updated or newly created `TileOverlay` objects. + +--- + +### onRemove + +#### Signature + +```kotlin +override suspend fun onRemove(data: List>) +``` + +#### Description + +Asynchronously removes a list of raster layer overlays from the map. Each `TileOverlay` in the provided list will be detached from the `GoogleMap` instance. + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :--------------------------------------------- | :----------------------------------------------------------------------- | +| `data` | `List>`| A list of `RasterLayerEntityInterface` objects representing the layers to be removed. | + +#### Returns + +This method does not return a value. + +--- + +### onPostProcess + +#### Signature + +```kotlin +override suspend fun onPostProcess() +``` + +#### Description + +A lifecycle method called after all add, change, and remove operations in a batch have been completed. In this implementation, this method is a no-op and performs no actions. + +#### Parameters + +This method takes no parameters. + +#### Returns + +This method does not return a value. + +## Example + +The `GoogleMapRasterLayerOverlayRenderer` is typically used within a larger map management system. The following conceptual example demonstrates how you might instantiate the renderer and use it to add a raster layer to a map. + +```kotlin +import com.google.android.gms.maps.GoogleMap +import com.mapconductor.core.raster.RasterLayerSource +import com.mapconductor.core.raster.RasterLayerState +import com.mapconductor.core.raster.TileScheme +import com.mapconductor.googlemaps.GoogleMapViewHolder +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import okhttp3.OkHttpClient + +// Assume you have these variables initialized +val googleMap: GoogleMap +val mapView: MapView +val okHttpClient = OkHttpClient() +val coroutineScope = CoroutineScope(Dispatchers.Main) + +// 1. Create a GoogleMapViewHolder +val mapViewHolder = GoogleMapViewHolder(googleMap, mapView) + +// 2. Instantiate the renderer +val rasterRenderer = GoogleMapRasterLayerOverlayRenderer( + holder = mapViewHolder, + okHttpClient = okHttpClient, + coroutine = coroutineScope +) + +// 3. Define the state for a new raster layer +val openStreetMapLayerState = RasterLayerState( + id = "osm-layer", + source = RasterLayerSource.UrlTemplate( + template = "https://a.tile.openstreetmap.org/{z}/{x}/{y}.png", + tileSize = 256, + scheme = TileScheme.XYZ + ), + opacity = 0.8f, + visible = true, + zIndex = 1, + debug = true // Enable debug overlay on tiles +) + +// 4. Create the parameters for the onAdd call +val addParams = object : RasterLayerOverlayRendererInterface.AddParamsInterface { + override val state: RasterLayerState = openStreetMapLayerState +} + +// 5. Use the renderer to add the layer to the map +coroutineScope.launch { + val addedOverlays = rasterRenderer.onAdd(listOf(addParams)) + if (addedOverlays.isNotEmpty() && addedOverlays[0] != null) { + println("Successfully added OpenStreetMap layer with overlay: ${addedOverlays[0]}") + } else { + println("Failed to add OpenStreetMap layer.") + } +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/zoom/ZoomAltitudeConverter.kt.md b/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/zoom/ZoomAltitudeConverter.kt.md new file mode 100644 index 00000000..9074aae5 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/zoom/ZoomAltitudeConverter.kt.md @@ -0,0 +1,133 @@ +# class `ZoomAltitudeConverter` + +A utility class for converting between Google Maps zoom levels and physical altitude values. + +## Description + +The `ZoomAltitudeConverter` class provides a bridge between the abstract, screen-pixel-based zoom levels used by Google Maps and a physical altitude value (in meters) used by MapConductor. This conversion is essential for creating a unified camera control system across different map providers. + +The conversion is not a simple linear mapping; it is a dynamic calculation that takes into account the camera's geographical latitude and its tilt angle. This ensures that the perceived distance from the map surface remains consistent, accounting for the Earth's curvature (approximated by latitude) and perspective distortion (caused by tilt). + +This class inherits from `AbstractZoomAltitudeConverter`. + +## Signature + +```kotlin +class ZoomAltitudeConverter( + zoom0Altitude: Double = DEFAULT_ZOOM0_ALTITUDE +) : AbstractZoomAltitudeConverter(zoom0Altitude) +``` + +### Constructor + +Creates a new instance of `ZoomAltitudeConverter`. + +#### Parameters + +| Parameter | Type | Description | +|-----------|------|-------------| +| `zoom0Altitude` | `Double` | The reference altitude in meters corresponding to zoom level 0 at the equator. This value is optional and defaults to `DEFAULT_ZOOM0_ALTITUDE`. | + +--- + +## Methods + +### `zoomLevelToAltitude` + +Converts a Google Maps zoom level to a corresponding physical altitude in meters. + +#### Signature + +```kotlin +override fun zoomLevelToAltitude( + zoomLevel: Double, + latitude: Double, + tilt: Double +): Double +``` + +#### Description + +This method calculates the physical altitude of the camera based on a given Google Maps zoom level. The calculation is adjusted based on the map's center latitude and the camera's tilt angle to provide a more accurate physical representation of the camera's height above the ground. Input values are clamped to ensure they fall within a valid, practical range. + +#### Parameters + +| Parameter | Type | Description | +|-----------|------|-------------| +| `zoomLevel` | `Double` | The Google Maps zoom level to convert. | +| `latitude` | `Double` | The current latitude of the map's center, in degrees. | +| `tilt` | `Double` | The current camera tilt angle, in degrees (e.g., 0 for a top-down view). | + +#### Returns + +| Type | Description | +|------|-------------| +| `Double` | The calculated altitude in meters, clamped within a valid range. | + +--- + +### `altitudeToZoomLevel` + +Converts a physical altitude in meters to the corresponding Google Maps zoom level. + +#### Signature + +```kotlin +override fun altitudeToZoomLevel( + altitude: Double, + latitude: Double, + tilt: Double +): Double +``` + +#### Description + +This method performs the inverse operation of `zoomLevelToAltitude`. It calculates the appropriate Google Maps zoom level that corresponds to a given physical altitude in meters. The calculation also considers the map's latitude and camera tilt to ensure accuracy. Input values are clamped to ensure they fall within a valid, practical range. + +#### Parameters + +| Parameter | Type | Description | +|-----------|------|-------------| +| `altitude` | `Double` | The camera's altitude in meters to convert. | +| `latitude` | `Double` | The current latitude of the map's center, in degrees. | +| `tilt` | `Double` | The current camera tilt angle, in degrees. | + +#### Returns + +| Type | Description | +|------|-------------| +| `Double` | The calculated Google Maps zoom level, clamped within a valid range. | + +--- + +## Example + +The following example demonstrates how to create an instance of `ZoomAltitudeConverter` and use its methods for conversion. + +```kotlin +import com.mapconductor.googlemaps.zoom.ZoomAltitudeConverter + +fun main() { + // Instantiate the converter. + // We can use the default zoom0Altitude or provide a custom one. + val converter = ZoomAltitudeConverter() + + // --- Scenario 1: Convert Zoom Level to Altitude --- + val zoomLevel = 15.0 + val latitude = 40.7128 // New York City + val tilt = 45.0 // A 45-degree camera tilt + + val calculatedAltitude = converter.zoomLevelToAltitude(zoomLevel, latitude, tilt) + println("Zoom level $zoomLevel at latitude $latitude with tilt $tilt corresponds to an altitude of %.2f meters.".format(calculatedAltitude)) + // Expected output might be similar to: + // Zoom level 15.0 at latitude 40.7128 with tilt 45.0 corresponds to an altitude of 1234.56 meters. + + // --- Scenario 2: Convert Altitude back to Zoom Level --- + val altitude = 1234.56 // Use the altitude from the previous calculation + + val calculatedZoomLevel = converter.altitudeToZoomLevel(altitude, latitude, tilt) + println("Altitude of %.2f meters at latitude $latitude with tilt $tilt corresponds to zoom level %.2f.".format(altitude, calculatedZoomLevel)) + // Expected output should be close to the original zoom level: + // Altitude of 1234.56 meters at latitude 40.7128 with tilt 45.0 corresponds to zoom level 15.00. +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/BitmapIcon.kt.md b/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/BitmapIcon.kt.md new file mode 100644 index 00000000..1e97edc1 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/BitmapIcon.kt.md @@ -0,0 +1,86 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +--- + +# BitmapIcon Extensions for HERE SDK + +This document provides details on a set of `internal` Kotlin extension functions designed to facilitate the conversion of a custom `BitmapIcon` object into corresponding types required by the HERE SDK. These helpers streamline the process of creating map markers with custom visuals and anchor points. + +**Note:** As these functions are marked `internal`, they are intended for use only within the `com.mapconductor.here` module. + +--- + +## `toMapImage()` + +Converts a `BitmapIcon` into a `MapImage` object, which is used by the HERE SDK to render images on the map. + +### Signature + +```kotlin +internal fun BitmapIcon.toMapImage(): MapImage +``` + +### Description + +This extension function transforms a `BitmapIcon` instance into a `MapImage`. It extracts the raw pixel data as a byte array, along with the bitmap's width and height. The image format is consistently set to `ImageFormat.PNG`. The resulting `MapImage` is essential for creating visual representations of map markers. + +### Returns + +| Type | Description | +| :--- | :--- | +| `MapImage` | A `MapImage` object containing the icon's visual data, ready to be used for creating a `MapMarker`. | + +### Example + +```kotlin +import com.here.sdk.mapview.MapImage +import com.mapconductor.core.marker.BitmapIcon + +// Assume 'customIcon' is an instance of your BitmapIcon class +val customIcon: BitmapIcon = createCustomBitmapIcon() + +// Convert the BitmapIcon to a HERE SDK MapImage +val mapImage: MapImage = customIcon.toMapImage() + +// 'mapImage' can now be used to create a MapMarker +// val mapMarker = MapMarker(geoCoordinates, mapImage) +``` + +--- + +## `toAnchor2D()` + +Converts the anchor point of a `BitmapIcon` into an `Anchor2D` object for precise marker placement. + +### Signature + +```kotlin +internal fun BitmapIcon.toAnchor2D(): Anchor2D +``` + +### Description + +This extension function extracts the normalized anchor coordinates (`x`, `y`) from a `BitmapIcon` and uses them to create an `Anchor2D` object. The anchor point determines which pixel of the marker image is placed exactly at the marker's geographical coordinates. The coordinates are normalized, where `(0.0, 0.0)` is the top-left corner and `(1.0, 1.0)` is the bottom-right corner. + +### Returns + +| Type | Description | +| :--- | :--- | +| `Anchor2D` | An `Anchor2D` object representing the attachment point for the marker image. | + +### Example + +```kotlin +import com.here.sdk.core.Anchor2D +import com.mapconductor.core.marker.BitmapIcon + +// Assume 'customIcon' is an instance of your BitmapIcon class +// with its anchor property set. +val customIcon: BitmapIcon = createCustomBitmapIconWithAnchor() + +// Convert the icon's anchor to a HERE SDK Anchor2D +val markerAnchor: Anchor2D = customIcon.toAnchor2D() + +// 'markerAnchor' can now be applied to a MapMarker to set its anchor point +// val mapMarker = MapMarker(geoCoordinates, mapImage, markerAnchor) +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/GeoPoint.kt.md b/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/GeoPoint.kt.md new file mode 100644 index 00000000..0e585309 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/GeoPoint.kt.md @@ -0,0 +1,162 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +# HERE SDK Interoperability Extensions + +This document details a set of Kotlin extension functions designed to facilitate seamless conversion between the custom `GeoPoint` type and the HERE SDK's `GeoCoordinates` and `GeoOrientation` classes. These utilities simplify the process of passing data between your application's data models and the HERE SDK. + +--- + +### `GeoPoint.toGeoCoordinates()` + +Converts an instance of `GeoPoint` into a HERE SDK `GeoCoordinates` object. This is useful for passing a custom `GeoPoint` to HERE SDK APIs that require a `GeoCoordinates` parameter. + +**Signature** +```kotlin +fun GeoPoint.toGeoCoordinates(): GeoCoordinates +``` + +**Returns** +| Type | Description | +|---|---| +| `GeoCoordinates` | A new `GeoCoordinates` object with the same latitude and longitude as the source `GeoPoint`. | + +**Example** +```kotlin +import com.mapconductor.core.features.GeoPoint +import com.mapconductor.here.toGeoCoordinates + +// Assuming GeoPoint is a class like: data class GeoPoint(val latitude: Double, val longitude: Double) +val myGeoPoint = GeoPoint(latitude = 40.7128, longitude = -74.0060) + +// Convert to HERE SDK's GeoCoordinates +val hereGeoCoordinates = myGeoPoint.toGeoCoordinates() + +println("Latitude: ${hereGeoCoordinates.latitude}, Longitude: ${hereGeoCoordinates.longitude}") +// Output: Latitude: 40.7128, Longitude: -74.0060 +``` + +--- + +### `GeoPoint.Companion.from()` + +A factory function on the `GeoPoint` companion object that creates a new `GeoPoint` instance from a HERE SDK `GeoCoordinates` object. + +**Signature** +```kotlin +fun GeoPoint.Companion.from(geoCoordinates: GeoCoordinates): GeoPoint +``` + +**Parameters** +| Parameter | Type | Description | +|---|---|---| +| `geoCoordinates` | `GeoCoordinates` | The HERE SDK `GeoCoordinates` object to convert. | + +**Returns** +| Type | Description | +|---|---| +| `GeoPoint` | A new `GeoPoint` instance with latitude and longitude values from the provided `GeoCoordinates`. | + +**Example** +```kotlin +import com.here.sdk.core.GeoCoordinates +import com.mapconductor.core.features.GeoPoint +import com.mapconductor.here.from + +val hereGeoCoordinates = GeoCoordinates(34.0522, -118.2437) + +// Create a GeoPoint from HERE SDK's GeoCoordinates +val myGeoPoint = GeoPoint.from(hereGeoCoordinates) + +println("Latitude: ${myGeoPoint.latitude}, Longitude: ${myGeoPoint.longitude}") +// Output: Latitude: 34.0522, Longitude: -118.2437 +``` + +--- + +### `GeoCoordinates.toGeoPoint()` + +Converts a HERE SDK `GeoCoordinates` object into a `GeoPoint` object. This is useful for converting results from the HERE SDK into the application's custom `GeoPoint` type. + +**Signature** +```kotlin +fun GeoCoordinates.toGeoPoint(): GeoPoint +``` + +**Returns** +| Type | Description | +|---|---| +| `GeoPoint` | A new `GeoPoint` object with the same latitude and longitude as the source `GeoCoordinates`. | + +**Example** +```kotlin +import com.here.sdk.core.GeoCoordinates +import com.mapconductor.here.toGeoPoint + +val hereGeoCoordinates = GeoCoordinates(51.5074, -0.1278) // London + +// Convert to your application's GeoPoint +val myGeoPoint = hereGeoCoordinates.toGeoPoint() + +println("Latitude: ${myGeoPoint.latitude}, Longitude: ${myGeoPoint.longitude}") +// Output: Latitude: 51.5074, Longitude: -0.1278 +``` + +--- + +### `GeoCoordinates.toUpdate()` + +Creates a `GeoCoordinatesUpdate` from a `GeoCoordinates` instance. This is a convenience function for APIs that require updates, such as animating map camera properties. + +**Signature** +```kotlin +fun GeoCoordinates.toUpdate(): GeoCoordinatesUpdate +``` + +**Returns** +| Type | Description | +|---|---| +| `GeoCoordinatesUpdate` | A new `GeoCoordinatesUpdate` object containing the source `GeoCoordinates`. | + +**Example** +```kotlin +import com.here.sdk.core.GeoCoordinates +import com.mapconductor.here.toUpdate + +val targetCoordinates = GeoCoordinates(48.8566, 2.3522) // Paris + +// Create an update object for camera animation +val coordinateUpdate = targetCoordinates.toUpdate() + +// This update can now be used with the MapCamera +// mapCamera.lookAt(coordinateUpdate, ...) +``` + +--- + +### `GeoOrientation.toUpdate()` + +Creates a `GeoOrientationUpdate` from a `GeoOrientation` instance. This is a convenience function for APIs that require orientation updates, such as changing the map camera's bearing or tilt. + +**Signature** +```kotlin +fun GeoOrientation.toUpdate(): GeoOrientationUpdate +``` + +**Returns** +| Type | Description | +|---|---| +| `GeoOrientationUpdate` | A new `GeoOrientationUpdate` object containing the source `GeoOrientation`. | + +**Example** +```kotlin +import com.here.sdk.core.GeoOrientation +import com.mapconductor.here.toUpdate + +val newOrientation = GeoOrientation(45.0, 0.0) // 45-degree bearing, 0-degree tilt + +// Create an update object for camera orientation +val orientationUpdate = newOrientation.toUpdate() + +// This update can now be used to change the map's orientation +// mapCamera.lookAt(point, orientationUpdate, ...) +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/GeoRectBounds.kt.md b/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/GeoRectBounds.kt.md new file mode 100644 index 00000000..69a6ffb9 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/GeoRectBounds.kt.md @@ -0,0 +1,96 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +# GeoBox and GeoRectBounds Conversion Utilities + +This document provides details on the extension functions for converting between `GeoRectBounds` and the HERE SDK's `GeoBox` types. These utilities facilitate interoperability between the Map Conductor Core library and the HERE SDK. + +--- + +## `GeoRectBounds.toGeoBox()` + +Converts a `GeoRectBounds` object to a HERE SDK `GeoBox` object. + +### Signature +```kotlin +fun GeoRectBounds.toGeoBox(): GeoBox? +``` + +### Description +This extension function creates a `GeoBox` that represents the same geographical bounding box as the source `GeoRectBounds`. The conversion relies on the `southWest` and `northEast` corner points of the `GeoRectBounds`. + +If either the `southWest` or `northEast` property of the `GeoRectBounds` is `null`, the conversion cannot be completed, and the function will return `null`. + +### Returns +| Type | Description | +|---|---| +| `GeoBox?` | A new `GeoBox` instance representing the bounds, or `null` if the source `GeoRectBounds` has null corner points. | + +### Example +```kotlin +import com.mapconductor.core.features.GeoPoint +import com.mapconductor.core.features.GeoRectBounds +import com.mapconductor.here.toGeoBox + +// 1. Define the corner points for GeoRectBounds +val southWestPoint = GeoPoint(latitude = 40.7128, longitude = -74.0060) // New York City +val northEastPoint = GeoPoint(latitude = 48.8566, longitude = 2.3522) // Paris + +// 2. Create a GeoRectBounds instance +val rectBounds = GeoRectBounds(southWest = southWestPoint, northEast = northEastPoint) + +// 3. Convert to a GeoBox +val geoBox = rectBounds.toGeoBox() + +if (geoBox != null) { + println("Successfully converted to GeoBox.") + println("SW Corner: ${geoBox.southWestCorner}") + println("NE Corner: ${geoBox.northEastCorner}") +} else { + println("Conversion failed: GeoRectBounds contains null points.") +} + +// Example with null points +val invalidRectBounds = GeoRectBounds(southWest = null, northEast = northEastPoint) +val nullGeoBox = invalidRectBounds.toGeoBox() // This will be null +println("Result of converting invalid bounds: $nullGeoBox") +``` + +--- + +## `GeoBox.toGeoRectBounds()` + +Converts a HERE SDK `GeoBox` object to a `GeoRectBounds` object. + +### Signature +```kotlin +fun GeoBox.toGeoRectBounds(): GeoRectBounds +``` + +### Description +This extension function creates a `GeoRectBounds` object that represents the same geographical bounding box as the source `GeoBox`. It extracts the `southWestCorner` and `northEastCorner` from the `GeoBox` to construct the new `GeoRectBounds` instance. This operation is non-nullable and will always succeed. + +### Returns +| Type | Description | +|---|---| +| `GeoRectBounds` | A new `GeoRectBounds` instance representing the same area as the source `GeoBox`. | + +### Example +```kotlin +import com.here.sdk.core.GeoBox +import com.here.sdk.core.GeoCoordinates +import com.mapconductor.here.toGeoRectBounds + +// 1. Define the corner coordinates for a GeoBox +val southWestCoords = GeoCoordinates(52.5163, 13.3777) // Berlin +val northEastCoords = GeoCoordinates(51.5074, -0.1278) // London + +// 2. Create a GeoBox instance +val geoBox = GeoBox(southWestCoords, northEastCoords) + +// 3. Convert to a GeoRectBounds +val rectBounds = geoBox.toGeoRectBounds() + +println("Successfully converted to GeoRectBounds.") +println("SW Point: ${rectBounds.southWest}") +println("NE Point: ${rectBounds.northEast}") +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/HereMapDesign.kt.md b/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/HereMapDesign.kt.md new file mode 100644 index 00000000..046f2b53 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/HereMapDesign.kt.md @@ -0,0 +1,158 @@ +Of course! Here is a high-quality SDK document for the provided Kotlin code snippet. + +--- + +# HereMapDesign + +## `HereMapDesign` + +A sealed class that encapsulates the various map visual styles (schemes) available in the HERE SDK. Each object within this class represents a specific `MapScheme` and provides a type-safe way to manage and apply map designs. + +This class implements the `HereMapDesignType` interface, which standardizes the handling of map designs. + +### Available Map Designs + +The following singleton objects represent the available map designs. + +| Object | Corresponding `MapScheme` | Description | +| ------------------- | ----------------------------- | ------------------------------------------------------------------------ | +| `NormalDay` | `MapScheme.NORMAL_DAY` | The standard map style for daytime viewing. | +| `NormalNight` | `MapScheme.NORMAL_NIGHT` | The standard map style for nighttime viewing. | +| `Satellite` | `MapScheme.SATELLITE` | A map style that displays satellite imagery. | +| `HybridDay` | `MapScheme.HYBRID_DAY` | Combines satellite imagery with road network and label overlays for daytime. | +| `HybridNight` | `MapScheme.HYBRID_NIGHT` | Combines satellite imagery with road network and label overlays for nighttime. | +| `LiteDay` | `MapScheme.LITE_DAY` | A lightweight, performance-optimized map style for daytime. | +| `LiteNight` | `MapScheme.LITE_NIGHT` | A lightweight, performance-optimized map style for nighttime. | +| `LiteHybridDay` | `MapScheme.LITE_HYBRID_DAY` | A lightweight hybrid map style for daytime. | +| `LiteHybridNight` | `MapScheme.LITE_HYBRID_NIGHT` | A lightweight hybrid map style for nighttime. | +| `LogisticsDay` | `MapScheme.LOGISTICS_DAY` | A map style optimized for logistics and trucking applications for daytime. | +| `LogisticsNight` | `MapScheme.LOGISTICS_NIGHT` | A map style optimized for logistics and trucking applications for nighttime. | +| `LogisticsHybridDay`| `MapScheme.LOGISTICS_HYBRID_DAY`| A hybrid map style optimized for logistics applications for daytime. | +| `RoadNetworkDay` | `MapScheme.ROAD_NETWORK_DAY` | A map style that displays only the road network for daytime. | +| `RoadNetworkNight` | `MapScheme.ROAD_NETWORK_NIGHT`| A map style that displays only the road network for nighttime. | + +### Methods + +#### `getValue()` + +Retrieves the underlying HERE SDK `MapScheme` enum associated with the `HereMapDesign` instance. + +**Signature** +```kotlin +fun getValue(): MapScheme +``` + +**Returns** +| Type | Description | +| ----------- | ----------------------------------------- | +| `MapScheme` | The corresponding `MapScheme` enum value. | + +--- + +## Companion Object + +Provides factory methods to create `HereMapDesign` instances. + +### `CreateById()` + +Creates a `HereMapDesign` instance from its corresponding integer ID. + +**Signature** +```kotlin +fun CreateById(id: Int): HereMapDesign +``` + +**Parameters** +| Parameter | Type | Description | +| --------- | ----- | ------------------------------------------------------------------------ | +| `id` | `Int` | The integer identifier of the map scheme, corresponding to `MapScheme.value`. | + +**Returns** +| Type | Description | +| --------------- | ------------------------------------------------------------------------------------------------------- | +| `HereMapDesign` | The `HereMapDesign` object that corresponds to the given ID. | + +**Throws** +| Exception | Condition | +| -------------------------- | -------------------------------------------------- | +| `IllegalArgumentException` | If the provided `id` does not match any supported map scheme. | + +### `Create()` + +Creates a `HereMapDesign` instance from a `MapScheme` enum value. + +**Signature** +```kotlin +fun Create(id: MapScheme): HereMapDesign +``` + +**Parameters** +| Parameter | Type | Description | +| --------- | ----------- | ---------------------------------- | +| `id` | `MapScheme` | The `MapScheme` enum value to use. | + +**Returns** +| Type | Description | +| --------------- | ------------------------------------------------------------------------------------------------------- | +| `HereMapDesign` | The `HereMapDesign` object that corresponds to the given `MapScheme`. | + +**Throws** +| Exception | Condition | +| -------------------------- | -------------------------------------------------- | +| `IllegalArgumentException` | If the provided `MapScheme` is not supported. | + +--- + +## `HereMapDesignType` + +A type alias for a generic map design interface, specialized for the HERE SDK. + +**Signature** +```kotlin +typealias HereMapDesignType = MapDesignTypeInterface +``` + +**Description** +This alias simplifies the use of `MapDesignTypeInterface` by fixing its generic type to `MapScheme`, making it specific to the HERE map implementation. + +--- + +## Example + +The following examples demonstrate how to use `HereMapDesign` to set a map's visual style. + +```kotlin +import com.here.sdk.mapview.MapView +import com.mapconductor.here.HereMapDesign + +// Assume 'mapView' is an initialized instance of com.here.sdk.mapview.MapView + +// --- Usage 1: Applying a map scheme directly --- +// Use one of the predefined objects to set the map scheme. +val dayDesign = HereMapDesign.NormalDay +mapView.mapScene.loadScene(dayDesign.getValue()) { mapError -> + if (mapError == null) { + println("Successfully loaded NormalDay scheme.") + } else { + println("Error loading scene: ${mapError.name}") + } +} + +// --- Usage 2: Creating a design from a MapScheme enum --- +// This is useful when you have a MapScheme from another part of your application. +val satelliteScheme = com.here.sdk.mapview.MapScheme.SATELLITE +val satelliteDesign = HereMapDesign.Create(satelliteScheme) +mapView.mapScene.loadScene(satelliteDesign.getValue(), null) + + +// --- Usage 3: Creating a design from an integer ID --- +// This can be useful for deserialization or when working with legacy code. +val schemeId = com.here.sdk.mapview.MapScheme.HYBRID_NIGHT.value +try { + val hybridNightDesign = HereMapDesign.CreateById(schemeId) + mapView.mapScene.loadScene(hybridNightDesign.getValue(), null) + println("Successfully created design from ID: $schemeId") +} catch (e: IllegalArgumentException) { + println("Error: ${e.message}") +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/HereMapView.kt.md b/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/HereMapView.kt.md new file mode 100644 index 00000000..475c1558 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/HereMapView.kt.md @@ -0,0 +1,174 @@ +# SDK Documentation: HereMapView + +This document provides a detailed reference for the `HereMapView` composable functions. + +--- + +## HereMapView + +This is the primary composable for embedding a HERE map into your Jetpack Compose application. It handles the map's lifecycle, state management, and provides a declarative scope for adding map overlays. + +### Signature + +```kotlin +@Composable +fun HereMapView( + state: HereViewState, + modifier: Modifier = Modifier, + markerTiling: MarkerTilingOptions? = null, + sdkInitialize: (suspend (android.content.Context) -> Boolean)? = null, + onMapLoaded: OnMapLoadedHandler? = null, + onMapClick: OnMapEventHandler? = null, + onCameraMoveStart: OnCameraMoveHandler? = null, + onCameraMove: OnCameraMoveHandler? = null, + onCameraMoveEnd: OnCameraMoveHandler? = null, + content: (@Composable HereViewScope.() -> Unit)? = null, +) +``` + +### Description + +The `HereMapView` composable is the root component for displaying a HERE map. It integrates with the underlying HERE SDK for Android, managing its lifecycle within the Compose framework. + +It uses a state-driven approach, where the `HereViewState` controls aspects like camera position and map style. The `content` lambda provides a `HereViewScope`, allowing you to declaratively add map objects such as markers, polylines, and polygons directly inside the map view. + +### Parameters + +| Parameter | Type | Description | +|---|---|---| +| `state` | `HereViewState` | **Required**. The state holder for the map. It controls the camera position, map design, and provides imperative access to the map controller. Typically created using `rememberHereViewState`. | +| `modifier` | `Modifier` | Optional. A standard Jetpack Compose `Modifier` to be applied to the map container. | +| `markerTiling` | `MarkerTilingOptions?` | Optional. Configuration for marker tiling (clustering) to improve performance with a large number of markers. Defaults to `MarkerTilingOptions.Default`. | +| `sdkInitialize` | `suspend (Context) -> Boolean` | Optional. A custom suspendable lambda for initializing the HERE SDK. If `null`, a default initialization is performed automatically. | +| `onMapLoaded` | `OnMapLoadedHandler?` | Optional. A callback invoked once the map scene has finished loading and the map is ready for interaction. | +| `onMapClick` | `OnMapEventHandler?` | Optional. A callback invoked when the user clicks on a point on the map that is not an overlay. The callback receives the `GeoCoordinate` of the click. | +| `onCameraMoveStart` | `OnCameraMoveHandler?` | Optional. A callback invoked when the map camera starts moving, either due to user interaction or programmatic changes. | +| `onCameraMove` | `OnCameraMoveHandler?` | Optional. A callback invoked continuously as the map camera is moving. | +| `onCameraMoveEnd` | `OnCameraMoveHandler?` | Optional. A callback invoked when the map camera has finished moving. | +| `content` | `@Composable HereViewScope.() -> Unit` | Optional. A composable lambda that defines the content to be displayed on the map. Use this to add overlays like `Marker`, `Polyline`, `Polygon`, etc. | + +### Returns + +This composable function does not return a value. It emits the HERE map UI into the composition. + +### Example + +```kotlin +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import com.here.sdk.core.GeoCoordinate +import com.mapconductor.core.map.MapCameraPosition +import com.mapconductor.here.HereMapView +import com.mapconductor.here.Marker +import com.mapconductor.here.rememberHereViewState +import com.mapconductor.here.rememberMarkerState + +@Composable +fun MyMapScreen() { + // 1. Define the initial camera position + val tokyoStation = GeoCoordinate(35.681236, 139.767125) + val cameraPosition = MapCameraPosition(target = tokyoStation, zoom = 14.0) + + // 2. Create and remember the map's state + val mapState = rememberHereViewState(cameraPosition = cameraPosition) + + // 3. Add the HereMapView to your composition + HereMapView( + state = mapState, + modifier = Modifier.fillMaxSize(), + onMapLoaded = { + println("Map has finished loading.") + }, + onMapClick = { coordinate -> + println("Map clicked at: $coordinate") + } + ) { + // 4. Add map overlays declaratively within the content scope + Marker( + state = rememberMarkerState(position = tokyoStation), + title = "Tokyo Station", + onClick = { + println("Tokyo Station marker clicked!") + true // Consume the click event + } + ) + } +} +``` + +--- + +## HereMapView (Deprecated) + +This overload is deprecated. Event handling for overlays should be done on the state object of each individual overlay. + +### Signature + +```kotlin +@Deprecated("Use CircleState/PolylineState/PolygonState onClick instead.") +@Composable +fun HereMapView( + // ... other parameters + onMarkerClick: OnMarkerEventHandler?, + onMarkerDragStart: OnMarkerEventHandler? = null, + onMarkerDrag: OnMarkerEventHandler? = null, + onMarkerDragEnd: OnMarkerEventHandler? = null, + onCircleClick: OnCircleEventHandler? = null, + onPolylineClick: OnPolylineEventHandler? = null, + onPolygonClick: OnPolygonEventHandler? = null, + // ... other parameters +) +``` + +### Description + +**Deprecated:** This version of `HereMapView` is deprecated. It uses global callbacks for overlay events (e.g., `onMarkerClick`, `onCircleClick`). The recommended approach is to handle events on a per-overlay basis. + +**Migration:** Move event handling logic from these global callbacks to the corresponding parameters on the individual overlay composables (e.g., the `onClick` parameter of the `Marker` composable). This provides a more granular, predictable, and state-driven way to manage interactions. + +### Deprecated Parameters + +| Parameter | Type | Migration Guide | +|---|---|---| +| `onMarkerClick` | `OnMarkerEventHandler?` | **Deprecated**. Use the `onClick` parameter on the `Marker` composable. | +| `onMarkerDragStart`| `OnMarkerEventHandler?` | **Deprecated**. Use the `onDragStart` parameter on a draggable `Marker`. | +| `onMarkerDrag` | `OnMarkerEventHandler?` | **Deprecated**. Use the `onDrag` parameter on a draggable `Marker`. | +| `onMarkerDragEnd` | `OnMarkerEventHandler?` | **Deprecated**. Use the `onDragEnd` parameter on a draggable `Marker`. | +| `onCircleClick` | `OnCircleEventHandler?` | **Deprecated**. Use the `onClick` parameter on the `Circle` composable. | +| `onPolylineClick` | `OnPolylineEventHandler?` | **Deprecated**. Use the `onClick` parameter on the `Polyline` composable. | +| `onPolygonClick` | `OnPolygonEventHandler?` | **Deprecated**. Use the `onClick` parameter on the `Polygon` composable. | + +### Migration Example + +**Before (Deprecated Pattern):** + +```kotlin +// Deprecated: Avoid this pattern +HereMapView( + state = mapState, + onMarkerClick = { marker -> + println("Marker ${marker.id} was clicked.") + true + } +) { + Marker(state = rememberMarkerState(position = somePosition)) +} +``` + +**After (Recommended Pattern):** + +```kotlin +// Recommended: Handle events on the overlay itself +HereMapView( + state = mapState +) { + Marker( + state = rememberMarkerState(position = somePosition), + onClick = { + println("Marker was clicked.") + true // Consume the event + } + ) +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/HereMapViewController.kt.md b/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/HereMapViewController.kt.md new file mode 100644 index 00000000..6910572b --- /dev/null +++ b/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/HereMapViewController.kt.md @@ -0,0 +1,781 @@ +Of course! Here is the high-quality SDK documentation for the provided `HereMapViewController` code snippet. + +--- + +# HereMapViewController + +## Class: HereMapViewController + +The `HereMapViewController` is a central controller for the HERE SDK map view. It manages the lifecycle and interactions of various map overlays such as markers, polylines, polygons, circles, and ground images. It also handles camera movements, user gestures (taps, long presses), and map styling. This class serves as the primary interface for developers to interact with the map. + +### Signature + +```kotlin +class HereMapViewController( + private val markerController: HereMarkerController, + private val polylineController: HerePolylineController, + private val polygonController: HerePolygonController, + private val groundImageController: HereGroundImageController, + private val circleController: HereCircleController, + private val rasterLayerController: HereRasterLayerController, + override val holder: HereViewHolder, + override val coroutine: CoroutineScope = CoroutineScope(Dispatchers.Main), + val backCoroutine: CoroutineScope = CoroutineScope(Dispatchers.Default), +) : BaseMapViewController(), + CircleCapableInterface, + HereMapViewControllerInterface, + MapCameraListener, + TapListener, + LongPressListener +``` + +### Constructor + +Initializes a new instance of the `HereMapViewController`. + +#### Parameters + +| Name | Type | Description | +| --------------------- | ------------------------- | ------------------------------------------------------------------------------ | +| `markerController` | `HereMarkerController` | The controller responsible for managing marker overlays. | +| `polylineController` | `HerePolylineController` | The controller responsible for managing polyline overlays. | +| `polygonController` | `HerePolygonController` | The controller responsible for managing polygon overlays. | +| `groundImageController`| `HereGroundImageController`| The controller responsible for managing ground image overlays. | +| `circleController` | `HereCircleController` | The controller responsible for managing circle overlays. | +| `rasterLayerController`| `HereRasterLayerController`| The controller responsible for managing raster layer overlays. | +| `holder` | `HereViewHolder` | The view holder that contains the `MapView` instance. | +| `coroutine` | `CoroutineScope` | The coroutine scope for main-thread operations. Defaults to `Dispatchers.Main`.| +| `backCoroutine` | `CoroutineScope` | The coroutine scope for background operations. Defaults to `Dispatchers.Default`.| + +--- + +## Overlay Management + +### clearOverlays + +Clears all overlays (markers, polylines, polygons, etc.) from the map. + +#### Signature + +```kotlin +override suspend fun clearOverlays() +``` + +--- + +### compositionMarkers + +Adds a list of markers to the map based on the provided `MarkerState` data. + +#### Signature + +```kotlin +override suspend fun compositionMarkers(data: List) +``` + +#### Parameters + +| Name | Type | Description | +| ------ | ------------------- | ----------------------------------------- | +| `data` | `List` | A list of `MarkerState` objects to render. | + +--- + +### updateMarker + +Updates an existing marker on the map with new state information. + +#### Signature + +```kotlin +override suspend fun updateMarker(state: MarkerState) +``` + +#### Parameters + +| Name | Type | Description | +| ------- | ------------- | -------------------------------------------- | +| `state` | `MarkerState` | The new state for the marker to be updated. | + +--- + +### hasMarker + +Checks if a specific marker exists on the map. + +#### Signature + +```kotlin +override fun hasMarker(state: MarkerState): Boolean +``` + +#### Parameters + +| Name | Type | Description | +| ------- | ------------- | ----------------------------------------- | +| `state` | `MarkerState` | The state of the marker to check for. | + +#### Returns + +`Boolean` - `true` if the marker exists, `false` otherwise. + +--- + +### compositionPolylines + +Adds a list of polylines to the map. + +#### Signature + +```kotlin +override suspend fun compositionPolylines(data: List) +``` + +#### Parameters + +| Name | Type | Description | +| ------ | --------------------- | ------------------------------------------- | +| `data` | `List` | A list of `PolylineState` objects to render. | + +--- + +### updatePolyline + +Updates an existing polyline on the map. + +#### Signature + +```kotlin +override suspend fun updatePolyline(state: PolylineState) +``` + +#### Parameters + +| Name | Type | Description | +| ------- | --------------- | ---------------------------------------------- | +| `state` | `PolylineState` | The new state for the polyline to be updated. | + +--- + +### hasPolyline + +Checks if a specific polyline exists on the map. + +#### Signature + +```kotlin +override fun hasPolyline(state: PolylineState): Boolean +``` + +#### Parameters + +| Name | Type | Description | +| ------- | --------------- | ------------------------------------------- | +| `state` | `PolylineState` | The state of the polyline to check for. | + +#### Returns + +`Boolean` - `true` if the polyline exists, `false` otherwise. + +--- + +### compositionPolygons + +Adds a list of polygons to the map. + +#### Signature + +```kotlin +override suspend fun compositionPolygons(data: List) +``` + +#### Parameters + +| Name | Type | Description | +| ------ | -------------------- | ------------------------------------------ | +| `data` | `List` | A list of `PolygonState` objects to render. | + +--- + +### updatePolygon + +Updates an existing polygon on the map. + +#### Signature + +```kotlin +override suspend fun updatePolygon(state: PolygonState) +``` + +#### Parameters + +| Name | Type | Description | +| ------- | -------------- | --------------------------------------------- | +| `state` | `PolygonState` | The new state for the polygon to be updated. | + +--- + +### hasPolygon + +Checks if a specific polygon exists on the map. + +#### Signature + +```kotlin +override fun hasPolygon(state: PolygonState): Boolean +``` + +#### Parameters + +| Name | Type | Description | +| ------- | -------------- | ------------------------------------------ | +| `state` | `PolygonState` | The state of the polygon to check for. | + +#### Returns + +`Boolean` - `true` if the polygon exists, `false` otherwise. + +--- + +### compositionCircles + +Adds a list of circles to the map. + +#### Signature + +```kotlin +override suspend fun compositionCircles(data: List) +``` + +#### Parameters + +| Name | Type | Description | +| ------ | ------------------- | ----------------------------------------- | +| `data` | `List` | A list of `CircleState` objects to render. | + +--- + +### updateCircle + +Updates an existing circle on the map. + +#### Signature + +```kotlin +override suspend fun updateCircle(state: CircleState) +``` + +#### Parameters + +| Name | Type | Description | +| ------- | ------------- | -------------------------------------------- | +| `state` | `CircleState` | The new state for the circle to be updated. | + +--- + +### hasCircle + +Checks if a specific circle exists on the map. + +#### Signature + +```kotlin +override fun hasCircle(state: CircleState): Boolean +``` + +#### Parameters + +| Name | Type | Description | +| ------- | ------------- | ----------------------------------------- | +| `state` | `CircleState` | The state of the circle to check for. | + +#### Returns + +`Boolean` - `true` if the circle exists, `false` otherwise. + +--- + +### compositionGroundImages + +Adds a list of ground images to the map. + +#### Signature + +```kotlin +override suspend fun compositionGroundImages(data: List) +``` + +#### Parameters + +| Name | Type | Description | +| ------ | ------------------------ | ------------------------------------------------ | +| `data` | `List` | A list of `GroundImageState` objects to render. | + +--- + +### updateGroundImage + +Updates an existing ground image on the map. + +#### Signature + +```kotlin +override suspend fun updateGroundImage(state: GroundImageState) +``` + +#### Parameters + +| Name | Type | Description | +| ------- | ------------------ | -------------------------------------------------- | +| `state` | `GroundImageState` | The new state for the ground image to be updated. | + +--- + +### hasGroundImage + +Checks if a specific ground image exists on the map. + +#### Signature + +```kotlin +override fun hasGroundImage(state: GroundImageState): Boolean +``` + +#### Parameters + +| Name | Type | Description | +| ------- | ------------------ | ---------------------------------------------- | +| `state` | `GroundImageState` | The state of the ground image to check for. | + +#### Returns + +`Boolean` - `true` if the ground image exists, `false` otherwise. + +--- + +### compositionRasterLayers + +Adds a list of raster layers to the map. + +#### Signature + +```kotlin +override suspend fun compositionRasterLayers(data: List) +``` + +#### Parameters + +| Name | Type | Description | +| ------ | ------------------------ | ------------------------------------------------ | +| `data` | `List` | A list of `RasterLayerState` objects to render. | + +--- + +### updateRasterLayer + +Updates an existing raster layer on the map. + +#### Signature + +```kotlin +override suspend fun updateRasterLayer(state: RasterLayerState) +``` + +#### Parameters + +| Name | Type | Description | +| ------- | ------------------ | -------------------------------------------------- | +| `state` | `RasterLayerState` | The new state for the raster layer to be updated. | + +--- + +### hasRasterLayer + +Checks if a specific raster layer exists on the map. + +#### Signature + +```kotlin +override fun hasRasterLayer(state: RasterLayerState): Boolean +``` + +#### Parameters + +| Name | Type | Description | +| ------- | ------------------ | ---------------------------------------------- | +| `state` | `RasterLayerState` | The state of the raster layer to check for. | + +#### Returns + +`Boolean` - `true` if the raster layer exists, `false` otherwise. + +--- + +## Camera Control + +### moveCamera + +Moves the map camera to a specified position instantly. + +#### Signature + +```kotlin +override fun moveCamera(position: MapCameraPosition) +``` + +#### Parameters + +| Name | Type | Description | +| ---------- | ------------------- | ----------------------------------------- | +| `position` | `MapCameraPosition` | The target position for the map camera. | + +#### Example + +```kotlin +val newPosition = MapCameraPosition( + position = GeoPoint(35.681236, 139.767125), // Tokyo Station + zoom = 15.0, + bearing = 0.0, + tilt = 0.0 +) +mapViewController.moveCamera(newPosition) +``` + +--- + +### animateCamera + +Animates the map camera to a new position over a specified duration. This creates a "fly-to" effect. + +#### Signature + +```kotlin +override fun animateCamera( + position: MapCameraPosition, + duration: Long, +) +``` + +#### Parameters + +| Name | Type | Description | +| ---------- | ------------------- | ------------------------------------------------------------------------ | +| `position` | `MapCameraPosition` | The target position for the map camera. | +| `duration` | `Long` | The duration of the animation in milliseconds. | + +#### Example + +```kotlin +val targetPosition = MapCameraPosition( + position = GeoPoint(40.7128, -74.0060), // New York City + zoom = 14.0, + bearing = 45.0, + tilt = 25.0 +) +mapViewController.animateCamera(targetPosition, duration = 2000L) // 2-second animation +``` + +--- + +## Event Listener Configuration + +### setOnMarkerClickListener + +Sets a global listener for marker click events. + +> **Deprecated:** This method is deprecated. Use the `onClick` lambda on the `MarkerState` object for individual marker event handling. + +#### Signature + +```kotlin +@Deprecated("Use MarkerState.onClick instead.") +override fun setOnMarkerClickListener(listener: OnMarkerEventHandler?) +``` + +#### Parameters + +| Name | Type | Description | +| ---------- | ---------------------- | ----------------------------------------- | +| `listener` | `OnMarkerEventHandler?` | The listener to be invoked on marker click. | + +--- + +### setOnMarkerDragStart + +Sets a global listener for marker drag start events. + +> **Deprecated:** This method is deprecated. Use the `onDragStart` lambda on the `MarkerState` object. + +#### Signature + +```kotlin +@Deprecated("Use MarkerState.onDragStart instead.") +override fun setOnMarkerDragStart(listener: OnMarkerEventHandler?) +``` + +#### Parameters + +| Name | Type | Description | +| ---------- | ---------------------- | ----------------------------------------------- | +| `listener` | `OnMarkerEventHandler?` | The listener to be invoked when a marker drag starts. | + +--- + +### setOnMarkerDrag + +Sets a global listener for marker drag events. + +> **Deprecated:** This method is deprecated. Use the `onDrag` lambda on the `MarkerState` object. + +#### Signature + +```kotlin +@Deprecated("Use MarkerState.onDrag instead.") +override fun setOnMarkerDrag(listener: OnMarkerEventHandler?) +``` + +#### Parameters + +| Name | Type | Description | +| ---------- | ---------------------- | ----------------------------------------------- | +| `listener` | `OnMarkerEventHandler?` | The listener to be invoked during a marker drag. | + +--- + +### setOnMarkerDragEnd + +Sets a global listener for marker drag end events. + +> **Deprecated:** This method is deprecated. Use the `onDragEnd` lambda on the `MarkerState` object. + +#### Signature + +```kotlin +@Deprecated("Use MarkerState.onDragEnd instead.") +override fun setOnMarkerDragEnd(listener: OnMarkerEventHandler?) +``` + +#### Parameters + +| Name | Type | Description | +| ---------- | ---------------------- | --------------------------------------------- | +| `listener` | `OnMarkerEventHandler?` | The listener to be invoked when a marker drag ends. | + +--- + +### setOnCircleClickListener + +Sets a global listener for circle click events. + +> **Deprecated:** This method is deprecated. Use the `onClick` lambda on the `CircleState` object. + +#### Signature + +```kotlin +@Deprecated("Use CircleState.onClick instead.") +override fun setOnCircleClickListener(listener: OnCircleEventHandler?) +``` + +#### Parameters + +| Name | Type | Description | +| ---------- | --------------------- | ----------------------------------------- | +| `listener` | `OnCircleEventHandler?` | The listener to be invoked on circle click. | + +--- + +### setOnGroundImageClickListener + +Sets a global listener for ground image click events. + +> **Deprecated:** This method is deprecated. Use the `onClick` lambda on the `GroundImageState` object. + +#### Signature + +```kotlin +@Deprecated("Use GroundImageState.onClick instead.") +override fun setOnGroundImageClickListener(listener: OnGroundImageEventHandler?) +``` + +#### Parameters + +| Name | Type | Description | +| ---------- | -------------------------- | --------------------------------------------- | +| `listener` | `OnGroundImageEventHandler?` | The listener to be invoked on ground image click. | + +--- + +### setOnPolylineClickListener + +Sets a global listener for polyline click events. + +> **Deprecated:** This method is deprecated. Use the `onClick` lambda on the `PolylineState` object. + +#### Signature + +```kotlin +@Deprecated("Use PolylineState.onClick instead.") +override fun setOnPolylineClickListener(listener: OnPolylineEventHandler?) +``` + +#### Parameters + +| Name | Type | Description | +| ---------- | ----------------------- | ------------------------------------------- | +| `listener` | `OnPolylineEventHandler?` | The listener to be invoked on polyline click. | + +--- + +### setOnPolygonClickListener + +Sets a global listener for polygon click events. + +> **Deprecated:** This method is deprecated. Use the `onClick` lambda on the `PolygonState` object. + +#### Signature + +```kotlin +@Deprecated("Use PolygonState.onClick instead.") +override fun setOnPolygonClickListener(listener: OnPolygonEventHandler?) +``` + +#### Parameters + +| Name | Type | Description | +| ---------- | ---------------------- | ------------------------------------------ | +| `listener` | `OnPolygonEventHandler?` | The listener to be invoked on polygon click. | + +--- + +## Map Styling + +### setMapDesignType + +Sets the visual style (theme) of the map, such as normal day, satellite, or night mode. This operation is asynchronous. + +#### Signature + +```kotlin +override fun setMapDesignType(value: HereMapDesignType) +``` + +#### Parameters + +| Name | Type | Description | +| ------- | ------------------- | ------------------------------------------------------------------------ | +| `value` | `HereMapDesignType` | The desired map design type (e.g., `HereMapDesign.NormalDay`). | + +#### Example + +```kotlin +// Switch to the satellite map style +mapViewController.setMapDesignType(HereMapDesign.Satellite) +``` + +--- + +### setMapDesignTypeChangeListener + +Registers a listener that is notified whenever the map's design type changes. The listener is immediately invoked with the current map design type upon registration. + +#### Signature + +```kotlin +override fun setMapDesignTypeChangeListener(listener: HereMapDesignTypeChangeHandler) +``` + +#### Parameters + +| Name | Type | Description | +| ---------- | ------------------------------ | ------------------------------------------------------------------------ | +| `listener` | `HereMapDesignTypeChangeHandler` | A handler that receives the new `HereMapDesignType` when the style changes. | + +#### Example + +```kotlin +mapViewController.setMapDesignTypeChangeListener { newDesignType -> + println("Map design changed to: ${newDesignType::class.simpleName}") +} +``` + +--- + +## Utility and Factory Methods + +### setupListeners + +Sets up or re-applies the internal listeners for camera updates and gesture handling on the `MapView`. This is typically called during initialization. + +#### Signature + +```kotlin +fun setupListeners() +``` + +--- + +### createMarkerRenderer + +Creates a renderer for markers that use a specific rendering strategy. + +#### Signature + +```kotlin +fun createMarkerRenderer( + strategy: MarkerRenderingStrategyInterface, +): MarkerOverlayRendererInterface +``` + +#### Parameters + +| Name | Type | Description | +| ---------- | ------------------------------------------------------ | ----------------------------------------- | +| `strategy` | `MarkerRenderingStrategyInterface` | The strategy to use for rendering markers. | + +#### Returns + +`MarkerOverlayRendererInterface` - A new marker renderer instance. + +--- + +### createMarkerEventController + +Creates an event controller for markers that use a specific rendering strategy. + +#### Signature + +```kotlin +fun createMarkerEventController( + controller: StrategyMarkerController, + renderer: MarkerOverlayRendererInterface, +): MarkerEventControllerInterface +``` + +#### Parameters + +| Name | Type | Description | +| ------------ | ------------------------------------------------ | ----------------------------------------- | +| `controller` | `StrategyMarkerController` | The strategy controller for the markers. | +| `renderer` | `MarkerOverlayRendererInterface` | The renderer associated with the markers. | + +#### Returns + +`MarkerEventControllerInterface` - A new marker event controller instance. + +--- + +### registerMarkerEventController + +Registers a custom marker event controller to handle user interactions like clicks and drags for a specific set of markers. + +#### Signature + +```kotlin +fun registerMarkerEventController(controller: MarkerEventControllerInterface) +``` + +#### Parameters + +| Name | Type | Description | +| ------------ | ---------------------------------------------- | ------------------------------------------------------------------------ | +| `controller` | `MarkerEventControllerInterface` | The event controller to register. It will be configured with any global listeners. | \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/HereMapViewControllerInterface.kt.md b/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/HereMapViewControllerInterface.kt.md new file mode 100644 index 00000000..168fc281 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/HereMapViewControllerInterface.kt.md @@ -0,0 +1,99 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +# Interface: `HereMapViewControllerInterface` + +Provides the main interface for controlling and interacting with a HERE Map view. + +## Description + +The `HereMapViewControllerInterface` serves as a central controller for a map instance. It aggregates functionalities for managing various map objects like markers, polygons, polylines, and more by inheriting from multiple `Capable` interfaces. + +In addition to the common map functionalities, this interface provides specific methods for managing the visual design and style of the HERE map, allowing you to change the map's appearance (e.g., satellite, terrain) and listen for those changes. + +This interface inherits from the following interfaces, gaining their capabilities: +* `MapViewControllerInterface` +* `MarkerCapableInterface` +* `PolygonCapableInterface` +* `PolylineCapableInterface` +* `CircleCapableInterface` +* `GroundImageCapableInterface` +* `RasterLayerCapableInterface` + +--- + +## Methods + +### `setMapDesignType` + +Sets the visual design for the map. This function allows you to dynamically change the map's appearance, for example, switching between a normal street map and a satellite view. + +#### Signature +```kotlin +fun setMapDesignType(value: HereMapDesignType) +``` + +#### Parameters +| Parameter | Type | Description | +|-----------|------|-------------| +| `value` | `HereMapDesignType` | The desired map design type to apply to the map. | + +#### Returns +This function does not return any value. + +#### Example +```kotlin +// Assuming 'mapController' is an instance of HereMapViewControllerInterface +// and HereMapDesignType is an enum with values like SATELLITE, NORMAL, etc. + +// Change the map's visual style to satellite view +mapController.setMapDesignType(HereMapDesignType.SATELLITE) +``` + +--- + +### `setMapDesignTypeChangeListener` + +Registers a listener that will be invoked whenever the map's design type changes. This is useful for reacting to user-initiated or programmatic changes to the map style. + +#### Signature +```kotlin +fun setMapDesignTypeChangeListener(listener: HereMapDesignTypeChangeHandler) +``` + +#### Parameters +| Parameter | Type | Description | +|-----------|------|-------------| +| `listener` | `HereMapDesignTypeChangeHandler` | A lambda function that will be called with the new `HereMapDesignType` when a change occurs. | + +#### Returns +This function does not return any value. + +#### Example +```kotlin +// Assuming 'mapController' is an instance of HereMapViewControllerInterface + +// Define a listener to handle map design changes +val designChangeListener: HereMapDesignTypeChangeHandler = { newDesignType -> + println("Map design has been updated to: $newDesignType") + // You can update UI elements or perform other actions here +} + +// Set the listener on the map controller +mapController.setMapDesignTypeChangeListener(designChangeListener) +``` + +--- + +## Type Aliases + +### `HereMapDesignTypeChangeHandler` + +A type alias for the lambda function used to handle map design type change events. + +#### Signature +```kotlin +typealias HereMapDesignTypeChangeHandler = (HereMapDesignType) -> Unit +``` + +#### Description +This handler receives a single parameter, the new `HereMapDesignType`, and does not return a value. It is used with the `setMapDesignTypeChangeListener` method. \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/HereTypeAlias.kt.md b/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/HereTypeAlias.kt.md new file mode 100644 index 00000000..6ea93b97 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/HereTypeAlias.kt.md @@ -0,0 +1,85 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +# HERE Map Object Type Aliases + +This document provides details on the type aliases used to represent native HERE map objects within the MapConductor framework. These aliases provide a consistent abstraction layer, simplifying interaction with the underlying HERE SDK implementation. + +--- + +### `HereActualMarker` + +A type alias for the native HERE SDK map marker. + +#### Signature +```kotlin +typealias HereActualMarker = com.here.sdk.mapview.MapMarker +``` + +#### Description +`HereActualMarker` represents a single marker object displayed on the map. It is a direct alias for the `MapMarker` class from the HERE SDK. Any object of this type is the actual, underlying marker instance used by the map view. + +**Underlying Type:** `com.here.sdk.mapview.MapMarker` + +--- + +### `HereActualCircle` + +A type alias for a circle, which is implemented as a native HERE SDK polygon. + +#### Signature +```kotlin +typealias HereActualCircle = com.here.sdk.mapview.MapPolygon +``` + +#### Description +`HereActualCircle` represents a circle shape drawn on the map. Internally, a circle is rendered as a high-resolution polygon. This alias points to the underlying `MapPolygon` object that constitutes the circle. + +**Underlying Type:** `com.here.sdk.mapview.MapPolygon` + +--- + +### `HereActualPolyline` + +A type alias for the native HERE SDK map polyline. + +#### Signature +```kotlin +typealias HereActualPolyline = com.here.sdk.mapview.MapPolyline +``` + +#### Description +`HereActualPolyline` represents a polyline (a series of connected line segments) on the map. It is a direct alias for the `MapPolyline` class from the HERE SDK. + +**Underlying Type:** `com.here.sdk.mapview.MapPolyline` + +--- + +### `HereActualPolygon` + +A type alias for a polygon, which is represented as a list of native HERE SDK polygon objects. + +#### Signature +```kotlin +typealias HereActualPolygon = List +``` + +#### Description +`HereActualPolygon` represents a single logical polygon shape on the map. It is defined as a `List` of `MapPolygon` objects to support complex polygons, such as those containing one or more holes (donuts). The first `MapPolygon` in the list typically defines the outer boundary, while subsequent polygons define the inner boundaries (holes). + +**Underlying Type:** `List` + +--- + +### `HereActualGroundImage` + +A type alias for a handle that manages a ground image overlay. + +#### Signature +```kotlin +typealias HereActualGroundImage = com.mapconductor.here.groundimage.HereGroundImageHandle +``` + +#### Description +`HereActualGroundImage` is an alias for a custom `HereGroundImageHandle` class. This class acts as a wrapper and manager for a ground image overlay on the map, abstracting the underlying implementation details of placing and managing the image with the HERE SDK. + +**Underlying Type:** `com.mapconductor.here.groundimage.HereGroundImageHandle` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/HereViewControllerStore.kt.md b/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/HereViewControllerStore.kt.md new file mode 100644 index 00000000..bd2ea119 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/HereViewControllerStore.kt.md @@ -0,0 +1,90 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +*** + +## `HereViewHolderInterface` + +A type alias for `MapViewHolderInterface` specialized for the HERE SDK. It simplifies type declarations for view holders that manage a HERE `MapView` and its `MapScene`. + +### Signature + +```kotlin +typealias HereViewHolderInterface = MapViewHolderInterface +``` + +--- + +## `HereMapViewControllerStore` + +A singleton object that manages `HereMapViewController` instances and controls the global initialization of the HERE SDK. + +### `initSDK(context: Context)` + +Initializes the HERE SDK engine using credentials provided in the application's manifest. This method must be called once before any map-related components are used. + +It is safe to call this function multiple times, as the underlying initialization will only occur on the first call. The recommended practice is to call this method in your `Application.onCreate()` to ensure the SDK is ready before any activity or view requires it. + +#### Signature + +```kotlin +fun initSDK(context: Context) +``` + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :-------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `context` | `Context` | The application context used to access application metadata for credentials and to initialize the SDK. The function internally uses the `applicationContext` to avoid memory leaks. | + +#### Returns + +`Unit` - This function does not return a value. + +#### Throws + +| Exception | Description | +| :---------- | :------------------------------------------------------------------------------------------------------------------------------------- | +| `Exception` | Thrown if the `` tag for `HERE_ACCESS_KEY_ID` or `HERE_ACCESS_KEY_SECRET` is not found in the `AndroidManifest.xml` file. | + +#### Example + +To use this function, you must first add your HERE SDK credentials to your `AndroidManifest.xml` file and then call `initSDK()` from your custom `Application` class. + +1. **Add credentials to `AndroidManifest.xml`** + + Place your credentials inside the `` tag. + + ```xml + + + + + + + + + + + ``` + +2. **Call `initSDK` in your `Application` class** + + Create a custom `Application` class and call `initSDK()` within its `onCreate()` method. + + ```kotlin + import android.app.Application + import com.mapconductor.here.HereMapViewControllerStore + + class MainApplication : Application() { + override fun onCreate() { + super.onCreate() + + // Initialize the HERE SDK on application startup + HereMapViewControllerStore.initSDK(this) + } + } + ``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/HereViewHolder.kt.md b/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/HereViewHolder.kt.md new file mode 100644 index 00000000..9b55d42e --- /dev/null +++ b/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/HereViewHolder.kt.md @@ -0,0 +1,146 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +*** + +# Class `HereViewHolder` + +A container class that holds instances of the HERE SDK's `MapView` and `MapScene`. It implements the `MapViewHolderInterface` to provide a standardized way of interacting with the map, specifically for converting between geographical coordinates and screen coordinates. + +This class acts as a bridge between the generic `MapViewHolderInterface` and the specific implementation details of the HERE Maps SDK. + +## Signature + +```kotlin +class HereViewHolder( + override val mapView: MapView, + override val map: MapScene, +) : MapViewHolderInterface +``` + +## Parameters + +| Parameter | Type | Description | +| :-------- | :-------- | :---------------------------------------- | +| `mapView` | `MapView` | The HERE SDK `MapView` instance. | +| `map` | `MapScene` | The HERE SDK `MapScene` instance for the map. | + +--- + +## Methods + +### `toScreenOffset` + +Converts a geographical coordinate (`GeoPointInterface`) into a screen coordinate (`Offset`) relative to the `MapView`. + +#### Signature + +```kotlin +override fun toScreenOffset(position: GeoPointInterface): Offset? +``` + +#### Description + +This function takes a geographical point and calculates its corresponding pixel offset on the screen. If the provided geographical point is not currently visible on the map view, this function will return `null`. + +#### Parameters + +| Parameter | Type | Description | +| :--------- | :------------------ | :------------------------------------------- | +| `position` | `GeoPointInterface` | The geographical coordinate to be converted. | + +#### Returns + +**`Offset?`**: An `Offset` object containing the x and y screen coordinates, or `null` if the geographical point is outside the visible map area. + +#### Example + +```kotlin +val geoPoint: GeoPointInterface = GeoPoint(52.5200, 13.4050) // Berlin +val screenOffset: Offset? = hereViewHolder.toScreenOffset(geoPoint) + +if (screenOffset != null) { + println("Screen coordinates: x=${screenOffset.x}, y=${screenOffset.y}") +} else { + println("The location is not visible on the screen.") +} +``` + +--- + +### `fromScreenOffset` + +Asynchronously converts a screen coordinate (`Offset`) into a geographical coordinate (`GeoPoint`). + +#### Signature + +```kotlin +override suspend fun fromScreenOffset(offset: Offset): GeoPoint? +``` + +#### Description + +This suspend function takes a pixel offset from the screen and converts it into the corresponding geographical coordinate on the map. Since this is a `suspend` function, it should be called from a coroutine or another suspend function. + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :------- | :------------------------------------------------ | +| `offset` | `Offset` | The screen coordinate (`x`, `y`) to be converted. | + +#### Returns + +**`GeoPoint?`**: A `GeoPoint` object representing the geographical coordinate, or `null` if the conversion is not possible (e.g., the offset is outside the map view). + +#### Example + +```kotlin +import kotlinx.coroutines.launch +import androidx.lifecycle.lifecycleScope + +// Assuming this is called within a Composable or an Activity/Fragment +lifecycleScope.launch { + val screenTapOffset = Offset(400f, 600f) + val geoPoint: GeoPoint? = hereViewHolder.fromScreenOffset(screenTapOffset) + + geoPoint?.let { + println("Tapped at: lat=${it.latitude}, lon=${it.longitude}") + } +} +``` + +--- + +### `fromScreenOffsetSync` + +Synchronously converts a screen coordinate (`Offset`) into a geographical coordinate (`GeoPoint`). + +#### Signature + +```kotlin +override fun fromScreenOffsetSync(offset: Offset): GeoPoint? +``` + +#### Description + +This function takes a pixel offset from the screen and converts it into the corresponding geographical coordinate on the map. Unlike `fromScreenOffset`, this operation is performed synchronously. + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :------- | :------------------------------------------------ | +| `offset` | `Offset` | The screen coordinate (`x`, `y`) to be converted. | + +#### Returns + +**`GeoPoint?`**: A `GeoPoint` object representing the geographical coordinate, or `null` if the conversion is not possible. + +#### Example + +```kotlin +val screenTapOffset = Offset(400f, 600f) +val geoPoint: GeoPoint? = hereViewHolder.fromScreenOffsetSync(screenTapOffset) + +geoPoint?.let { + println("Tapped at: lat=${it.latitude}, lon=${it.longitude}") +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/HereViewInitOptions.kt.md b/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/HereViewInitOptions.kt.md new file mode 100644 index 00000000..38a241f2 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/HereViewInitOptions.kt.md @@ -0,0 +1,48 @@ +# HereViewInitOptions + +The `HereViewInitOptions` data class is used to specify the initial configuration options for a HERE map view. + +## Signature + +```kotlin +data class HereViewInitOptions( + val scheme: MapScheme = MapScheme.NORMAL_DAY +) +``` + +## Description + +This data class holds various settings for initializing a map view component. An instance of this class can be passed during the map view's creation to customize its initial appearance and behavior. + +## Parameters + +| Parameter | Type | Description | +| :-------- | :---------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `scheme` | `MapScheme` | The visual map scheme to apply upon initialization. This determines the map's visual style, such as day, night, or satellite. Defaults to `MapScheme.NORMAL_DAY`. | + +## Example + +The following examples demonstrate how to create an instance of `HereViewInitOptions`. + +### Example 1: Using Default Options + +To initialize the map view with the default settings, you can create an instance without providing any arguments. + +```kotlin +import com.mapconductor.here.HereViewInitOptions + +// Creates options with the default map scheme (NORMAL_DAY) +val defaultOptions = HereViewInitOptions() +``` + +### Example 2: Specifying a Custom Map Scheme + +To initialize the map with a different visual style, such as the night mode scheme, specify the `scheme` parameter during instantiation. + +```kotlin +import com.mapconductor.here.HereViewInitOptions +import com.here.sdk.mapview.MapScheme + +// Creates options to set the initial map scheme to night mode +val nightModeOptions = HereViewInitOptions(scheme = MapScheme.NORMAL_NIGHT) +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/HereViewScope.kt.md b/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/HereViewScope.kt.md new file mode 100644 index 00000000..440578fd --- /dev/null +++ b/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/HereViewScope.kt.md @@ -0,0 +1,37 @@ +# HereViewScope + +### Signature +```kotlin +class HereViewScope : MapViewScope() +``` + +### Description +The `HereViewScope` class provides a specific receiver scope for the `hereMap` builder function. It establishes a context dedicated to configuring a HERE Map instance. + +This class inherits from `MapViewScope`, making all common map configuration functions available within its scope. Its primary purpose is to enable HERE Maps-specific extensions and ensure type-safe construction of the map UI, preventing the mixing of components from different map providers. + +You will typically interact with this class implicitly when using the `hereMap` builder lambda. + +### Example +The following example demonstrates how `HereViewScope` is used as the receiver in the `hereMap` builder to configure a map. + +```kotlin +// A hypothetical MapConductor composable +@Composable +fun MyMapScreen() { + // The lambda block for hereMap operates within the HereViewScope + hereMap( + modifier = Modifier.fillMaxSize(), + // `this` inside the lambda is an instance of HereViewScope + ) { + // Functions from the parent MapViewScope are available + addMarker( + position = GeoPosition(52.5200, 13.4050), + title = "Berlin" + ) + + // You can also use HERE Maps-specific extensions (if available) + // enableTrafficFlow(true) + } +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/HereViewStateImpl.kt.md b/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/HereViewStateImpl.kt.md new file mode 100644 index 00000000..7704bc77 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/HereViewStateImpl.kt.md @@ -0,0 +1,136 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet, formatted in Markdown. + +--- + +## `rememberHereMapViewState` + +A Jetpack Compose composable function that creates and remembers an instance of `HereViewState`. + +### Signature + +```kotlin +@Composable +fun rememberHereMapViewState( + mapDesign: HereMapDesign = HereMapDesign.NormalDay, + cameraPosition: MapCameraPositionInterface = MapCameraPosition.Default, +): HereViewState +``` + +### Description + +This function is the recommended way to create and manage the state of a HERE map within a composable UI. It automatically handles the persistence of the map's state (including camera position and map style) across recompositions and configuration changes, such as screen rotation. The returned `HereViewState` object serves as the single source of truth and the primary interface for controlling the map programmatically. + +### Parameters + +| Parameter | Type | Description | Default | +| :--- | :--- | :--- | :--- | +| `mapDesign` | `HereMapDesign` | The initial visual style and design of the map. | `HereMapDesign.NormalDay` | +| `cameraPosition` | `MapCameraPositionInterface` | The initial camera position, including target coordinates, zoom, tilt, and bearing. | `MapCameraPosition.Default` | + +### Returns + +| Type | Description | +| :--- | :--- | +| `HereViewState` | A remembered `HereViewState` instance that can be used to control the map. | + +### Example + +The following example demonstrates how to create a `HereViewState` and use it to animate the map camera to a new location when a button is clicked. + +```kotlin +import androidx.compose.runtime.Composable +import androidx.compose.runtime.rememberCoroutineScope +import com.mapconductor.core.features.GeoPoint +import com.mapconductor.here.HereMapDesign +import com.mapconductor.here.rememberHereMapViewState +import kotlinx.coroutines.launch + +@Composable +fun MyMapScreen() { + // 1. Create and remember the map view state + val mapViewState = rememberHereMapViewState( + mapDesign = HereMapDesign.NormalDay, + ) + + val coroutineScope = rememberCoroutineScope() + + Column { + // The HereMapView composable would take the state as a parameter + // HereMapView( + // modifier = Modifier.weight(1f), + // viewState = mapViewState + // ) + + Button(onClick = { + coroutineScope.launch { + // 2. Use the state to control the map + val newYork = GeoPoint(40.7128, -74.0060) + mapViewState.moveCameraTo( + position = newYork, + durationMillis = 1000L // Animate over 1 second + ) + } + }) { + Text("Go to New York") + } + } +} +``` + +--- + +## `HereViewState` + +A state-holder class that manages the state and programmatic control of a HERE map view. + +### Description + +An instance of `HereViewState` represents the current state of the map, including its camera position and design. It provides methods to manipulate the map's camera and properties to observe its state. You typically obtain an instance of this class by calling the `rememberHereMapViewState` composable. + +### Properties + +| Property | Type | Description | +| :--- | :--- | :--- | +| `id` | `String` | A unique, stable identifier for the map state instance. | +| `cameraPosition` | `MapCameraPosition` | (Read-only) The current position of the map's camera. This property is updated automatically as the user interacts with the map. | +| `mapDesignType` | `HereMapDesignType` | The current visual style of the map. Setting this property will update the map's appearance in real-time. | + +### Methods + +#### `moveCameraTo(position: GeoPoint, ...)` + +Moves the map camera to a new geographical coordinate, preserving the current zoom, tilt, and bearing. + +**Signature** +```kotlin +fun moveCameraTo( + position: GeoPoint, + durationMillis: Long? = null, +) +``` + +**Parameters** +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `position` | `GeoPoint` | The target geographical coordinate (`latitude`, `longitude`) to center the map on. | +| `durationMillis` | `Long?` | The duration of the camera animation in milliseconds. If `null` or `0`, the camera moves instantly. | + +--- + +#### `moveCameraTo(cameraPosition: MapCameraPosition, ...)` + +Moves the map camera to a new, fully specified camera position, including target, zoom, tilt, and bearing. + +**Signature** +```kotlin +fun moveCameraTo( + cameraPosition: MapCameraPosition, + durationMillis: Long? = null, +) +``` + +**Parameters** +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `cameraPosition` | `MapCameraPosition` | The complete target camera position. | +| `durationMillis` | `Long?` | The duration of the camera animation in milliseconds. If `null` or `0`, the camera moves instantly. | \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/MapCameraPosition.kt.md b/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/MapCameraPosition.kt.md new file mode 100644 index 00000000..7482fa71 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/MapCameraPosition.kt.md @@ -0,0 +1,151 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +--- + +# Map Camera Extensions + +This document provides detailed documentation for a set of Kotlin extension functions designed to facilitate conversion between the HERE SDK's map camera representations (`MapCameraUpdate`, `MapCamera.State`) and a custom, platform-agnostic `MapCameraPosition` class. These utilities are essential for creating a consistent camera control abstraction layer in a multi-map environment. + +## toMapCameraUpdate + +Converts a platform-agnostic `MapCameraPosition` object into a `MapCameraUpdate` object, which can be used to programmatically update the camera of a HERE `MapView`. + +This function handles the necessary transformations, including converting the zoom level from a generic representation to the specific altitude-based zoom level used by the HERE SDK. + +### Signature + +```kotlin +fun MapCameraPosition.toMapCameraUpdate(): MapCameraUpdate +``` + +### Description + +This extension function is called on a `MapCameraPosition` instance. It creates a `MapCameraUpdate` that instructs the map to look at the specified target coordinates (`position`), with the given orientation (`bearing` and `tilt`), and from a calculated distance (zoom level). + +### Returns + +| Type | Description | +| :--- | :--- | +| `MapCameraUpdate` | An object ready to be applied to the HERE `MapView` camera to change its position, orientation, and zoom. | + +### Example + +```kotlin +import com.mapconductor.core.features.GeoPoint +import com.mapconductor.core.map.MapCameraPosition +import com.mapconductor.here.toMapCameraUpdate +import com.here.sdk.mapview.MapView // Assuming mapView is an initialized MapView instance + +// 1. Define a target camera position +val customCameraPosition = MapCameraPosition( + position = GeoPoint(latitude = 48.8584, longitude = 2.2945), // Eiffel Tower + zoom = 15.0, + bearing = 90.0, + tilt = 25.0 +) + +// 2. Convert the custom position to a HERE SDK MapCameraUpdate +val cameraUpdate = customCameraPosition.toMapCameraUpdate() + +// 3. Apply the update to the map view's camera +mapView.camera.applyUpdate(cameraUpdate) +``` + +--- + +## MapCameraPosition.Companion.from + +A factory function that creates a `MapCameraPosition` instance from any object implementing the `MapCameraPositionInterface`. + +### Signature + +```kotlin +fun MapCameraPosition.Companion.from(position: MapCameraPositionInterface): MapCameraPosition +``` + +### Description + +This companion object extension function serves as a convenient constructor. It takes an object that conforms to the `MapCameraPositionInterface` and converts it into a concrete `MapCameraPosition` instance. If the provided object is already a `MapCameraPosition`, it is returned directly to avoid unnecessary object creation. + +### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `position` | `MapCameraPositionInterface` | An object containing camera position data (position, zoom, bearing, tilt, etc.). | + +### Returns + +| Type | Description | +| :--- | :--- | +| `MapCameraPosition` | A new `MapCameraPosition` instance based on the data from the `position` parameter. | + +### Example + +```kotlin +import com.mapconductor.core.features.GeoPoint +import com.mapconductor.core.map.MapCameraPosition +import com.mapconductor.core.map.MapCameraPositionInterface + +// Assume you have a class that implements the interface +data class MyCameraState( + override val position: GeoPoint, + override val zoom: Double, + override val bearing: Double, + override val tilt: Double, + override val paddings: Any? = null, + override val visibleRegion: Any? = null +) : MapCameraPositionInterface + +// 1. Create an instance of your custom state object +val myState = MyCameraState( + position = GeoPoint(51.5074, -0.1278), // London + zoom = 12.0, + bearing = 0.0, + tilt = 0.0 +) + +// 2. Use the factory function to convert it to a standard MapCameraPosition +val mapCameraPosition = MapCameraPosition.from(myState) + +println("Converted zoom: ${mapCameraPosition.zoom}") // Outputs: Converted zoom: 12.0 +``` + +--- + +## toMapCameraPosition + +Converts a HERE SDK `MapCamera.State` object into the platform-agnostic `MapCameraPosition` representation. + +### Signature + +```kotlin +fun MapCamera.State.toMapCameraPosition(): MapCameraPosition +``` + +### Description + +This extension function is called on a `MapCamera.State` instance, which represents the current state of the HERE map camera. It extracts the target coordinates, orientation, and zoom level, converting them into a `MapCameraPosition` object. This is particularly useful for saving the current camera state or synchronizing it with other application components that use the abstract `MapCameraPosition` type. The function also handles the conversion from the HERE SDK's zoom level to the generic zoom representation. + +### Returns + +| Type | Description | +| :--- | :--- | +| `MapCameraPosition` | A `MapCameraPosition` object representing the current state of the HERE map camera. | + +### Example + +```kotlin +import com.mapconductor.here.toMapCameraPosition +import com.here.sdk.mapview.MapView // Assuming mapView is an initialized MapView instance + +// 1. Get the current state of the map camera +val currentHereCameraState = mapView.camera.state + +// 2. Convert the HERE SDK state to the custom MapCameraPosition +val customCameraPosition = currentHereCameraState.toMapCameraPosition() + +// Now you can use the customCameraPosition object for other purposes, +// like saving it to preferences or passing it to another module. +println("Current Latitude: ${customCameraPosition.position.latitude}") +println("Current Zoom: ${customCameraPosition.zoom}") +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/circle/HereCircleController.kt.md b/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/circle/HereCircleController.kt.md new file mode 100644 index 00000000..1a65641d --- /dev/null +++ b/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/circle/HereCircleController.kt.md @@ -0,0 +1,61 @@ +# HereCircleController + +### Signature + +```kotlin +class HereCircleController( + circleManager: CircleManager = CircleManager(), + renderer: HereCircleOverlayRenderer, +) : CircleController(circleManager, renderer) +``` + +### Description + +The `HereCircleController` is a specialized controller responsible for managing and rendering circle overlays on a HERE map. It serves as a concrete implementation of the abstract `CircleController`, bridging the generic circle management logic with the platform-specific rendering capabilities of the HERE Maps SDK. + +This controller orchestrates the `CircleManager`, which handles the data and state of circle objects, and the `HereCircleOverlayRenderer`, which performs the actual drawing on the map canvas. By inheriting from `CircleController`, it exposes a consistent API for adding, updating, and removing circles, while handling the underlying HERE-specific implementation details. + +### Parameters + +| Parameter | Type | Description | +|---|---|---| +| `circleManager` | `CircleManager` | The manager responsible for handling the collection and state of all circle data models. It defaults to a new `CircleManager()` instance if not provided. | +| `renderer` | `HereCircleOverlayRenderer` | The renderer that draws the `HereActualCircle` objects onto the HERE map view. This parameter is required. | + +### Example + +The following example demonstrates how to initialize the `HereCircleController` and use it to add a circle to a HERE map. + +```kotlin +import android.graphics.Color +import com.here.sdk.core.GeoCoordinates +import com.here.sdk.mapview.MapView +import com.mapconductor.core.circle.Circle + +// Assume 'mapView' is an initialized instance of a HERE MapView. +// val mapView: MapView = ... + +// 1. Create the platform-specific renderer for HERE maps. +val circleRenderer = HereCircleOverlayRenderer(mapView) + +// 2. Instantiate the controller with the renderer. +// The CircleManager is created with its default value. +val circleController = HereCircleController(renderer = circleRenderer) + +// 3. Define the properties of the circle you want to draw. +val circleCenter = GeoCoordinates(52.5200, 13.4050) // Berlin, Germany +val myCircle = Circle( + id = "berlin-circle-1", + center = circleCenter, + radiusInMeters = 1000.0, + fillColor = Color.argb(120, 255, 165, 0), // Semi-transparent orange + strokeWidthInPixels = 8f, + strokeColor = Color.rgb(200, 80, 0) // Dark orange +) + +// 4. Add the circle to the controller to render it on the map. +circleController.add(listOf(myCircle)) + +// To remove the circle later using its ID: +// circleController.remove(listOf("berlin-circle-1")) +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/circle/HereCircleOverlayRenderer.kt.md b/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/circle/HereCircleOverlayRenderer.kt.md new file mode 100644 index 00000000..07157736 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/circle/HereCircleOverlayRenderer.kt.md @@ -0,0 +1,160 @@ +Of course! Here is a high-quality SDK document for the provided code snippet, formatted in Markdown. + +# HereCircleOverlayRenderer + +The `HereCircleOverlayRenderer` is a concrete implementation of `AbstractCircleOverlayRenderer` for the HERE SDK. It is responsible for managing the lifecycle of circle overlays on a HERE map, including their creation, removal, and property updates. + +This renderer draws circles as `MapPolygon` objects. It supports both native geodesic circles and non-geodesic circles approximated by a series of points. All map operations are performed asynchronously using a provided `CoroutineScope`. + +```kotlin +class HereCircleOverlayRenderer( + override val holder: HereViewHolder, + override val coroutine: CoroutineScope = CoroutineScope(Dispatchers.Default), +) : AbstractCircleOverlayRenderer() +``` + +## Constructor + +### Signature + +```kotlin +HereCircleOverlayRenderer( + holder: HereViewHolder, + coroutine: CoroutineScope = CoroutineScope(Dispatchers.Default) +) +``` + +### Description + +Creates a new instance of the `HereCircleOverlayRenderer`. + +### Parameters + +| Parameter | Type | Description | +|-------------|-------------------|---------------------------------------------------------------------------------------------------------| +| `holder` | `HereViewHolder` | The view holder that contains the active HERE `MapView` instance where circles will be rendered. | +| `coroutine` | `CoroutineScope` | The coroutine scope used to execute asynchronous map operations. Defaults to `CoroutineScope(Dispatchers.Default)`. | + +--- + +## Methods + +### createCircle + +#### Signature + +```kotlin +override suspend fun createCircle(state: CircleState): HereActualCircle? +``` + +#### Description + +Asynchronously creates a new circle polygon on the map based on the provided state. It constructs a `MapPolygon` and adds it to the map scene. The method can create either a native geodesic circle or a planar circle approximated with a high number of vertices, depending on the `geodesic` flag in the `CircleState`. + +#### Parameters + +| Parameter | Type | Description | +|-----------|---------------|----------------------------------------------------------------------------------------------------------------------------------------| +| `state` | `CircleState` | An object containing all configuration for the circle, including its center, radius, colors, stroke width, z-index, and geodesic flag. | + +#### Returns + +A `HereActualCircle` (type alias for `MapPolygon`) representing the newly created circle on the map. + +--- + +### removeCircle + +#### Signature + +```kotlin +override suspend fun removeCircle(entity: CircleEntityInterface) +``` + +#### Description + +Asynchronously removes a specified circle from the map. + +#### Parameters + +| Parameter | Type | Description | +|-----------|------------------------------------------|--------------------------------------------------------------------------| +| `entity` | `CircleEntityInterface` | The circle entity wrapper that contains the native `MapPolygon` to remove. | + +--- + +### updateCircleProperties + +#### Signature + +```kotlin +override suspend fun updateCircleProperties( + circle: HereActualCircle, + current: CircleEntityInterface, + prev: CircleEntityInterface +): HereActualCircle? +``` + +#### Description + +Asynchronously updates the properties of an existing circle on the map. It performs an efficient update by comparing the `fingerPrint` of the `current` and `prev` states. Only the properties that have changed (e.g., geometry, color, stroke width, z-index) are applied to the native `MapPolygon` object. + +#### Parameters + +| Parameter | Type | Description | +|-----------|------------------------------------------|---------------------------------------------------------------------------------------------------------| +| `circle` | `HereActualCircle` | The native `MapPolygon` object that needs to be updated. | +| `current` | `CircleEntityInterface` | The entity representing the new, updated state of the circle. | +| `prev` | `CircleEntityInterface` | The entity representing the previous state of the circle, used for detecting which properties have changed. | + +#### Returns + +The updated `HereActualCircle` instance. + +--- + +## Example + +Here is a basic example of how to instantiate the renderer and use it to add a circle to the map. + +```kotlin +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp +import com.mapconductor.core.circle.CircleState +import com.mapconductor.core.types.MapPoint +import com.mapconductor.here.HereViewHolder +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch + +// Assume 'hereViewHolder' is an initialized instance of HereViewHolder +val hereViewHolder: HereViewHolder = /* ... */ + +// 1. Instantiate the renderer +val circleRenderer = HereCircleOverlayRenderer( + holder = hereViewHolder, + coroutine = CoroutineScope(Dispatchers.Main) // Use Main dispatcher for UI-related scopes +) + +// 2. Define the state for a new circle +val circleState = CircleState( + id = "my-unique-circle-id", + center = MapPoint(latitude = 37.7749, longitude = -122.4194), // San Francisco + radiusMeters = 1000.0, + fillColor = Color.Blue.copy(alpha = 0.3f), + strokeColor = Color.Blue, + strokeWidth = 2.dp, + geodesic = true, + zIndex = 10 +) + +// 3. Create and add the circle to the map +var nativeCircle: HereActualCircle? = null +CoroutineScope(Dispatchers.Main).launch { + nativeCircle = circleRenderer.createCircle(circleState) + println("Circle created on the map.") +} + +// To remove the circle later, you would need the CircleEntityInterface +// This is typically managed by a higher-level overlay controller. +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/groundimage/HereGroundImageController.kt.md b/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/groundimage/HereGroundImageController.kt.md new file mode 100644 index 00000000..b23c9f33 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/groundimage/HereGroundImageController.kt.md @@ -0,0 +1,69 @@ +# HereGroundImageController + +## Signature + +```kotlin +class HereGroundImageController( + groundImageManager: GroundImageManagerInterface = GroundImageManager(), + renderer: HereGroundImageOverlayRenderer, +) : GroundImageController(groundImageManager, renderer) +``` + +## Description + +The `HereGroundImageController` is a specialized controller for managing and rendering ground image overlays on a HERE map. It serves as a crucial link between the abstract ground image management logic in `GroundImageController` and the concrete rendering implementation required by the HERE SDK for Android. + +This class takes a `HereGroundImageOverlayRenderer` to handle the actual drawing of images on the map canvas. It orchestrates the entire lifecycle of ground images, including their creation, addition, removal, and updates, ensuring they are correctly displayed on the HERE map. + +## Parameters + +This section describes the parameters for the `HereGroundImageController` constructor. + +| Parameter | Type | Description | Default | +| -------------------- | ----------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | ---------------------- | +| `groundImageManager` | `GroundImageManagerInterface` | The manager responsible for handling the state, data, and lifecycle of all ground images. | `GroundImageManager()` | +| `renderer` | `HereGroundImageOverlayRenderer` | The platform-specific renderer that draws the ground image overlays onto the associated HERE `MapView`. | - | + +## Example + +The following example demonstrates how to initialize and use the `HereGroundImageController` to add a ground image to a HERE map. + +```kotlin +import com.here.sdk.mapview.MapView +import com.here.sdk.core.GeoCoordinates +import com.here.sdk.core.Anchor2D +import com.mapconductor.core.graphics.BitmapDescriptorFactory +import com.mapconductor.here.groundimage.HereActualGroundImage +import com.mapconductor.here.groundimage.HereGroundImageController +import com.mapconductor.here.groundimage.HereGroundImageOverlayRenderer + +// Assume 'mapView' is an initialized instance of com.here.sdk.mapview.MapView +// and 'imageBitmap' is a valid android.graphics.Bitmap object. + +// 1. Create a renderer instance associated with your HERE MapView. +val groundImageRenderer = HereGroundImageOverlayRenderer(mapView) + +// 2. Instantiate the controller, passing the renderer. +val groundImageController = HereGroundImageController(renderer = groundImageRenderer) + +// 3. Define the properties for the new ground image. +val imageDescriptor = BitmapDescriptorFactory.fromBitmap(imageBitmap) +val location = GeoCoordinates(52.530932, 13.384915) // Center coordinates for the image +val dimensions = Anchor2D(250.0, 125.0) // Width and height in meters + +// 4. Create a HereActualGroundImage object. +val groundImage = HereActualGroundImage( + id = "unique-image-id-1", + image = imageDescriptor, + coordinates = location, + dimensions = dimensions, + bearing = 45f // Rotation in degrees +) + +// 5. Add the ground image to the map using the controller. +// The controller will delegate the rendering task to the HereGroundImageOverlayRenderer. +groundImageController.add(groundImage) + +// To remove the image from the map later: +// groundImageController.remove("unique-image-id-1") +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/groundimage/HereGroundImageHandle.kt.md b/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/groundimage/HereGroundImageHandle.kt.md new file mode 100644 index 00000000..27e693ad --- /dev/null +++ b/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/groundimage/HereGroundImageHandle.kt.md @@ -0,0 +1,56 @@ +# HereGroundImageHandle + +A data class that acts as a handle for a ground image overlay on the map. It encapsulates all the necessary components and identifiers associated with a single ground image instance. + +This handle is typically returned when a ground image is added to the map and can be used to manage the lifecycle of the image, such as hiding, showing, or removing it. + +## Signature + +```kotlin +data class HereGroundImageHandle( + val routeId: String, + val generation: Long, + val cacheKey: String, + val sourceName: String, + val layerName: String, + val dataSource: RasterDataSource, + val layer: MapLayer, + val tileProvider: GroundImageTileProvider, +) +``` + +## Parameters + +This data class contains the following properties: + +| Parameter | Type | Description | +| --- | --- | --- | +| `routeId` | `String` | The unique identifier for the route associated with this ground image. | +| `generation` | `Long` | A version or generation number for the ground image, used to differentiate between updates. | +| `cacheKey` | `String` | A unique key used for caching the ground image tiles. | +| `sourceName` | `String` | The internal name assigned to the `RasterDataSource`. | +| `layerName` | `String` | The internal name assigned to the `MapLayer`. | +| `dataSource` | `RasterDataSource` | The HERE SDK `RasterDataSource` instance that provides the image data to the map view. | +| `layer` | `MapLayer` | The HERE SDK `MapLayer` instance that renders the ground image on the map. | +| `tileProvider` | `GroundImageTileProvider` | The custom tile provider responsible for supplying image tile data to the `dataSource`. | + +## Example + +While you do not instantiate this class directly, you would receive it from a function that adds a ground image to the map. You can then use the properties of the handle to interact with the map layer. + +```kotlin +// Assume 'groundImageManager.addGroundImage(...)' returns a HereGroundImageHandle +val groundImageHandle: HereGroundImageHandle? = groundImageManager.addGroundImage(someParameters) + +// You can then use the handle to manage the layer +groundImageHandle?.let { handle -> + // Hide the ground image layer from the map + handle.layer.isEnabled = false + + // Show the ground image layer again + handle.layer.isEnabled = true + + // To remove the layer, you would likely pass the handle to another function + // groundImageManager.removeGroundImage(handle) +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/groundimage/HereGroundImageOverlayRenderer.kt.md b/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/groundimage/HereGroundImageOverlayRenderer.kt.md new file mode 100644 index 00000000..d87ee608 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/groundimage/HereGroundImageOverlayRenderer.kt.md @@ -0,0 +1,191 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +--- + +# HereGroundImageOverlayRenderer + +## Class: `HereGroundImageOverlayRenderer` + +### Description + +The `HereGroundImageOverlayRenderer` is a concrete implementation of `AbstractGroundImageOverlayRenderer` designed specifically for the HERE SDK. It manages the rendering and lifecycle of ground image overlays on a HERE map. + +This class handles the creation, updating, and removal of ground images by interacting with a `LocalTileServer` to serve image tiles and with the HERE SDK's `MapLayer` and `RasterDataSource` components to display them. It is responsible for translating a platform-agnostic `GroundImageState` into a visible overlay on the map. + +### Constructor + +#### Signature + +```kotlin +class HereGroundImageOverlayRenderer( + override val holder: HereViewHolder, + private val tileServer: LocalTileServer, + override val coroutine: CoroutineScope = CoroutineScope(Dispatchers.Default), +) : AbstractGroundImageOverlayRenderer() +``` + +#### Description + +Initializes a new instance of the `HereGroundImageOverlayRenderer`. + +#### Parameters + +| Parameter | Type | Description | +| :---------- | :---------------- | :------------------------------------------------------------------------------------------------------ | +| `holder` | `HereViewHolder` | The view holder containing the HERE `MapView` and `MapContext` where the overlays will be rendered. | +| `tileServer`| `LocalTileServer` | The local server instance responsible for generating and serving image tiles for the ground overlays. | +| `coroutine` | `CoroutineScope` | The coroutine scope used for managing background tasks, such as creating and updating map layers. Defaults to `CoroutineScope(Dispatchers.Default)`. | + +--- + +## Methods + +### createGroundImage + +#### Signature + +```kotlin +override suspend fun createGroundImage(state: GroundImageState): HereActualGroundImage? +``` + +#### Description + +Creates and displays a new ground image overlay on the map based on the provided state. This method sets up a `GroundImageTileProvider`, registers it with the `LocalTileServer`, and then configures the necessary HERE SDK `RasterDataSource` and `MapLayer` to render the image. + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :--------------- | :------------------------------------------------------------------------------------------------------ | +| `state` | `GroundImageState` | An object defining the properties of the ground image, such as the image bitmap, geographic bounds, and opacity. | + +#### Returns + +A `HereActualGroundImage` handle to the newly created map objects on success, or `null` if the layer could not be created. + +--- + +### updateGroundImageProperties + +#### Signature + +```kotlin +override suspend fun updateGroundImageProperties( + groundImage: HereActualGroundImage, + current: GroundImageEntityInterface, + prev: GroundImageEntityInterface, +): HereActualGroundImage? +``` + +#### Description + +Updates an existing ground image overlay with new properties. The method efficiently determines if a visual refresh is needed by comparing the fingerprints of the current and previous states. If properties such as the image, bounds, opacity, or tile size have changed, it rebuilds the underlying map layer and data source to reflect the updates. + +#### Parameters + +| Parameter | Type | Description | +| :------------ | :-------------------------------------------- | :----------------------------------------------------------------------- | +| `groundImage` | `HereActualGroundImage` | The existing ground image handle to be updated. | +| `current` | `GroundImageEntityInterface` | The entity wrapper containing the new state for the ground image. | +| `prev` | `GroundImageEntityInterface` | The entity wrapper containing the previous state for comparison purposes. | + +#### Returns + +The updated `HereActualGroundImage` handle. This may be a new handle if the underlying map objects were recreated, or the original handle if no visual update was necessary. Returns `null` if the update fails. + +--- + +### removeGroundImage + +#### Signature + +```kotlin +override suspend fun removeGroundImage(entity: GroundImageEntityInterface) +``` + +#### Description + +Removes a ground image overlay from the map and cleans up all associated resources. This function destroys the HERE SDK `MapLayer` and `RasterDataSource` and unregisters the corresponding tile provider from the `LocalTileServer`. + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :-------------------------------------------- | :----------------------------------------------------------------------- | +| `entity` | `GroundImageEntityInterface` | The entity wrapper for the ground image to be removed from the map. | + +#### Returns + +This is a suspending function and does not return a value. + +--- + +### Example + +The following example demonstrates how to initialize the `HereGroundImageOverlayRenderer` and use it to add, update, and remove a ground image overlay. + +```kotlin +import com.here.sdk.core.GeoCoordinates +import com.here.sdk.core.GeoBox +import com.mapconductor.core.groundimage.GroundImageState +import com.mapconductor.core.groundimage.GroundImageEntity +import com.mapconductor.here.groundimage.HereGroundImageOverlayRenderer +import com.mapconductor.core.tileserver.LocalTileServer +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch + +// Assume 'hereViewHolder' is an initialized HereViewHolder instance +// and 'myBitmap' is a valid Bitmap object. + +// 1. Initialize the tile server and renderer +val tileServer = LocalTileServer() +val renderer = HereGroundImageOverlayRenderer(hereViewHolder, tileServer) + +// 2. Define the initial state for the ground image +val initialBounds = GeoBox( + GeoCoordinates(40.7128, -74.0060), // NYC South-West + GeoCoordinates(40.7528, -73.9860) // NYC North-East +) +val initialState = GroundImageState( + id = "nyc-overlay", + image = myBitmap, + bounds = initialBounds, + opacity = 0.8f, + tileSize = 512 +) + +// 3. Create and add the ground image to the map +val groundImageEntity = GroundImageEntity(initialState) + +CoroutineScope(Dispatchers.Main).launch { + val groundImageHandle = renderer.createGroundImage(initialState) + if (groundImageHandle != null) { + groundImageEntity.groundImage = groundImageHandle + println("Ground image created successfully.") + + // 4. (Optional) Update the ground image with new properties + val updatedBounds = GeoBox( + GeoCoordinates(40.7128, -74.0160), // Shift west + GeoCoordinates(40.7528, -73.9960) + ) + val updatedState = initialState.copy(bounds = updatedBounds, opacity = 1.0f) + val updatedEntity = GroundImageEntity(updatedState) + + val updatedHandle = renderer.updateGroundImageProperties( + groundImage = groundImageEntity.groundImage!!, + current = updatedEntity, + prev = groundImageEntity + ) + + if (updatedHandle != null) { + updatedEntity.groundImage = updatedHandle + println("Ground image updated successfully.") + + // 5. Remove the ground image from the map + renderer.removeGroundImage(updatedEntity) + println("Ground image removed.") + } + } else { + println("Failed to create ground image.") + } +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/marker/HereMarkerController.kt.md b/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/marker/HereMarkerController.kt.md new file mode 100644 index 00000000..d8741644 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/marker/HereMarkerController.kt.md @@ -0,0 +1,283 @@ +Excellent. Here is the high-quality SDK documentation for the provided `HereMarkerController` code snippet. + +# HereMarkerController + +The `HereMarkerController` is a class responsible for managing and rendering markers on a HERE map instance. It serves as a high-level abstraction over the map's native marker system, providing a unified API for adding, updating, and removing markers. + +A key feature of this controller is its ability to automatically switch between rendering individual markers and using a server-side tile-based rendering strategy. This optimization, known as marker tiling, significantly improves performance when displaying a large number of markers by rendering them as raster tile images instead of individual map objects. + +An instance of this controller should be created using the `HereMarkerController.create()` factory method. + +## Companion Object + +### create + +Creates a new instance of the `HereMarkerController`. This is the designated factory method for initializing the controller and its required dependencies, such as the marker renderer and manager. + +**Signature** +```kotlin +fun create( + holder: HereViewHolder, + markerTiling: MarkerTilingOptions = MarkerTilingOptions.Default, +): HereMarkerController +``` + +**Description** +This method sets up the `HereMarkerController` with the necessary components to interact with a HERE map. It initializes the `HereMarkerRenderer` for drawing markers and the `MarkerManager` for state management. + +**Parameters** + +| Parameter | Type | Description | +| :------------- | :-------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `holder` | `HereViewHolder` | The view holder that provides the connection to the underlying `MapView` instance. | +| `markerTiling` | `MarkerTilingOptions` | *(Optional)* Configuration for the marker tiling optimization. Tiling is enabled when the number of markers exceeds `minMarkerCount`. Defaults to `MarkerTilingOptions.Default`. | + +**Returns** + +A new, fully initialized `HereMarkerController` instance. + +**Example** +```kotlin +// Assuming 'hereViewHolder' is an initialized HereViewHolder instance +val markerController = HereMarkerController.create( + holder = hereViewHolder, + markerTiling = MarkerTilingOptions( + enabled = true, + minMarkerCount = 100 + ) +) +``` + +--- + +## Methods + +### setRasterLayerCallback + +Sets a callback to receive updates for the raster layer used for tiled markers. When marker tiling is active, the controller generates a `RasterLayerState` that must be added to the map to display the tiled markers. This callback provides that state. + +**Signature** +```kotlin +fun setRasterLayerCallback(callback: MarkerTileRasterLayerCallback?) +``` + +**Description** +Register a callback function that will be invoked whenever the raster layer for tiled markers is created, updated, or removed. Your application should use this callback to manage the `RasterLayer` on the map view. Pass `null` to remove a previously set callback. + +**Parameters** + +| Parameter | Type | Description | +| :--------- | :---------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `callback` | `MarkerTileRasterLayerCallback?` | The callback to be invoked with the `RasterLayerState`. It receives the new state, or `null` if the tile layer is removed (e.g., when all markers are cleared). | + +**Returns** + +This method does not return a value. + +**Example** +```kotlin +markerController.setRasterLayerCallback { rasterLayerState -> + // In a real app, you would have a RasterLayerController + // to add/update/remove layers on the map. + if (rasterLayerState != null) { + println("Raster layer updated: ${rasterLayerState.id}") + // rasterLayerController.addOrUpdateLayer(rasterLayerState) + } else { + println("Raster layer removed.") + // rasterLayerController.removeLayer("marker-tile-layer-id") + } +} +``` + +--- + +### find + +Finds the nearest marker to a given geographic position within a specified tap tolerance. This is primarily used to detect user interactions like taps on a marker. + +**Signature** +```kotlin +override fun find(position: GeoPointInterface): MarkerEntityInterface? +``` + +**Description** +This method calculates if the given `position` (e.g., from a map tap event) falls within the tappable area of a nearby marker. The tappable area is determined by the marker's icon size, anchor point, and a system-defined tolerance, ensuring a user-friendly touch target. + +**Parameters** + +| Parameter | Type | Description | +| :--------- | :------------------ | :----------------------------------------------------------------------- | +| `position` | `GeoPointInterface` | The geographic coordinate to search for a marker, typically from a user tap. | + +**Returns** + +A `MarkerEntityInterface` representing the found marker, or `null` if no marker is found at the specified position. + +**Example** +```kotlin +mapView.gestures.tapListener = TapListener { touchPoint -> + val geoCoordinates = mapView.viewToGeoCoordinates(touchPoint) + val tappedMarker = geoCoordinates?.let { markerController.find(it) } + + if (tappedMarker != null) { + println("Marker tapped: ${tappedMarker.state.id}") + } +} +``` + +--- + +### add + +Adds a list of markers to the map. The controller will automatically decide whether to render them as individual objects or as part of a tiled layer for performance. + +**Signature** +```kotlin +override suspend fun add(data: List) +``` + +**Description** +This asynchronous method ingests a list of `MarkerState` objects. If tiling is enabled and the total marker count meets the threshold, markers that are not draggable and have no animations will be added to the tiled layer. Otherwise, they will be rendered as individual map markers. + +**Parameters** + +| Parameter | Type | Description | +| :-------- | :------------------ | :----------------------------------------------------------------------- | +| `data` | `List` | A list of `MarkerState` objects, each defining a marker to be added. | + +**Returns** + +This is a `suspend` function and does not return a value. + +**Example** +```kotlin +// This should be called from a coroutine scope +lifecycleScope.launch { + val newMarkers = listOf( + MarkerState(id = "marker-1", position = GeoPoint(40.7128, -74.0060)), + MarkerState(id = "marker-2", position = GeoPoint(34.0522, -118.2437)) + ) + markerController.add(newMarkers) +} +``` + +--- + +### update + +Updates the state of a single existing marker. + +**Signature** +```kotlin +override suspend fun update(state: MarkerState) +``` + +**Description** +This asynchronous method updates an existing marker identified by the `id` in the provided `MarkerState`. It efficiently handles changes to properties like position, icon, and visibility. It also manages the transition of a marker between being individually rendered and being part of a tiled layer. If no marker with the given ID exists, the operation is ignored. + +**Parameters** + +| Parameter | Type | Description | +| :-------- | :------------ | :------------------------------------------------------------------------------------------------------ | +| `state` | `MarkerState` | The new state for the marker. The `id` field is used to identify which existing marker to update. | + +**Returns** + +This is a `suspend` function and does not return a value. + +**Example** +```kotlin +// This should be called from a coroutine scope +lifecycleScope.launch { + // Update the position of an existing marker with id "marker-1" + val updatedState = MarkerState( + id = "marker-1", + position = GeoPoint(40.7130, -74.0062) // New position + ) + markerController.update(updatedState) +} +``` + +--- + +### clear + +Removes all markers from the map. + +**Signature** +```kotlin +override suspend fun clear() +``` + +**Description** +This asynchronous method removes all individually rendered markers and clears any active marker tile layers. It effectively resets the controller to an empty state. + +**Parameters** + +This method takes no parameters. + +**Returns** + +This is a `suspend` function and does not return a value. + +**Example** +```kotlin +// This should be called from a coroutine scope +lifecycleScope.launch { + markerController.clear() +} +``` + +--- + +### onCameraChanged + +A lifecycle method called by the map framework when the camera position changes. + +**Signature** +```kotlin +override suspend fun onCameraChanged(mapCameraPosition: MapCameraPosition) +``` + +**Description** +This method is intended for internal use by the map integration layer. It listens to camera updates to track the current zoom level, which is crucial for determining which marker tiles to display. Developers typically do not need to call this method directly. + +**Parameters** + +| Parameter | Type | Description | +| :------------------ | :------------------ | :---------------------------------------- | +| `mapCameraPosition` | `MapCameraPosition` | The new state of the map camera. | + +**Returns** + +This is a `suspend` function and does not return a value. + +--- + +### destroy + +Cleans up all resources used by the controller. + +**Signature** +```kotlin +override fun destroy() +``` + +**Description** +This method must be called to release resources when the controller is no longer needed (e.g., in `onDestroy` of an Activity or Fragment). It unregisters tile providers, clears internal references, and ensures no memory leaks occur. + +**Parameters** + +This method takes no parameters. + +**Returns** + +This method does not return a value. + +**Example** +```kotlin +override fun onDestroy() { + super.onDestroy() + markerController.destroy() +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/marker/HereMarkerEventController.kt.md b/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/marker/HereMarkerEventController.kt.md new file mode 100644 index 00000000..6a1e02e9 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/marker/HereMarkerEventController.kt.md @@ -0,0 +1,264 @@ +# SDK Documentation: HereMarkerEventControllerInterface + +The `HereMarkerEventControllerInterface` provides a standardized way to manage and interact with map markers, specifically for the HERE Maps SDK integration. It handles marker selection, event dispatching (clicks, drags), and listener registration. + +This interface is implemented by `DefaultHereMarkerEventController` and `StrategyHereMarkerEventController`, which provide the concrete logic for different marker management strategies. + +## find + +### Signature +```kotlin +fun find(position: GeoPoint): MarkerEntityInterface? +``` + +### Description +Searches for a marker entity at a specific geographical coordinate. + +### Parameters +| Name | Type | Description | +| :--- | :--- | :---------- | +| `position` | `GeoPoint` | The geographical coordinate to search for a marker. | + +### Returns +`MarkerEntityInterface?` - The found marker entity, or `null` if no marker exists at the specified position. + +--- + +## getSelectedMarker + +### Signature +```kotlin +fun getSelectedMarker(): MarkerEntityInterface? +``` + +### Description +Retrieves the currently selected marker entity. + +### Returns +`MarkerEntityInterface?` - The currently selected marker entity, or `null` if no marker is selected. + +--- + +## setSelectedMarker + +### Signature +```kotlin +fun setSelectedMarker(entity: MarkerEntityInterface?) +``` + +### Description +Sets the specified marker entity as the currently selected one. To clear the current selection, pass `null`. + +### Parameters +| Name | Type | Description | +| :--- | :--- | :---------- | +| `entity` | `MarkerEntityInterface?` | The marker entity to select, or `null` to deselect the current marker. | + +--- + +## dispatchClick + +### Signature +```kotlin +fun dispatchClick(state: MarkerState) +``` + +### Description +Dispatches a click event to the registered click listener. This is typically called by the underlying map implementation when a user taps a marker. + +### Parameters +| Name | Type | Description | +| :--- | :--- | :---------- | +| `state` | `MarkerState` | The state of the marker at the time of the click event. | + +--- + +## dispatchDragStart + +### Signature +```kotlin +fun dispatchDragStart(state: MarkerState) +``` + +### Description +Dispatches a drag start event to the registered drag start listener. + +### Parameters +| Name | Type | Description | +| :--- | :--- | :---------- | +| `state` | `MarkerState` | The state of the marker when the drag operation begins. | + +--- + +## dispatchDrag + +### Signature +```kotlin +fun dispatchDrag(state: MarkerState) +``` + +### Description +Dispatches a drag event to the registered drag listener as the marker is being dragged. + +### Parameters +| Name | Type | Description | +| :--- | :--- | :---------- | +| `state` | `MarkerState` | The current state of the marker during the drag operation. | + +--- + +## dispatchDragEnd + +### Signature +```kotlin +fun dispatchDragEnd(state: MarkerState) +``` + +### Description +Dispatches a drag end event to the registered drag end listener when the user finishes dragging the marker. + +### Parameters +| Name | Type | Description | +| :--- | :--- | :---------- | +| `state` | `MarkerState` | The final state of the marker after the drag operation has completed. | + +--- + +## setClickListener + +### Signature +```kotlin +fun setClickListener(listener: OnMarkerEventHandler?) +``` + +### Description +Registers a listener to be invoked when a marker is clicked. The listener receives the state of the marker that was clicked. + +### Parameters +| Name | Type | Description | +| :--- | :--- | :---------- | +| `listener` | `OnMarkerEventHandler?` | The callback to invoke on a marker click. Pass `null` to remove the existing listener. | + +--- + +## setDragStartListener + +### Signature +```kotlin +fun setDragStartListener(listener: OnMarkerEventHandler?) +``` + +### Description +Registers a listener to be invoked when a marker drag operation begins. + +### Parameters +| Name | Type | Description | +| :--- | :--- | :---------- | +| `listener` | `OnMarkerEventHandler?` | The callback to invoke when a marker drag starts. Pass `null` to remove the existing listener. | + +--- + +## setDragListener + +### Signature +```kotlin +fun setDragListener(listener: OnMarkerEventHandler?) +``` + +### Description +Registers a listener to be invoked repeatedly as a marker is being dragged. + +### Parameters +| Name | Type | Description | +| :--- | :--- | :---------- | +| `listener` | `OnMarkerEventHandler?` | The callback to invoke during a marker drag. Pass `null` to remove the existing listener. | + +--- + +## setDragEndListener + +### Signature +```kotlin +fun setDragEndListener(listener: OnMarkerEventHandler?) +``` + +### Description +Registers a listener to be invoked when a marker drag operation ends. + +### Parameters +| Name | Type | Description | +| :--- | :--- | :---------- | +| `listener` | `OnMarkerEventHandler?` | The callback to invoke when a marker drag ends. Pass `null` to remove the existing listener. | + +--- + +## setAnimateStartListener + +### Signature +```kotlin +fun setAnimateStartListener(listener: OnMarkerEventHandler?) +``` + +### Description +Registers a listener to be invoked when a marker animation starts. + +### Parameters +| Name | Type | Description | +| :--- | :--- | :---------- | +| `listener` | `OnMarkerEventHandler?` | The callback to invoke when an animation starts. Pass `null` to remove the existing listener. | + +--- + +## setAnimateEndListener + +### Signature +```kotlin +fun setAnimateEndListener(listener: OnMarkerEventHandler?) +``` + +### Description +Registers a listener to be invoked when a marker animation ends. + +### Parameters +| Name | Type | Description | +| :--- | :--- | :---------- | +| `listener` | `OnMarkerEventHandler?` | The callback to invoke when an animation ends. Pass `null` to remove the existing listener. | + +--- + +## Example + +Here's an example of how to use the `HereMarkerEventControllerInterface` to manage marker interactions. + +```kotlin +// Assume 'markerEventController' is an instance of a class +// that implements HereMarkerEventControllerInterface. + +// 1. Set a click listener to handle marker taps +markerEventController.setClickListener { markerState -> + println("Marker clicked! ID: ${markerState.id}, Position: ${markerState.position}") + // You can now show an info window or perform another action. +} + +// 2. Set a drag end listener to get the new position of a marker +markerEventController.setDragEndListener { markerState -> + println("Marker drag finished. New position: ${markerState.position}") + // Update your application's state with the marker's new coordinates. +} + +// 3. Programmatically select a marker +// First, find a marker by its geographical position +val position = GeoPoint(48.8584, 2.2945) // e.g., Eiffel Tower +val markerToSelect = markerEventController.find(position) + +// Then, set it as the selected marker +markerEventController.setSelectedMarker(markerToSelect) + +// 4. Retrieve the currently selected marker +val currentSelection = markerEventController.getSelectedMarker() +if (currentSelection != null) { + println("Currently selected marker ID: ${currentSelection.id}") +} else { + println("No marker is currently selected.") +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/marker/HereMarkerRenderer.kt.md b/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/marker/HereMarkerRenderer.kt.md new file mode 100644 index 00000000..6b50bc97 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/marker/HereMarkerRenderer.kt.md @@ -0,0 +1,185 @@ +# SDK Documentation: HereMarkerRenderer + +The `HereMarkerRenderer` class is a concrete implementation of `AbstractMarkerOverlayRenderer` designed to render and manage map markers for the HERE SDK. It acts as a bridge between the generic `MapConductor` marker management system and the specific `MapMarker` objects of the HERE SDK, handling their creation, updates, and removal on the map. + +This renderer is optimized for performance by updating existing marker properties (`position`, `icon`, `drawOrder`) directly, rather than recreating them when their state changes. + +## Class Signature + +```kotlin +class HereMarkerRenderer( + holder: HereViewHolder, + coroutine: CoroutineScope = CoroutineScope(Dispatchers.Main), +) : AbstractMarkerOverlayRenderer +``` + +## Constructor + +### `HereMarkerRenderer(holder, coroutine)` + +Creates a new instance of the marker renderer. + +#### Parameters + +| Parameter | Type | Description | +|-------------|-------------------|---------------------------------------------------------------------------------------------------------| +| `holder` | `HereViewHolder` | The view holder that contains the HERE `MapView` instance where markers will be rendered. | +| `coroutine` | `CoroutineScope` | *(Optional)* The coroutine scope used for launching asynchronous map operations. Defaults to `Main`. | + +--- + +## Methods + +### `setMarkerPosition` + +Asynchronously updates the geographic position of a single map marker. + +#### Signature + +```kotlin +override fun setMarkerPosition( + markerEntity: MarkerEntityInterface, + position: GeoPoint, +) +``` + +#### Parameters + +| Parameter | Type | Description | +|------------------|----------------------------------------------|------------------------------------------------------| +| `markerEntity` | `MarkerEntityInterface` | The marker entity whose position needs to be updated. | +| `position` | `GeoPoint` | The new geographical point for the marker. | + +--- + +### `onAdd` + +Creates and adds new map markers to the map scene based on a list of parameters. Each marker is configured with its position, icon, anchor point, and draw order. + +#### Signature + +```kotlin +override suspend fun onAdd( + data: List +): List +``` + +#### Metadata Handling + +When a marker is created, its `metadata` property is populated as follows: +- A unique ID from `MarkerState.id` is always stored with the key `"mc:id"`. +- If `MarkerState.extra` is provided, its contents are added to the metadata: + - If `extra` is a `Map<*, *>`, its key-value pairs are added. Values are converted to the best-matching HERE `Metadata` type (String, Integer, Double). + - For any other `Serializable` type, its `toString()` representation is stored under the key `"mc:extra"`. + +#### Parameters + +| Parameter | Type | Description | +|-----------|---------------------------------------------------------|--------------------------------------------------------------------------| +| `data` | `List` | A list of parameter objects, each containing the state and icon for a new marker. | + +#### Returns + +| Type | Description | +|----------------------------|----------------------------------------------------------------------------------------------------------------------------------------| +| `List` | A list containing the newly created `MapMarker` instances corresponding to the input data. The list maintains the same order as the input. | + +--- + +### `onRemove` + +Asynchronously removes a list of specified map markers from the map scene. + +#### Signature + +```kotlin +override suspend fun onRemove(data: List>) +``` + +#### Parameters + +| Parameter | Type | Description | +|-----------|--------------------------------------------|--------------------------------------------------------------------------| +| `data` | `List>` | A list of marker entities to be removed from the map. | + +--- + +### `onPostProcess` + +A lifecycle method called after all other processing. This method is not implemented in `HereMarkerRenderer` and performs no action. + +#### Signature + +```kotlin +override suspend fun onPostProcess() +``` + +--- + +### `onChange` + +Efficiently updates the properties of existing markers based on state changes. This method checks for differences in the marker's icon, position, and draw order and applies updates directly to the existing `MapMarker` instance to avoid the performance cost of removing and re-adding it. + +#### Signature + +```kotlin +override suspend fun onChange( + data: List> +): List +``` + +#### Parameters + +| Parameter | Type | Description | +|-----------|-----------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------| +| `data` | `List>` | A list of objects, each containing the previous and current state of a marker that has been changed. | + +#### Returns + +| Type | Description | +|----------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `List` | A list containing the updated `MapMarker` instances. If a marker's visibility is set to `false`, `null` is returned in its place. The returned marker instance is the same one that was updated. | + +--- + +## Example + +The following example demonstrates how to initialize and use the `HereMarkerRenderer`. This typically happens within a class that manages a map view. + +```kotlin +import android.content.Context +import com.here.sdk.mapview.MapView +import com.mapconductor.here.HereViewHolder +import com.mapconductor.here.marker.HereMarkerRenderer +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers + +class MapManager(context: Context) { + + // 1. Initialize HERE MapView + private val mapView = MapView(context) + + // 2. Create a HereViewHolder to wrap the MapView + private val hereViewHolder = HereViewHolder(mapView) + + // 3. Create a CoroutineScope for async operations + private val coroutineScope = CoroutineScope(Dispatchers.Main) + + // 4. Instantiate the HereMarkerRenderer + val markerRenderer = HereMarkerRenderer( + holder = hereViewHolder, + coroutine = coroutineScope + ) + + fun setupMap() { + // The markerRenderer is now ready to be used by a higher-level + // component, such as a MarkerOverlay, to add, remove, and + // update markers on the map. + + // For example, a MarkerOverlay would internally call: + // markerRenderer.onAdd(...) + // markerRenderer.onChange(...) + // markerRenderer.onRemove(...) + } +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/polygon/HerePolygonController.kt.md b/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/polygon/HerePolygonController.kt.md new file mode 100644 index 00000000..ce3ba2ad --- /dev/null +++ b/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/polygon/HerePolygonController.kt.md @@ -0,0 +1,54 @@ +# HerePolygonController + +## Signature + +```kotlin +class HerePolygonController( + polygonManager: PolygonManagerInterface = PolygonManager(), + renderer: HerePolygonOverlayRenderer, +) : PolygonController(polygonManager, renderer) +``` + +## Description + +The `HerePolygonController` is the primary class for managing and displaying polygon overlays on a HERE map. It serves as a specialized implementation of the generic `PolygonController`, tailored specifically for the HERE Maps SDK environment. + +This controller orchestrates the entire lifecycle of polygons, from data management to visual rendering. It connects the core `PolygonManager`, which handles the state of `HereActualPolygon` objects, with the `HerePolygonOverlayRenderer`, which is responsible for drawing the polygons onto the map canvas. + +Use this controller to add, remove, update, and interact with polygons on your map. + +## Parameters + +This section describes the parameters for the `HerePolygonController` constructor. + +| Parameter | Type | Description | Default | +| :--- | :--- | :--- | :--- | +| `polygonManager` | `PolygonManagerInterface` | The manager responsible for handling the state and lifecycle of polygon data. | `PolygonManager()` | +| `renderer` | `HerePolygonOverlayRenderer` | The HERE-specific renderer that draws the polygons on the map view. This parameter is required. | *none* | + +## Example + +The following example demonstrates how to initialize the `HerePolygonController`. You typically create it once per map instance. + +```kotlin +// Assuming 'mapView' is an instance of a HERE MapView +// 1. Initialize the HERE-specific renderer for polygons. +val polygonRenderer = HerePolygonOverlayRenderer(mapView) + +// 2. (Optional) Initialize a custom polygon manager if needed. +// If not provided, a default PolygonManager will be used. +val polygonManager = PolygonManager() + +// 3. Create an instance of the HerePolygonController. +val herePolygonController = HerePolygonController( + polygonManager = polygonManager, + renderer = polygonRenderer +) + +// The controller is now ready to manage polygons. +// For example, to add a polygon (assuming an 'addPolygon' method exists +// on the parent PolygonController): +// +// val myPolygon = createHerePolygon() // Your polygon creation logic +// herePolygonController.addPolygon(myPolygon) +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/polygon/HerePolygonOverlayRenderer.kt.md b/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/polygon/HerePolygonOverlayRenderer.kt.md new file mode 100644 index 00000000..8bbd1592 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/polygon/HerePolygonOverlayRenderer.kt.md @@ -0,0 +1,218 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +--- + +# Class: `HerePolygonOverlayRenderer` + +## Signature + +```kotlin +class HerePolygonOverlayRenderer( + override val holder: HereViewHolder, + private val rasterLayerController: HereRasterLayerController, + private val tileServer: LocalTileServer = TileServerRegistry.get(forceNoStoreCache = true), + override val coroutine: CoroutineScope = CoroutineScope(Dispatchers.Default), +) : AbstractPolygonOverlayRenderer() +``` + +## Description + +The `HerePolygonOverlayRenderer` is responsible for rendering polygon overlays on a HERE map. It implements the `AbstractPolygonOverlayRenderer` and provides a specialized rendering strategy to work around limitations in the HERE SDK, particularly with rendering polygons that contain holes (inner boundaries). + +The renderer uses a hybrid approach: + +* **Simple Polygons (No Holes):** These are rendered as standard, single `MapPolygon` objects. This is efficient and straightforward. +* **Complex Polygons (With Holes):** To ensure reliable rendering, especially for large or world-spanning polygons, the fill and stroke are handled separately: + * The **fill** is rendered using a dynamically generated raster tile layer. The renderer sets up a local tile server (`LocalTileServer`) that creates image tiles for the polygon's filled area on the fly. + * The **strokes** (outlines) for the outer boundary and each hole are rendered as separate `MapPolygon` objects with transparent fills. + +This approach guarantees that complex polygons with inner boundaries are displayed correctly across all zoom levels. + +The type `HereActualPolygon` is an alias for `List`, reflecting that a single logical polygon from the user's perspective may be composed of multiple underlying map objects. + +## Constructor + +### Signature + +```kotlin +HerePolygonOverlayRenderer( + holder: HereViewHolder, + rasterLayerController: HereRasterLayerController, + tileServer: LocalTileServer = TileServerRegistry.get(forceNoStoreCache = true), + coroutine: CoroutineScope = CoroutineScope(Dispatchers.Default) +) +``` + +### Description + +Creates a new instance of the `HerePolygonOverlayRenderer`. + +### Parameters + +| Parameter | Type | Description | +| --------------------- | --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | +| `holder` | `HereViewHolder` | Provides access to the core HERE map components, including the `MapView` instance where polygons will be drawn. | +| `rasterLayerController` | `HereRasterLayerController` | A controller used to manage the raster tile layers required for rendering the fill of polygons with holes. | +| `tileServer` | `LocalTileServer` | Optional. A local server that generates and serves raster tiles for polygon fills. Defaults to a shared, non-caching instance. | +| `coroutine` | `CoroutineScope` | Optional. The coroutine scope used for managing asynchronous operations like creating or updating polygons. Defaults to `Dispatchers.Default`. | + +--- + +## Methods + +### `createPolygon` + +#### Signature + +```kotlin +override suspend fun createPolygon(state: PolygonState): HereActualPolygon? +``` + +#### Description + +Creates and displays a new polygon on the map based on the provided `PolygonState`. It automatically selects the appropriate rendering strategy. If the `state` contains holes, it will set up a raster layer for the fill and individual `MapPolygon` objects for the strokes. Otherwise, it will create a single `MapPolygon`. + +#### Parameters + +| Parameter | Type | Description | +| --------- | -------------- | ------------------------------------------------------------------------------------------------------- | +| `state` | `PolygonState` | An object containing all the properties of the polygon to be created, such as points, holes, and styles. | + +#### Returns + +| Type | Description - | +| `HereActualPolygon?` (`List?`) | A list containing the native HERE `MapPolygon` objects that were created and added to the map, or `null` if creation failed. For a simple polygon, this list contains one object. For a complex polygon with holes, it contains multiple stroke-only polygon objects. | + +--- + +### `removePolygon` + +#### Signature + +```kotlin +override suspend fun removePolygon(entity: PolygonEntityInterface) +``` + +#### Description + +Removes a polygon from the map. This action is comprehensive: it removes all constituent `MapPolygon` objects from the map view and, if a raster mask layer was used for the fill, it unregisters and removes that layer as well. + +#### Parameters + +| Parameter | Type | Description - | +| `entity` | `PolygonEntityInterface` | The polygon entity to be removed. This interface provides access to both the polygon's state and its native map objects. | + +--- + +### `updatePolygonProperties` + +#### Signature + +```kotlin +override suspend fun updatePolygonProperties( + polygon: HereActualPolygon, + current: PolygonEntityInterface, + prev: PolygonEntityInterface, +): HereActualPolygon? +``` + +#### Description + +Updates the properties of an existing polygon on the map. The method intelligently determines the most efficient way to apply changes: + +* **Geometry Change:** If the polygon's geometry (i.e., `points`, `holes`, or `geodesic` property) has changed, the existing polygon is completely removed and a new one is created based on the `current` state. +* **Style Change:** If only visual properties (e.g., `fillColor`, `strokeColor`, `strokeWidth`, `zIndex`) have changed, the existing native `MapPolygon` objects are updated directly without being recreated. + +#### Parameters + +| Parameter | Type | Description - | +| `polygon` | `HereActualPolygon` | The list of native `MapPolygon` objects currently on the map that represent the polygon to be updated. - | +| `current` | `PolygonEntityInterface` | An entity representing the new, updated state of the polygon. - | +| `prev` | `PolygonEntityInterface` | An entity representing the previous state of the polygon, used for comparison to determine what changed. - | + +#### Returns + +| Type | Description - | +| `HereActualPolygon?` (`List?`) | The updated or newly created list of native `MapPolygon` objects, or `null` if the update resulted in a recreation failure. | + +--- + +## Example + +The following example demonstrates how to initialize the renderer and use it to create, update, and remove a complex polygon with a hole. + +```kotlin +import com.mapconductor.here.polygon.HerePolygonOverlayRenderer +import com.mapconductor.core.polygon.PolygonState +import com.mapconductor.core.features.GeoPoint +import com.mapconductor.core.polygon.PolygonEntity +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch + +// Assume these dependencies are already initialized in your application +val holder: HereViewHolder = getMyHereViewHolder() +val rasterLayerController: HereRasterLayerController = getMyRasterLayerController() +val coroutineScope = CoroutineScope(Dispatchers.Main) + +// 1. Initialize the renderer +val polygonRenderer = HerePolygonOverlayRenderer( + holder = holder, + rasterLayerController = rasterLayerController, + coroutine = coroutineScope +) + +// 2. Define the state for a polygon with a hole (e.g., a building with a courtyard) +val initialState = PolygonState( + id = "building-with-courtyard", + points = listOf( // Outer boundary + GeoPoint(40.748817, -73.985428), + GeoPoint(40.748817, -73.984428), + GeoPoint(40.747817, -73.984428), + GeoPoint(40.747817, -73.985428) + ), + holes = listOf( + listOf( // Inner boundary (hole) + GeoPoint(40.748517, -73.985128), + GeoPoint(40.748517, -73.984728), + GeoPoint(40.748117, -73.984728), + GeoPoint(40.748117, -73.985128) + ) + ), + fillColor = androidx.compose.ui.graphics.Color.Blue.copy(alpha = 0.4f), + strokeColor = androidx.compose.ui.graphics.Color.DarkGray, + strokeWidth = Dp(2f), + zIndex = 150 +) + +coroutineScope.launch { + // 3. Create the polygon on the map + // This returns a list of native MapPolygon objects used for the strokes. + val nativePolygons = polygonRenderer.createPolygon(initialState) + println("Polygon created with ${nativePolygons?.size ?: 0} stroke objects.") + + if (nativePolygons != null) { + // For update/remove, we need an object that implements PolygonEntityInterface + // This typically wraps the state and the native objects. + var currentEntity = PolygonEntity(initialState, nativePolygons) + + // 4. Update a visual property (e.g., stroke color) + val updatedState = initialState.copy(strokeColor = androidx.compose.ui.graphics.Color.Red) + val updatedEntity = PolygonEntity(updatedState, currentEntity.polygon) + + val newNativeObjects = polygonRenderer.updatePolygonProperties( + polygon = currentEntity.polygon, + current = updatedEntity, + prev = currentEntity + ) + println("Polygon properties updated.") + + // Update our reference to the current entity + currentEntity = PolygonEntity(updatedState, newNativeObjects!!) + + // 5. Remove the polygon from the map + polygonRenderer.removePolygon(currentEntity) + println("Polygon removed successfully.") + } +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/polyline/HerePolylineController.kt.md b/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/polyline/HerePolylineController.kt.md new file mode 100644 index 00000000..efeefde6 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/polyline/HerePolylineController.kt.md @@ -0,0 +1,62 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +# HerePolylineController + +The `HerePolylineController` is a specialized controller responsible for managing and rendering polyline overlays on a HERE map. It extends the generic `PolylineController`, tailoring its functionality specifically for the HERE Maps SDK environment. + +This class orchestrates the management of polyline data through a `PolylineManager` and handles the visual representation on the map using a `HerePolylineOverlayRenderer`. Developers should use this controller as the primary interface for all polyline-related operations, such as adding, removing, and updating polylines on the map. + +## Signature + +```kotlin +class HerePolylineController( + polylineManager: PolylineManagerInterface = PolylineManager(), + renderer: HerePolylineOverlayRenderer, +) : PolylineController(polylineManager, renderer) +``` + +## Parameters + +This section describes the parameters for the `HerePolylineController` constructor. + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `polylineManager` | `PolylineManagerInterface` | (Optional) The manager responsible for the lifecycle and state of polyline objects. If not provided, a default `PolylineManager()` instance is created. | +| `renderer` | `HerePolylineOverlayRenderer` | (Required) The renderer responsible for drawing the polylines onto the HERE map view. This object handles the platform-specific rendering logic. | + +## Example + +The following example demonstrates how to initialize and use the `HerePolylineController` to add a polyline to a HERE map. + +```kotlin +import com.mapconductor.core.options.PolylineOptions +import com.mapconductor.core.model.GeoCoordinate +import android.graphics.Color + +// Assume 'mapView' is an initialized instance of a HERE MapView object. + +// 1. Initialize the platform-specific renderer with the map view instance. +// This renderer will handle the actual drawing of polylines on the map. +val polylineRenderer = HerePolylineOverlayRenderer(mapView) + +// 2. Instantiate the controller with the renderer. +// We can use the default PolylineManager by omitting it from the constructor. +val polylineController = HerePolylineController(renderer = polylineRenderer) + +// 3. Define a new polyline using generic PolylineOptions. +// The controller will translate these options into a native HERE map polyline. +val polylineOptions = PolylineOptions( + id = "route-66", + points = listOf( + GeoCoordinate(34.0522, -118.2437), // Los Angeles + GeoCoordinate(39.7392, -104.9903), // Denver + GeoCoordinate(41.8781, -87.6298) // Chicago + ), + color = Color.BLUE, + width = 10f +) + +// 4. Use the controller to add the polyline to the map. +// The controller delegates this task to its manager and renderer. +polylineController.add(polylineOptions) +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/polyline/HerePolylineOverlayRenderer.kt.md b/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/polyline/HerePolylineOverlayRenderer.kt.md new file mode 100644 index 00000000..762d6498 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/polyline/HerePolylineOverlayRenderer.kt.md @@ -0,0 +1,191 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +# Class: `HerePolylineOverlayRenderer` + +Manages the rendering and lifecycle of polyline overlays on a HERE map. This class acts as a concrete implementation of `AbstractPolylineOverlayRenderer` for the HERE SDK, bridging the gap between a generic `PolylineState` and the platform-specific `MapPolyline`. + +It handles the creation, property updates, and removal of polylines on the map. All map-related operations are performed asynchronously using a provided `CoroutineScope`. + +```kotlin +class HerePolylineOverlayRenderer( + override val holder: HereViewHolder, + override val coroutine: CoroutineScope = CoroutineScope(Dispatchers.Default), +) : AbstractPolylineOverlayRenderer() +``` + +## Constructor + +### Signature + +```kotlin +HerePolylineOverlayRenderer( + holder: HereViewHolder, + coroutine: CoroutineScope = CoroutineScope(Dispatchers.Default) +) +``` + +### Description + +Creates an instance of the `HerePolylineOverlayRenderer`. + +### Parameters + +| Parameter | Type | Description | +|-------------|-------------------|---------------------------------------------------------------------------------------------------------| +| `holder` | `HereViewHolder` | The view holder that contains the HERE `MapView` instance where polylines will be rendered. | +| `coroutine` | `CoroutineScope` | The scope used to launch asynchronous map operations. Defaults to a scope on `Dispatchers.Default`. | + +--- + +## Methods + +### `createPolyline` + +#### Signature + +```kotlin +suspend fun createPolyline(state: PolylineState): HereActualPolyline? +``` + +#### Description + +Creates a new `MapPolyline` from a given `PolylineState` and adds it to the map. This method constructs the polyline's geometry, handling both geodesic and linear interpolation, and sets its visual representation (e.g., color, width, z-index). The created polyline is then added to the map asynchronously. + +#### Parameters + +| Parameter | Type | Description | +|-----------|-----------------|----------------------------------------------------------------------------------------------------------------------------------------| +| `state` | `PolylineState` | An object containing all the configuration for the polyline, including its geographical points, color, width, z-index, and geodesic flag. | + +#### Returns + +| Type | Description | +|----------------------|----------------------------------------------------------------------------------------------------------------------------------------| +| `HereActualPolyline?` | The newly created `MapPolyline` instance that was added to the map. `HereActualPolyline` is a type alias for `com.here.sdk.mapview.MapPolyline`. | + +--- + +### `updatePolylineProperties` + +#### Signature + +```kotlin +suspend fun updatePolylineProperties( + polyline: HereActualPolyline, + current: PolylineEntityInterface, + prev: PolylineEntityInterface, +): HereActualPolyline? +``` + +#### Description + +Updates the properties of an existing `MapPolyline` on the map. This method performs an efficient update by comparing the properties of the `current` and `prev` states. Only the properties that have changed (e.g., points, color, width, z-index) are applied to the native `MapPolyline` object. If necessary, the polyline is removed and re-added to the map to ensure visual correctness. + +#### Parameters + +| Parameter | Type | Description | +|------------|---------------------------------------------|--------------------------------------------------------------------------| +| `polyline` | `HereActualPolyline` | The existing `MapPolyline` object to be updated. | +| `current` | `PolylineEntityInterface` | The entity wrapper containing the new, updated `PolylineState`. | +| `prev` | `PolylineEntityInterface` | The entity wrapper containing the previous `PolylineState` for comparison. | + +#### Returns + +| Type | Description | +|----------------------|----------------------------------------------------------------------------------------------------------------------------------------| +| `HereActualPolyline?` | The updated `MapPolyline` instance. `HereActualPolyline` is a type alias for `com.here.sdk.mapview.MapPolyline`. | + +--- + +### `removePolyline` + +#### Signature + +```kotlin +suspend fun removePolyline(entity: PolylineEntityInterface) +``` + +#### Description + +Asynchronously removes a specified `MapPolyline` from the map. + +#### Parameters + +| Parameter | Type | Description | +|-----------|---------------------------------------------|--------------------------------------------------------------------------| +| `entity` | `PolylineEntityInterface` | The entity wrapper containing the `MapPolyline` instance to be removed. | + +--- + +## Example + +The following example demonstrates the complete lifecycle of a polyline using `HerePolylineOverlayRenderer`: creating it, updating its color, and finally removing it. + +```kotlin +import com.mapconductor.core.features.GeoPoint +import com.mapconductor.core.polyline.PolylineEntity +import com.mapconductor.core.polyline.PolylineState +import com.mapconductor.core.types.StrokeWidth +import com.mapconductor.here.HereViewHolder +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import androidx.compose.ui.graphics.Color + +// Assume 'hereViewHolder' is an initialized instance of HereViewHolder +// containing your MapView. +lateinit var hereViewHolder: HereViewHolder + +// 1. Initialize the renderer +val renderer = HerePolylineOverlayRenderer( + holder = hereViewHolder, + coroutine = CoroutineScope(Dispatchers.Main) // Use Main dispatcher for map updates +) + +fun managePolylineLifecycle() = CoroutineScope(Dispatchers.Main).launch { + // 2. Define the initial state of the polyline + val initialPoints = listOf( + GeoPoint(52.530, 13.384), + GeoPoint(52.531, 13.385), + GeoPoint(52.532, 13.386) + ) + val initialState = PolylineState( + points = initialPoints, + strokeColor = Color.Blue, + strokeWidth = StrokeWidth(10f), + zIndex = 100, + geodesic = true + ) + + // 3. Create the polyline and add it to the map + println("Creating polyline...") + val mapPolyline = renderer.createPolyline(initialState) + + // Create an entity to hold the state and the actual polyline object + var polylineEntity = PolylineEntity(initialState, mapPolyline!!) + println("Polyline created.") + + // (Wait for some time or user action) + + // 4. Define an updated state for the polyline (e.g., change color) + val updatedState = initialState.copy(strokeColor = Color.Red) + val updatedEntity = polylineEntity.copy(state = updatedState) + + // 5. Update the polyline's properties on the map + println("Updating polyline color to Red...") + renderer.updatePolylineProperties( + polyline = polylineEntity.polyline, + current = updatedEntity, + prev = polylineEntity + ) + polylineEntity = updatedEntity // Keep the entity reference updated + println("Polyline updated.") + + // (Wait for some time or user action) + + // 6. Remove the polyline from the map + println("Removing polyline...") + renderer.removePolyline(polylineEntity) + println("Polyline removed.") +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/raster/HereRasterLayerController.kt.md b/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/raster/HereRasterLayerController.kt.md new file mode 100644 index 00000000..e7cc9dda --- /dev/null +++ b/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/raster/HereRasterLayerController.kt.md @@ -0,0 +1,47 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +--- + +### `warmupNetworkIfNeeded(holder: HereViewHolder)` + +#### Signature +```kotlin +fun warmupNetworkIfNeeded(holder: HereViewHolder) +``` + +#### Description +Pre-warms the HERE SDK's network stack to reduce initial latency when raster layers are first added to the map. + +This method works by creating and immediately destroying a temporary `RasterDataSource`. This process triggers the HERE SDK's internal network initialization and reachability checks ahead of time. The operation is designed to run only once during the application's lifecycle, controlled by an internal flag. If the warmup process fails, the flag is reset, allowing for a subsequent attempt. + +It is recommended to call this function early in your application's startup sequence, for example, after the `MapView` has been initialized. + +#### Parameters +| Parameter | Type | Description | +| :-------- | :--------------- | :------------------------------------------------------------------------------------------------------------------------------------- | +| `holder` | `HereViewHolder` | The view holder containing the `MapView` instance. It provides the necessary `mapContext` and `applicationContext` for the operation. | + +#### Returns +This function does not return a value. + +#### Example +Here's how you might call `warmupNetworkIfNeeded` after initializing your map view and controller. + +```kotlin +// Assuming you have an instance of HereViewHolder and HereRasterLayerController +// For example, in your Activity or Fragment's `onMapReady` callback + +// 1. Initialize the controller +val rasterLayerController = HereRasterLayerController( + renderer = yourHereRasterLayerOverlayRenderer +) + +// 2. Obtain your HereViewHolder instance +val hereViewHolder: HereViewHolder = getMyHereViewHolder() // Your implementation + +// 3. Call the warmup function early in the app lifecycle +rasterLayerController.warmupNetworkIfNeeded(hereViewHolder) + +// Now, when you add your first raster layer later, the network stack +// is already initialized, reducing potential delays. +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/raster/HereRasterLayerOverlayRenderer.kt.md b/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/raster/HereRasterLayerOverlayRenderer.kt.md new file mode 100644 index 00000000..2fa2ed33 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/raster/HereRasterLayerOverlayRenderer.kt.md @@ -0,0 +1,200 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet, formatted in Markdown. + +# HereRasterLayerOverlayRenderer + +The `HereRasterLayerOverlayRenderer` class is responsible for rendering and managing raster tile layers on a HERE map. It implements the `RasterLayerOverlayRendererInterface` to handle the lifecycle of raster layers, including their creation, modification, and removal, by translating abstract `RasterLayerState` objects into concrete HERE SDK `MapLayer` instances. + +This renderer supports various raster sources, such as URL templates (XYZ and TMS) and ArcGIS map services. It manages the underlying `RasterDataSource` and `MapLayer` objects from the HERE SDK. + +```kotlin +class HereRasterLayerOverlayRenderer( + private val holder: HereViewHolder, + override val coroutine: CoroutineScope = CoroutineScope(Dispatchers.Default), +) : RasterLayerOverlayRendererInterface +``` + +## Constructor + +### Signature + +```kotlin +HereRasterLayerOverlayRenderer( + holder: HereViewHolder, + coroutine: CoroutineScope = CoroutineScope(Dispatchers.Default) +) +``` + +### Description + +Creates a new instance of the `HereRasterLayerOverlayRenderer`. + +### Parameters + +| Parameter | Type | Description | +| :---------- | :---------------- | :------------------------------------------------------------------------------------------------------ | +| `holder` | `HereViewHolder` | The view holder that provides access to the `MapView` and `HereMap` instances. | +| `coroutine` | `CoroutineScope` | The coroutine scope used for executing asynchronous operations. Defaults to `CoroutineScope(Dispatchers.Default)`. | + +## Methods + +### onAdd + +#### Signature + +```kotlin +override suspend fun onAdd( + data: List, +): List +``` + +#### Description + +Adds one or more new raster layers to the map based on the provided state data. For each item in the input list, it creates a new `RasterDataSource` and a corresponding `MapLayer`. + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :---------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------ | +| `data` | `List` | A list of parameters, where each element contains the `RasterLayerState` for a new layer to be added. | + +#### Returns + +A `List` of `HereRasterLayerHandle?` objects. Each handle corresponds to a newly created layer. An element will be `null` if the layer creation failed (e.g., due to an unsupported source type or invalid configuration). + +### onChange + +#### Signature + +```kotlin +override suspend fun onChange( + data: List>, +): List +``` + +#### Description + +Processes updates for existing raster layers. If the underlying `source` of a layer has changed, the old layer is destroyed and a new one is created. If only other properties like visibility have changed, the existing `MapLayer` is updated in place. + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :-------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------- | +| `data` | `List>` | A list of change parameters. Each element contains the previous layer entity (`prev`) and the new state information (`current`). | + +#### Returns + +A `List` of `HereRasterLayerHandle?` objects representing the state of the layers after the update. This could be the original handle, a new handle if the layer was recreated, or `null` if recreation failed. + +### onRemove + +#### Signature + +```kotlin +override suspend fun onRemove(data: List>) +``` + +#### Description + +Removes one or more raster layers from the map. This method destroys the associated `MapLayer` and `RasterDataSource` for each layer, freeing up their resources. + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :-------------------------------------------------------- | :---------------------------------------------- | +| `data` | `List>` | A list of layer entities to be removed from the map. | + +#### Returns + +This method does not return a value. + +### onPostProcess + +#### Signature + +```kotlin +override suspend fun onPostProcess() +``` + +#### Description + +A lifecycle method called after all add, change, and remove operations for a given update cycle are complete. In this implementation, the method is empty and performs no action. + +## Data Classes + +### HereRasterLayerHandle + +A data class that acts as a handle to the native HERE SDK objects associated with a rendered raster layer. It encapsulates the `RasterDataSource` and `MapLayer` for easy management. + +#### Signature + +```kotlin +data class HereRasterLayerHandle( + val dataSource: RasterDataSource, + val layer: MapLayer, + val sourceName: String, + val layerName: String, +) +``` + +#### Properties + +| Property | Type | Description | +| :----------- | :--------------- | :------------------------------------------------------- | +| `dataSource` | `RasterDataSource` | The HERE SDK data source providing the raster tiles. | +| `layer` | `MapLayer` | The HERE SDK map layer that displays the raster data. | +| `sourceName` | `String` | The unique name assigned to the data source. | +| `layerName` | `String` | The unique name assigned to the map layer. | + +## Example + +The following example demonstrates how to instantiate the `HereRasterLayerOverlayRenderer` and use it to add a new raster layer from a URL template. + +```kotlin +import com.mapconductor.core.raster.RasterLayerSource +import com.mapconductor.core.raster.RasterLayerState +import com.mapconductor.core.raster.TileScheme +import com.mapconductor.here.HereViewHolder +import com.mapconductor.here.raster.HereRasterLayerOverlayRenderer +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch + +// Assume 'hereViewHolder' is an initialized instance of HereViewHolder +// containing a valid MapView and HereMap. +val hereViewHolder: HereViewHolder = /* ... */ + +// 1. Instantiate the renderer +val rasterRenderer = HereRasterLayerOverlayRenderer(hereViewHolder) + +// 2. Define the state for the new raster layer +val openStreetMapLayerState = RasterLayerState( + id = "osm-layer-1", + visible = true, + source = RasterLayerSource.UrlTemplate( + template = "https://a.tile.openstreetmap.org/{z}/{x}/{y}.png", + scheme = TileScheme.XYZ, + minZoom = 0, + maxZoom = 19 + ) +) + +// 3. Create the parameters for the 'onAdd' method +// In a real application, this would likely be part of a larger system. +// For this example, we create a simple mock implementation. +data class AddParams(override val state: RasterLayerState) : RasterLayerOverlayRendererInterface.AddParamsInterface + +val addParams = listOf(AddParams(openStreetMapLayerState)) + +// 4. Call 'onAdd' within a coroutine to add the layer to the map +CoroutineScope(Dispatchers.Main).launch { + val layerHandles = rasterRenderer.onAdd(addParams) + + if (layerHandles.isNotEmpty() && layerHandles[0] != null) { + println("Successfully added OpenStreetMap layer.") + val handle = layerHandles[0]!! + println("Layer Name: ${handle.layerName}, Source Name: ${handle.sourceName}") + } else { + println("Failed to add OpenStreetMap layer.") + } +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/zoom/ZoomAltitudeConverter.kt.md b/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/zoom/ZoomAltitudeConverter.kt.md new file mode 100644 index 00000000..74b5f868 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-here/src/main/java/com/mapconductor/here/zoom/ZoomAltitudeConverter.kt.md @@ -0,0 +1,216 @@ +# SDK Documentation: ZoomAltitudeConverter + +This document provides a detailed reference for the `ZoomAltitudeConverter` class and its associated functions, designed for converting between map zoom levels and camera altitudes within the HERE Maps SDK context. + +## `ZoomAltitudeConverter` + +### Description + +The `ZoomAltitudeConverter` class provides a concrete implementation for converting between camera altitude and map zoom level, specifically tailored for the HERE Maps SDK. It extends `AbstractZoomAltitudeConverter` and factors in variables like latitude and camera tilt to provide accurate conversions, accounting for the map's projection. + +### Constructor + +#### Signature + +```kotlin +ZoomAltitudeConverter( + zoom0Altitude: Double = DEFAULT_ZOOM0_ALTITUDE +) +``` + +#### Description + +Creates an instance of the `ZoomAltitudeConverter`. + +#### Parameters + +| Parameter | Type | Description | +| :-------------- | :----- | :------------------------------------------------------------------------------------------------------ | +| `zoom0Altitude` | Double | Optional. The camera altitude in meters that corresponds to zoom level 0 at the equator. Defaults to `DEFAULT_ZOOM0_ALTITUDE`. | + +--- + +## Methods + +### `zoomLevelToAltitude` + +#### Signature + +```kotlin +override fun zoomLevelToAltitude( + zoomLevel: Double, + latitude: Double, + tilt: Double +): Double +``` + +#### Description + +Calculates the camera altitude in meters required to display a specific map zoom level. The calculation is sensitive to the current latitude and camera tilt angle. + +#### Parameters + +| Parameter | Type | Description | +| :---------- | :----- | :----------------------------------------------------------------------- | +| `zoomLevel` | Double | The target HERE Maps zoom level. | +| `latitude` | Double | The current latitude of the map's center, in degrees. | +| `tilt` | Double | The current camera tilt angle in degrees, where 0 is looking straight down. | + +#### Returns + +`Double` - The calculated camera altitude in meters, clamped within a valid range. + +#### Example + +```kotlin +val converter = ZoomAltitudeConverter() +val targetZoom = 15.5 +val currentLatitude = 48.8584 // Paris +val currentTilt = 30.0 + +val requiredAltitude = converter.zoomLevelToAltitude(targetZoom, currentLatitude, currentTilt) +println("To achieve zoom level $targetZoom, set altitude to $requiredAltitude meters.") +// Example output: To achieve zoom level 15.5, set altitude to 1098.5 meters. +``` + +--- + +### `altitudeToZoomLevel` + +#### Signature + +```kotlin +override fun altitudeToZoomLevel( + altitude: Double, + latitude: Double, + tilt: Double +): Double +``` + +#### Description + +Calculates the map zoom level that corresponds to a given camera altitude. This is the inverse operation of `zoomLevelToAltitude`. + +#### Parameters + +| Parameter | Type | Description | +| :--------- | :----- | :----------------------------------------------------------------------- | +| `altitude` | Double | The current camera altitude above the map, in meters. | +| `latitude` | Double | The current latitude of the map's center, in degrees. | +| `tilt` | Double | The current camera tilt angle in degrees, where 0 is looking straight down. | + +#### Returns + +`Double` - The calculated HERE Maps zoom level, clamped within a valid range. + +#### Example + +```kotlin +val converter = ZoomAltitudeConverter() +val currentAltitude = 1100.0 // meters +val currentLatitude = 48.8584 // Paris +val currentTilt = 30.0 + +val zoomLevel = converter.altitudeToZoomLevel(currentAltitude, currentLatitude, currentTilt) +println("At an altitude of $currentAltitude meters, the zoom level is approximately $zoomLevel.") +// Example output: At an altitude of 1100.0 meters, the zoom level is approximately 15.49. +``` + +--- + +## Companion Object + +The `ZoomAltitudeConverter` class contains a companion object with utility functions and constants for zoom level conversions between different map provider standards. + +### Companion Object Functions + +#### `hereZoomToGoogleZoom` + +##### Signature + +```kotlin +fun hereZoomToGoogleZoom( + hereZoom: Double, + latitude: Double +): Double +``` + +##### Description + +Converts a HERE Maps zoom level to an equivalent "Google-like" (Web Mercator) zoom level. The conversion is latitude-dependent to account for differences in map projection scaling between the two systems. + +##### Parameters + +| Parameter | Type | Description | +| :--------- | :----- | :---------------------------------------------------- | +| `hereZoom` | Double | The zoom level from the HERE Maps SDK. | +| `latitude` | Double | The current latitude of the map's center, in degrees. | + +##### Returns + +`Double` - The equivalent Google-like zoom level. + +##### Example + +```kotlin +val hereZoom = 14.0 +val latitude = 60.1699 // Helsinki +val googleZoom = ZoomAltitudeConverter.hereZoomToGoogleZoom(hereZoom, latitude) +println("HERE zoom $hereZoom at latitude $latitude is equivalent to Google zoom $googleZoom.") +// Example output: HERE zoom 14.0 at latitude 60.1699 is equivalent to Google zoom 13.0. +``` + +--- + +#### `googleZoomToHereZoom` + +##### Signature + +```kotlin +fun googleZoomToHereZoom( + googleZoom: Double, + latitude: Double +): Double +``` + +##### Description + +Converts a "Google-like" (Web Mercator) zoom level to an equivalent HERE Maps zoom level. This is the inverse operation of `hereZoomToGoogleZoom`. + +##### Parameters + +| Parameter | Type | Description | +| :----------- | :----- | :---------------------------------------------------- | +| `googleZoom` | Double | The Google-like (Web Mercator) zoom level. | +| `latitude` | Double | The current latitude of the map's center, in degrees. | + +##### Returns + +`Double` - The equivalent HERE Maps zoom level. + +##### Example + +```kotlin +val googleZoom = 13.0 +val latitude = 60.1699 // Helsinki +val hereZoom = ZoomAltitudeConverter.googleZoomToHereZoom(googleZoom, latitude) +println("Google zoom $googleZoom at latitude $latitude is equivalent to HERE zoom $hereZoom.") +// Example output: Google zoom 13.0 at latitude 60.1699 is equivalent to HERE zoom 14.0. +``` + +--- + +### Companion Object Properties + +#### `HERE_ZOOM_TO_GOOGLE_ZOOM_AT_EQUATOR` + +##### Signature + +```kotlin +const val HERE_ZOOM_TO_GOOGLE_ZOOM_AT_EQUATOR: Double +``` + +##### Description + +An empirical constant representing the offset between HERE Maps zoom and "Google-like" zoom at the equator (latitude ≈ 0). The relationship is approximately: +`GoogleZoom ≈ HereZoom + HERE_ZOOM_TO_GOOGLE_ZOOM_AT_EQUATOR` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/GeoPoint.kt.md b/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/GeoPoint.kt.md new file mode 100644 index 00000000..89a2b67e --- /dev/null +++ b/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/GeoPoint.kt.md @@ -0,0 +1,135 @@ +# Mapbox GeoJSON Interoperability + +This document provides a reference for the extension functions that facilitate conversion between `com.mapconductor.core.features.GeoPoint` and `com.mapbox.geojson.Point` objects. These utilities streamline the process of using MapConductor Core types with the Mapbox SDK. + +--- + +## `GeoPoint.toPoint()` + +Converts a `GeoPoint` object to its equivalent Mapbox `Point` representation. + +### Signature + +```kotlin +fun GeoPoint.toPoint(): Point +``` + +### Description + +This extension function is called on a `GeoPoint` instance. It creates a new Mapbox `Point` object using the `latitude`, `longitude`, and `altitude` from the source `GeoPoint`. This is useful when you need to pass a MapConductor location to a Mapbox SDK function that expects a `Point`. + +### Returns + +| Type | Description | +|---|---| +| `Point` | A new Mapbox `Point` object with the same coordinate values. | + +### Example + +```kotlin +import com.mapconductor.core.features.GeoPoint +import com.mapconductor.mapbox.toPoint +import com.mapbox.geojson.Point + +// Create a GeoPoint instance +val geoPoint = GeoPoint(latitude = 40.7128, longitude = -74.0060, altitude = 10.0) + +// Convert it to a Mapbox Point +val mapboxPoint: Point = geoPoint.toPoint() + +// Verify the coordinates +println("Longitude: ${mapboxPoint.longitude()}") // -74.0060 +println("Latitude: ${mapboxPoint.latitude()}") // 40.7128 +println("Altitude: ${mapboxPoint.altitude()}") // 10.0 +``` + +--- + +## `GeoPoint.Companion.from()` + +Creates a `GeoPoint` instance from a Mapbox `Point` object. + +### Signature + +```kotlin +fun GeoPoint.Companion.from(point: Point): GeoPoint +``` + +### Description + +This extension function on the `GeoPoint.Companion` object acts as a factory method. It takes a Mapbox `Point` and constructs a `GeoPoint` with the corresponding `latitude`, `longitude`, and `altitude`. + +### Parameters + +| Parameter | Type | Description | +|---|---|---| +| `point` | `Point` | The Mapbox `Point` object to convert. | + +### Returns + +| Type | Description | +|---|---| +| `GeoPoint` | A new `GeoPoint` instance with coordinate values from the source `Point`. | + +### Example + +```kotlin +import com.mapconductor.core.features.GeoPoint +import com.mapconductor.mapbox.from +import com.mapbox.geojson.Point + +// Create a Mapbox Point instance +val mapboxPoint = Point.fromLngLat(-74.0060, 40.7128, 10.0) + +// Create a GeoPoint from the Mapbox Point +val geoPoint: GeoPoint = GeoPoint.from(mapboxPoint) + +// Verify the coordinates +println("Longitude: ${geoPoint.longitude}") // -74.0060 +println("Latitude: ${geoPoint.latitude}") // 40.7128 +println("Altitude: ${geoPoint.altitude}") // 10.0 +``` + +--- + +## `Point.toGeoPoint()` + +Converts a Mapbox `Point` object to a `GeoPoint`. + +### Signature + +```kotlin +fun Point.toGeoPoint(): GeoPoint +``` + +### Description + +This extension function is called on a Mapbox `Point` instance. It creates a new `GeoPoint` using the `latitude` and `longitude` from the source `Point`. + +**Note:** This conversion does not preserve the altitude. The resulting `GeoPoint` will have a default altitude value. If altitude is important, use `GeoPoint.Companion.from(point)` instead. + +### Returns + +| Type | Description | +|---|---| +| `GeoPoint` | A new `GeoPoint` instance. | + +### Example + +```kotlin +import com.mapconductor.core.features.GeoPoint +import com.mapconductor.mapbox.toGeoPoint +import com.mapbox.geojson.Point + +// Create a Mapbox Point with altitude +val mapboxPoint = Point.fromLngLat(-74.0060, 40.7128, 10.0) + +// Convert it to a GeoPoint +val geoPoint: GeoPoint = mapboxPoint.toGeoPoint() + +// Verify the coordinates +println("Longitude: ${geoPoint.longitude}") // -74.0060 +println("Latitude: ${geoPoint.latitude}") // 40.7128 +// Note that altitude is not carried over +println("Altitude: ${geoPoint.altitude}") // Will be the default value for GeoPoint +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/GeoRectBounds.kt.md b/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/GeoRectBounds.kt.md new file mode 100644 index 00000000..e84378bd --- /dev/null +++ b/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/GeoRectBounds.kt.md @@ -0,0 +1,101 @@ +# Mapbox Interoperability Extensions + +This document provides details on the extension functions that facilitate conversion between `GeoRectBounds` and the Mapbox `CoordinateBounds` types. These utilities are essential for seamless integration with the Mapbox SDK. + +--- + +## `toGeoBox()` + +### Signature +```kotlin +fun GeoRectBounds.toGeoBox(): CoordinateBounds? +``` + +### Description +Converts a `GeoRectBounds` instance into a Mapbox `CoordinateBounds` object. This function is useful for passing custom bounding box definitions to Mapbox APIs that expect `CoordinateBounds`. + +The function handles cases where the source `GeoRectBounds` might be incomplete. If either the `southWest` or `northEast` property of the `GeoRectBounds` is `null`, this function will return `null`. + +### Returns +| Type | Description | +| -------------------- | ------------------------------------------------------------------------------------------------------- | +| `CoordinateBounds?` | The corresponding `CoordinateBounds` object, or `null` if the source `GeoRectBounds` is missing corner points. | + +### Example +```kotlin +import com.mapbox.maps.CoordinateBounds +import com.mapconductor.core.features.GeoPoint +import com.mapconductor.core.features.GeoRectBounds + +// Assuming toPoint() and other necessary extensions are available + +// --- Scenario 1: Successful conversion --- +val validBounds = GeoRectBounds( + southWest = GeoPoint(lat = 34.0522, lon = -118.2437), // Los Angeles SW + northEast = GeoPoint(lat = 40.7128, lon = -74.0060) // New York NE +) + +val coordinateBounds: CoordinateBounds? = validBounds.toGeoBox() + +// coordinateBounds will be a valid CoordinateBounds object +println("Successful conversion: ${coordinateBounds != null}") +// Expected output: Successful conversion: true + + +// --- Scenario 2: Incomplete bounds leading to null --- +val incompleteBounds = GeoRectBounds( + southWest = null, + northEast = GeoPoint(lat = 40.7128, lon = -74.0060) +) + +val nullCoordinateBounds: CoordinateBounds? = incompleteBounds.toGeoBox() + +// nullCoordinateBounds will be null +println("Incomplete bounds conversion: ${nullCoordinateBounds == null}") +// Expected output: Incomplete bounds conversion: true +``` + +--- + +## `toGeoRectBounds()` + +### Signature +```kotlin +fun CoordinateBounds.toGeoRectBounds(): GeoRectBounds +``` + +### Description +Converts a Mapbox `CoordinateBounds` instance into a `GeoRectBounds` object. This is useful for translating bounding box information from the Mapbox SDK into the application's custom `GeoRectBounds` type. + +This function assumes the source `CoordinateBounds` is always valid and contains non-null `southwest` and `northeast` points. + +### Returns +| Type | Description | +| --------------- | ----------------------------------------- | +| `GeoRectBounds` | The newly created `GeoRectBounds` object. | + +### Example +```kotlin +import com.mapbox.geojson.Point +import com.mapbox.maps.CoordinateBounds +import com.mapconductor.core.features.GeoRectBounds + +// Assuming toGeoPoint() and other necessary extensions are available + +// Define southwest and northeast points for Mapbox CoordinateBounds +val swPoint = Point.fromLngLat(-118.2437, 34.0522) // Los Angeles +val nePoint = Point.fromLngLat(-74.0060, 40.7128) // New York + +// Create a Mapbox CoordinateBounds instance +val mapboxBounds = CoordinateBounds(swPoint, nePoint) + +// Convert to GeoRectBounds +val geoRectBounds: GeoRectBounds = mapboxBounds.toGeoRectBounds() + +// geoRectBounds will be a valid GeoRectBounds object +println("SW Latitude: ${geoRectBounds.southWest?.lat}") +println("NE Longitude: ${geoRectBounds.northEast?.lon}") +// Expected output: +// SW Latitude: 34.0522 +// NE Longitude: -74.0060 +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/MapCameraPosition.kt.md b/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/MapCameraPosition.kt.md new file mode 100644 index 00000000..5b23d313 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/MapCameraPosition.kt.md @@ -0,0 +1,214 @@ +# Mapbox Camera Conversion Utilities + +This document provides an overview of the Kotlin extension functions used for converting between Mapbox-specific camera objects (`CameraOptions`, `CameraState`, `CameraChanged`) and a generic `MapCameraPosition` data class. These utilities help in creating a platform-agnostic camera management layer. + +--- + +### `CameraChanged.toMapCameraPosition()` + +Converts a `CameraChanged` event object into a `CameraOptions` object. + +**Note:** Despite its name, this function returns `CameraOptions`, not `MapCameraPosition`. It serves as a helper to extract camera properties from a `CameraChanged` event. It also converts the zoom level from the Mapbox scale to the Google Maps scale. + +#### Signature +```kotlin +fun CameraChanged.toMapCameraPosition(): CameraOptions +``` + +#### Parameters +| Parameter | Type | Description | +| :--- | :--- | :--- | +| **(receiver)** | `CameraChanged` | The `CameraChanged` event object from which to extract camera state. | + +#### Returns +A `CameraOptions` object containing the camera properties from the `CameraChanged` event. + +#### Example +```kotlin +val cameraChangedListener = CameraChanged { cameraChangedEvent -> + // The cameraChangedEvent is a CameraChanged object + val cameraOptions = cameraChangedEvent.toMapCameraPosition() + + // The returned object is of type CameraOptions + println("Converted to CameraOptions with zoom: ${cameraOptions.zoom}") +} +``` + +--- + +### `MapCameraPosition.toCameraOptions()` + +Converts a generic `MapCameraPosition` object into a Mapbox-specific `CameraOptions` object. This is useful for applying a defined camera position to the Mapbox map. The function converts the zoom level from the Google Maps scale to the Mapbox scale. + +**Note:** The conversion for `paddings` is currently unimplemented (`TODO` in the source code). + +#### Signature +```kotlin +fun MapCameraPosition.toCameraOptions(): CameraOptions +``` + +#### Parameters +| Parameter | Type | Description | +| :--- | :--- | :--- | +| **(receiver)** | `MapCameraPosition` | The source `MapCameraPosition` object to convert. | + +#### Returns +A `CameraOptions` object that can be used with `MapboxMap.setCamera()`. + +#### Example +```kotlin +val mapCameraPosition = MapCameraPosition( + position = GeoPoint.fromLongLat(-74.0060, 40.7128), // New York City + zoom = 12.0, + tilt = 30.0, + bearing = 45.0 +) + +val cameraOptions = mapCameraPosition.toCameraOptions() + +// Use the result to set the camera on a MapboxMap instance +// mapboxMap.setCamera(cameraOptions) +``` + +--- + +### `MapCameraPosition.toCameraState()` + +Converts a `MapCameraPosition` object into a Mapbox `CameraState` object. `CameraState` represents a snapshot of the map's camera properties. This function converts the zoom level from the Google Maps scale to the Mapbox scale and initializes padding to zero. + +#### Signature +```kotlin +fun MapCameraPosition.toCameraState(): CameraState +``` + +#### Parameters +| Parameter | Type | Description | +| :--- | :--- | :--- | +| **(receiver)** | `MapCameraPosition` | The source `MapCameraPosition` object to convert. | + +#### Returns +A `CameraState` object representing the given camera position. + +#### Example +```kotlin +val mapCameraPosition = MapCameraPosition( + position = GeoPoint.fromLongLat(-0.1278, 51.5074), // London + zoom = 14.0, + tilt = 0.0, + bearing = 0.0 +) + +val cameraState = mapCameraPosition.toCameraState() + +println("Created CameraState with center: ${cameraState.center}") +``` + +--- + +### `MapCameraPosition.Companion.from()` + +A factory function that creates a `MapCameraPosition` instance from any object that implements the `MapCameraPositionInterface`. This provides a standardized way to convert different camera position representations into a concrete `MapCameraPosition`. If the input object is already a `MapCameraPosition`, it is returned directly to avoid redundant object creation. + +#### Signature +```kotlin +fun MapCameraPosition.Companion.from(cameraPosition: MapCameraPositionInterface): MapCameraPosition +``` + +#### Parameters +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `cameraPosition` | `MapCameraPositionInterface` | An object conforming to the `MapCameraPositionInterface`. | + +#### Returns +A `MapCameraPosition` instance based on the provided interface implementation. + +#### Example +```kotlin +// Assume MyCustomCameraPosition implements MapCameraPositionInterface +// data class MyCustomCameraPosition(...) : MapCameraPositionInterface + +val customCamera = MyCustomCameraPosition( + position = GeoPoint.fromLongLat(139.6917, 35.6895), // Tokyo + zoom = 10.0, + bearing = 0.0, + tilt = 0.0, + paddings = null, + visibleRegion = null +) + +// Use the factory function to create a standard MapCameraPosition +val mapCameraPosition = MapCameraPosition.from(customCamera) + +println("Converted to MapCameraPosition with zoom: ${mapCameraPosition.zoom}") +``` + +--- + +### `CameraOptions.toMapCameraPosition()` + +Converts a Mapbox `CameraOptions` object into a generic `MapCameraPosition` object. This is useful for abstracting camera details away from the Mapbox SDK. The function handles nullable properties in `CameraOptions` by providing sensible defaults and converts the zoom level from the Mapbox scale to the Google Maps scale. + +#### Signature +```kotlin +fun CameraOptions.toMapCameraPosition(): MapCameraPosition +``` + +#### Parameters +| Parameter | Type | Description | +| :--- | :--- | :--- | +| **(receiver)** | `CameraOptions` | The source Mapbox `CameraOptions` object. | + +#### Returns +A new `MapCameraPosition` object populated with data from the `CameraOptions`. + +#### Example +```kotlin +val cameraOptions = CameraOptions.Builder() + .center(Point.fromLngLat(2.3522, 48.8566)) // Paris + .zoom(11.0) // Mapbox zoom level + .build() + +val mapCameraPosition = cameraOptions.toMapCameraPosition() + +// The zoom level is now converted to the Google Maps scale +println("Converted to MapCameraPosition with zoom: ${mapCameraPosition.zoom}") +``` + +--- + +### `CameraState.toMapCameraPosition()` + +Converts a Mapbox `CameraState` object into a generic `MapCameraPosition`. This allows you to capture the current state of the Mapbox map's camera and store it in a platform-agnostic format. The zoom level is converted from the Mapbox scale to the Google Maps scale. + +#### Signature +```kotlin +fun CameraState.toMapCameraPosition(): MapCameraPosition +``` + +#### Parameters +| Parameter | Type | Description | +| :--- | :--- | :--- | +| **(receiver)** | `CameraState` | The source Mapbox `CameraState` object. | + +#### Returns +A new `MapCameraPosition` object representing the camera's state. + +#### Example +```kotlin +// Typically, you would get CameraState from a MapboxMap instance +// val currentState = mapboxMap.cameraState + +// For demonstration, we create one manually +val cameraState = CameraState( + Point.fromLngLat(151.2093, -33.8688), // Sydney + EdgeInsets(0.0, 0.0, 0.0, 0.0), + 13.0, // Mapbox zoom level + 0.0, + 0.0 +) + +val mapCameraPosition = cameraState.toMapCameraPosition() + +// The zoom level is now converted +println("Converted to MapCameraPosition with position: ${mapCameraPosition.position}") +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/MapboxExtension.kt.md b/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/MapboxExtension.kt.md new file mode 100644 index 00000000..76a6179b --- /dev/null +++ b/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/MapboxExtension.kt.md @@ -0,0 +1,69 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +*** + +# Mapbox Utility Extensions + +This document provides details on utility extension functions used for converting custom types into Mapbox-specific formats. + +## `BitmapIcon.toPointAnnotationOptions()` + +> **Note:** This is an `internal` function and is not part of the public API. + +Converts a `BitmapIcon` object into a Mapbox `PointAnnotationOptions` object, which is used to display a marker on the map. + +The function translates the relative anchor point of the `BitmapIcon` into a pixel offset required by Mapbox. It uses `IconAnchor.BOTTOM` as a base and calculates the difference to correctly position the icon according to the specified anchor. + +### Signature +```kotlin +internal fun BitmapIcon.toPointAnnotationOptions(): PointAnnotationOptions +``` + +### Description +This extension function is called on a `BitmapIcon` instance. It reads the `bitmap` and `anchor` properties to create a fully configured `PointAnnotationOptions` object. The bitmap is copied to ensure it's mutable (`ARGB_8888`), which is a requirement for Mapbox annotations. + +### Returns +| Type | Description | +| :--- | :--- | +| `PointAnnotationOptions` | An options object configured with the icon image, anchor, and calculated offset, ready to be used to create a point annotation on a Mapbox map. | + +
+ +--- + +## `Color.toMapboxColorString()` + +Converts a Jetpack Compose `Color` object into a Mapbox-compatible RGBA color string. + +### Signature +```kotlin +fun Color.toMapboxColorString(): String +``` + +### Description +This extension function is called on a `androidx.compose.ui.graphics.Color` instance. It transforms the color's float-based components (0.0 to 1.0) into the standard 0-255 range for RGB values. The resulting string is formatted as `"rgba(r, g, b, a)"`, which can be used in various Mapbox style properties. + +### Parameters +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `this` | `androidx.compose.ui.graphics.Color` | The Jetpack Compose `Color` instance to convert. | + +### Returns +| Type | Description | +| :--- | :--- | +| `String` | A `String` representing the color in the `"rgba(r, g, b, a)"` format. | + +### Example +```kotlin +import androidx.compose.ui.graphics.Color + +// Convert a predefined color +val composeColor = Color.Red +val mapboxColorString = composeColor.toMapboxColorString() +// Result: "rgba(255.0, 0.0, 0.0, 1.0)" + +// Convert a custom semi-transparent color +val semiTransparentBlue = Color(red = 0f, green = 0f, blue = 1f, alpha = 0.5f) +val mapboxColorString2 = semiTransparentBlue.toMapboxColorString() +// Result: "rgba(0.0, 0.0, 255.0, 0.5)" +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/MapboxInitSDK.kt.md b/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/MapboxInitSDK.kt.md new file mode 100644 index 00000000..9fec4a50 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/MapboxInitSDK.kt.md @@ -0,0 +1,86 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +# MapboxInitSDK() + +## Signature + +```kotlin +fun MapboxInitSDK(context: Context) +``` + +## Description + +Initializes the Mapbox SDK with the required access token. This function must be called once before using any other Mapbox components, such as maps or navigation services. + +The function retrieves your Mapbox access token from the application's `AndroidManifest.xml` metadata. It then sets this token globally for the SDK to use. It is highly recommended to call this function in your `Application` class's `onCreate()` method to ensure the SDK is initialized as early as possible. + +## Parameters + +| Parameter | Type | Description | +| :-------- | :------ | :------------------------------------------------------------------------------------------------------------------------------------- | +| `context` | `Context` | The application context. It is used to access the app's metadata to find the `MAPBOX_ACCESS_TOKEN`. Using an `ApplicationContext` is recommended. | + +## Returns + +This function does not return any value. + +## Throws + +| Type | Condition | +| :---------- | :---------------------------------------------------------------------------------------------------- | +| `Exception` | Thrown if the `` is not found in `AndroidManifest.xml`. | + +## Example + +To use this function, you must first add your access token to your `AndroidManifest.xml` file and then call `MapboxInitSDK()` from your `Application` class. + +**1. Add Access Token to `AndroidManifest.xml`** + +Place the `` tag containing your access token inside the `` tag. + +```xml + + + + + + + + + + ... + + + +``` + +**2. Call `MapboxInitSDK` in your Application Class** + +Create a custom `Application` class and call the function within the `onCreate()` method. + +```kotlin +// MyApplication.kt + +import android.app.Application +import com.mapconductor.mapbox.MapboxInitSDK + +class MyApplication : Application() { + override fun onCreate() { + super.onCreate() + + // Initialize the Mapbox SDK with the access token + // This should be done once when the application starts. + try { + MapboxInitSDK(this) + } catch (e: Exception) { + // Handle the exception, e.g., log an error or disable map features + e.printStackTrace() + } + } +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/MapboxMapDesign.kt.md b/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/MapboxMapDesign.kt.md new file mode 100644 index 00000000..50c42643 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/MapboxMapDesign.kt.md @@ -0,0 +1,156 @@ +Of course! Here is the high-quality SDK documentation for the provided Kotlin code snippet. + +--- + +# MapboxMapDesign SDK Documentation + +This document provides detailed information about the `MapboxMapDesign` sealed class and its related components, which are used to represent and manage Mapbox map styles within the SDK. + +## `MapboxDesignType` + +### Signature +```kotlin +typealias MapboxDesignType = MapDesignTypeInterface +``` + +### Description +A type alias for `MapDesignTypeInterface`. It standardizes the representation of a map design type for Mapbox, where the underlying value is a `String` representing the style URL. + +--- + +## `MapboxMapDesign` + +### Signature +```kotlin +sealed class MapboxMapDesign( + override val id: String, +) : MapboxDesignType +``` + +### Description +A sealed class that represents a specific Mapbox map style. It provides a set of predefined, commonly used Mapbox styles and allows for the use of custom styles through the `Custom` class. Each design corresponds to a Mapbox Style URL. + +### Predefined Styles +The `MapboxMapDesign` class includes several predefined objects for standard Mapbox styles. + +| Object Name | Style ID | Description | +| ------------------- | ------------------------- | ----------------------------------------- | +| `Standard` | `standard` | The standard Mapbox map style. | +| `StandardSatellite` | `standard-satellite` | The standard satellite style with labels. | +| `Streets` | `streets-v12` | The Mapbox Streets style. | +| `Outdoors` | `outdoors-v12` | The Mapbox Outdoors style. | +| `Light` | `light-v11` | The Mapbox Light style. | +| `Dark` | `dark-v11` | The Mapbox Dark style. | +| `Satellite` | `satellite-v9` | The Mapbox Satellite style. | +| `SatelliteStreets` | `satellite-streets-v12` | The Mapbox Satellite Streets style. | +| `NavigationDay` | `navigation-day-v1` | The Mapbox Navigation Day style. | +| `NavigationNight` | `navigation-night-v1` | The Mapbox Navigation Night style. | + +### `Custom` Class + +#### Signature +```kotlin +class Custom(layerId: String) : MapboxMapDesign(layerId) +``` + +#### Description +Represents a custom or non-predefined Mapbox style. Use this class when you need to load a style that is not one of the standard options, such as a style created in your Mapbox account. + +#### Parameters +| Parameter | Type | Description | +| --------- | -------- | ------------------------------------------------------------------------ | +| `layerId` | `String` | The unique identifier of the custom Mapbox style (e.g., `your-user/ck...`). | + +--- + +## Functions + +### `getValue()` + +#### Signature +```kotlin +override fun getValue(): String +``` + +#### Description +Returns the complete Mapbox style URL for the given map design. This URL is formatted as `mapbox://styles/mapbox/{id}` for standard styles or `mapbox://styles/{id}` for custom styles (where `id` includes the user, e.g., `username/style-id`). + +#### Returns +| Type | Description | +| -------- | ----------------------------------------- | +| `String` | The fully qualified Mapbox style URL. | + +#### Example +```kotlin +val streetsUrl = MapboxMapDesign.Streets.getValue() +// streetsUrl is "mapbox://styles/mapbox/streets-v12" + +val customUrl = MapboxMapDesign.Custom("my-user/c1k2e3f4g5").getValue() +// customUrl is "mapbox://styles/mapbox/my-user/c1k2e3f4g5" +``` + +### `MapboxMapDesign.Create()` + +#### Signature +```kotlin +fun Create(layerId: String): MapboxMapDesign +``` + +#### Description +A factory function that creates a `MapboxMapDesign` instance from a style ID string. If the `layerId` matches one of the predefined styles, it returns the corresponding singleton object. Otherwise, it returns a new `Custom` instance. + +#### Parameters +| Parameter | Type | Description | +| --------- | -------- | ------------------------------------------------------------------------ | +| `layerId` | `String` | The style ID to create the `MapboxMapDesign` from (e.g., `streets-v12`). | + +#### Returns +| Type | Description | +| ----------------- | ------------------------------------------------------------------------------------------------------- | +| `MapboxMapDesign` | The corresponding predefined `MapboxMapDesign` object or a `Custom` instance if the ID does not match. | + +#### Example +```kotlin +// Creates a predefined style object +val streetsDesign = MapboxMapDesign.Create("streets-v12") // Returns MapboxMapDesign.Streets + +// Creates a custom style instance +val customDesign = MapboxMapDesign.Create("my-user/c1k2e3f4g5") // Returns Custom("my-user/c1k2e3f4g5") +``` + +### `Style.toMapDesignType()` + +#### Signature +```kotlin +fun Style.toMapDesignType(): MapboxDesignType +``` + +#### Description +An extension function for the Mapbox `Style` class. It converts a `Style` object into its corresponding `MapboxMapDesign` representation by parsing the `styleURI`. This is useful for determining which `MapboxMapDesign` is currently active on the map. + +#### Returns +| Type | Description | +| ------------------ | -------------------------------------------------------------------------------------------------------------------------------------- | +| `MapboxDesignType` | The `MapboxMapDesign` that corresponds to the `Style` object's URI. Returns a `Custom` instance if the URI does not match a predefined style. | + +#### Example +```kotlin +// Assuming 'mapboxMap' is an instance of the MapboxMap object +mapboxMap.getStyle { style -> + // Convert the current map style to a MapboxDesignType + val currentDesign = style.toMapDesignType() + + when (currentDesign) { + is MapboxMapDesign.Dark -> { + // The current map style is Dark + } + is MapboxMapDesign.Custom -> { + // The current map style is a custom one + println("Custom style ID: ${currentDesign.id}") + } + else -> { + // Handle other styles + } + } +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/MapboxMapView.kt.md b/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/MapboxMapView.kt.md new file mode 100644 index 00000000..b96379a8 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/MapboxMapView.kt.md @@ -0,0 +1,148 @@ +# MapboxMapView + +A Jetpack Compose composable that renders an interactive Mapbox map. This is the primary entry point for displaying a map and its contents. It manages the map's lifecycle, state, and user interactions. Map overlays, such as markers, polylines, and polygons, are added declaratively within the `content` lambda. + +## Signature + +```kotlin +@Composable +fun MapboxMapView( + state: MapboxViewState, + modifier: Modifier = Modifier, + markerTiling: MarkerTilingOptions? = null, + sdkInitialize: (suspend (android.content.Context) -> Boolean)? = null, + onMapLoaded: OnMapLoadedHandler? = null, + onMapClick: OnMapEventHandler? = null, + onCameraMoveStart: OnCameraMoveHandler? = null, + onCameraMove: OnCameraMoveHandler? = null, + onCameraMoveEnd: OnCameraMoveHandler? = null, + content: (@Composable MapboxMapViewScope.() -> Unit)? = null, +) +``` + +## Description + +The `MapboxMapView` composable integrates a Mapbox map into your Compose UI. It requires a `MapboxViewState` to manage the map's properties, such as camera position and map style. It also provides various callbacks for map events like loading, camera movement, and clicks. + +Map overlays (e.g., `Marker`, `Polyline`, `Polygon`) are defined within the trailing `content` lambda, which provides a `MapboxMapViewScope`. This declarative approach allows you to tie your UI state directly to the map's content. + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| `state` | `MapboxViewState` | Manages the map's state, including camera position, map style, and provides imperative control over the map. | +| `modifier` | `Modifier` | Standard Jetpack Compose modifier to be applied to the map view. Defaults to `Modifier`. | +| `markerTiling` | `MarkerTilingOptions?` | Optional configuration for marker tiling and clustering. If `null`, default options are used. | +| `sdkInitialize` | `(suspend (Context) -> Boolean)?` | An optional lambda to handle custom initialization of the Mapbox SDK. If not provided, a default initialization is performed. | +| `onMapLoaded` | `OnMapLoadedHandler?` | A callback invoked once the map has finished loading its style and is ready for interaction. | +| `onMapClick` | `OnMapEventHandler?` | A callback invoked when the user clicks on a point on the map that is not an overlay. | +| `onCameraMoveStart` | `OnCameraMoveHandler?` | A callback invoked when the map camera starts moving. The new camera position is provided. | +| `onCameraMove` | `OnCameraMoveHandler?` | A callback invoked continuously while the map camera is moving. The new camera position is provided. | +| `onCameraMoveEnd` | `OnCameraMoveHandler?` | A callback invoked when the map camera finishes moving. The final camera position is provided. | +| `content` | `(@Composable MapboxMapViewScope.() -> Unit)?` | A composable lambda block where map overlays like `Marker`, `Polyline`, and `Polygon` can be declared. It operates within a `MapboxMapViewScope`. | + +## Returns + +This is a composable function that emits UI and does not have a direct return value. + +## Example + +Here's a basic example of how to set up `MapboxMapView` and add a `Marker`. + +```kotlin +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import android.util.Log +import com.mapconductor.core.map.MapCameraPosition +import com.mapconductor.core.types.LatLng +import com.mapconductor.mapbox.MapboxMapView +import com.mapconductor.mapbox.marker.Marker +import com.mapconductor.mapbox.state.rememberMapboxViewState +import com.mapconductor.mapbox.state.rememberMarkerState + +@Composable +fun MyMapScreen() { + // 1. Define the initial camera position. + val cameraPosition = MapCameraPosition( + target = LatLng(37.7749, -122.4194), // San Francisco + zoom = 12.0 + ) + + // 2. Create and remember the map state. + val mapState = rememberMapboxViewState(cameraPosition) + + // 3. Add the MapboxMapView composable to your UI. + MapboxMapView( + state = mapState, + modifier = Modifier.fillMaxSize(), + onMapLoaded = { + Log.d("MyMapScreen", "Map is loaded and ready.") + }, + onMapClick = { latLng -> + Log.d("MyMapScreen", "Map clicked at: $latLng") + } + ) { + // 4. Add map content declaratively within the content lambda. + Marker( + state = rememberMarkerState( + position = LatLng(37.7749, -122.4194) + ), + title = "San Francisco City Hall", + onClick = { + Log.d("MyMapScreen", "Marker clicked!") + // Return true to indicate the event was consumed + true + } + ) + } +} +``` + +--- + +## MapboxMapView (Deprecated) + +This overload is deprecated. It provides direct click and drag event handlers for markers, circles, polylines, and polygons at the map level. + +**Reason for Deprecation:** The recommended approach is to use the `onClick` handlers available on the respective state objects (e.g., `MarkerState`, `CircleState`) or composables (`Marker`, `Circle`). This provides a more idiomatic and scalable pattern in Jetpack Compose by associating behavior directly with the UI element it belongs to. + +### Signature + +```kotlin +@Deprecated("Use CircleState/PolylineState/PolygonState onClick instead.") +@Composable +fun MapboxMapView( + state: MapboxViewState, + modifier: Modifier = Modifier, + markerTiling: MarkerTilingOptions? = null, + sdkInitialize: (suspend (android.content.Context) -> Boolean)? = null, + onMapLoaded: OnMapLoadedHandler? = null, + onMapClick: OnMapEventHandler? = null, + onCameraMoveStart: OnCameraMoveHandler? = null, + onCameraMove: OnCameraMoveHandler? = null, + onCameraMoveEnd: OnCameraMoveHandler? = null, + onMarkerClick: OnMarkerEventHandler?, + onMarkerDragStart: OnMarkerEventHandler? = null, + onMarkerDrag: OnMarkerEventHandler? = null, + onMarkerDragEnd: OnMarkerEventHandler? = null, + onMarkerAnimateStart: OnMarkerEventHandler? = null, + onMarkerAnimateEnd: OnMarkerEventHandler? = null, + onCircleClick: OnCircleEventHandler? = null, + onPolylineClick: OnPolylineEventHandler? = null, + onPolygonClick: OnPolygonEventHandler? = null, + content: (@Composable MapboxMapViewScope.() -> Unit)? = null, +) +``` + +### Deprecated Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| `onMarkerClick` | `OnMarkerEventHandler?` | **Deprecated**. Use the `onClick` lambda on the `Marker` composable instead. | +| `onMarkerDragStart` | `OnMarkerEventHandler?` | **Deprecated**. Drag events are handled via `MarkerState` and its `draggable` property. | +| `onMarkerDrag` | `OnMarkerEventHandler?` | **Deprecated**. Drag events are handled via `MarkerState` and its `draggable` property. | +| `onMarkerDragEnd` | `OnMarkerEventHandler?` | **Deprecated**. Drag events are handled via `MarkerState` and its `draggable` property. | +| `onCircleClick` | `OnCircleEventHandler?` | **Deprecated**. Use the `onClick` lambda on the `Circle` composable instead. | +| `onPolylineClick` | `OnPolylineEventHandler?` | **Deprecated**. Use the `onClick` lambda on the `Polyline` composable instead. | +| `onPolygonClick` | `OnPolygonEventHandler?` | **Deprecated**. Use the `onClick` lambda on the `Polygon` composable instead. | \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/MapboxMapViewControllerImpl.kt.md b/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/MapboxMapViewControllerImpl.kt.md new file mode 100644 index 00000000..85eacfda --- /dev/null +++ b/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/MapboxMapViewControllerImpl.kt.md @@ -0,0 +1,291 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +--- + +# MapboxMapViewController + +## Description + +The `MapboxMapViewController` is the primary class for programmatic interaction with the map. It serves as a central controller for managing map overlays (like markers, polygons, and polylines), handling user gestures and map events, and controlling the map's camera. An instance of this class orchestrates various specialized controllers for different feature types, providing a unified API for map manipulation. + +Since the constructor is `internal`, you will typically obtain an instance of this controller through a map view setup or a factory, rather than creating it directly. + +--- + +## Methods + +### clearOverlays + +**Signature** +```kotlin +suspend fun clearOverlays() +``` + +**Description** +Asynchronously removes all overlays (markers, polylines, polygons, ground images, circles, and raster layers) from the map. + +**Example** +```kotlin +coroutineScope.launch { + mapViewController.clearOverlays() +} +``` + +--- + +### Overlay Composition + +These methods efficiently add a list of new overlays to the map. + +#### compositionMarkers + +**Signature** +```kotlin +suspend fun compositionMarkers(data: List) +``` +**Description** +Adds a collection of markers to the map. + +**Parameters** +| Parameter | Type | Description | +|---|---|---| +| `data` | `List` | A list of `MarkerState` objects to be added to the map. | + +#### Other Composition Methods +Similar methods are available for other overlay types: +- `suspend fun compositionGroundImages(data: List)` +- `suspend fun compositionPolylines(data: List)` +- `suspend fun compositionPolygons(data: List)` +- `suspend fun compositionCircles(data: List)` +- `suspend fun compositionRasterLayers(data: List)` + +--- + +### Overlay Updates + +These methods update a single existing overlay on the map. The overlay is identified by the `id` within its `State` object. + +#### updateMarker + +**Signature** +```kotlin +suspend fun updateMarker(state: MarkerState) +``` +**Description** +Updates an existing marker on the map based on its `id`. If a marker with the same `id` exists, its properties are updated. + +**Parameters** +| Parameter | Type | Description | +|---|---|---| +| `state` | `MarkerState` | The `MarkerState` object containing the updated properties. | + +#### Other Update Methods +Similar methods are available for other overlay types: +- `suspend fun updateGroundImage(state: GroundImageState)` +- `suspend fun updatePolyline(state: PolylineState)` +- `suspend fun updatePolygon(state: PolygonState)` +- `suspend fun updateCircle(state: CircleState)` +- `suspend fun updateRasterLayer(state: RasterLayerState)` + +--- + +### Overlay Existence Checks + +These methods check if a specific overlay exists on the map. + +#### hasMarker + +**Signature** +```kotlin +fun hasMarker(state: MarkerState): Boolean +``` +**Description** +Checks if a marker with the same ID as the provided `MarkerState` exists on the map. + +**Parameters** +| Parameter | Type | Description | +|---|---|---| +| `state` | `MarkerState` | The `MarkerState` object whose ID is used for the check. | + +**Returns** +| Type | Description | +|---|---| +| `Boolean` | Returns `true` if the marker exists, `false` otherwise. | + +#### Other Existence Check Methods +Similar methods are available for other overlay types: +- `fun hasPolyline(state: PolylineState): Boolean` +- `fun hasPolygon(state: PolygonState): Boolean` +- `fun hasCircle(state: CircleState): Boolean` +- `fun hasGroundImage(state: GroundImageState): Boolean` +- `fun hasRasterLayer(state: RasterLayerState): Boolean` + +--- + +### Camera Control + +#### moveCamera + +**Signature** +```kotlin +fun moveCamera(position: MapCameraPosition) +``` +**Description** +Instantly moves the map's camera to the specified position without animation. + +**Parameters** +| Parameter | Type | Description | +|---|---|---| +| `position` | `MapCameraPosition` | The target camera position, including target, zoom, bearing, and tilt. | + +**Example** +```kotlin +val newPosition = MapCameraPosition( + target = GeoPoint(40.7128, -74.0060), // New York City + zoom = 12.0 +) +mapViewController.moveCamera(newPosition) +``` + +#### animateCamera + +**Signature** +```kotlin +fun animateCamera(position: MapCameraPosition, duration: Long) +``` +**Description** +Animates the map's camera from its current position to the specified position over a given duration. This uses a "fly-to" animation that provides a smooth, cinematic transition. + +**Parameters** +| Parameter | Type | Description | +|---|---|---| +| `position` | `MapCameraPosition` | The target camera position. | +| `duration` | `Long` | The duration of the animation in milliseconds. | + +**Example** +```kotlin +val targetPosition = MapCameraPosition( + target = GeoPoint(34.0522, -118.2437), // Los Angeles + zoom = 14.0, + tilt = 30.0 +) +mapViewController.animateCamera(targetPosition, duration = 2000L) // 2-second animation +``` + +--- + +### Map Design and Style + +#### setMapDesignType + +**Signature** +```kotlin +fun setMapDesignType(value: MapboxDesignType) +``` +**Description** +Sets the map's style (e.g., Standard, Satellite, Streets). This operation is asynchronous and will cause the map style to reload, which may re-trigger style-loaded listeners. + +**Parameters** +| Parameter | Type | Description | +|---|---|---| +| `value` | `MapboxDesignType` | The desired map design type to apply. | + +#### setMapDesignTypeChangeListener + +**Signature** +```kotlin +fun setMapDesignTypeChangeListener(listener: MapboxMapDesignTypeChangeHandler) +``` +**Description** +Registers a listener that gets notified whenever the map's design type changes, for example, after a new style has been loaded. + +**Parameters** +| Parameter | Type | Description | +|---|---|---| +| `listener` | `MapboxMapDesignTypeChangeHandler` | A callback function that receives the new `MapboxDesignType` after a style change. | + +--- + +### Advanced Marker Customization + +These methods are for advanced use cases, such as implementing a custom marker rendering or interaction strategy. + +#### createMarkerRenderer + +**Signature** +```kotlin +fun createMarkerRenderer(strategy: MarkerRenderingStrategyInterface): MarkerOverlayRendererInterface +``` +**Description** +Creates a new marker overlay renderer based on a custom rendering strategy. This allows for complete control over how markers are drawn on the map. + +**Parameters** +| Parameter | Type | Description | +|---|---|---| +| `strategy` | `MarkerRenderingStrategyInterface` | The custom strategy defining marker management and rendering logic. | + +**Returns** +| Type | Description | +|---|---| +| `MarkerOverlayRendererInterface` | A new renderer instance configured with the provided strategy. | + +#### createMarkerEventController + +**Signature** +```kotlin +fun createMarkerEventController(controller: StrategyMarkerController, renderer: MarkerOverlayRendererInterface): MarkerEventControllerInterface +``` +**Description** +Creates a new marker event controller that links a strategy controller with a renderer. This is used to define custom marker interaction handling (clicks, drags, etc.). + +**Parameters** +| Parameter | Type | Description | +|---|---|---| +| `controller` | `StrategyMarkerController` | The strategy controller that manages the marker logic. | +| `renderer` | `MarkerOverlayRendererInterface` | The renderer responsible for drawing the markers managed by the controller. | + +**Returns** +| Type | Description | +|---|---| +| `MarkerEventControllerInterface` | A new event controller instance. | + +#### registerMarkerEventController + +**Signature** +```kotlin +fun registerMarkerEventController(controller: MarkerEventControllerInterface) +``` +**Description** +Registers a custom marker event controller with the map view. Once registered, the map will delegate marker-related events to this controller, enabling custom interaction behaviors. + +**Parameters** +| Parameter | Type | Description | +|---|---|---| +| `controller` | `MarkerEventControllerInterface` | The custom event controller to register. | + +--- + +### Deprecated Event Listeners + +The following methods for setting global event listeners are deprecated. It is recommended to set event handlers directly on the state object for each individual overlay (e.g., `MarkerState.onClick`, `PolygonState.onClick`). + +- `setOnCircleClickListener(listener: OnCircleEventHandler?)` + - **Deprecated:** Use `CircleState.onClick` instead. +- `setOnGroundImageClickListener(listener: OnGroundImageEventHandler?)` + - **Deprecated:** Use `GroundImageState.onClick` instead. +- `setOnMarkerDragStart(listener: OnMarkerEventHandler?)` + - **Deprecated:** Use `MarkerState.onDragStart` instead. +- `setOnMarkerDrag(listener: OnMarkerEventHandler?)` + - **Deprecated:** Use `MarkerState.onDrag` instead. +- `setOnMarkerDragEnd(listener: OnMarkerEventHandler?)` + - **Deprecated:** Use `MarkerState.onDragEnd` instead. +- `setOnMarkerAnimateStart(listener: OnMarkerEventHandler?)` + - **Deprecated:** Use `MarkerState.onAnimateStart` instead. +- `setOnMarkerAnimateEnd(listener: OnMarkerEventHandler?)` + - **Deprecated:** Use `MarkerState.onAnimateEnd` instead. +- `setOnMarkerClickListener(listener: OnMarkerEventHandler?)` + - **Deprecated:** Use `MarkerState.onClick` instead. +- `setOnPolylineClickListener(listener: OnPolylineEventHandler?)` + - **Deprecated:** Use `PolylineState.onClick` instead. +- `setOnPolygonClickListener(listener: OnPolygonEventHandler?)` + - **Deprecated:** Use `PolygonState.onClick` instead. \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/MapboxMapViewControllerInterface.kt.md b/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/MapboxMapViewControllerInterface.kt.md new file mode 100644 index 00000000..ce48d983 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/MapboxMapViewControllerInterface.kt.md @@ -0,0 +1,76 @@ +# MapboxMapViewControllerInterface + +## Description + +The `MapboxMapViewControllerInterface` provides a comprehensive interface for controlling and interacting with a Mapbox map view. It serves as a high-level controller that unifies various map functionalities into a single, easy-to-use API. + +This interface extends several capability interfaces, enabling the management of various map elements such as markers, polylines, polygons, circles, ground images, and raster layers. In addition to these common map features, it offers methods specific to the Mapbox implementation, such as changing the map's visual style (design type) and listening for those changes. + +--- + +## Methods + +### setMapDesignType + +Sets or updates the visual design (style) of the map. This allows for dynamically changing the map's appearance, for example, switching between street, satellite, or dark mode styles. + +**Signature** +```kotlin +fun setMapDesignType(value: MapboxDesignType) +``` + +**Parameters** + +| Parameter | Type | Description | +|-----------|--------------------|---------------------------------------------| +| `value` | `MapboxDesignType` | The desired map design type to apply. | + +**Returns** + +This method does not return any value. + +--- + +### setMapDesignTypeChangeListener + +Registers a listener to receive notifications when the map's design type has finished changing. This is useful for performing actions after a new map style has been fully loaded and rendered. + +**Signature** +```kotlin +fun setMapDesignTypeChangeListener(listener: MapboxMapDesignTypeChangeHandler) +``` + +**Parameters** + +| Parameter | Type | Description | +|-----------|-----------------------------------|------------------------------------------------------------------------------------| +| `listener` | `MapboxMapDesignTypeChangeHandler` | An object that implements the `MapboxMapDesignTypeChangeHandler` to handle the change event. | + +**Returns** + +This method does not return any value. + +--- + +## Example + +The following example demonstrates how to set a listener for map design changes and then trigger a change to a new design type. + +```kotlin +// Assume 'mapboxController' is an instance of MapboxMapViewControllerInterface +lateinit var mapboxController: MapboxMapViewControllerInterface + +// 1. Register a listener to be notified when the map style has finished loading. +mapboxController.setMapDesignTypeChangeListener(object : MapboxMapDesignTypeChangeHandler { + override fun onMapDesignTypeChanged(newType: MapboxDesignType) { + // This block is executed after the new map style is fully loaded. + println("Map design successfully changed to: ${newType.name}") + // You can now safely interact with the new map style. + } +}) + +// 2. Set the map to a new design type (e.g., Satellite). +// This will trigger the listener's onMapDesignTypeChanged method upon completion. +println("Changing map design to Satellite...") +mapboxController.setMapDesignType(MapboxDesignType.SATELLITE) +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/MapboxMapViewHolderImpl.kt.md b/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/MapboxMapViewHolderImpl.kt.md new file mode 100644 index 00000000..a8123250 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/MapboxMapViewHolderImpl.kt.md @@ -0,0 +1,193 @@ +# MapboxMapViewHolder + +A view holder class that encapsulates Mapbox's `MapView` and `MapboxMap` objects. It acts as a bridge between the MapConductor core library and the Mapbox SDK, providing essential functionalities like coordinate transformations and lifecycle management. + +This class implements `MapViewHolderInterface` and `MapboxLifecycleObserver`. Upon initialization, it automatically registers itself as a lifecycle observer for the provided `MapView`. + +```kotlin +class MapboxMapViewHolder( + override val mapView: MapView, + override val map: MapboxMap, +) : MapViewHolderInterface, + MapboxLifecycleObserver +``` + +## Constructor + +### Signature + +```kotlin +MapboxMapViewHolder(mapView: MapView, map: MapboxMap) +``` + +### Description + +Creates an instance of `MapboxMapViewHolder`. + +### Parameters + +| Parameter | Type | Description | +| :-------- | :---------- | :---------------------------------------------- | +| `mapView` | `MapView` | The Mapbox `MapView` instance. | +| `map` | `MapboxMap` | The `MapboxMap` instance associated with the `mapView`. | + +--- + +## Functions + +### toScreenOffset + +#### Signature + +```kotlin +override fun toScreenOffset(position: GeoPointInterface): Offset? +``` + +#### Description + +Converts a geographical coordinate (`GeoPointInterface`) to a screen pixel `Offset` relative to the top-left corner of the map view. + +#### Parameters + +| Parameter | Type | Description | +| :--------- | :------------------ | :------------------------------- | +| `position` | `GeoPointInterface` | The geographical point to convert. | + +#### Returns + +| Type | Description | +| :--------- | :----------------------------------------------------------------------- | +| `Offset?` | The corresponding `Offset` on the screen, or `null` if the conversion fails. | + +--- + +### fromScreenOffsetSync + +#### Signature + +```kotlin +override fun fromScreenOffsetSync(offset: Offset): GeoPoint? +``` + +#### Description + +Synchronously converts a screen pixel `Offset` to its corresponding geographical coordinate (`GeoPoint`). + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :------- | :--------------------------- | +| `offset` | `Offset` | The screen offset to convert. | + +#### Returns + +| Type | Description | +| :---------- | :----------------------------------------------------------------------- | +| `GeoPoint?` | The corresponding `GeoPoint`, or `null` if the conversion fails. | + +--- + +### fromScreenOffset (from ScreenCoordinate) + +#### Signature + +```kotlin +fun fromScreenOffset(coordinate: ScreenCoordinate): GeoPoint? +``` + +#### Description + +Converts a Mapbox `ScreenCoordinate` object to its corresponding geographical coordinate (`GeoPoint`). + +#### Parameters + +| Parameter | Type | Description | +| :----------- | :----------------- | :-------------------------------- | +| `coordinate` | `ScreenCoordinate` | The `ScreenCoordinate` to convert. | + +#### Returns + +| Type | Description | +| :---------- | :----------------------------------------------------------------------- | +| `GeoPoint?` | The corresponding `GeoPoint`, or `null` if the conversion fails. | + +--- + +### fromScreenOffset (from Offset) + +#### Signature + +```kotlin +override suspend fun fromScreenOffset(offset: Offset): GeoPoint? +``` + +#### Description + +Asynchronously converts a screen pixel `Offset` to its corresponding geographical coordinate (`GeoPoint`). This is the suspend function override from the `MapViewHolderInterface`. + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :------- | :--------------------------- | +| `offset` | `Offset` | The screen offset to convert. | + +#### Returns + +| Type | Description | +| :---------- | :----------------------------------------------------------------------- | +| `GeoPoint?` | The corresponding `GeoPoint`, or `null` if the conversion fails. | + +--- + +### Lifecycle Methods + +The following methods are part of the `MapboxLifecycleObserver` interface. They are automatically called by the Mapbox SDK's lifecycle plugin. In this class, they are currently empty implementations (no-op). + +- `onDestroy()` +- `onLowMemory()` +- `onStart()` +- `onStop()` + +--- + +## Example + +```kotlin +import androidx.compose.ui.geometry.Offset +import com.mapbox.maps.MapView +import com.mapbox.maps.MapboxMap +import com.mapconductor.core.features.GeoPoint +import kotlinx.coroutines.runBlocking + +// Assume mapView and mapboxMap are initialized instances +// val mapView: MapView = ... +// val mapboxMap: MapboxMap = ... + +// 1. Create an instance of MapboxMapViewHolder +val mapViewHolder = MapboxMapViewHolder(mapView, mapboxMap) + +// 2. Convert a geographical point to a screen offset +val geoPoint = GeoPoint(latitude = 40.7128, longitude = -74.0060) // New York City +val screenOffset = mapViewHolder.toScreenOffset(geoPoint) + +screenOffset?.let { + println("Screen offset for NYC: (${it.x}, ${it.y})") +} + +// 3. Convert a screen offset back to a geographical point +val someOffset = Offset(x = 500f, y = 300f) + +// Using the synchronous method +val convertedGeoPointSync = mapViewHolder.fromScreenOffsetSync(someOffset) +convertedGeoPointSync?.let { + println("GeoPoint from sync conversion: Lat ${it.latitude}, Lon ${it.longitude}") +} + +// Using the suspend method +runBlocking { + val convertedGeoPointAsync = mapViewHolder.fromScreenOffset(someOffset) + convertedGeoPointAsync?.let { + println("GeoPoint from async conversion: Lat ${it.latitude}, Lon ${it.longitude}") + } +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/MapboxMapViewScope.kt.md b/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/MapboxMapViewScope.kt.md new file mode 100644 index 00000000..e8982d40 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/MapboxMapViewScope.kt.md @@ -0,0 +1,33 @@ +# MapboxMapViewScope + +## Signature +```kotlin +class MapboxMapViewScope : MapViewScope() +``` + +## Description +Provides a dedicated scope for interacting with a Mapbox map instance within the Map conductor framework. + +This class extends the base `MapViewScope`, inheriting all common map functionalities like camera control, marker management, and UI settings. Its primary purpose is to serve as an extension point for features and APIs that are unique to the Mapbox Maps SDK. + +When you are working within the context of a Mapbox map, you will be provided with an instance of `MapboxMapViewScope`. This allows you to access both the shared functionalities from `MapViewScope` and any Mapbox-specific methods that are defined within this class. + +## Example +The `MapboxMapViewScope` is typically accessed within the lambda of a map initialization block, such as `MapConductor.showMap`. This provides the correct context to interact with the map. + +```kotlin +// Assume MapConductor is configured to use Mapbox +MapConductor.showMap { + // `this` refers to MapboxMapViewScope + + // You can call common functions from the parent MapViewScope + moveCamera( + target = LatLng(35.681236, 139.767125), // Tokyo Station + zoom = 15.0 + ) + + // You can also call Mapbox-specific functions defined in this class. + // For example (hypothetical): + // setMapboxStyle("mapbox://styles/mapbox/streets-v11") +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/MapboxPaddings.kt.md b/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/MapboxPaddings.kt.md new file mode 100644 index 00000000..82bbb338 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/MapboxPaddings.kt.md @@ -0,0 +1,128 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +--- + +# Mapbox Paddings Utilities + +This document provides detailed information about the `MapboxPaddings` data class, its associated interface, and utility functions. These components are designed to manage map padding within a Mapbox environment, facilitating conversion between the core `MapPaddingsInterface` and Mapbox's native `EdgeInsets`. + +## `IMapboxPaddingsInterface` + +### Signature +```kotlin +interface IMapboxPaddingsInterface : MapPaddingsInterface +``` + +### Description +An interface that extends the core `MapPaddingsInterface` to include Mapbox-specific functionalities. It ensures that any Mapbox padding implementation can be converted to a native Mapbox `EdgeInsets` object. + +### Methods + +#### `toEdgeInsects()` +Converts the padding data into a Mapbox `EdgeInsets` object. + +**Signature** +```kotlin +fun toEdgeInsects(): EdgeInsets +``` + +**Returns** +| Type | Description | +| :--- | :--- | +| `EdgeInsets` | A Mapbox `EdgeInsets` object representing the same padding values. | + +--- + +## `MapboxPaddings` + +### Signature +```kotlin +data class MapboxPaddings( + override val top: Double, + override val left: Double, + override val bottom: Double, + override val right: Double, +) : MapPaddings(top, left, bottom, right), IMapboxPaddingsInterface +``` + +### Description +A data class that represents the padding on the four sides of the map. It serves as the concrete implementation of `IMapboxPaddingsInterface` for the Mapbox SDK, holding padding values in pixels. + +### Parameters +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `top` | `Double` | The padding from the top edge of the map, in pixels. | +| `left` | `Double` | The padding from the left edge of the map, in pixels. | +| `bottom` | `Double` | The padding from the bottom edge of the map, in pixels. | +| `right` | `Double` | The padding from the right edge of the map, in pixels. | + +### Companion Object + +#### `Zeros` +A predefined `MapboxPaddings` instance with all padding values set to `0.0`. + +**Signature** +```kotlin +val Zeros: MapboxPaddings +``` + +**Example** +```kotlin +// Use the Zeros constant for no padding +val noPadding = MapboxPaddings.Zeros +``` + +#### `from()` +A factory method that creates a `MapboxPaddings` instance from any object implementing `MapPaddingsInterface`. + +**Signature** +```kotlin +fun from(paddings: MapPaddingsInterface? = null): MapboxPaddings? +``` + +**Description** +This function safely converts a generic `MapPaddingsInterface` object into a `MapboxPaddings` object. If the input is already a `MapboxPaddings` instance, it is returned directly. If the input is `null`, it returns `null`. + +**Parameters** +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `paddings` | `MapPaddingsInterface?` | The padding object to convert. Defaults to `null`. | + +**Returns** +| Type | Description | +| :--- | :--- | +| `MapboxPaddings?` | A `MapboxPaddings` instance if the input is not null; otherwise, `null`. | + +--- + +## Extension Functions + +### `EdgeInsets.toPaddings()` + +### Signature +```kotlin +@Keep +fun EdgeInsets.toPaddings(): MapboxPaddings +``` + +### Description +An extension function for the Mapbox `EdgeInsets` class. It provides a convenient way to convert a native `EdgeInsets` object into a `MapboxPaddings` object. + +### Returns +| Type | Description | +| :--- | :--- | +| `MapboxPaddings` | A new `MapboxPaddings` instance with values copied from the `EdgeInsets` object. | + +### Example +```kotlin +import com.mapbox.maps.EdgeInsets +import com.mapconductor.mapbox.toPaddings + +// Given a Mapbox EdgeInsets object +val edgeInsets = EdgeInsets(100.0, 50.0, 20.0, 50.0) + +// Convert it to a MapboxPaddings object +val mapboxPaddings = edgeInsets.toPaddings() + +// Now mapboxPaddings.top is 100.0, mapboxPaddings.left is 50.0, etc. +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/MapboxPolyUtils.kt.md b/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/MapboxPolyUtils.kt.md new file mode 100644 index 00000000..f2f7dbe6 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/MapboxPolyUtils.kt.md @@ -0,0 +1,146 @@ +Excellent! Here is the high-quality SDK documentation for the provided Kotlin code snippet, formatted in Markdown. + +# Mapbox Feature Creation Utilities + +This document provides details on utility functions used to create Mapbox `Feature` objects for lines and polygons from `GeoPointInterface` data. These functions handle geodesic interpolation and antimeridian splitting. + +--- + +## `createMapboxLines` + +Creates a list of Mapbox `Feature` objects representing one or more polylines. + +### Signature +```kotlin +internal fun createMapboxLines( + id: String, + points: List, + geodesic: Boolean, + strokeColor: Color, + strokeWidth: Dp, + zIndex: Int = 0, +): List +``` + +### Description +This function generates a list of Mapbox `Feature` objects that represent a polyline defined by a sequence of geographical points. It can create either geodesic lines (which follow the curvature of the Earth) or linear rhumb lines. + +The function automatically handles cases where the line crosses the antimeridian (180th meridian) by splitting it into multiple `Feature` objects. Each feature is assigned style properties such as stroke color, width, and z-index, which are used by the Mapbox style layer. + +### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `id` | `String` | A unique base identifier for the polyline. This is used to generate unique IDs for each resulting `Feature`. | +| `points` | `List` | A list of geographical points that define the vertices of the polyline. | +| `geodesic` | `Boolean` | If `true`, the line is drawn as a geodesic path (the shortest path on the Earth's surface). If `false`, it's a straight rhumb line. | +| `strokeColor` | `Color` | The color of the polyline's stroke. | +| `strokeWidth` | `Dp` | The width of the polyline's stroke, specified in density-independent pixels (`Dp`). | +| `zIndex` | `Int` | *(Optional)* The drawing order of the line. Lines with a higher `zIndex` are drawn over those with a lower `zIndex`. Defaults to `0`. | + +### Returns +**`List`** + +A list of Mapbox `Feature` objects. If the polyline crosses the antimeridian, this list will contain multiple features, one for each segment. Each feature includes the line geometry and its style properties. + +### Example + +```kotlin +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp +import com.mapconductor.core.features.GeoPoint + +// 1. Define the points for the polyline +val linePoints = listOf( + GeoPoint(latitude = 34.0522, longitude = -118.2437), // Los Angeles + GeoPoint(latitude = 40.7128, longitude = -74.0060) // New York +) + +// 2. Create the Mapbox line features +val lineFeatures = createMapboxLines( + id = "my-trip-line", + points = linePoints, + geodesic = true, + strokeColor = Color.Blue, + strokeWidth = 4.dp, + zIndex = 2 +) + +// 3. 'lineFeatures' can now be added to a Mapbox source to be displayed on the map. +``` + +--- + +## `createMapboxPolygons` + +Creates a list of Mapbox `Feature` objects representing one or more polygons. + +### Signature +```kotlin +internal fun createMapboxPolygons( + id: String, + points: List, + holes: List> = emptyList(), + geodesic: Boolean, + fillColor: Color, + zIndex: Int, +): List +``` + +### Description +This function generates a list of Mapbox `Feature` objects for a polygon defined by an outer boundary and optional inner holes. It supports both geodesic and linear polygon edges. + +The function automatically closes the polygon's outer ring if the first and last points are not identical. It also handles polygons that cross the antimeridian by splitting them into multiple `Feature` objects. + +**Important:** Holes are only supported for polygons that do **not** cross the antimeridian. If the polygon is split, any provided holes will be ignored. + +### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `id` | `String` | A unique base identifier for the polygon. This is used to generate unique IDs for each resulting `Feature`. | +| `points` | `List` | A list of geographical points defining the outer boundary of the polygon. | +| `holes` | `List>` | *(Optional)* A list of point lists, where each inner list defines a hole within the polygon. Ignored if the polygon crosses the antimeridian. Defaults to `emptyList()`. | +| `geodesic` | `Boolean` | If `true`, the polygon edges are drawn as geodesic paths. If `false`, they are straight rhumb lines. | +| `fillColor` | `Color` | The fill color of the polygon. | +| `zIndex` | `Int` | The drawing order of the polygon. Polygons with a higher `zIndex` are drawn over those with a lower `zIndex`. | + +### Returns +**`List`** + +A list of Mapbox `Feature` objects. If the polygon crosses the antimeridian, this list will contain multiple features. Each feature includes the polygon geometry and its style properties. + +### Example + +```kotlin +import androidx.compose.ui.graphics.Color +import com.mapconductor.core.features.GeoPoint + +// 1. Define the outer boundary of a polygon +val outerRing = listOf( + GeoPoint(37.8, -122.5), + GeoPoint(37.8, -122.4), + GeoPoint(37.7, -122.4), + GeoPoint(37.7, -122.5) +) + +// 2. Define a hole within the polygon +val innerHole = listOf( + GeoPoint(37.78, -122.45), + GeoPoint(37.78, -122.44), + GeoPoint(37.77, -122.44), + GeoPoint(37.77, -122.45) +) + +// 3. Create the Mapbox polygon features +val polygonFeatures = createMapboxPolygons( + id = "bay-area-zone", + points = outerRing, + holes = listOf(innerHole), + geodesic = false, + fillColor = Color.Red.copy(alpha = 0.5f), + zIndex = 1 +) + +// 4. 'polygonFeatures' can now be added to a Mapbox source to be displayed on the map. +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/MapboxTypeAlias.kt.md b/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/MapboxTypeAlias.kt.md new file mode 100644 index 00000000..35a202e9 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/MapboxTypeAlias.kt.md @@ -0,0 +1,85 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +# Mapbox Type Aliases + +This document outlines the type aliases used within the MapConductor Mapbox provider. These aliases serve as an abstraction layer, mapping generic MapConductor concepts to the specific underlying types required by the Mapbox SDK. This allows for a consistent internal API while interacting with Mapbox-specific objects. + +--- + +### `MapboxActualMarker` + +A type alias representing the underlying Mapbox object for a single marker. + +**Signature** +```kotlin +typealias MapboxActualMarker = Feature +``` + +**Description** +This alias maps to a `com.mapbox.geojson.Feature`. In the Mapbox SDK, individual markers are typically represented as `Feature` objects with a `Point` geometry and associated properties for styling and metadata. This alias standardizes the representation of a marker within the MapConductor framework. + +**Underlying Type**: `com.mapbox.geojson.Feature` + +--- + +### `MapboxActualCircle` + +A type alias representing the underlying Mapbox object for a circle. + +**Signature** +```kotlin +typealias MapboxActualCircle = Feature +``` + +**Description** +Similar to `MapboxActualMarker`, this alias also maps to a `com.mapbox.geojson.Feature`. Circles on a Mapbox map are often implemented as styled `Feature` objects that have a `Point` geometry. The visual representation as a circle is achieved through the style layer properties (e.g., `circle-radius`, `circle-color`). + +**Underlying Type**: `com.mapbox.geojson.Feature` + +--- + +### `MapboxActualPolyline` + +A type alias representing the underlying Mapbox object(s) for a polyline. + +**Signature** +```kotlin +typealias MapboxActualPolyline = List +``` + +**Description** +This alias maps to a `List`. A polyline can be composed of one or more `Feature` objects. For instance, a simple line is a single `Feature` with a `LineString` geometry, while a more complex polyline with different colored segments or custom start/end caps might be represented as a list of features. + +**Underlying Type**: `List` + +--- + +### `MapboxActualPolygon` + +A type alias representing the underlying Mapbox object(s) for a polygon. + +**Signature** +```kotlin +typealias MapboxActualPolygon = List +``` + +**Description** +This alias maps to a `List`. A polygon is typically represented by a `Feature` with a `Polygon` geometry. Using a `List` allows for more complex representations, such as separating the polygon's fill and its outline (stroke) into two distinct `Feature` objects, each with its own styling. + +**Underlying Type**: `List` + +--- + +### `MapboxActualGroundImage` + +A type alias for a handle that manages a ground image overlay. + +**Signature** +```kotlin +typealias MapboxActualGroundImage = com.mapconductor.mapbox.groundimage.MapboxGroundImageHandle +``` + +**Description** +This alias refers to a custom `MapboxGroundImageHandle` class. This handle acts as a wrapper and manager for a ground image (also known as a ground overlay) on the map. It likely encapsulates the logic for adding, removing, and updating the image source and its geographic coordinates. + +**Underlying Type**: `com.mapconductor.mapbox.groundimage.MapboxGroundImageHandle` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/MapboxViewStateImpl.kt.md b/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/MapboxViewStateImpl.kt.md new file mode 100644 index 00000000..82c79e5d --- /dev/null +++ b/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/MapboxViewStateImpl.kt.md @@ -0,0 +1,126 @@ +Of course! Here is a high-quality SDK document for the provided code snippet, formatted in Markdown. + +# Mapbox Map State SDK + +This document provides detailed documentation for the Mapbox Map State management components, designed for use with Jetpack Compose. The primary entry point is the `rememberMapboxMapViewState` composable function, which creates and manages the state of a Mapbox map view. + +--- + +## `rememberMapboxMapViewState` + +A Jetpack Compose function that creates and remembers a `MapboxViewState` instance. This function is lifecycle-aware and preserves the map's state across recompositions and configuration changes (e.g., screen rotation) by leveraging `rememberSaveable`. + +### Signature + +```kotlin +@Composable +fun rememberMapboxMapViewState( + mapDesign: MapboxDesignType = Standard, + cameraPosition: MapCameraPositionInterface = MapCameraPosition.Default, +): MapboxViewState +``` + +### Description + +Use this composable function within your UI to instantiate a `MapboxViewState` object. This state object can then be passed to a `MapboxMapView` composable (not shown in the snippet) and used to programmatically control the map's camera and style. + +### Parameters + +| Parameter | Type | Description | +|---|---|---| +| `mapDesign` | `MapboxDesignType` | The initial map style to be applied. Defaults to `MapboxMapDesign.Standard`. | +| `cameraPosition` | `MapCameraPositionInterface` | The initial camera position of the map, including location, zoom, tilt, and bearing. Defaults to `MapCameraPosition.Default`. | + +### Returns + +| Type | Description | +|---|---| +| `MapboxViewState` | A state object that holds the map's configuration and provides methods to control it. | + +### Example + +The following example demonstrates how to create a `MapboxViewState` and use it to control the map from a button click. + +```kotlin +import androidx.compose.runtime.Composable +import androidx.compose.material.Button +import androidx.compose.material.Text +import com.mapconductor.core.features.GeoPoint +import com.mapconductor.core.map.MapCameraPosition +import com.mapconductor.mapbox.rememberMapboxMapViewState + +@Composable +fun MyMapScreen() { + // 1. Create and remember the map state. + // The state will be preserved across recompositions and screen rotations. + val mapState = rememberMapboxMapViewState( + cameraPosition = MapCameraPosition( + position = GeoPoint(latitude = 40.7128, longitude = -74.0060), // New York City + zoom = 12.0 + ) + ) + + // 2. Pass the state to your MapView composable (implementation assumed). + // MapboxMapView(state = mapState) + + // 3. Use the state object to control the map programmatically. + Button(onClick = { + val sanFrancisco = GeoPoint(latitude = 37.7749, longitude = -122.4194) + // Animate the camera to a new location over 1.5 seconds. + mapState.moveCameraTo(sanFrancisco, durationMillis = 1500L) + }) { + Text("Go to San Francisco") + } +} +``` + +--- + +## `MapboxViewState` Class + +A state holder and controller for a Mapbox map. It manages the map's camera position and design style and provides methods to manipulate the map view. An instance of this class is created using the `rememberMapboxMapViewState` composable. + +### Properties + +| Property | Type | Description | +|---|---|---| +| `mapDesignType` | `var mapDesignType: MapboxDesignType` | Gets or sets the current design style of the map. Setting this property will update the map's appearance in real-time. | +| `cameraPosition` | `val cameraPosition: MapCameraPosition` | A read-only property representing the current camera position of the map (location, zoom, tilt, bearing). This value is updated automatically as the user interacts with the map. | + +### Methods + +#### `moveCameraTo(position: GeoPoint, ...)` + +Moves the map camera to a specific geographic coordinate, preserving the current zoom, tilt, and bearing. + +**Signature** +```kotlin +fun moveCameraTo( + position: GeoPoint, + durationMillis: Long? = null, +) +``` + +**Parameters** +| Parameter | Type | Description | +|---|---|---| +| `position` | `GeoPoint` | The geographic coordinate (`latitude`, `longitude`) to center the map camera on. | +| `durationMillis` | `Long?` | The duration of the camera animation in milliseconds. If `null` or `0`, the camera moves instantly. Defaults to `null`. | + +#### `moveCameraTo(cameraPosition: MapCameraPosition, ...)` + +Moves the map camera to a new, fully specified camera position. This allows you to change the location, zoom, tilt, and bearing simultaneously. + +**Signature** +```kotlin +fun moveCameraTo( + cameraPosition: MapCameraPosition, + durationMillis: Long? = null, +) +``` + +**Parameters** +| Parameter | Type | Description | +|---|---|---| +| `cameraPosition` | `MapCameraPosition` | The complete target camera state, including position, zoom, tilt, and bearing. | +| `durationMillis` | `Long?` | The duration of the camera animation in milliseconds. If `null` or `0`, the camera moves instantly. Defaults to `null`. | \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/circle/MapboxCircleController.kt.md b/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/circle/MapboxCircleController.kt.md new file mode 100644 index 00000000..53a15e39 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/circle/MapboxCircleController.kt.md @@ -0,0 +1,57 @@ +# MapboxCircleController + +### Signature + +```kotlin +class MapboxCircleController( + override val renderer: MapboxCircleOverlayRenderer, + circleManager: CircleManagerInterface = renderer.circleManager, +) : CircleController(circleManager, renderer) +``` + +### Description + +The `MapboxCircleController` is a specialized controller for managing and rendering circle overlays on a Mapbox map. It serves as a concrete implementation of the generic `CircleController`, bridging the core circle management logic with the Mapbox-specific rendering engine. + +This controller coordinates the state of circle objects (`MapboxActualCircle`) with their visual representation on the map, which is handled by the `MapboxCircleOverlayRenderer`. + +### Parameters + +This documentation describes the parameters for the `MapboxCircleController` constructor. + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `renderer` | `MapboxCircleOverlayRenderer` | The Mapbox-specific renderer responsible for drawing the circles on the map. | +| `circleManager` | `CircleManagerInterface` | **Optional**. The manager responsible for the underlying data and state of the circles. If not provided, it defaults to the `circleManager` instance from the supplied `renderer`. | + +### Example + +The following example demonstrates how to instantiate and use the `MapboxCircleController`. + +```kotlin +// Assume you have a Mapbox MapView and a loaded Style object +// val mapView: MapView = ... +// val mapboxMap: MapboxMap = ... +// val style: Style = ... + +// 1. Create an instance of the Mapbox-specific renderer +val circleRenderer = MapboxCircleOverlayRenderer(mapView, mapboxMap, style) + +// 2. Instantiate the controller with the renderer. +// The circleManager will be implicitly taken from the renderer. +val mapboxCircleController = MapboxCircleController(renderer = circleRenderer) + +// 3. Now you can use the controller to add, remove, or update circles. +// (This example assumes the controller has an `addCircle` method that accepts CircleOptions) +val circleOptions = CircleOptions() + .withLatLng(LatLng(34.0522, -118.2437)) // Los Angeles + .withRadius(1000f) // in meters + .withFillColor(Color.BLUE) + .withFillOpacity(0.5f) + +val circle = mapboxCircleController.addCircle(circleOptions) + +// You can later update the circle using the controller +circle.radius = 1500f +mapboxCircleController.updateCircle(circle) +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/circle/MapboxCircleLayer.kt.md b/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/circle/MapboxCircleLayer.kt.md new file mode 100644 index 00000000..82b3aa15 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/circle/MapboxCircleLayer.kt.md @@ -0,0 +1,162 @@ +### `MapboxCircleLayer` Class + +#### Description + +The `MapboxCircleLayer` class encapsulates the creation and management of a Mapbox `CircleLayer` and its corresponding `GeoJsonSource`. It is specifically designed to render circles on a map that maintain a constant real-world radius (in meters) regardless of the map's zoom level. This is achieved by using a complex Mapbox expression that dynamically calculates the pixel radius based on the current zoom. + +This class simplifies the process of adding data-driven, realistically-scaled circles to your map. + +#### Constructor + +##### Signature + +```kotlin +MapboxCircleLayer( + val sourceId: String, + val layerId: String +) +``` + +##### Description + +Creates a new instance of `MapboxCircleLayer`, initializing the underlying GeoJSON source and circle layer with the provided unique identifiers. + +##### Parameters + +| Parameter | Type | Description | +|------------|----------|----------------------------------------------------| +| `sourceId` | `String` | A unique identifier for the `GeoJsonSource`. | +| `layerId` | `String` | A unique identifier for the `CircleLayer`. | + +--- + +### Properties + +#### `layer` + +The configured Mapbox `CircleLayer` instance. This layer is ready to be added to a `Style`. Its visual properties, such as color, stroke, and radius, are data-driven, meaning they are determined by the properties of the GeoJSON features supplied to the `source`. + +##### Signature + +```kotlin +val layer: CircleLayer +``` + +#### `source` + +The `GeoJsonSource` that provides the data for the `layer`. You must add this source to the map's `Style` before adding the `layer`. The `draw()` method updates the data within this source. + +##### Signature + +```kotlin +val source: GeoJsonSource +``` + +--- + +### Methods + +#### `draw` + +Updates the map by drawing a new set of circles. This method takes a list of circle entities, converts them into a GeoJSON `FeatureCollection`, and applies it to the layer's `GeoJsonSource`. Any previously drawn circles from this layer are replaced. + +##### Signature + +```kotlin +fun draw(entities: List>) +``` + +##### Parameters + +| Parameter | Type | Description | +|------------|-----------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------| +| `entities` | `List>` | A list of entity objects representing the circles to be drawn on the map. Each entity must contain a `MapboxActualCircle` (GeoJSON Feature). | + +--- + +### Nested Objects + +#### `Prop` + +An object that holds constant string keys for the properties of the GeoJSON features. These keys are used to link feature data to the layer's style attributes. When creating the GeoJSON features for your circles, you must add properties using these keys to control their appearance. + +##### Signature + +```kotlin +object Prop +``` + +##### Properties + +| Property | Type | Description | +|-----------------------|----------|---------------------------------------------------------------------------------------------------------| +| `RADIUS` | `String` | The key for the circle's radius in meters. | +| `LATITUDE_CORRECTION` | `String` | The key for the latitude correction factor, used for accurate radius scaling at different latitudes. | +| `FILL_COLOR` | `String` | The key for the circle's fill color (e.g., `"#FF0000"`). | +| `STROKE_COLOR` | `String` | The key for the circle's stroke (outline) color. | +| `STROKE_WIDTH` | `String` | The key for the circle's stroke width in pixels. | +| `Z_INDEX` | `String` | The key for the circle's sort key, which influences rendering order. Higher values are drawn on top. | + +--- + +### Example + +Here is an example of how to initialize `MapboxCircleLayer`, add it to a map, and draw circles. + +```kotlin +import com.mapbox.geojson.Feature +import com.mapbox.geojson.Point +import com.mapbox.maps.Style + +// Assuming you have a MapboxMap instance from your map view +val mapboxMap = getMapboxMap() + +// 1. Initialize the MapboxCircleLayer +val circleLayerManager = MapboxCircleLayer( + sourceId = "my-circle-source", + layerId = "my-circle-layer" +) + +// 2. Create circle data (as GeoJSON Features) +// Note: MapboxActualCircle is assumed to be a type alias or wrapper for com.mapbox.geojson.Feature +val circle1 = Feature.fromGeometry( + Point.fromLngLat(-74.0060, 40.7128) // New York City +).apply { + addStringProperty(MapboxCircleLayer.Prop.FILL_COLOR, "#F74A4A") + addStringProperty(MapboxCircleLayer.Prop.STROKE_COLOR, "#FFFFFF") + addNumberProperty(MapboxCircleLayer.Prop.RADIUS, 1000) // 1000 meters + addNumberProperty(MapboxCircleLayer.Prop.STROKE_WIDTH, 2.0) + addNumberProperty(MapboxCircleLayer.Prop.Z_INDEX, 1) + // Latitude correction factor is 1/cos(latitude). + // For 40.7 degrees, it's 1 / cos(40.7 * PI / 180) ≈ 1.31 + addNumberProperty(MapboxCircleLayer.Prop.LATITUDE_CORRECTION, 1.31) +} + +val circle2 = Feature.fromGeometry( + Point.fromLngLat(139.6917, 35.6895) // Tokyo +).apply { + addStringProperty(MapboxCircleLayer.Prop.FILL_COLOR, "#3E86F0") + addStringProperty(MapboxCircleLayer.Prop.STROKE_COLOR, "#FFFFFF") + addNumberProperty(MapboxCircleLayer.Prop.RADIUS, 1500) // 1500 meters + addNumberProperty(MapboxCircleLayer.Prop.STROKE_WIDTH, 2.0) + addNumberProperty(MapboxCircleLayer.Prop.Z_INDEX, 2) + // For 35.7 degrees, it's 1 / cos(35.7 * PI / 180) ≈ 1.23 + addNumberProperty(MapboxCircleLayer.Prop.LATITUDE_CORRECTION, 1.23) +} + +// Assume CircleEntityInterface has a 'circle' property that returns a Feature +val circleEntities = listOf( + object : CircleEntityInterface { override val circle = circle1 }, + object : CircleEntityInterface { override val circle = circle2 } +) + +// 3. Add the source and layer to the map style +mapboxMap.getStyle { style: Style -> + style.addSource(circleLayerManager.source) + style.addLayer(circleLayerManager.layer) + + // 4. Draw the circles on the map + // This can be called any time you need to update the circle data. + circleLayerManager.draw(circleEntities) +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/circle/MapboxCircleOverlayRenderer.kt.md b/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/circle/MapboxCircleOverlayRenderer.kt.md new file mode 100644 index 00000000..e8bc1c5a --- /dev/null +++ b/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/circle/MapboxCircleOverlayRenderer.kt.md @@ -0,0 +1,160 @@ +# MapboxCircleOverlayRenderer + +The `MapboxCircleOverlayRenderer` class is a concrete implementation of `AbstractCircleOverlayRenderer` designed specifically for the Mapbox Maps SDK. It is responsible for rendering and managing circle overlays on a Mapbox map. This class translates abstract `CircleEntityInterface` objects into visual `Feature` objects that can be displayed on a map layer, handling their creation, updates, and removal. + +## Constructor + +### Signature + +```kotlin +class MapboxCircleOverlayRenderer( + val layer: MapboxCircleLayer = MapboxCircleLayer( + sourceId = "circles-source", + layerId = "circles-layer", + ), + val circleManager: CircleManagerInterface, + override val holder: MapboxMapViewHolder, + override val coroutine: CoroutineScope = CoroutineScope(Dispatchers.Main), +) : AbstractCircleOverlayRenderer() +``` + +### Description + +Initializes a new instance of the `MapboxCircleOverlayRenderer`. This renderer links the circle data managed by a `CircleManagerInterface` to a visual representation on the map using a `MapboxCircleLayer`. + +### Parameters + +| Parameter | Type | Description | +| :-------------- | :--------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------- | +| `layer` | `MapboxCircleLayer` | The layer configuration that defines the source and layer IDs for rendering circles on the map. Defaults to a standard configuration. | +| `circleManager` | `CircleManagerInterface` | The manager that holds the state and collection of all circle entities to be rendered. | +| `holder` | `MapboxMapViewHolder` | The view holder that provides access to the underlying Mapbox `Map` instance. | +| `coroutine` | `CoroutineScope` | The coroutine scope used to launch asynchronous operations, such as drawing circles on the map. Defaults to `CoroutineScope(Dispatchers.Main)`. | + +--- + +## Methods + +### createCircle + +#### Signature + +```kotlin +override suspend fun createCircle(state: CircleState): MapboxActualCircle? +``` + +#### Description + +Asynchronously creates a new Mapbox `Feature` (which is type-aliased as `MapboxActualCircle`) based on the provided `CircleState`. The method constructs a GeoJSON `Feature` with a `Point` geometry and a `JsonObject` containing all the necessary style properties for rendering the circle, such as radius, colors, stroke width, and z-index. It also calculates a latitude correction factor for geodesic circles to ensure accurate rendering at different latitudes. + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :------------ | :------------------------------------------------------------------------------------------------------ | +| `state` | `CircleState` | An object containing the complete set of properties for the new circle, including its center, radius, and visual styles. | + +#### Returns + +| Type | Description | +| :------------------- | :------------------------------------------------------------------------------------------------------------------------------------- | +| `MapboxActualCircle?` | A Mapbox `Feature` representing the newly created circle. The current implementation always returns a valid feature. | + +### removeCircle + +#### Signature + +```kotlin +override suspend fun removeCircle(entity: CircleEntityInterface) +``` + +#### Description + +Asynchronously removes a circle's corresponding `Feature` from the map's GeoJSON source. The feature to be removed is identified using a unique ID derived from the `CircleEntityInterface`'s state. + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :--------------------------------------- | :------------------------------------------------------------------------------------------------------ | +| `entity` | `CircleEntityInterface` | The circle entity to be removed from the map. The renderer uses its ID to find and delete the feature. | + +### updateCircleProperties + +#### Signature + +```kotlin +override suspend fun updateCircleProperties( + circle: MapboxActualCircle, + current: CircleEntityInterface, + prev: CircleEntityInterface, +): MapboxActualCircle? +``` + +#### Description + +Asynchronously updates the properties of an existing circle. This method functions by creating a completely new `Feature` with the updated properties from the `current` entity state. This new feature is intended to replace the old one in the GeoJSON source, ensuring the visual representation on the map reflects the latest state. + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :--------------------------------------- | :------------------------------------------------------------------------------------------------------ | +| `circle` | `MapboxActualCircle` | The existing Mapbox `Feature` that is being updated. | +| `current` | `CircleEntityInterface` | The circle entity containing the new, updated state. | +| `prev` | `CircleEntityInterface` | The circle entity representing the state before the update. | + +#### Returns + +| Type | Description | +| :------------------- | :------------------------------------------------------------------------------------------------------------------------------------- | +| `MapboxActualCircle?` | A new Mapbox `Feature` containing the updated properties for the circle. | + +### onPostProcess + +#### Signature + +```kotlin +override suspend fun onPostProcess() +``` + +#### Description + +A lifecycle method invoked after all individual create, update, and remove operations in a given cycle have been processed. It retrieves the complete list of current circle features from the `circleManager` and launches a coroutine to redraw them on the `MapboxCircleLayer`, applying all changes to the map in a single, batched update. + +--- + +## Example + +The following example demonstrates how to instantiate the `MapboxCircleOverlayRenderer`. In a real-world application, this renderer would typically be managed by a higher-level component that orchestrates map interactions. + +```kotlin +import com.mapconductor.mapbox.circle.MapboxCircleLayer +import com.mapconductor.mapbox.circle.MapboxCircleOverlayRenderer +import com.mapconductor.core.circle.CircleManagerInterface +import com.mapconductor.mapbox.MapboxActualCircle +import com.mapconductor.mapbox.MapboxMapViewHolder +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers + +// Assume these dependencies are provided by your application's architecture +val circleManager: CircleManagerInterface = getCircleManager() +val mapViewHolder: MapboxMapViewHolder = getMapViewHolder() +val mainScope = CoroutineScope(Dispatchers.Main) + +// 1. Define a custom layer configuration (optional) +val customCircleLayer = MapboxCircleLayer( + sourceId = "my-app-circles-source", + layerId = "my-app-circles-layer" +) + +// 2. Instantiate the renderer +val circleOverlayRenderer = MapboxCircleOverlayRenderer( + layer = customCircleLayer, + circleManager = circleManager, + holder = mapViewHolder, + coroutine = mainScope +) + +// The renderer is now set up. A managing component would typically call its +// methods (createCircle, removeCircle, etc.) in response to data changes. +// For example, adding a new circle to the circleManager would trigger +// a call to circleOverlayRenderer.createCircle(...). +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/groundimage/MapboxGroundImageController.kt.md b/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/groundimage/MapboxGroundImageController.kt.md new file mode 100644 index 00000000..1e648492 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/groundimage/MapboxGroundImageController.kt.md @@ -0,0 +1,62 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +--- + +## `MapboxGroundImageController` +The `MapboxGroundImageController` is a specialized controller for managing `GroundImage` overlays on a Mapbox map. It extends the generic `GroundImageController` and bridges the abstract ground image management logic with the concrete rendering implementation provided by `MapboxGroundImageOverlayRenderer`. + +This controller is responsible for orchestrating the addition, removal, and styling of ground images on the map canvas. + +### Signature +```kotlin +class MapboxGroundImageController( + groundImageManager: GroundImageManagerInterface = GroundImageManager(), + renderer: MapboxGroundImageOverlayRenderer, +) : GroundImageController(groundImageManager, renderer) +``` + +### Parameters +| Parameter | Type | Description | Default | +|---|---|---|---| +| `groundImageManager` | `GroundImageManagerInterface` | The manager responsible for tracking the state and entities of all ground images. | `GroundImageManager()` | +| `renderer` | `MapboxGroundImageOverlayRenderer` | The Mapbox-specific renderer responsible for drawing the ground images on the map. | - | + +--- + +## Functions + +### `reapplyStyle()` +Asynchronously reapplies the style to all managed ground images. + +This method is essential for scenarios where the map's style is changed at runtime. It retrieves the state of all current ground images, instructs the renderer to re-add them to the map, and updates the internal entity registry with the new map objects. This ensures that ground images remain visible and correctly styled after a map style transition. + +Since this is a `suspend` function, it must be called from a coroutine or another suspend function. + +#### Signature +```kotlin +suspend fun reapplyStyle() +``` + +#### Parameters +This method does not take any parameters. + +#### Returns +This method does not return any value. + +#### Example +This function is typically called in response to a map style change event. + +```kotlin +// Assuming 'controller' is an initialized instance of MapboxGroundImageController +// and you are within a coroutine scope (e.g., viewModelScope). + +// Call this method when the Mapbox map's style has been loaded or changed. +viewModelScope.launch { + try { + controller.reapplyStyle() + Log.d("Mapbox", "Ground image styles have been successfully reapplied.") + } catch (e: Exception) { + Log.e("Mapbox", "Failed to reapply ground image styles.", e) + } +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/groundimage/MapboxGroundImageHandle.kt.md b/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/groundimage/MapboxGroundImageHandle.kt.md new file mode 100644 index 00000000..f64b7fb8 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/groundimage/MapboxGroundImageHandle.kt.md @@ -0,0 +1,71 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +--- + +### `MapboxGroundImageHandle` + +A data class that serves as a handle for a ground image overlay on a Mapbox map. + +This class encapsulates all the necessary identifiers and components required to manage a specific ground image instance, including its associated route, version, cache key, and the underlying Mapbox source and layer IDs. + +As a Kotlin `data class`, it automatically provides helpful methods like `equals()`, `hashCode()`, `toString()`, and `copy()`. + +### Signature + +```kotlin +data class MapboxGroundImageHandle( + val routeId: String, + val generation: Long, + val cacheKey: String, + val sourceId: String, + val layerId: String, + val tileProvider: GroundImageTileProvider, +) +``` + +### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `routeId` | `String` | The unique identifier for the route associated with the ground image. | +| `generation` | `Long` | A version number, often a timestamp, used to distinguish between different generations or updates of the same ground image. | +| `cacheKey` | `String` | A unique key used for caching the ground image tiles and associated data. | +| `sourceId` | `String` | The ID of the Mapbox `ImageSource` used to supply the image data for the overlay. | +| `layerId` | `String` | The ID of the Mapbox `RasterLayer` used to render the ground image on the map. | +| `tileProvider` | `GroundImageTileProvider` | The provider instance responsible for fetching and supplying the image tiles for the overlay. | + +### Example + +The following example demonstrates how to create an instance of `MapboxGroundImageHandle`. This handle can then be used by other parts of the application to manage the lifecycle of the ground image on the map. + +```kotlin +import com.mapconductor.core.groundimage.GroundImageTileProvider +import com.mapconductor.mapbox.groundimage.MapboxGroundImageHandle + +// Assume MyTileProvider is a custom implementation of GroundImageTileProvider +class MyTileProvider : GroundImageTileProvider { + // Implementation details for fetching tiles... + // For example, it might fetch tiles from a network source or local storage. +} + +fun createGroundImage() { + val tileProvider = MyTileProvider() + val currentRouteId = "route-sf-to-la-123" + val imageGeneration = System.currentTimeMillis() + + // Create an instance of the handle to represent a new ground image + val groundImageHandle = MapboxGroundImageHandle( + routeId = currentRouteId, + generation = imageGeneration, + cacheKey = "ground-image-cache-$currentRouteId-$imageGeneration", + sourceId = "ground-image-source-$currentRouteId", + layerId = "ground-image-layer-$currentRouteId", + tileProvider = tileProvider + ) + + println("Created ground image handle for route: ${groundImageHandle.routeId}") + + // This handle can now be passed to a manager class that adds, updates, + // or removes the corresponding source and layer from the Mapbox map. +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/groundimage/MapboxGroundImageOverlayRenderer.kt.md b/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/groundimage/MapboxGroundImageOverlayRenderer.kt.md new file mode 100644 index 00000000..9ca3e854 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/groundimage/MapboxGroundImageOverlayRenderer.kt.md @@ -0,0 +1,194 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +--- + +# Class `MapboxGroundImageOverlayRenderer` + +## Description + +The `MapboxGroundImageOverlayRenderer` class is responsible for managing the lifecycle of ground image overlays on a Mapbox map. It handles the creation, updating, and removal of these overlays by interfacing with the Mapbox Maps SDK. + +This renderer operates by creating a `RasterLayer` for each ground image. The image data is served as tiles through a `LocalTileServer`, which this class configures and manages. It efficiently handles updates by detecting changes and applying only the necessary modifications to the map style, such as replacing the tile source or simply adjusting layer opacity. + +This class extends `AbstractGroundImageOverlayRenderer`. + +## Constructor + +### Signature + +```kotlin +class MapboxGroundImageOverlayRenderer( + override val holder: MapboxMapViewHolder, + private val tileServer: LocalTileServer, + override val coroutine: CoroutineScope = CoroutineScope(Dispatchers.Main), +) +``` + +### Description + +Creates a new instance of the `MapboxGroundImageOverlayRenderer`. + +### Parameters + +| Parameter | Type | Description | +| :--------- | :-------------------- | :------------------------------------------------------------------------------------------------------ | +| `holder` | `MapboxMapViewHolder` | The view holder that provides access to the Mapbox `Map` instance. | +| `tileServer` | `LocalTileServer` | The local server used to generate and serve image tiles for the ground overlay's raster source. | +| `coroutine` | `CoroutineScope` | The coroutine scope for executing asynchronous operations. Defaults to `CoroutineScope(Dispatchers.Main)`. | + +## Methods + +### `createGroundImage` + +#### Signature + +```kotlin +override suspend fun createGroundImage(state: GroundImageState): MapboxActualGroundImage? +``` + +#### Description + +Asynchronously creates and displays a new ground image overlay on the map. This method sets up a `GroundImageTileProvider`, registers it with the `LocalTileServer`, and then adds a corresponding raster source and layer to the Mapbox map's style. + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :--------------- | :------------------------------------------------------------------------------------------------------ | +| `state` | `GroundImageState` | An object containing the initial configuration for the ground image, including its ID, bounds, and image. | + +#### Returns + +A `MapboxActualGroundImage` handle for the newly created overlay, which contains identifiers for the map source and layer. Returns `null` if the creation fails. + +--- + +### `updateGroundImageProperties` + +#### Signature + +```kotlin +override suspend fun updateGroundImageProperties( + groundImage: MapboxActualGroundImage, + current: GroundImageEntityInterface, + prev: GroundImageEntityInterface, +): MapboxActualGroundImage? +``` + +#### Description + +Asynchronously updates an existing ground image overlay based on changes between its previous and current states. The method performs an efficient diff to determine what changed: +- If the image content, bounds, or tile size have changed, the underlying tile provider and Mapbox source/layer are recreated. +- If only the opacity has changed, the `raster-opacity` property of the existing layer is updated, which is more performant. +- If no relevant properties have changed, no action is taken. + +#### Parameters + +| Parameter | Type | Description | +| :------------ | :---------------------------------------------------- | :----------------------------------------------------------------------- | +| `groundImage` | `MapboxActualGroundImage` | The current handle for the ground image on the map. | +| `current` | `GroundImageEntityInterface` | The entity representing the new, updated state of the ground image. | +| `prev` | `GroundImageEntityInterface` | The entity representing the previous state, used for comparison. | + +#### Returns + +An updated `MapboxActualGroundImage` handle reflecting the changes. If no properties were changed, the original `groundImage` handle is returned. + +--- + +### `removeGroundImage` + +#### Signature + +```kotlin +override suspend fun removeGroundImage(entity: GroundImageEntityInterface) +``` + +#### Description + +Asynchronously removes a ground image overlay from the map. This method cleans up all associated resources by removing the raster layer and source from the map style and unregistering the tile provider from the `LocalTileServer`. + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :---------------------------------------------------- | :----------------------------------------------------------------- | +| `entity` | `GroundImageEntityInterface` | The entity containing the handle of the ground image to be removed. | + +--- + +## Example + +The following example demonstrates how to instantiate and use the `MapboxGroundImageOverlayRenderer` to manage a ground image on the map. + +```kotlin +import com.mapbox.maps.MapboxMap +import com.mapconductor.core.groundimage.GroundImageState +import com.mapconductor.core.groundimage.GroundImageEntity +import com.mapconductor.mapbox.MapboxMapViewHolder +import com.mapconductor.core.tileserver.LocalTileServer +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import android.graphics.Bitmap +import com.mapbox.geojson.BoundingBox + +// Assume these are provided by your application's context +val mapboxMap: MapboxMap = // ... get your MapboxMap instance +val mapHolder = MapboxMapViewHolder(mapboxMap) +val tileServer = LocalTileServer() +val coroutineScope = CoroutineScope(Dispatchers.Main) + +// 1. Initialize the renderer +val groundImageRenderer = MapboxGroundImageOverlayRenderer( + holder = mapHolder, + tileServer = tileServer, + coroutine = coroutineScope +) + +// 2. Define the state for a new ground image +val imageBitmap: Bitmap = // ... load your image bitmap +val imageBounds: BoundingBox = // ... define the geographic bounds +val initialState = GroundImageState( + id = "unique-image-1", + image = imageBitmap, + bounds = imageBounds, + opacity = 0.8f +) + +// An entity to hold the state and the map-specific handle +var imageEntity: GroundImageEntity? = null + +coroutineScope.launch { + // 3. Create the ground image on the map + val groundImageHandle = groundImageRenderer.createGroundImage(initialState) + if (groundImageHandle != null) { + imageEntity = GroundImageEntity(initialState, groundImageHandle) + println("Ground image created successfully.") + } + + // ... later, you might want to update the image + + val currentEntity = imageEntity ?: return@launch + val previousEntity = currentEntity.copy() // Keep a copy of the old state + + // 4. Define the new state (e.g., change opacity) + val updatedState = currentEntity.state.copy(opacity = 0.5f) + currentEntity.state = updatedState + + // 5. Update the ground image properties on the map + val updatedHandle = groundImageRenderer.updateGroundImageProperties( + groundImage = currentEntity.groundImage, + current = currentEntity, + prev = previousEntity + ) + if (updatedHandle != null) { + currentEntity.groundImage = updatedHandle + println("Ground image updated successfully.") + } + + // ... finally, when it's no longer needed + + // 6. Remove the ground image from the map + groundImageRenderer.removeGroundImage(currentEntity) + println("Ground image removed.") +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/marker/MapboxMarkerController.kt.md b/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/marker/MapboxMarkerController.kt.md new file mode 100644 index 00000000..8e8c3456 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/marker/MapboxMarkerController.kt.md @@ -0,0 +1,203 @@ +Of course! Here is the high-quality SDK documentation for the provided `MapboxMarkerController` code snippet. + +--- + +# MapboxMarkerController + +## Class Description + +The `MapboxMarkerController` is the primary class for managing the lifecycle of markers on a Mapbox map. It serves as the main interface for adding, updating, removing, and finding markers. + +A key feature of this controller is its ability to handle a large number of markers efficiently through an optional tiling mechanism. When the number of markers exceeds a certain threshold, the controller can automatically switch to rendering them as a raster tile layer instead of individual objects. This significantly improves performance by reducing the number of objects managed by the map's renderer. + +The controller also manages marker selection and dragging states, moving selected markers to a dedicated drag layer for smooth user interaction. + +## Creating an Instance + +The `MapboxMarkerController` is instantiated using the `create` factory method within its `companion object`. + +### `create()` + +Creates and initializes a new instance of `MapboxMarkerController`. + +#### Signature + +```kotlin +fun create( + holder: MapboxMapViewHolder, + markerManager: MarkerManager, + markerLayer: MarkerLayer, + dragLayer: MarkerDragLayer, + markerTiling: MarkerTilingOptions = MarkerTilingOptions.Default, +): MapboxMarkerController +``` + +#### Description + +This factory method constructs a `MapboxMarkerController` and wires it up with the necessary Mapbox-specific components and configuration. It initializes the renderer and sets the initial camera position. + +#### Parameters + +| Parameter | Type | Description | +| :-------------- | :-------------------------- | :------------------------------------------------------------------------------------------------------ | +| `holder` | `MapboxMapViewHolder` | The view holder that provides access to the Mapbox map instance and its context. | +| `markerManager` | `MarkerManager` | The manager responsible for the in-memory storage and querying of marker data. | +| `markerLayer` | `MarkerLayer` | The Mapbox layer where standard, non-tiled markers will be rendered. | +| `dragLayer` | `MarkerDragLayer` | The dedicated Mapbox layer for rendering a selected or dragged marker, ensuring it appears above others. | +| `markerTiling` | `MarkerTilingOptions` | (Optional) Configuration options for the marker tiling feature. Defaults to `MarkerTilingOptions.Default`. | + +#### Returns + +| Type | Description | +| :----------------------- | :------------------------------------------- | +| `MapboxMarkerController` | A new, fully initialized `MapboxMarkerController` instance. | + +#### Example + +```kotlin +val markerController = MapboxMarkerController.create( + holder = mapViewHolder, + markerManager = myMarkerManager, + markerLayer = myMapboxMarkerLayer, + dragLayer = myMapboxDragLayer, + markerTiling = MarkerTilingOptions(enabled = true) +) +``` + +## Properties + +### `selectedMarker` + +Gets or sets the currently selected marker. + +#### Signature + +```kotlin +var selectedMarker: MarkerEntityInterface? +``` + +#### Description + +Setting this property to a `MarkerEntityInterface` visually "lifts" the corresponding marker from the standard marker layer to a dedicated `dragLayer`. This is typically done when a user begins to drag a marker. The marker is temporarily removed from the `markerManager` to prevent it from being rendered in two places. + +Setting the property to `null` deselects the current marker, returning it to the standard marker layer and re-registering it with the `markerManager`. + +## Public Functions + +### `setRasterLayerCallback()` + +Sets the callback for receiving updates about the raster layer used for tiled markers. + +#### Signature + +```kotlin +fun setRasterLayerCallback(callback: MarkerTileRasterLayerCallback?) +``` + +#### Description + +This function registers a callback that will be invoked whenever the state of the marker tile raster layer changes (e.g., when it's created, updated, or removed). This is essential for integrating the tiled markers with the map's layer management system. + +**Note:** This method must be called before adding markers if you intend to use the marker tiling feature. + +#### Parameters + +| Parameter | Type | Description | +| :--------- | :---------------------------- | :------------------------------------------------------------------------------------------------------ | +| `callback` | `MarkerTileRasterLayerCallback?` | The callback to be invoked with `RasterLayerState` updates, or `null` to clear the existing callback. | + +### `find()` + +Finds a marker entity at a given geographic position, accounting for touch tolerance. + +#### Signature + +```kotlin +override fun find(position: GeoPointInterface): MarkerEntityInterface? +``` + +#### Description + +This function searches for the nearest marker to the specified `position`. It then checks if the `position` falls within the marker's tappable area, which is defined by the marker's icon size, anchor point, and a system-defined touch tolerance. This makes it ideal for handling user taps on markers. + +#### Parameters + +| Parameter | Type | Description | +| :--------- | :------------------ | :---------------------------------------- | +| `position` | `GeoPointInterface` | The geographic coordinate to search near. | + +#### Returns + +| Type | Description | +| :----------------------------------------- | :----------------------------------------------------------------------- | +| `MarkerEntityInterface?` | The found marker entity, or `null` if no marker is within the touch area. | + +### `add()` + +Adds a list of markers to the map. + +#### Signature + +```kotlin +override suspend fun add(data: List) +``` + +#### Description + +This suspend function processes and adds a list of markers defined by their `MarkerState`. It intelligently decides whether to render markers individually or as part of a tiled raster layer. This decision is based on whether tiling is enabled, the total number of markers, and the properties of each individual marker (e.g., non-draggable markers are candidates for tiling). + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :------------------ | :---------------------------------------- | +| `data` | `List` | A list of `MarkerState` objects to add. | + +### `clear()` + +Removes all markers from the map. + +#### Signature + +```kotlin +override suspend fun clear() +``` + +#### Description + +This suspend function removes all markers currently managed by the controller. It clears both individually rendered markers and removes the tiled marker raster layer if it exists. + +### `update()` + +Updates the state of a single existing marker. + +#### Signature + +```kotlin +override suspend fun update(state: MarkerState) +``` + +#### Description + +This suspend function updates an existing marker identified by `state.id`. For performance, it first checks if the marker's visual representation has changed. If so, it applies the changes. + +The function also handles transitions for a marker between a tiled and non-tiled state. For example, if a marker becomes draggable, it will be moved from the tile layer to the individual marker layer. Conversely, if it ceases to be draggable, it may be moved to the tile layer. + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :------------ | :----------------------------------------------------------------------- | +| `state` | `MarkerState` | The new state for the marker. The `id` field must match an existing marker. | + +### `destroy()` + +Cleans up all resources used by the controller. + +#### Signature + +```kotlin +override fun destroy() +``` + +#### Description + +This function releases all resources, including unregistering tile server endpoints, signaling for the removal of any raster layers, and canceling internal coroutines. It is crucial to call this method when the controller is no longer needed (e.g., in `onDestroy` of a Fragment/Activity) to prevent memory leaks and resource contention. \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/marker/MapboxMarkerEventController.kt.md b/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/marker/MapboxMarkerEventController.kt.md new file mode 100644 index 00000000..fd6d3ea9 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/marker/MapboxMarkerEventController.kt.md @@ -0,0 +1,271 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +# Mapbox Marker Event Controllers + +This document provides a detailed reference for the `MapboxMarkerEventControllerInterface` and its implementations, which are responsible for managing marker interactions and events on a Mapbox map. + +## `MapboxMarkerEventControllerInterface` + +An interface that defines the contract for controlling marker events and interactions within the Mapbox map environment. It provides a unified API for finding markers, handling selection, dispatching user interactions (clicks, drags), and setting up event listeners. + +### Properties + +#### `renderer` +The renderer responsible for drawing markers and overlays on the map. + +**Signature** +```kotlin +val renderer: MapboxMarkerOverlayRenderer +``` + +--- + +### Methods + +#### `find()` +Finds the topmost marker entity at a given geographic position. + +**Signature** +```kotlin +fun find(position: GeoPointInterface): MarkerEntityInterface? +``` + +**Parameters** + +| Parameter | Type | Description | +|------------|---------------------|----------------------------------------------| +| `position` | `GeoPointInterface` | The geographic coordinate to search for a marker at. | + +**Returns** + +`MarkerEntityInterface?`: The found marker entity or `null` if no marker exists at the specified position. + +--- + +#### `getSelectedMarker()` +Retrieves the currently selected marker entity. + +**Signature** +```kotlin +fun getSelectedMarker(): MarkerEntityInterface? +``` + +**Returns** + +`MarkerEntityInterface?`: The currently selected marker entity or `null` if no marker is selected. + +--- + +#### `setSelectedMarker()` +Sets the specified marker entity as the currently selected one. To clear the current selection, pass `null`. + +**Signature** +```kotlin +fun setSelectedMarker(entity: MarkerEntityInterface?) +``` + +**Parameters** + +| Parameter | Type | Description | +|-----------|----------------------------------------------|----------------------------------------------------| +| `entity` | `MarkerEntityInterface?` | The marker entity to select, or `null` to deselect. | + +--- + +#### `dispatchClick()` +Dispatches a marker click event. This is typically invoked by the map's internal gesture handlers when a marker is tapped. + +**Signature** +```kotlin +fun dispatchClick(state: MarkerState) +``` + +**Parameters** + +| Parameter | Type | Description | +|-----------|---------------|-------------------------------------------| +| `state` | `MarkerState` | The state of the marker at the time of the event. | + +--- + +#### `dispatchDragStart()` +Dispatches a marker drag start event. + +**Signature** +```kotlin +fun dispatchDragStart(state: MarkerState) +``` + +**Parameters** + +| Parameter | Type | Description | +|-----------|---------------|-------------------------------------------| +| `state` | `MarkerState` | The state of the marker at the time of the event. | + +--- + +#### `dispatchDrag()` +Dispatches a marker drag event, which is fired continuously as the user drags a marker. + +**Signature** +```kotlin +fun dispatchDrag(state: MarkerState) +``` + +**Parameters** + +| Parameter | Type | Description | +|-----------|---------------|-------------------------------------------| +| `state` | `MarkerState` | The state of the marker at the time of the event. | + +--- + +#### `dispatchDragEnd()` +Dispatches a marker drag end event, fired when the user releases a marker after dragging it. + +**Signature** +```kotlin +fun dispatchDragEnd(state: MarkerState) +``` + +**Parameters** + +| Parameter | Type | Description | +|-----------|---------------|-------------------------------------------| +| `state` | `MarkerState` | The state of the marker at the time of the event. | + +--- + +#### `setClickListener()` +Sets a listener to be invoked when a marker is clicked. + +**Signature** +```kotlin +fun setClickListener(listener: OnMarkerEventHandler?) +``` + +**Parameters** + +| Parameter | Type | Description | +|------------|------------------------|--------------------------------------------------------------------| +| `listener` | `OnMarkerEventHandler?` | The handler to be called on a marker click, or `null` to remove it. | + +--- + +#### `setDragStartListener()` +Sets a listener to be invoked when a marker drag gesture begins. + +**Signature** +```kotlin +fun setDragStartListener(listener: OnMarkerEventHandler?) +``` + +**Parameters** + +| Parameter | Type | Description | +|------------|------------------------|--------------------------------------------------------------------------| +| `listener` | `OnMarkerEventHandler?` | The handler to be called when a marker drag starts, or `null` to remove it. | + +--- + +#### `setDragListener()` +Sets a listener to be invoked repeatedly while a marker is being dragged. + +**Signature** +```kotlin +fun setDragListener(listener: OnMarkerEventHandler?) +``` + +**Parameters** + +| Parameter | Type | Description | +|------------|------------------------|--------------------------------------------------------------------------| +| `listener` | `OnMarkerEventHandler?` | The handler to be called during a marker drag, or `null` to remove it. | + +--- + +#### `setDragEndListener()` +Sets a listener to be invoked when a marker drag gesture ends. + +**Signature** +```kotlin +fun setDragEndListener(listener: OnMarkerEventHandler?) +``` + +**Parameters** + +| Parameter | Type | Description | +|------------|------------------------|------------------------------------------------------------------------| +| `listener` | `OnMarkerEventHandler?` | The handler to be called when a marker drag ends, or `null` to remove it. | + +--- + +#### `setAnimateStartListener()` +Sets a listener to be invoked when a marker animation begins. + +**Signature** +```kotlin +fun setAnimateStartListener(listener: OnMarkerEventHandler?) +``` + +**Parameters** + +| Parameter | Type | Description | +|------------|------------------------|------------------------------------------------------------------------------| +| `listener` | `OnMarkerEventHandler?` | The handler to be called when a marker animation starts, or `null` to remove it. | + +--- + +#### `setAnimateEndListener()` +Sets a listener to be invoked when a marker animation completes. + +**Signature** +```kotlin +fun setAnimateEndListener(listener: OnMarkerEventHandler?) +``` + +**Parameters** + +| Parameter | Type | Description | +|------------|------------------------|----------------------------------------------------------------------------| +| `listener` | `OnMarkerEventHandler?` | The handler to be called when a marker animation ends, or `null` to remove it. | + +--- + +## Implementations + +### `DefaultMapboxMarkerEventController` +A default implementation of `MapboxMarkerEventControllerInterface`. It acts as a simple proxy, delegating all event handling and state management calls to an underlying `MapboxMarkerController`. This is the standard controller for basic marker management. + +### `StrategyMapboxMarkerEventController` +An advanced implementation of `MapboxMarkerEventControllerInterface` designed to work with a `StrategyMarkerController`. This controller provides specialized behavior for marker selection and dragging, such as moving the selected marker to a dedicated "drag layer" for smoother interaction and visual separation. This is typically used in scenarios with complex rendering strategies like marker clustering. + +--- + +## Example + +The following example demonstrates how to use the `MapboxMarkerEventControllerInterface` to set listeners for marker click and drag events. + +```kotlin +// Assume 'mapController' is an instance of your main map controller +// and 'eventController' is obtained from it. +val eventController: MapboxMarkerEventControllerInterface = mapController.markerEventController + +// Set a listener for marker click events +eventController.setClickListener { markerState -> + println("Marker clicked! ID: ${markerState.id}") + // You can now select the marker + val markerEntity = eventController.find(markerState.position) + eventController.setSelectedMarker(markerEntity) + true // Return true to indicate the event was handled +} + +// Set a listener for marker drag end events +eventController.setDragEndListener { markerState -> + println("Marker drag ended at position: ${markerState.position.latitude}, ${markerState.position.longitude}") + true // Event handled +} + +// To remove a listener, pass null +eventController.setClickListener(null) +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/marker/MapboxMarkerOverlayRenderer.kt.md b/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/marker/MapboxMarkerOverlayRenderer.kt.md new file mode 100644 index 00000000..eb3a0e7e --- /dev/null +++ b/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/marker/MapboxMarkerOverlayRenderer.kt.md @@ -0,0 +1,154 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +--- + +# Class `MapboxMarkerOverlayRenderer` + +## Description + +A concrete implementation of `AbstractMarkerOverlayRenderer` for the Mapbox Maps SDK. This class is responsible for rendering and managing marker objects on a Mapbox map. + +It translates abstract `MarkerEntity` objects into a `FeatureCollection` of GeoJSON points, which are then displayed using a `GeoJsonSource` and a `SymbolLayer`. The renderer also handles the complex lifecycle of marker icons, including adding them as images to the map's style, caching them, reference counting their usage, and cleaning them up after a grace period to prevent rendering artifacts. + +## Signature + +```kotlin +class MapboxMarkerOverlayRenderer( + holder: MapboxMapViewHolder, + val markerManager: MarkerManager, + val markerLayer: MarkerLayer, + val dragLayer: MarkerDragLayer, + coroutine: CoroutineScope = CoroutineScope(Dispatchers.Main), +) : AbstractMarkerOverlayRenderer< + MapboxMapViewHolder, + MapboxActualMarker, + > +``` + +## Constructor + +Initializes a new instance of the `MapboxMarkerOverlayRenderer`. + +### Parameters + +| Parameter | Type | Description | +| :-------------- | :--------------------------------- | :------------------------------------------------------------------------------------------------------ | +| `holder` | `MapboxMapViewHolder` | The view holder that provides access to the `MapboxMap` instance. | +| `markerManager` | `MarkerManager` | The manager that holds the state of all markers to be rendered. | +| `markerLayer` | `MarkerLayer` | The layer responsible for drawing the markers on the map. | +| `dragLayer` | `MarkerDragLayer` | The layer used to render a marker while it is being dragged. | +| `coroutine` | `CoroutineScope` | The `CoroutineScope` for launching asynchronous operations, defaulting to `CoroutineScope(Dispatchers.Main)`. | + +## Public Functions + +### `onStyleImageMissing` + +A handler that should be invoked when the Mapbox map reports a missing style image. This can happen during style changes or race conditions. The method attempts to re-add the required image to the style from an in-memory cache or by regenerating it from the original marker data. + +#### Signature + +```kotlin +fun onStyleImageMissing(imageId: String) +``` + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :------- | :---------------------------------------------------------------- | +| `imageId` | `String` | The ID of the missing image that Mapbox failed to find in its style. | + +### `ensureStyleImages` + +Re-populates the given Mapbox style with all necessary marker icon images. This method is crucial for restoring markers after the map's style has been reloaded (e.g., switching from a light to a dark theme). It adds the default marker icon and iterates through all existing markers to re-add their custom icons. + +#### Signature + +```kotlin +fun ensureStyleImages(style: com.mapbox.maps.Style) +``` + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :---------------------- | :----------------------------------------------------------------------- | +| `style` | `com.mapbox.maps.Style` | The new or reloaded `Style` instance to which the images should be added. | + +### `redraw` + +Triggers a full redraw of all markers currently managed by the `markerManager`. This is useful for forcing a visual update when the underlying data has changed in a way not automatically tracked by the renderer. + +#### Signature + +```kotlin +fun redraw() +``` + +### `drawDragLayer` + +Renders the dedicated layer for displaying a marker as it is being dragged by the user. This typically involves drawing a single marker at its current drag position. + +#### Signature + +```kotlin +fun drawDragLayer() +``` + +### `setMarkerPosition` + +Updates the geographical position of a specific marker on the map. It modifies the geometry of the corresponding GeoJSON `Feature` and updates the map's data source. + +#### Signature + +```kotlin +override fun setMarkerPosition( + markerEntity: MarkerEntityInterface, + position: GeoPoint, +) +``` + +#### Parameters + +| Parameter | Type | Description | +| :------------- | :---------------------------- | :------------------------------------------------ | +| `markerEntity` | `MarkerEntityInterface` | The marker entity whose position is being updated. | +| `position` | `GeoPoint` | The new `GeoPoint` coordinates for the marker. | + +## Example + +The following conceptual example demonstrates how to instantiate and use `MapboxMarkerOverlayRenderer` within an application. + +```kotlin +// Assuming you have these components initialized from your application's context +val mapboxMap: MapboxMap = /* ... */ +val mapViewHolder = MapboxMapViewHolder(mapboxMap) +val markerManager = MarkerManager() +val coroutineScope = CoroutineScope(Dispatchers.Main) + +// Initialize the marker and drag layers (custom classes within the framework) +val markerLayer = MarkerLayer(mapViewHolder) +val dragLayer = MarkerDragLayer(mapViewHolder) + +// Create the renderer instance +val markerRenderer = MapboxMarkerOverlayRenderer( + holder = mapViewHolder, + markerManager = markerManager, + markerLayer = markerLayer, + dragLayer = dragLayer, + coroutine = coroutineScope +) + +// The renderer is now set up to listen for changes in markerManager +// and render them on the map via markerLayer. + +// It's important to handle map style load events to ensure markers are visible. +mapboxMap.getStyle { style -> + // When the style is loaded or changed, ensure the marker images are present. + markerRenderer.ensureStyleImages(style) +} + +// To handle cases where Mapbox loses an image, you would set up a listener. +// Note: The specific listener may vary based on the Mapbox SDK version. +// mapboxMap.addOnStyleImageMissingListener { event -> +// markerRenderer.onStyleImageMissing(event.id) +// } +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/marker/MarkerDragLayer.kt.md b/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/marker/MarkerDragLayer.kt.md new file mode 100644 index 00000000..ddc8b7eb --- /dev/null +++ b/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/marker/MarkerDragLayer.kt.md @@ -0,0 +1,110 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +--- + +### Class `MarkerDragLayer` + +Manages a dedicated map layer for rendering a single marker during a drag-and-drop operation. This class is responsible for updating the marker's position and redrawing it on the map as it's being moved, providing real-time visual feedback to the user. + +It extends `MarkerLayer` and is designed to work with a specific `GeoJsonSource` that contains only the marker currently being dragged. + +**Constructor** + +```kotlin +MarkerDragLayer( + sourceId: String, + layerId: String +) +``` + +#### Parameters + +| Parameter | Type | Description | +|------------|----------|------------------------------------------------------------------| +| `sourceId` | `String` | The unique identifier for the `GeoJsonSource` associated with this layer. | +| `layerId` | `String` | The unique identifier for the map layer used to render the marker. | + +--- + +### Properties + +#### `selected` + +The marker entity that is currently being dragged. When a user begins to drag a marker, assign the corresponding entity to this property. To clear the drag layer (e.g., when the drag operation is complete), set this property back to `null`. + +**Signature** +```kotlin +var selected: MarkerEntityInterface? = null +``` + +--- + +### Methods + +#### `updatePosition` + +Updates the geographical coordinates of the `selected` marker entity. This method should be called continuously during a drag gesture (e.g., from a touch move event listener) to track the pointer's location. + +**Signature** +```kotlin +fun updatePosition(geoPoint: GeoPoint) +``` + +**Parameters** + +| Parameter | Type | Description | +|------------|------------|-------------------------------------------| +| `geoPoint` | `GeoPoint` | The new geographical point for the marker. | + +**Returns** + +This method does not return any value. + +--- + +#### `draw` + +Redraws the drag layer on the map to reflect the current state of the `selected` marker. This method converts the marker entity into a Mapbox `Feature` and updates the layer's underlying `GeoJsonSource`. If `selected` is `null`, it clears the source, effectively removing the marker from this layer. + +This method should be called after `updatePosition` to render the marker's new position on the map. + +**Signature** +```kotlin +fun draw() +``` + +**Returns** + +This method does not return any value. + +--- + +### Example + +The following example demonstrates the typical lifecycle of using `MarkerDragLayer` to handle a marker drag operation. + +```kotlin +// Assume 'dragLayer' is an initialized instance of MarkerDragLayer. +// 'markerToDrag' is the MarkerEntityInterface the user wants to move. + +// 1. On drag start (e.g., user long-presses a marker) +// Assign the marker to the drag layer to make it "active". +dragLayer.selected = markerToDrag +dragLayer.draw() // Initially draw the marker on the drag layer. + +// 2. On drag move (e.g., in a gesture listener that provides new coordinates) +// 'newGeoPoint' is the current pointer location on the map. +dragLayer.updatePosition(newGeoPoint) +dragLayer.draw() // Redraw the marker at its new position. + +// 3. On drag end (e.g., user lifts their finger) +// You can get the final position from the selected marker if needed. +val finalPosition = dragLayer.selected?.state?.position + +// Update the original marker's data source with the finalPosition. +// ... + +// Clear the drag layer by setting 'selected' to null. +dragLayer.selected = null +dragLayer.draw() // This will remove the marker from the drag layer. +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/marker/MarkerLayer.kt.md b/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/marker/MarkerLayer.kt.md new file mode 100644 index 00000000..f1c17105 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/marker/MarkerLayer.kt.md @@ -0,0 +1,105 @@ +Of course. Here is the high-quality SDK documentation for the provided code snippet, created with your instructions and feedback in mind. + +--- + +# MarkerLayer Class + +## Signature + +```kotlin +open class MarkerLayer( + open val sourceId: String, + open val layerId: String +) +``` + +## Description + +The `MarkerLayer` class encapsulates a Mapbox `SymbolLayer` and its corresponding `GeoJsonSource` to manage and render a collection of markers on a map. It simplifies the process of creating, configuring, and updating a layer of markers by providing pre-configured layer properties and a straightforward method to draw entities. + +This class is `open`, allowing it to be extended for more specialized marker layer behaviors. + +## Constructor + +Creates an instance of `MarkerLayer`. + +### Parameters + +| Parameter | Type | Description | +|------------|----------|-------------------------------------------| +| `sourceId` | `String` | The unique identifier for the GeoJSON source. | +| `layerId` | `String` | The unique identifier for the symbol layer. | + +## Properties + +| Property | Type | Description - `sourceId` | `String` | The unique identifier for the GeoJSON source that provides data to the layer. - `layerId` | `String` | The unique identifier for the symbol layer. - `layer` | `SymbolLayer` | The configured Mapbox `SymbolLayer` instance. This layer is responsible for rendering the marker icons and is pre-configured with properties like `iconAllowOverlap`, `iconIgnorePlacement`, and dynamic `iconOffset`. | +- `source` | `GeoJsonSource` | The `GeoJsonSource` that holds the marker data as a `FeatureCollection`. The `draw()` method updates this source. - + +## Methods + +### draw() + +Updates the layer's GeoJSON source with a new set of markers to be displayed on the map. + +This method filters the input list to include only entities where `visible` is `true` and the `marker` property (a GeoJSON `Feature`) is not `null`. It then creates a `FeatureCollection` from these visible markers and applies it to the `source`. + +#### Signature + +```kotlin +fun draw(entities: List>) +``` + +#### Parameters + +| Parameter | Type | Description | +|------------|---------------------------------------|---------------------------------------------------------------------------------------------------------| +| `entities` | `List>` | A list of marker entities. Only entities that are visible and have a non-null `marker` feature will be drawn. | + +#### Returns + +This method does not return a value. + +## Example + +The following example demonstrates how to instantiate `MarkerLayer`, use it to draw a list of markers, and add the resulting source and layer to a Mapbox style. + +```kotlin +// Assume mapboxMap and style are available +// and MarkerEntity is a class implementing MarkerEntityInterface + +// 1. Create an instance of MarkerLayer +val markerLayer = MarkerLayer( + sourceId = "my-marker-source", + layerId = "my-marker-layer" +) + +// 2. Create some marker entities to draw +// (This is a simplified example; features would typically have geometry and properties) +val markerEntity1 = MarkerEntity( + visible = true, + marker = Feature.fromGeometry(Point.fromLngLat(10.0, 20.0)).apply { + addStringProperty("icon-id", "icon-one") + addNumberProperty("z-index", 1) + } +) +val markerEntity2 = MarkerEntity( + visible = true, + marker = Feature.fromGeometry(Point.fromLngLat(10.1, 20.1)).apply { + addStringProperty("icon-id", "icon-two") + addNumberProperty("z-index", 2) + } +) +val hiddenMarker = MarkerEntity(visible = false, marker = Feature.fromGeometry(Point.fromLngLat(0.0, 0.0))) + +val allEntities = listOf(markerEntity1, markerEntity2, hiddenMarker) + +// 3. Call draw() to update the source with the visible markers +markerLayer.draw(allEntities) + +// 4. Add the source and layer to the map's style +style.addSource(markerLayer.source) +style.addLayer(markerLayer.layer) + +// Now, markerEntity1 and markerEntity2 will be rendered on the map. +// hiddenMarker will be ignored. +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/polygon/MapboxPolygonConductor.kt.md b/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/polygon/MapboxPolygonConductor.kt.md new file mode 100644 index 00000000..24d8e139 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/polygon/MapboxPolygonConductor.kt.md @@ -0,0 +1,207 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +--- + +# MapboxPolygonConductor + +## Class: `MapboxPolygonConductor` + +### Description + +The `MapboxPolygonConductor` class is a controller responsible for managing and rendering polygon overlays on a Mapbox map. It implements the `OverlayControllerInterface` to handle the lifecycle of polygon features, including their filled areas and outlines. + +This conductor uses two distinct renderers: one for the polygon fills (`MapboxPolygonOverlayRenderer`) and another for the polyline outlines (`MapboxPolylineOverlayRenderer`). It orchestrates these renderers to add, update, clear, and find polygons, as well as to handle user interactions like clicks. + +### Signature + +```kotlin +class MapboxPolygonConductor( + val polygonOverlay: MapboxPolygonOverlayRenderer, + val polylineOverlay: MapboxPolylineOverlayRenderer, +) : OverlayControllerInterface< + PolygonState, + PolygonEntityInterface, + PolygonEvent, + > +``` + +### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `polygonOverlay` | `MapboxPolygonOverlayRenderer` | The renderer responsible for drawing the filled area of the polygons. | +| `polylineOverlay` | `MapboxPolylineOverlayRenderer` | The renderer responsible for drawing the outlines (strokes) of the polygons. | + +## Properties + +### zIndex + +The stacking order of this overlay controller on the map. Overlays with higher z-indices are drawn on top of those with lower indices. + +**Signature** +```kotlin +override val zIndex: Int = 2 +``` + +### clickListener + +A callback function that is invoked when any polygon managed by this conductor is clicked. This provides a centralized way to handle click events for all polygons. + +**Signature** +```kotlin +override var clickListener: ((PolygonEvent) -> Unit)? = null +``` + +## Methods + +### add + +Adds a list of polygons to the map. This method performs a diffing operation: it removes any previously added polygons that are not in the new `data` list and then adds or updates the polygons specified in the list. For each polygon, it also creates a corresponding closed polyline to serve as its outline. + +**Signature** +```kotlin +override suspend fun add(data: List) +``` + +**Parameters** + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `data` | `List` | A list of `PolygonState` objects representing the polygons to be displayed on the map. | + +### update + +Adds or updates a single polygon on the map. If a polygon with the same ID already exists, it will be replaced with the new state. A corresponding outline is also created or updated. + +**Signature** +```kotlin +override suspend fun update(state: PolygonState) +``` + +**Parameters** + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `state` | `PolygonState` | The state of the single polygon to add or update. | + +### dispatchClick + +Dispatches a click event. This method is typically called by the underlying map framework when a tap is detected on a polygon. It triggers two callbacks in order: +1. The `onClick` handler defined within the specific `PolygonState` of the clicked polygon. +2. The global `clickListener` set on this `MapboxPolygonConductor` instance. + +**Signature** +```kotlin +fun dispatchClick(event: PolygonEvent) +``` + +**Parameters** + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `event` | `PolygonEvent` | The event object containing details about the click, including the state of the clicked polygon. | + +### find + +Finds the topmost polygon entity at a given geographic coordinate. + +**Signature** +```kotlin +override fun find(position: GeoPointInterface): PolygonEntityInterface? +``` + +**Parameters** + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `position` | `GeoPointInterface` | The geographic coordinates (latitude and longitude) to search at. | + +**Returns** + +| Type | Description | +| :--- | :--- | +| `PolygonEntityInterface?` | The found polygon entity, or `null` if no polygon exists at the specified position. | + +### clear + +Removes all polygons and their associated outlines from the map that are managed by this conductor. + +**Signature** +```kotlin +override suspend fun clear() +``` + +### onCameraChanged + +A lifecycle method called when the map's camera position changes. This implementation is currently empty as no action is required for polygons on camera change. + +**Signature** +```kotlin +override suspend fun onCameraChanged(mapCameraPosition: MapCameraPosition) +``` + +**Parameters** + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `mapCameraPosition` | `MapCameraPosition` | The new camera position of the map. | + +### destroy + +Cleans up resources used by the controller. This implementation is empty as there are no specific native resources to release for polygons. + +**Signature** +```kotlin +override fun destroy() +``` + +## Example + +Here is an example of how to instantiate `MapboxPolygonConductor` and use it to manage polygons on a map. + +```kotlin +// Assume polygonOverlay and polylineOverlay are already initialized +val polygonConductor = MapboxPolygonConductor(polygonOverlay, polylineOverlay) + +// Set a global click listener for all polygons +polygonConductor.clickListener = { event -> + println("Polygon with ID ${event.state.id} was clicked!") +} + +// Define the state for two polygons +val polygon1State = PolygonState( + id = "p1", + points = listOf( + GeoPoint(40.7128, -74.0060), // New York + GeoPoint(34.0522, -118.2437), // Los Angeles + GeoPoint(29.7604, -95.3698) // Houston + ), + fillColor = Color.argb(100, 0, 0, 255), // Semi-transparent blue + strokeColor = Color.BLUE, + strokeWidth = 2f, + onClick = { event -> + println("Specific onClick for polygon ${event.state.id}") + } +) + +val polygon2State = PolygonState( + id = "p2", + points = listOf( + GeoPoint(41.8781, -87.6298), // Chicago + GeoPoint(39.9526, -75.1652), // Philadelphia + GeoPoint(33.4484, -112.0740) // Phoenix + ), + fillColor = Color.argb(100, 255, 0, 0) // Semi-transparent red +) + +// Add the polygons to the map +// This must be called from a coroutine scope +coroutineScope.launch { + polygonConductor.add(listOf(polygon1State, polygon2State)) +} + +// ... later, to remove all polygons +coroutineScope.launch { + polygonConductor.clear() +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/polygon/MapboxPolygonLayer.kt.md b/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/polygon/MapboxPolygonLayer.kt.md new file mode 100644 index 00000000..12cad718 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/polygon/MapboxPolygonLayer.kt.md @@ -0,0 +1,171 @@ +Excellent! Here is the high-quality SDK documentation for the provided code snippet. + +### `MapboxPolygonLayer` Class + +**Description** + +The `MapboxPolygonLayer` class is a controller for managing and rendering a collection of polygons on a Mapbox map. It encapsulates a Mapbox `GeoJsonSource` and a corresponding `FillLayer`, simplifying the process of drawing and styling polygon features. + +The layer is pre-configured to style polygons based on properties within their GeoJSON features. Specifically, it uses the `fillColor` property for the polygon's color and the `zIndex` property to control the rendering order of polygons within the same layer (higher `zIndex` values are drawn on top). + +### Constructor + +**Signature** + +```kotlin +MapboxPolygonLayer( + val sourceId: String, + val layerId: String +) +``` + +**Description** + +Creates a new instance of `MapboxPolygonLayer`. This initializes the underlying `GeoJsonSource` and `FillLayer` with the provided unique identifiers. + +**Parameters** + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `sourceId` | `String` | A unique identifier for the GeoJSON source. | +| `layerId` | `String` | A unique identifier for the `FillLayer`. | + +### `Prop` Object + +**Description** + +A nested object that contains constants for the property keys used within the GeoJSON features to control styling. + +**Properties** + +| Property | Value | Description | +| :--- | :--- | :--- | +| `FILL_COLOR` | `"fillColor"` | The key for the property that defines the polygon's fill color (e.g., `"#FF0000"`). | +| `Z_INDEX` | `"zIndex"` | The key for the property that defines the polygon's rendering order. Higher values are drawn on top. | + +### Properties + +#### `source` + +**Signature** + +```kotlin +val source: GeoJsonSource +``` + +**Description** + +The underlying Mapbox `GeoJsonSource` instance managed by this class. You must add this source to the map's style for the polygons to be available for rendering. + +#### `layer` + +**Signature** + +```kotlin +val layer: FillLayer +``` + +**Description** + +The underlying Mapbox `FillLayer` instance managed by this class. This layer is configured to render data from the `source`. It dynamically sets the `fill-color` and `fill-sort-key` based on the `fillColor` and `zIndex` properties of each feature in the source. You must add this layer to the map's style to make the polygons visible. + +### Methods + +#### `draw` + +**Signature** + +```kotlin +fun draw(entities: List>) +``` + +**Description** + +Updates the GeoJSON source with a new set of polygon features, which causes them to be rendered on the map. This method processes a list of `PolygonEntityInterface` objects, extracts their geometries and properties, and builds a `FeatureCollection` to update the source. + +**Parameters** + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `entities` | `List>` | A list of polygon entities to draw. Each entity must provide its polygon geometry (`MapboxActualPolygon`, which resolves to a list of GeoJSON `Feature`s) and state (including `zIndex`). | + +### Example + +The following example demonstrates how to initialize `MapboxPolygonLayer`, create polygon features, draw them on the map, and add the necessary source and layer to the map's style. + +```kotlin +import com.mapbox.geojson.Feature +import com.mapbox.geojson.Point +import com.mapbox.geojson.Polygon +import com.mapbox.maps.MapView +import com.mapbox.maps.getStyle +import com.mapconductor.core.polygon.PolygonEntityInterface +import com.mapconductor.mapbox.MapboxActualPolygon +import com.mapconductor.mapbox.polygon.MapboxPolygonLayer + +// Assume these data classes and typealias for the example. +// MapboxActualPolygon is a typealias for List. +typealias MapboxActualPolygon = List + +data class PolygonState(val zIndex: Int) +data class MyPolygonEntity( + override val polygon: MapboxActualPolygon, + override val state: PolygonState +) : PolygonEntityInterface + +// --- Usage within your map setup code --- + +// 1. Define unique IDs for the source and layer +val polygonSourceId = "my-polygon-source" +val polygonLayerId = "my-polygon-layer" + +// 2. Create an instance of MapboxPolygonLayer +val polygonLayer = MapboxPolygonLayer(polygonSourceId, polygonLayerId) + +// 3. Add the source and layer to the map style. +// This only needs to be done once when the style is loaded. +val mapboxMap = mapView.getMapboxMap() +mapboxMap.getStyle { style -> + style.addSource(polygonLayer.source) + style.addLayer(polygonLayer.layer) +} + +// 4. Create polygon features to be drawn. +// Each feature MUST have "fillColor" and "zIndex" properties. +val redPolygonFeature = Feature.fromGeometry( + Polygon.fromLngLats(listOf(listOf( + Point.fromLngLat(-122.4, 37.8), + Point.fromLngLat(-122.5, 37.8), + Point.fromLngLat(-122.5, 37.7), + Point.fromLngLat(-122.4, 37.7), + Point.fromLngLat(-122.4, 37.8) + ))) +) +redPolygonFeature.addStringProperty(MapboxPolygonLayer.Prop.FILL_COLOR, "#F44336") // Red +redPolygonFeature.addNumberProperty(MapboxPolygonLayer.Prop.Z_INDEX, 10) + +val bluePolygonFeature = Feature.fromGeometry( + Polygon.fromLngLats(listOf(listOf( + Point.fromLngLat(-122.3, 37.7), + Point.fromLngLat(-122.4, 37.7), + Point.fromLngLat(-122.4, 37.6), + Point.fromLngLat(-122.3, 37.6), + Point.fromLngLat(-122.3, 37.7) + ))) +) +bluePolygonFeature.addStringProperty(MapboxPolygonLayer.Prop.FILL_COLOR, "#2196F3") // Blue +bluePolygonFeature.addNumberProperty(MapboxPolygonLayer.Prop.Z_INDEX, 20) // Higher zIndex, will draw on top + +// 5. Create entity objects from the features +val redEntity = MyPolygonEntity( + polygon = listOf(redPolygonFeature), + state = PolygonState(zIndex = 10) +) +val blueEntity = MyPolygonEntity( + polygon = listOf(bluePolygonFeature), + state = PolygonState(zIndex = 20) +) + +// 6. Call draw() to render the polygons on the map +polygonLayer.draw(listOf(redEntity, blueEntity)) +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/polygon/MapboxPolygonOverlayRenderer.kt.md b/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/polygon/MapboxPolygonOverlayRenderer.kt.md new file mode 100644 index 00000000..7204911b --- /dev/null +++ b/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/polygon/MapboxPolygonOverlayRenderer.kt.md @@ -0,0 +1,207 @@ +Of course! Here is the high-quality SDK documentation for the provided Kotlin code snippet. + +--- + +# Class `MapboxPolygonOverlayRenderer` + +## Description + +Manages the rendering of polygon overlays on a Mapbox map. This class extends `AbstractPolygonOverlayRenderer` and provides a Mapbox-specific implementation for creating, updating, and removing polygons. + +A key feature of this renderer is its handling of polygons with holes. Since native Mapbox vector polygons do not support holes, this class implements a fallback strategy. For polygons with holes, it creates a transparent vector polygon and then overlays a dynamically generated raster tile layer that accurately renders the polygon's shape, including its holes. For simple polygons without holes, it creates a standard filled Mapbox polygon. + +This class is typically instantiated and managed by a higher-level controller and is not intended for direct use by application developers. + +## Signature + +```kotlin +class MapboxPolygonOverlayRenderer( + val layer: MapboxPolygonLayer, + val polygonManager: PolygonManagerInterface, + override val holder: MapboxMapViewHolder, + private val rasterLayerController: MapboxRasterLayerController, + private val tileServer: LocalTileServer = TileServerRegistry.get(forceNoStoreCache = true), + override val coroutine: CoroutineScope = CoroutineScope(Dispatchers.Main), +) : AbstractPolygonOverlayRenderer() +``` + +## Constructor + +Creates an instance of `MapboxPolygonOverlayRenderer`. + +### Parameters + +| Parameter | Type | Description | +| :-------------------- | :---------------------------------------------------- | :------------------------------------------------------------------------------------------------------ | +| `layer` | `MapboxPolygonLayer` | The polygon layer responsible for drawing the polygons on the map. | +| `polygonManager` | `PolygonManagerInterface` | The manager that holds the state of all polygon entities. | +| `holder` | `MapboxMapViewHolder` | The view holder for the Mapbox map instance. | +| `rasterLayerController` | `MapboxRasterLayerController` | The controller used to manage raster layers, essential for rendering polygons with holes. | +| `tileServer` | `LocalTileServer` | *(Optional)* The local tile server used to generate raster tiles for polygons with holes. Defaults to a new instance from `TileServerRegistry`. | +| `coroutine` | `CoroutineScope` | *(Optional)* The coroutine scope for launching asynchronous operations. Defaults to `CoroutineScope(Dispatchers.Main)`. | + +--- + +## Methods + +### `createPolygon` + +Creates a new polygon on the map based on the provided state. + +#### Description + +If the `PolygonState` defines holes, this method employs the raster tile overlay strategy: it creates a transparent vector polygon and overlays a raster tile layer that renders the polygon shape with its holes. If the polygon has no holes, it creates a standard filled Mapbox polygon directly. + +#### Signature + +```kotlin +override suspend fun createPolygon(state: PolygonState): MapboxActualPolygon? +``` + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :------------- | :------------------------------------------- | +| `state` | `PolygonState` | The state object describing the polygon to create. | + +#### Returns + +| Type | Description | +| :-------------------- | :----------------------------------------------------------------------- | +| `MapboxActualPolygon?` | The created `MapboxActualPolygon` instance, or `null` if creation failed. | + +--- + +### `updatePolygonProperties` + +Updates the properties of an existing polygon. + +#### Description + +This method compares the `fingerPrint` of the current and previous polygon states. If the fingerprints differ (indicating a change in properties like points, holes, or color), it triggers a full recreation of the polygon by calling `createPolygon`. Otherwise, it returns the existing polygon instance without modification. + +#### Signature + +```kotlin +override suspend fun updatePolygonProperties( + polygon: MapboxActualPolygon, + current: PolygonEntityInterface, + prev: PolygonEntityInterface, +): MapboxActualPolygon? +``` + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :------------------------------------------- | :---------------------------------------- | +| `polygon` | `MapboxActualPolygon` | The actual Mapbox polygon object on the map. | +| `current` | `PolygonEntityInterface` | The new state of the polygon entity. | +| `prev` | `PolygonEntityInterface` | The previous state of the polygon entity. | + +#### Returns + +| Type | Description | +| :-------------------- | :------------------------------------------------------------------------------------------------------ | +| `MapboxActualPolygon?` | The updated or newly created `MapboxActualPolygon` instance, or the previous polygon if no changes were detected. | + +--- + +### `removePolygon` + +Removes a single polygon entity from the map. + +#### Description + +This method handles the removal of a polygon. It also ensures that any associated raster mask layer, which might have been created for a polygon with holes, is properly cleaned up and removed from the map. + +#### Signature + +```kotlin +override suspend fun removePolygon(entity: PolygonEntityInterface) +``` + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :------------------------------------------- | :----------------------------------- | +| `entity` | `PolygonEntityInterface` | The polygon entity to be removed. | + +--- + +### `onPostProcess` + +Performs actions after a batch of polygon updates has been processed. + +#### Description + +This method is called after a series of updates. It retrieves all current polygon entities from the `PolygonManager` and triggers a single, batched redraw of the entire `MapboxPolygonLayer` to reflect all changes efficiently. + +#### Signature + +```kotlin +override suspend fun onPostProcess() +``` + +--- + +### `onRemove` + +Handles the removal of a batch of polygon entities. + +#### Description + +This method is intended to be called when multiple polygons are removed at once. + +**Note:** The current implementation of this method is empty (the logic is commented out) and does not perform any action. + +#### Signature + +```kotlin +override suspend fun onRemove(data: List>) +``` + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :--------------------------------------------- | :---------------------------------------- | +| `data` | `List>` | A list of polygon entities to be removed. | + +--- + +## Example + +The `MapboxPolygonOverlayRenderer` is an internal component used by a `PolygonController`. Here is a conceptual example of how it might be instantiated within a map setup. + +```kotlin +// Assume these dependencies are already initialized +val mapboxMapViewHolder: MapboxMapViewHolder = ... +val polygonManager: PolygonManagerInterface = ... +val rasterLayerController: MapboxRasterLayerController = ... +val coroutineScope: CoroutineScope = ... + +// 1. Create the layer that will visually contain the polygons +val polygonLayer = MapboxPolygonLayer( + id = "my-polygon-layer", + source = MapboxPolygonSource("my-polygon-source"), + holder = mapboxMapViewHolder +) + +// 2. Instantiate the renderer +val polygonRenderer = MapboxPolygonOverlayRenderer( + layer = polygonLayer, + polygonManager = polygonManager, + holder = mapboxMapViewHolder, + rasterLayerController = rasterLayerController, + coroutine = coroutineScope +) + +// 3. The renderer would then be used by a PolygonController to manage polygons +// For example, adding a polygon would internally call polygonRenderer.createPolygon(state) +val polygonController = PolygonController( + polygonManager = polygonManager, + renderer = polygonRenderer +) + +// Now, using the controller will delegate rendering tasks to our renderer +// polygonController.add(polygonState) +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/polyline/MapboxPolylineController.kt.md b/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/polyline/MapboxPolylineController.kt.md new file mode 100644 index 00000000..441a8d79 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/polyline/MapboxPolylineController.kt.md @@ -0,0 +1,65 @@ +# MapboxPolylineController + +### Signature + +```kotlin +class MapboxPolylineController( + override val renderer: MapboxPolylineOverlayRenderer, + polylineManager: PolylineManagerInterface = renderer.polylineManager, +) : PolylineController(polylineManager, renderer) +``` + +### Description + +The `MapboxPolylineController` is a specialized controller class responsible for managing and rendering polylines on a Mapbox map. It acts as a bridge between the data-handling logic of a `PolylineManagerInterface` and the visual rendering provided by `MapboxPolylineOverlayRenderer`. + +By inheriting from the generic `PolylineController`, it provides a consistent API for polyline manipulation while being specifically tailored for the Mapbox environment. This class simplifies the process of adding, updating, and removing polylines by coordinating the underlying data manager and the renderer. + +### Parameters + +| Parameter | Type | Description | +|---|---|---| +| `renderer` | `MapboxPolylineOverlayRenderer` | The Mapbox-specific renderer instance responsible for drawing the polylines onto the map's style layer. | +| `polylineManager` | `PolylineManagerInterface` | The manager responsible for the underlying data and state of the polylines. If not provided, it defaults to the manager instance associated with the `renderer`, ensuring both components operate on the same data source. | + +### Example + +Here is an example of how to set up and use the `MapboxPolylineController` to draw a polyline between two coordinates on a Mapbox map. + +```kotlin +import android.graphics.Color +import com.mapbox.mapboxsdk.geometry.LatLng +import com.mapbox.mapboxsdk.maps.Style +import com.mapconductor.core.polyline.PolylineOptions +import com.mapconductor.mapbox.polyline.MapboxPolylineOverlayRenderer +import com.mapconductor.mapbox.polyline.MapboxPolylineController + +// Assume 'mapboxStyle' is an instance of com.mapbox.mapboxsdk.maps.Style +// and 'context' is an Android Context. + +// 1. Initialize the Mapbox-specific renderer with the map's style +val polylineRenderer = MapboxPolylineOverlayRenderer(mapboxStyle, context) + +// 2. Create the controller, passing in the renderer. +// The controller will automatically use the renderer's polyline manager by default. +val polylineController = MapboxPolylineController(renderer = polylineRenderer) + +// 3. Define the properties of the polyline you want to add +val polylineOptions = PolylineOptions( + points = listOf( + LatLng(37.7749, -122.4194), // San Francisco + LatLng(34.0522, -118.2437) // Los Angeles + ), + color = Color.BLUE, + width = 5f +) + +// 4. Use the controller to add the polyline to the map. +// The controller delegates this task to its manager and renderer. +val addedPolyline = polylineController.addPolyline(polylineOptions) + +// The polyline is now visible on the map. You can continue to interact +// with it through the controller. +// For example, to remove the polyline: +// polylineController.removePolyline(addedPolyline) +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/polyline/MapboxPolylineLayer.kt.md b/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/polyline/MapboxPolylineLayer.kt.md new file mode 100644 index 00000000..f88d90db --- /dev/null +++ b/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/polyline/MapboxPolylineLayer.kt.md @@ -0,0 +1,171 @@ +# MapboxPolylineLayer + +The `MapboxPolylineLayer` class encapsulates the creation and management of a data-driven polyline layer for Mapbox Maps. It simplifies the process of rendering collections of polylines by managing the underlying `GeoJsonSource` and `LineLayer`, allowing for dynamic styling based on feature properties. + +## Signature + +```kotlin +class MapboxPolylineLayer( + val sourceId: String, + val layerId: String, +) +``` + +## Description + +This class provides a convenient abstraction for displaying polylines on a Mapbox map. Upon instantiation, it creates a `GeoJsonSource` to hold the polyline data and a `LineLayer` to control its visual representation. + +The layer is pre-configured for data-driven styling of color, width, and drawing order (z-index). This means you can control the appearance of each individual polyline by setting properties on its corresponding GeoJSON feature. + +## Constructor + +Initializes a new instance of the `MapboxPolylineLayer`. + +### Parameters + +| Parameter | Type | Description | +| :-------- | :----- | :--------------------------------------------------- | +| `sourceId` | `String` | A unique identifier for the underlying `GeoJsonSource`. | +| `layerId` | `String` | A unique identifier for the `LineLayer`. | + +## Nested Objects + +### `Prop` + +A companion object that holds constant string keys for data-driven styling properties. These keys are used to retrieve style values from the properties of each GeoJSON feature. + +#### Signature + +```kotlin +object Prop +``` + +#### Properties + +| Property | Type | Description | +| :------------- | :----- | :------------------------------------------------------------------------------------------------------ | +| `STROKE_COLOR` | `String` | The key for the feature property that defines the polyline's stroke color (e.g., `"#FF0000"`). | +| `STROKE_WIDTH` | `String` | The key for the feature property that defines the polyline's stroke width in pixels (e.g., `5.0`). | +| `Z_INDEX` | `String` | The key for the feature property that defines the polyline's sort key, controlling its drawing order. | + +## Properties + +### `source` + +The underlying `GeoJsonSource` that stores the polyline data as a `FeatureCollection`. + +#### Signature + +```kotlin +val source: GeoJsonSource +``` + +#### Description + +This source is configured with the `sourceId` provided in the constructor. You can update the polylines displayed on the map by updating this source's data using the `draw()` method. + +### `layer` + +The `LineLayer` responsible for rendering the polylines from the `source`. + +#### Signature + +```kotlin +val layer: LineLayer +``` + +#### Description + +This layer is configured to render data from the `source`. It has the following default and data-driven styles: +- **`lineJoin`**: `LineJoin.ROUND` (for smooth corners) +- **`lineCap`**: `LineCap.ROUND` (for rounded line ends) +- **`lineColor`**: Dynamically set from the feature's `strokeColor` property. +- **`lineWidth`**: Dynamically set from the feature's `strokeWidth` property. +- **`lineSortKey`**: Dynamically set from the feature's `zIndex` property to control stacking order. + +## Methods + +### `draw` + +Updates the GeoJSON source with a new set of polylines, effectively redrawing them on the map. + +#### Signature + +```kotlin +fun draw(entities: List>) +``` + +#### Parameters + +| Parameter | Type | Description - +| `entities` | `List>` | A list of polyline entities to be drawn on the map. This is a generic type with the following components:
- **`PolylineEntityInterface`**: An interface representing a logical polyline, which may consist of one or more visual segments.
- **`MapboxActualPolyline`**: The concrete Mapbox implementation of a polyline, which is a collection of `com.mapbox.geojson.Feature` objects.

The method extracts the underlying `Feature` objects from each entity to update the map source. | + +#### Returns + +`Unit` - This method does not return a value. + +## Example + +The following example demonstrates how to instantiate `MapboxPolylineLayer`, add it to a map, create styled polyline data, and draw it. + +```kotlin +import com.mapbox.geojson.Feature +import com.mapbox.geojson.LineString +import com.mapbox.geojson.Point +import com.mapconductor.core.polyline.PolylineEntityInterface +import com.mapconductor.mapbox.MapboxActualPolyline +import com.mapconductor.mapbox.polyline.MapboxPolylineLayer + +// Assume these type definitions and a concrete class for the example. +// typealias MapboxActualPolyline = List +// +// interface PolylineEntityInterface { +// val polyline: T +// } +// +// data class MyPolyline(override val polyline: MapboxActualPolyline) : PolylineEntityInterface + +fun setupPolylineLayer(style: Style) { + // 1. Define unique IDs for the source and layer + val SOURCE_ID = "my-polyline-source" + val LAYER_ID = "my-polyline-layer" + + // 2. Instantiate the layer manager + val polylineLayer = MapboxPolylineLayer(SOURCE_ID, LAYER_ID) + + // 3. Add the source and layer to the map's style + style.addSource(polylineLayer.source) + style.addLayer(polylineLayer.layer) + + // 4. Create polyline data with properties for styling + // Polyline 1: Red, 5px width, drawn on top + val line1 = LineString.fromLngLats(listOf( + Point.fromLngLat(-122.483696, 37.833818), + Point.fromLngLat(-122.483482, 37.833174) + )) + val feature1 = Feature.fromGeometry(line1).apply { + addStringProperty(MapboxPolylineLayer.Prop.STROKE_COLOR, "#FF0000") // Red + addNumberProperty(MapboxPolylineLayer.Prop.STROKE_WIDTH, 5.0) + addNumberProperty(MapboxPolylineLayer.Prop.Z_INDEX, 2) // Higher value, drawn on top + } + + // Polyline 2: Blue, 8px width, drawn underneath + val line2 = LineString.fromLngLats(listOf( + Point.fromLngLat(-122.483482, 37.833174), + Point.fromLngLat(-122.483396, 37.832700) + )) + val feature2 = Feature.fromGeometry(line2).apply { + addStringProperty(MapboxPolylineLayer.Prop.STROKE_COLOR, "#0000FF") // Blue + addNumberProperty(MapboxPolylineLayer.Prop.STROKE_WIDTH, 8.0) + addNumberProperty(MapboxPolylineLayer.Prop.Z_INDEX, 1) // Lower value, drawn below + } + + // 5. Wrap the features in the required entity structure + val entity1 = MyPolyline(polyline = listOf(feature1)) + val entity2 = MyPolyline(polyline = listOf(feature2)) + val polylineEntities = listOf(entity1, entity2) + + // 6. Call draw to render the polylines on the map + polylineLayer.draw(polylineEntities) +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/polyline/MapboxPolylineOverlayRenderer.kt.md b/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/polyline/MapboxPolylineOverlayRenderer.kt.md new file mode 100644 index 00000000..5aa5652a --- /dev/null +++ b/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/polyline/MapboxPolylineOverlayRenderer.kt.md @@ -0,0 +1,167 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +*** + +# MapboxPolylineOverlayRenderer + +## Class Signature + +```kotlin +class MapboxPolylineOverlayRenderer( + val layer: MapboxPolylineLayer, + val polylineManager: PolylineManagerInterface, + override val holder: MapboxMapViewHolder, + override val coroutine: CoroutineScope = CoroutineScope(Dispatchers.Main), +) : AbstractPolylineOverlayRenderer() +``` + +## Description + +The `MapboxPolylineOverlayRenderer` is a concrete implementation of `AbstractPolylineOverlayRenderer` responsible for rendering and managing polyline overlays on a Mapbox map. It handles the entire lifecycle of a polyline's visual representation, including creation, updates, and removal from the map. + +This renderer interacts with a `MapboxPolylineLayer` to draw polylines as features on a GeoJSON source. When a polyline's properties are updated, this class recreates the underlying map feature to reflect the changes, as direct property mutation is not optimal for Mapbox GL sources. + +### Constructor + +Initializes a new instance of the `MapboxPolylineOverlayRenderer`. + +#### Parameters + +| Parameter | Type | Description | +| ----------------- | -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | +| `layer` | `MapboxPolylineLayer` | The target Mapbox layer where the polylines will be rendered. | +| `polylineManager` | `PolylineManagerInterface` | The manager that holds the state of all polyline entities to be rendered. | +| `holder` | `MapboxMapViewHolder` | A view holder that provides access to the Mapbox map instance. | +| `coroutine` | `CoroutineScope` | The coroutine scope used for launching asynchronous operations, defaulting to `Dispatchers.Main`. | + +## Methods + +### createPolyline + +#### Signature + +```kotlin +override suspend fun createPolyline(state: PolylineState): MapboxActualPolyline? +``` + +#### Description + +Creates the visual representation of a single polyline on the map based on its state. This method is called by the polyline management system when a new polyline is added. It constructs the necessary Mapbox feature(s) to be drawn on the layer. + +The z-index is resolved by first checking `state.zIndex`. If it's `0`, it attempts to use `state.extra` cast as an `Int`. + +#### Parameters + +| Parameter | Type | Description | +| --------- | --------------- | ----------------------------------------- | +| `state` | `PolylineState` | The state object containing all polyline properties like points, color, and width. | + +#### Returns + +| Type | Description | +| ---------------------- | ------------------------------------------------------------------------ | +| `MapboxActualPolyline?` | The newly created Mapbox-specific polyline object, or `null` if creation fails. | + +--- + +### updatePolylineProperties + +#### Signature + +```kotlin +override suspend fun updatePolylineProperties( + polyline: MapboxActualPolyline, + current: PolylineEntityInterface, + prev: PolylineEntityInterface, +): MapboxActualPolyline? +``` + +#### Description + +Updates a polyline's visual properties. For the Mapbox implementation, this method handles updates by completely recreating the polyline's underlying features rather than modifying them in place. It uses the `current` state to generate a new visual representation. + +#### Parameters + +| Parameter | Type | Description | +| ---------- | ---------------------------------------------- | ------------------------------------------------------------------------ | +| `polyline` | `MapboxActualPolyline` | The existing Mapbox polyline object that needs to be updated. | +| `current` | `PolylineEntityInterface`| The entity containing the new, updated state of the polyline. | +| `prev` | `PolylineEntityInterface`| The entity containing the previous state of the polyline before the update. | + +#### Returns + +| Type | Description | +| ---------------------- | ------------------------------------------------------------------------ | +| `MapboxActualPolyline?` | The new Mapbox-specific polyline object created to reflect the updated properties. | + +--- + +### removePolyline + +#### Signature + +```kotlin +override suspend fun removePolyline(entity: PolylineEntityInterface) +``` + +#### Description + +Removes the visual representation of a polyline from the map. It identifies the corresponding features in the `MapboxPolylineLayer`'s source and removes them. + +#### Parameters + +| Parameter | Type | Description | +| --------- | ---------------------------------------------- | ----------------------------------------- | +| `entity` | `PolylineEntityInterface`| The polyline entity to be removed from the map. | + +--- + +### onPostProcess + +#### Signature + +```kotlin +override suspend fun onPostProcess() +``` + +#### Description + +A lifecycle callback that is triggered after a batch of create, update, or remove operations has been processed. This implementation uses the callback to trigger a full redraw of all polylines on the associated `MapboxPolylineLayer`, ensuring the map is in a consistent state. + +--- + +## Example + +Here is an example of how to instantiate and use the `MapboxPolylineOverlayRenderer`. + +```kotlin +import com.mapbox.maps.MapboxMap +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers + +// Assume these are your existing, properly initialized components: +val mapboxMap: MapboxMap = getMyMapboxMap() +val myPolylineLayer: MapboxPolylineLayer = MapboxPolylineLayer("my-polyline-layer-id", mapboxMap) +val myPolylineManager: PolylineManagerInterface = getMyPolylineManager() +val myMapViewHolder: MapboxMapViewHolder = MapboxMapViewHolder(mapboxMap) +val mainScope = CoroutineScope(Dispatchers.Main) + +// Instantiate the renderer +val polylineRenderer = MapboxPolylineOverlayRenderer( + layer = myPolylineLayer, + polylineManager = myPolylineManager, + holder = myMapViewHolder, + coroutine = mainScope +) + +// The renderer would typically be used by a higher-level manager, +// which would call its methods like createPolyline, updatePolylineProperties, etc., +// in response to changes in the application's data model. + +// For example, when a new polyline needs to be drawn, the manager would call: +// val polylineState = PolylineState(...) +// polylineRenderer.createPolyline(polylineState) + +// After processing changes, the manager might call: +// polylineRenderer.onPostProcess() +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/raster/MapboxRasterLayerController.kt.md b/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/raster/MapboxRasterLayerController.kt.md new file mode 100644 index 00000000..a9654204 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/raster/MapboxRasterLayerController.kt.md @@ -0,0 +1,80 @@ +# SDK Documentation: MapboxRasterLayerController + +## `MapboxRasterLayerController` + +**Signature** +```kotlin +class MapboxRasterLayerController( + rasterLayerManager: RasterLayerManagerInterface = RasterLayerManager(), + renderer: MapboxRasterLayerOverlayRenderer, +) : RasterLayerController(rasterLayerManager, renderer) +``` + +### Description + +A controller responsible for managing and rendering raster layers on a Mapbox map. This class extends the generic `RasterLayerController` and integrates it with Mapbox-specific rendering logic. It orchestrates the lifecycle of raster layers, bridging the abstract layer management with the concrete rendering implementation provided by `MapboxRasterLayerOverlayRenderer`. + +### Constructor + +Creates an instance of `MapboxRasterLayerController`. + +#### Parameters + +| Parameter | Type | Description | +|---|---|---| +| `rasterLayerManager` | `RasterLayerManagerInterface` | The manager for raster layer entities. It handles the registration and tracking of layers. Defaults to a new `RasterLayerManager` instance. | +| `renderer` | `MapboxRasterLayerOverlayRenderer` | The Mapbox-specific renderer responsible for drawing the raster layers on the map. | + +--- + +## Methods + +### `reapplyStyle()` + +**Signature** +```kotlin +suspend fun reapplyStyle() +``` + +### Description + +Reloads and re-renders all managed raster layers to apply updated styling. This function is essential for scenarios where layer style properties have been modified but are not automatically reflected on the map. + +It works by: +1. Retrieving the current state of all managed layers. +2. Instructing the renderer to create new visual layers based on these states. +3. Replacing the old layers with the newly created ones in the layer manager. +4. Executing any final post-processing steps required by the renderer. + +As a `suspend` function, it must be called from a coroutine or another `suspend` function. + +### Parameters + +This method does not take any parameters. + +### Returns + +This method does not return a value. + +### Example + +The following example demonstrates how to call `reapplyStyle()` within a coroutine scope after modifying layer properties. + +```kotlin +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking + +// Assume 'mapboxRasterLayerController' is an initialized instance +// of MapboxRasterLayerController. + +// Use a coroutine scope appropriate for your application's lifecycle, +// such as viewModelScope or lifecycleScope. +runBlocking { // Using runBlocking for a simple, self-contained example. + // ... perform some action that requires a style update ... + // For example, changing a global style property that affects all layers. + + println("Applying new style to raster layers...") + mapboxRasterLayerController.reapplyStyle() + println("Style reapplied successfully.") +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/raster/MapboxRasterLayerOverlayRenderer.kt.md b/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/raster/MapboxRasterLayerOverlayRenderer.kt.md new file mode 100644 index 00000000..3a2f88b0 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/raster/MapboxRasterLayerOverlayRenderer.kt.md @@ -0,0 +1,218 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +--- + +# MapboxRasterLayerOverlayRenderer + +## Signature + +```kotlin +class MapboxRasterLayerOverlayRenderer( + private val holder: MapboxMapViewHolder, + override val coroutine: CoroutineScope = CoroutineScope(Dispatchers.Main), +) : RasterLayerOverlayRendererInterface +``` + +## Description + +The `MapboxRasterLayerOverlayRenderer` is responsible for rendering and managing raster tile overlays on a Mapbox map. It acts as a bridge between the abstract `RasterLayerEntityInterface` and the concrete Mapbox SDK, handling the lifecycle of raster layers, including their creation, updates, and removal. + +This renderer translates different types of raster data sources (`UrlTemplate`, `TileJson`, `ArcGisService`) into Mapbox-compatible sources and layers. It also manages the visual properties of layers, such as opacity and visibility, and ensures they are stacked correctly according to their `zIndex`. + +A key feature is its special handling for "marker tile" layers (identified by the `marker-tile-` prefix in their ID), which are strategically placed below map markers to avoid obscuring them. + +## Constructor + +### Signature + +```kotlin +MapboxRasterLayerOverlayRenderer( + holder: MapboxMapViewHolder, + coroutine: CoroutineScope = CoroutineScope(Dispatchers.Main) +) +``` + +### Description + +Initializes a new instance of the `MapboxRasterLayerOverlayRenderer`. + +### Parameters + +| Parameter | Type | Description | +| :---------- | :------------------ | :------------------------------------------------------------------------------------------------------ | +| `holder` | `MapboxMapViewHolder` | The view holder that contains the `MapboxMap` instance where layers will be rendered. | +| `coroutine` | `CoroutineScope` | The coroutine scope used for executing asynchronous operations. Defaults to `CoroutineScope(Dispatchers.Main)`. | + +--- + +# Methods + +## onAdd + +### Signature + +```kotlin +override suspend fun onAdd( + data: List, +): List +``` + +### Description + +Adds a list of new raster layers to the map. For each item in the `data` list, this method creates a corresponding Mapbox `rasterSource` and `rasterLayer`. After adding the layers, it rebuilds the stacking order of all non-marker raster layers to respect their `zIndex` properties. + +### Parameters + +| Parameter | Type | Description | +| :-------- | :---------------------------------------------------------------- | :----------------------------------------------------------------------- | +| `data` | `List` | A list of `AddParamsInterface` objects, each defining a raster layer to add. | + +### Returns + +**Type:** `List` + +A list of `MapboxRasterLayerHandle` objects corresponding to the newly created layers. If a layer fails to be created, its entry in the list will be `null`. + +## onChange + +### Signature + +```kotlin +override suspend fun onChange( + data: List>, +): List +``` + +### Description + +Processes updates for a list of existing raster layers. + +If a layer's `source` has changed, the old Mapbox source and layer are removed and new ones are created. If only properties like `opacity` or `visible` have changed, the existing Mapbox layer is updated in place for better performance. After processing all changes, it rebuilds the stacking order of non-marker raster layers. + +### Parameters + +| Parameter | Type | Description | +| :-------- | :-------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------ | +| `data` | `List>` | A list of `ChangeParamsInterface` objects, each containing the previous and current state of a layer. | + +### Returns + +**Type:** `List` + +A list of `MapboxRasterLayerHandle` objects for the updated layers. + +## onRemove + +### Signature + +```kotlin +override suspend fun onRemove(data: List>) +``` + +### Description + +Removes a list of raster layers from the map. For each layer entity provided, this method removes its associated Mapbox layer and source from the map style. After removal, it rebuilds the stacking order of the remaining non-marker raster layers. + +### Parameters + +| Parameter | Type | Description | +| :-------- | :-------------------------------------------------------- | :---------------------------------------- | +| `data` | `List>` | A list of layer entities to be removed. | + +### Returns + +This function does not return a value. + +## onPostProcess + +### Signature + +```kotlin +override suspend fun onPostProcess() +``` + +### Description + +A lifecycle method from the `RasterLayerOverlayRendererInterface`. In this implementation, it is a no-op and performs no actions. + +### Parameters + +This function does not take any parameters. + +### Returns + +This function does not return a value. + +--- + +# Data Classes + +## MapboxRasterLayerHandle + +### Signature + +```kotlin +data class MapboxRasterLayerHandle( + val sourceId: String, + val layerId: String, +) +``` + +### Description + +A data class that serves as a handle for a raster layer managed by the `MapboxRasterLayerOverlayRenderer`. It encapsulates the unique identifiers for the underlying Mapbox `rasterSource` and `rasterLayer`, which are necessary for interacting with the layer via the Mapbox SDK. + +### Parameters + +| Parameter | Type | Description | +| :--------- | :------- | :---------------------------------------- | +| `sourceId` | `String` | The unique ID for the Mapbox `rasterSource`. | +| `layerId` | `String` | The unique ID for the Mapbox `rasterLayer`. | + +--- + +# Example + +The following example demonstrates how to instantiate the `MapboxRasterLayerOverlayRenderer` and use it to add a new raster layer from a URL template. + +```kotlin +import com.mapconductor.core.raster.* +import com.mapconductor.mapbox.MapboxMapViewHolder +import com.mapconductor.mapbox.raster.MapboxRasterLayerOverlayRenderer +import kotlinx.coroutines.runBlocking + +// Assume 'mapboxMapViewHolder' is an initialized instance of MapboxMapViewHolder +val mapboxMapViewHolder: MapboxMapViewHolder = /* ... */ + +// 1. Instantiate the renderer +val rasterRenderer = MapboxRasterLayerOverlayRenderer(mapboxMapViewHolder) + +// 2. Define the raster layer to be added +val layerState = RasterLayerState( + id = "open-street-map", + source = RasterLayerSource.UrlTemplate( + template = "https://a.tile.openstreetmap.org/{z}/{x}/{y}.png", + tileSize = 256, + attribution = "© OpenStreetMap contributors" + ), + visible = true, + opacity = 0.8f, + zIndex = 1 +) + +// A simple implementation of the AddParamsInterface for the example +data class AddParams(override val state: RasterLayerState) : + RasterLayerOverlayRendererInterface.AddParamsInterface + +val addParams = listOf(AddParams(layerState)) + +// 3. Call onAdd to render the layer on the map +runBlocking { + val handles = rasterRenderer.onAdd(addParams) + if (handles.isNotEmpty() && handles[0] != null) { + println("Successfully added raster layer. Handle: ${handles[0]}") + } else { + println("Failed to add raster layer.") + } +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/zoom/ZoomAltitudeConverter.kt.md b/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/zoom/ZoomAltitudeConverter.kt.md new file mode 100644 index 00000000..1f0e5551 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/zoom/ZoomAltitudeConverter.kt.md @@ -0,0 +1,205 @@ +Of course! Here is the high-quality SDK documentation for the provided Kotlin code snippet. + +--- + +# ZoomAltitudeConverter + +## Class: `ZoomAltitudeConverter` + +A concrete implementation of `AbstractZoomAltitudeConverter` for the Mapbox SDK. This class provides methods to convert between Mapbox zoom levels and camera altitude in meters. The conversion logic accounts for the map's latitude and the camera's tilt angle to provide more accurate results, especially in a 3D perspective view. + +It uses an empirical offset to reconcile the difference between Mapbox's zoom level system and the more common Web Mercator scale math (often associated with Google Maps). + +```kotlin +class ZoomAltitudeConverter( + zoom0Altitude: Double = DEFAULT_ZOOM0_ALTITUDE +) : AbstractZoomAltitudeConverter(zoom0Altitude) +``` + +### Constructor + +#### Signature + +```kotlin +ZoomAltitudeConverter(zoom0Altitude: Double = DEFAULT_ZOOM0_ALTITUDE) +``` + +#### Description + +Creates a new instance of the `ZoomAltitudeConverter`. + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `zoom0Altitude` | `Double` | **Optional**. The camera altitude in meters that corresponds to zoom level 0 at the equator. Defaults to `DEFAULT_ZOOM0_ALTITUDE`. | + +--- + +## Companion Object Methods + +### `mapboxZoomToGoogleZoom` + +#### Signature + +```kotlin +fun mapboxZoomToGoogleZoom(mapboxZoom: Double): Double +``` + +#### Description + +Converts a Mapbox SDK zoom level to an equivalent Google Maps-style zoom level. This is achieved by adding a constant offset (`MAPBOX_TO_GOOGLE_ZOOM_OFFSET`). The result is clamped within the valid zoom range. + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `mapboxZoom` | `Double` | The zoom level from the Mapbox SDK. | + +#### Returns + +`Double` - The equivalent Google Maps-style zoom level. + +--- + +### `googleZoomToMapboxZoom` + +#### Signature + +```kotlin +fun googleZoomToMapboxZoom(googleZoom: Double): Double +``` + +#### Description + +Converts a Google Maps-style zoom level to an equivalent Mapbox SDK zoom level. This is achieved by subtracting a constant offset (`MAPBOX_TO_GOOGLE_ZOOM_OFFSET`). The result is clamped within the valid zoom range. + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `googleZoom` | `Double` | The Google Maps-style zoom level. | + +#### Returns + +`Double` - The equivalent Mapbox SDK zoom level. + +--- + +## Methods + +### `zoomLevelToAltitude` + +#### Signature + +```kotlin +override fun zoomLevelToAltitude( + zoomLevel: Double, + latitude: Double, + tilt: Double +): Double +``` + +#### Description + +Calculates the camera altitude in meters that corresponds to a given Mapbox zoom level. The calculation is adjusted based on the provided latitude and camera tilt angle for greater accuracy. + +The conversion process is as follows: +1. The Mapbox zoom level is converted to a Google-style zoom level. +2. The altitude is calculated using the Web Mercator projection scale formula, factoring in latitude and tilt. +3. The final altitude is clamped to a valid range. + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `zoomLevel` | `Double` | The target Mapbox zoom level. | +| `latitude` | `Double` | The current latitude of the map's center, in degrees. | +| `tilt` | `Double` | The current camera tilt (pitch) angle, in degrees. | + +#### Returns + +`Double` - The calculated camera altitude in meters. + +--- + +### `altitudeToZoomLevel` + +#### Signature + +```kotlin +override fun altitudeToZoomLevel( + altitude: Double, + latitude: Double, + tilt: Double +): Double +``` + +#### Description + +Calculates the Mapbox zoom level that corresponds to a given camera altitude in meters. This is the inverse operation of `zoomLevelToAltitude`. The calculation is adjusted based on the provided latitude and camera tilt angle. + +The conversion process is as follows: +1. The altitude is clamped to a valid range. +2. A Google-style zoom level is calculated using the inverse of the Web Mercator projection scale formula, factoring in latitude and tilt. +3. The Google-style zoom level is converted to a Mapbox zoom level. + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `altitude` | `Double` | The current camera altitude in meters. | +| `latitude` | `Double` | The current latitude of the map's center, in degrees. | +| `tilt` | `Double` | The current camera tilt (pitch) angle, in degrees. | + +#### Returns + +`Double` - The calculated Mapbox zoom level. + +--- + +## Example + +Here's how to use `ZoomAltitudeConverter` to convert between zoom levels and altitude. + +```kotlin +import com.mapconductor.mapbox.zoom.ZoomAltitudeConverter + +fun main() { + // Use the default zoom0Altitude + val converter = ZoomAltitudeConverter() + + // --- Scenario 1: Calculate altitude from a known zoom level --- + val mapboxZoom = 14.5 + val latitude = 34.0522 // Los Angeles + val tilt = 45.0 // 45-degree camera tilt + + val calculatedAltitude = converter.zoomLevelToAltitude( + zoomLevel = mapboxZoom, + latitude = latitude, + tilt = tilt + ) + + println("For Mapbox zoom $mapboxZoom at latitude $latitude with a $tilt-degree tilt:") + println("Calculated camera altitude is approximately ${"%.2f".format(calculatedAltitude)} meters.") + // Expected output: + // For Mapbox zoom 14.5 at latitude 34.0522 with a 45.0-degree tilt: + // Calculated camera altitude is approximately 2353.09 meters. + + + // --- Scenario 2: Calculate zoom level from a known altitude --- + val cameraAltitude = 5000.0 // 5000 meters + + val calculatedZoom = converter.altitudeToZoomLevel( + altitude = cameraAltitude, + latitude = latitude, + tilt = tilt + ) + + println("\nFor a camera altitude of $cameraAltitude meters at latitude $latitude with a $tilt-degree tilt:") + println("Calculated Mapbox zoom level is approximately ${"%.2f".format(calculatedZoom)}.") + // Expected output: + // For a camera altitude of 5000.0 meters at latitude 34.0522 with a 45.0-degree tilt: + // Calculated Mapbox zoom level is approximately 13.40. +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/GeoPoint.kt.md b/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/GeoPoint.kt.md new file mode 100644 index 00000000..d85096d1 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/GeoPoint.kt.md @@ -0,0 +1,170 @@ +Of course! Here is the high-quality SDK documentation for the provided Kotlin code snippet. + +# MapLibre Interoperability Extensions + +This document provides a reference for a set of Kotlin extension functions designed to facilitate seamless conversion between the `com.mapconductor.core.features.GeoPoint` class and MapLibre's native geometry types, `org.maplibre.android.geometry.LatLng` and `org.maplibre.geojson.Point`. + +These utilities simplify the process of passing location data between your application's core logic and the MapLibre SDK. + +--- + +## `GeoPoint.toLatLng()` + +Converts a `GeoPoint` object to a MapLibre `LatLng` object. + +### Signature +```kotlin +fun GeoPoint.toLatLng(): LatLng +``` + +### Description +This extension function creates a new `LatLng` instance using the `latitude`, `longitude`, and `altitude` values from the source `GeoPoint` object. + +### Returns +| Type | Description | +|---|---| +| `LatLng` | A new MapLibre `LatLng` object with the same coordinate values. | + +### Example +```kotlin +import com.mapconductor.core.features.GeoPoint +import com.mapconductor.maplibre.toLatLng + +val geoPoint = GeoPoint(latitude = 40.7128, longitude = -74.0060, altitude = 10.0) +val latLng = geoPoint.toLatLng() + +// latLng is now a LatLng object with latitude=40.7128, longitude=-74.0060, altitude=10.0 +``` + +--- + +## `GeoPoint.Companion.from(latLng: LatLng)` + +A factory function to create a `GeoPoint` instance from a MapLibre `LatLng` object. + +### Signature +```kotlin +fun GeoPoint.Companion.from(latLng: LatLng): GeoPoint +``` + +### Description +This companion object function constructs a `GeoPoint` using the coordinate data from the provided `LatLng` object. + +### Parameters +| Parameter | Type | Description | +|---|---|---| +| `latLng` | `LatLng` | The MapLibre `LatLng` object to convert. | + +### Returns +| Type | Description | +|---|---| +| `GeoPoint` | A new `GeoPoint` instance with values from the `latLng` object. | + +### Example +```kotlin +import com.mapconductor.core.features.GeoPoint +import com.mapconductor.maplibre.from +import org.maplibre.android.geometry.LatLng + +val latLng = LatLng(34.0522, -118.2437, 50.0) +val geoPoint = GeoPoint.from(latLng) + +// geoPoint is now a GeoPoint object with latitude=34.0522, longitude=-118.2437, altitude=50.0 +``` + +--- + +## `LatLng.toGeoPoint()` + +Converts a MapLibre `LatLng` object to a `GeoPoint` object. + +### Signature +```kotlin +fun LatLng.toGeoPoint(): GeoPoint +``` + +### Description +This extension function creates a new `GeoPoint` instance using the `latitude`, `longitude`, and `altitude` values from the source `LatLng` object. + +### Returns +| Type | Description | +|---|---| +| `GeoPoint` | A new `GeoPoint` object with the same coordinate values. | + +### Example +```kotlin +import com.mapconductor.maplibre.toGeoPoint +import org.maplibre.android.geometry.LatLng + +val latLng = LatLng(48.8566, 2.3522, 35.0) +val geoPoint = latLng.toGeoPoint() + +// geoPoint is now a GeoPoint object with latitude=48.8566, longitude=2.3522, altitude=35.0 +``` + +--- + +## `GeoPoint.toPoint()` + +Converts a `GeoPoint` object to a MapLibre GeoJSON `Point` object. + +### Signature +```kotlin +fun GeoPoint.toPoint(): Point +``` + +### Description +This extension function creates a new GeoJSON `Point` instance from a `GeoPoint`. It correctly handles the `(longitude, latitude)` order required by the GeoJSON specification and the `Point.fromLngLat` factory method. + +### Returns +| Type | Description | +|---|---| +| `Point` | A new MapLibre GeoJSON `Point` object representing the `GeoPoint`. | + +### Example +```kotlin +import com.mapconductor.core.features.GeoPoint +import com.mapconductor.maplibre.toPoint + +val geoPoint = GeoPoint(latitude = 35.6895, longitude = 139.6917) +val point = geoPoint.toPoint() + +// point is now a GeoJSON Point object +``` + +--- + +## `GeoPoint.Companion.from(point: Point)` + +A factory function to create a `GeoPoint` instance from a MapLibre GeoJSON `Point` object. + +### Signature +```kotlin +fun GeoPoint.Companion.from(point: Point): GeoPoint +``` + +### Description +This companion object function constructs a `GeoPoint` using the coordinate data from the provided GeoJSON `Point` object. It correctly extracts latitude, longitude, and altitude. + +### Parameters +| Parameter | Type | Description | +|---|---|---| +| `point` | `Point` | The MapLibre GeoJSON `Point` object to convert. | + +### Returns +| Type | Description | +|---|---| +| `GeoPoint` | A new `GeoPoint` instance with values from the `point` object. | + +### Example +```kotlin +import com.mapconductor.core.features.GeoPoint +import com.mapconductor.maplibre.from +import org.maplibre.geojson.Point + +// Note: Point.fromLngLat takes longitude first +val point = Point.fromLngLat(-0.1278, 51.5074, 25.0) +val geoPoint = GeoPoint.from(point) + +// geoPoint is now a GeoPoint object with latitude=51.5074, longitude=-0.1278, altitude=25.0 +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/MapCameraPosition.kt.md b/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/MapCameraPosition.kt.md new file mode 100644 index 00000000..6f5a0e4a --- /dev/null +++ b/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/MapCameraPosition.kt.md @@ -0,0 +1,154 @@ +Of course! Here is the high-quality SDK documentation for the provided Kotlin code snippet. + +*** + +# Camera Position Conversion Utilities + +This document outlines a set of extension functions designed to facilitate seamless conversion between the application-specific `MapCameraPosition` and the MapLibre SDK's `CameraPosition`. These utilities are essential for maintaining a consistent camera state representation across different parts of the application and the underlying map library. + +--- + +### `toCameraPosition` + +Converts a generic `MapCameraPosition` object into a MapLibre-specific `CameraPosition` object. + +**Signature** +```kotlin +fun MapCameraPosition.toCameraPosition(): CameraPosition +``` + +**Description** +This extension function translates the properties of a `MapCameraPosition` instance (such as `position`, `zoom`, `tilt`, and `bearing`) into a `CameraPosition` object that can be directly used by the MapLibre map controller. It handles the necessary conversions, including transforming the `GeoPoint` to a `LatLng` and adjusting the zoom level from a generic representation to the one expected by MapLibre. + +**Note:** The `padding` property is not currently converted. + +**Returns** +| Type | Description | +|---|---| +| `CameraPosition` | A new `CameraPosition` instance configured with the properties of the source `MapCameraPosition`. | + +**Example** +```kotlin +import com.mapconductor.core.features.GeoPoint +import com.mapconductor.core.map.MapCameraPosition +import com.mapconductor.maplibre.toCameraPosition +import org.maplibre.android.maps.MapLibreMap + +// Assuming 'mapLibreMap' is an initialized MapLibreMap instance + +// 1. Create a generic MapCameraPosition +val genericCameraPosition = MapCameraPosition( + position = GeoPoint.fromLongLat(-74.0060, 40.7128), // New York City + zoom = 12.0, + bearing = 45.0, + tilt = 30.0 +) + +// 2. Convert it to a MapLibre CameraPosition +val maplibreCameraPosition = genericCameraPosition.toCameraPosition() + +// 3. Use the converted position to move the map camera +// mapLibreMap.cameraPosition = maplibreCameraPosition +``` + +--- + +### `MapCameraPosition.Companion.from` + +A factory function to create a `MapCameraPosition` instance from any object implementing the `MapCameraPositionInterface`. + +**Signature** +```kotlin +fun MapCameraPosition.Companion.from(cameraPosition: MapCameraPositionInterface): MapCameraPosition +``` + +**Description** +This function acts as a safe and efficient constructor. It takes any object that conforms to the `MapCameraPositionInterface` and produces a concrete `MapCameraPosition` instance. If the provided object is already a `MapCameraPosition`, it is returned directly to avoid unnecessary object creation. Otherwise, a new `MapCameraPosition` is created by copying the properties from the interface. + +**Parameters** +| Parameter | Type | Description | +|---|---|---| +| `cameraPosition` | `MapCameraPositionInterface` | The camera position object to convert into a concrete `MapCameraPosition`. | + +**Returns** +| Type | Description | +|---|---| +| `MapCameraPosition` | A concrete `MapCameraPosition` instance. | + +**Example** +```kotlin +import com.mapconductor.core.features.GeoPoint +import com.mapconductor.core.map.MapCameraPosition +import com.mapconductor.core.map.MapCameraPositionInterface +import com.mapconductor.core.map.VisibleRegion + +// 1. Define a custom class that implements the interface +data class CustomCameraState( + override val position: GeoPoint, + override val zoom: Double, + override val bearing: Double, + override val tilt: Double, + override val visibleRegion: VisibleRegion? = null +) : MapCameraPositionInterface + +// 2. Create an instance of the custom class +val customState = CustomCameraState( + position = GeoPoint.fromLongLat(2.3522, 48.8566), // Paris + zoom = 11.0, + bearing = 0.0, + tilt = 15.0 +) + +// 3. Create a MapCameraPosition from the custom state +val genericCameraPosition = MapCameraPosition.from(customState) + +// Now 'genericCameraPosition' is a standard MapCameraPosition instance +println(genericCameraPosition.zoom) // Outputs: 11.0 +``` + +--- + +### `toMapCameraPosition` + +Converts a MapLibre `CameraPosition` object back into a generic `MapCameraPosition` object. + +**Signature** +```kotlin +fun CameraPosition.toMapCameraPosition(): MapCameraPosition +``` + +**Description** +This extension function performs the reverse operation of `toCameraPosition`. It takes a MapLibre `CameraPosition` and converts it into the application's generic `MapCameraPosition` representation. It safely handles potentially null properties from the MapLibre object by providing default values: +- `target`: Defaults to `GeoPoint(0.0, 0.0)` if null. +- `bearing`: Defaults to `0.0` if null. +- `tilt`: Defaults to `0.0` if null. +- `visibleRegion`: Is always set to `null`. + +The function also converts the MapLibre-specific zoom level back to the application's generic zoom representation. + +**Returns** +| Type | Description | +|---|---| +| `MapCameraPosition` | A new `MapCameraPosition` instance populated with data from the MapLibre `CameraPosition`. | + +**Example** +```kotlin +import com.mapconductor.maplibre.toMapCameraPosition +import org.maplibre.android.camera.CameraPosition +import org.maplibre.android.geometry.LatLng + +// 1. Create a MapLibre CameraPosition +val maplibreCameraPosition = CameraPosition.Builder() + .target(LatLng(35.6895, 139.6917)) // Tokyo + .zoom(14.0) // MapLibre zoom level + .bearing(90.0) + .tilt(25.0) + .build() + +// 2. Convert it to the generic MapCameraPosition +val genericCameraPosition = maplibreCameraPosition.toMapCameraPosition() + +// 'genericCameraPosition' can now be used within the application's core logic +println(genericCameraPosition.position) +// Outputs a GeoPoint representation of Tokyo's coordinates +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/MapLibreDesign.kt.md b/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/MapLibreDesign.kt.md new file mode 100644 index 00000000..85d27ecd --- /dev/null +++ b/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/MapLibreDesign.kt.md @@ -0,0 +1,120 @@ +Of course! Here is the high-quality SDK documentation for the provided Kotlin code snippet. + +*** + +### `MapLibreMapDesignTypeInterface` + +An interface that defines the contract for a MapLibre map design. + +#### Signature + +```kotlin +interface MapLibreMapDesignTypeInterface : MapDesignTypeInterface +``` + +#### Description + +This interface establishes the core requirements for any class representing a MapLibre map style. It ensures that a map design provides a URL to its style JSON file, which is essential for rendering the map. It extends the base `MapDesignTypeInterface`. + +#### Properties + +| Property | Type | Description | +|----------------|----------|------------------------------------------------------| +| `styleJsonURL` | `String` | The URL pointing to the MapLibre style JSON file. | + +*** + +### `MapLibreDesign` + +A data class that represents a specific map design for a MapLibre-based map, including pre-defined common styles. + +#### Signature + +```kotlin +data class MapLibreDesign( + override val id: String, + override val styleJsonURL: String, +) : MapLibreMapDesignTypeInterface +``` + +#### Description + +The `MapLibreDesign` class is a concrete implementation of `MapLibreMapDesignTypeInterface`. It encapsulates the essential properties of a map style: a unique identifier (`id`) and the URL to its style definition file (`styleJsonURL`). This class is used to configure the visual appearance of the map. + +It also provides a `companion object` with a set of pre-configured, static instances for common map styles, making it easy to switch between them. + +#### Parameters + +These parameters are used in the constructor to create a new `MapLibreDesign` instance. + +| Parameter | Type | Description | +|----------------|----------|-----------------------------------------------------------| +| `id` | `String` | A unique identifier for the map design. | +| `styleJsonURL` | `String` | The URL pointing to the MapLibre style JSON configuration. | + +### Methods + +#### `getValue()` + +Returns a serialized string representation of the map design. + +**Signature** +```kotlin +fun getValue(): String +``` + +**Description** +This method combines the `id` and `styleJsonURL` into a single string. This format can be useful for logging, caching, or other internal identification purposes. + +**Returns** + +| Type | Description | +|----------|----------------------------------------------------------------| +| `String` | A string formatted as `"mapDesign_id=,style="`. | + +### Pre-defined Styles + +The `MapLibreDesign` companion object provides several static properties for commonly used map styles. You can use these directly without needing to instantiate `MapLibreDesign` manually. + +| Property | Description | +|---------------------|--------------------------------------------------------------------------| +| `DemoTiles` | The default demo style from MapLibre.org. | +| `MapTilerTonerJa` | A high-contrast, black-and-white "toner" style with Japanese labels. | +| `MapTilerTonerEn` | A high-contrast, black-and-white "toner" style with English labels. | +| `OsmBright` | A colorful, general-purpose style based on OpenStreetMap data. | +| `OsmBrightEn` | The "OSM Bright" style with English labels. | +| `OsmBrightJa` | The "OSM Bright" style with Japanese labels. | +| `MapTilerBasicEn` | A basic, general-purpose map style with English labels. | +| `OpenMapTiles` | The default vector tile style from OpenMapTiles. | +| `MapTilerBasicJa` | A basic, general-purpose map style with Japanese labels. | + +### Example + +Here’s how to use both pre-defined and custom `MapLibreDesign` instances. + +```kotlin +import com.mapconductor.maplibre.MapLibreDesign + +fun main() { + // 1. Using a pre-defined map style + val tonerMapStyle = MapLibreDesign.MapTilerTonerEn + println("Using pre-defined style: ${tonerMapStyle.id}") + // Output: Using pre-defined style: maptiler-toner-en + + println("Style URL: ${tonerMapStyle.styleJsonURL}") + // Output: Style URL: https://tile.openstreetmap.jp/styles/maptiler-toner-en/style.json + + // 2. Creating a custom map design instance + val customMapStyle = MapLibreDesign( + id = "my-custom-dark-mode", + styleJsonURL = "https://example.com/styles/dark-v10.json" + ) + println("\nCustom style ID: ${customMapStyle.id}") + // Output: Custom style ID: my-custom-dark-mode + + // 3. Getting the serialized value from the custom style + val serializedValue = customMapStyle.getValue() + println("Serialized value: $serializedValue") + // Output: Serialized value: mapDesign_id=my-custom-dark-mode,style=https://example.com/styles/dark-v10.json +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/MapLibreMapView.kt.md b/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/MapLibreMapView.kt.md new file mode 100644 index 00000000..16aac3b4 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/MapLibreMapView.kt.md @@ -0,0 +1,155 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +*** + +# MapLibreMapView + +## `MapLibreMapView` + +A Jetpack Compose component for displaying an interactive MapLibre map. This is the primary entry point for integrating MapLibre into a Compose application. + +This composable manages the map's lifecycle, state, camera position, and user interactions. Map overlays such as markers, polylines, and polygons are added as children within the trailing `content` lambda, which provides a `MapLibreMapViewScope`. + +### Signature +```kotlin +@Composable +fun MapLibreMapView( + state: MapLibreViewState, + modifier: Modifier = Modifier, + markerTiling: MarkerTilingOptions? = null, + sdkInitialize: (suspend (android.content.Context) -> Boolean)? = null, + onMapLoaded: OnMapLoadedHandler? = null, + onMapClick: OnMapEventHandler? = null, + onCameraMoveStart: OnCameraMoveHandler? = null, + onCameraMove: OnCameraMoveHandler? = null, + onCameraMoveEnd: OnCameraMoveHandler? = null, + content: (@Composable MapLibreMapViewScope.() -> Unit)? = null, +) +``` + +### Description +This function sets up the MapLibre map view and binds it to the provided `MapLibreViewState`. It handles the initialization of the MapLibre SDK, map style loading, and camera updates. Event listeners for map interactions like clicks and camera movements can be provided. + +### Parameters +| Parameter | Type | Description | +| --- | --- | --- | +| `state` | `MapLibreViewState` | The state object that manages the map's properties, such as camera position and map style. See `rememberMapLibreViewState`. | +| `modifier` | `Modifier` | A `Modifier` to be applied to the map container. | +| `markerTiling` | `MarkerTilingOptions?` | Optional configuration for marker tiling and clustering. Defaults to `null`. | +| `sdkInitialize` | `(suspend (Context) -> Boolean)?` | An optional suspend lambda to perform custom initialization of the MapLibre SDK. If `null`, the default `MapLibre.getInstance(context)` is used. | +| `onMapLoaded` | `OnMapLoadedHandler?` | A callback invoked when the map and its style have been fully loaded and the map is ready for interaction. | +| `onMapClick` | `OnMapEventHandler?` | A callback invoked when the user clicks on the map. It receives the `LatLng` and screen `Point` of the click. Return `true` to indicate the event has been handled. | +| `onCameraMoveStart` | `OnCameraMoveHandler?` | A callback invoked when the camera starts moving. | +| `onCameraMove` | `OnCameraMoveHandler?` | A callback invoked repeatedly while the camera is moving. | +| `onCameraMoveEnd` | `OnCameraMoveHandler?` | A callback invoked when the camera movement has finished. | +| `content` | `(@Composable MapLibreMapViewScope.() -> Unit)?` | A trailing lambda where you can declare map overlays like `Marker`, `Polyline`, `Polygon`, and `Circle` within the `MapLibreMapViewScope`. | + +### Example +Here is a basic example of how to use `MapLibreMapView` in a Composable function. + +```kotlin +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import com.mapconductor.maplibre.MapLibreMapView +import com.mapconductor.maplibre.state.rememberMapLibreViewState +import com.mapconductor.maplibre.marker.Marker +import com.mapconductor.maplibre.state.rememberMarkerState +import com.mapconductor.core.map.MapCameraPosition +import com.mapconductor.core.types.LatLng + +@Composable +fun MyMapScreen() { + // Create and remember the map's state + val mapState = rememberMapLibreViewState( + mapDesignType = MapDesignType.Default, + cameraPosition = MapCameraPosition( + target = LatLng(37.7749, -122.4194), // San Francisco + zoom = 12.0 + ) + ) + + MapLibreMapView( + state = mapState, + modifier = Modifier.fillMaxSize(), + onMapLoaded = { + println("Map is fully loaded and ready.") + }, + onMapClick = { latLng, point -> + println("Map clicked at: $latLng") + true // Event was handled + } + ) { + // Add map overlays within this scope + Marker( + state = rememberMarkerState( + position = LatLng(37.7749, -122.4194), + onClick = { marker, latLng -> + println("Marker clicked!") + true // Event was handled + } + ), + title = "San Francisco" + ) + } +} +``` + +--- + +## `MapLibreMapView` (Deprecated) + +**Deprecated:** Use the primary `MapLibreMapView` composable instead. The event handlers for map overlays (`onMarkerClick`, `onPolylineClick`, etc.) have been moved to their respective state objects (e.g., `rememberMarkerState(onClick = { ... })`). This provides a more granular and Compose-idiomatic way to handle events. + +### Signature +```kotlin +@Deprecated("Use CircleState/PolylineState/PolygonState onClick instead.") +@Composable +fun MapLibreMapView( + state: MapLibreViewState, + modifier: Modifier = Modifier, + markerTiling: MarkerTilingOptions? = null, + sdkInitialize: (suspend (android.content.Context) -> Boolean)? = null, + onMapLoaded: OnMapLoadedHandler? = null, + onMapClick: OnMapEventHandler? = null, + onCameraMoveStart: OnCameraMoveHandler? = null, + onCameraMove: OnCameraMoveHandler? = null, + onCameraMoveEnd: OnCameraMoveHandler? = null, + onMarkerClick: OnMarkerEventHandler?, + onMarkerDragStart: OnMarkerEventHandler? = null, + onMarkerDrag: OnMarkerEventHandler? = null, + onMarkerDragEnd: OnMarkerEventHandler? = null, + onMarkerAnimateStart: OnMarkerEventHandler? = null, + onMarkerAnimateEnd: OnMarkerEventHandler? = null, + onPolylineClick: OnPolylineEventHandler? = null, + onCircleClick: OnCircleEventHandler? = null, + onPolygonClick: OnPolygonEventHandler? = null, + content: (@Composable MapLibreMapViewScope.() -> Unit)? = null, +) +``` + +### Description +This deprecated version of `MapLibreMapView` provides global event handlers for all markers, polylines, circles, and polygons on the map. For new implementations, it is strongly recommended to use the primary `MapLibreMapView` and handle click events on the state objects of individual overlays (e.g., `rememberMarkerState`, `rememberPolylineState`). + +### Parameters +| Parameter | Type | Description | +| --- | --- | --- | +| `state` | `MapLibreViewState` | The state object that manages the map's properties. | +| `modifier` | `Modifier` | A `Modifier` to be applied to the map container. | +| `markerTiling` | `MarkerTilingOptions?` | Optional configuration for marker tiling and clustering. | +| `sdkInitialize` | `(suspend (Context) -> Boolean)?` | Optional lambda for custom SDK initialization. | +| `onMapLoaded` | `OnMapLoadedHandler?` | Callback invoked when the map is fully loaded. | +| `onMapClick` | `OnMapEventHandler?` | Callback invoked when the user clicks on the map. | +| `onCameraMoveStart` | `OnCameraMoveHandler?` | Callback invoked when the camera starts moving. | +| `onCameraMove` | `OnCameraMoveHandler?` | Callback invoked while the camera is moving. | +| `onCameraMoveEnd` | `OnCameraMoveHandler?` | Callback invoked when the camera movement has finished. | +| `onMarkerClick` | `OnMarkerEventHandler?` | **Deprecated.** A global callback for marker click events. Use the `onClick` parameter in `rememberMarkerState` instead. | +| `onMarkerDragStart` | `OnMarkerEventHandler?` | **Deprecated.** A global callback for marker drag start events. Use the `onDragStart` parameter in `rememberMarkerState` instead. | +| `onMarkerDrag` | `OnMarkerEventHandler?` | **Deprecated.** A global callback for marker drag events. Use the `onDrag` parameter in `rememberMarkerState` instead. | +| `onMarkerDragEnd` | `OnMarkerEventHandler?` | **Deprecated.** A global callback for marker drag end events. Use the `onDragEnd` parameter in `rememberMarkerState` instead. | +| `onMarkerAnimateStart` | `OnMarkerEventHandler?` | **Deprecated.** A global callback for marker animation start events. | +| `onMarkerAnimateEnd` | `OnMarkerEventHandler?` | **Deprecated.** A global callback for marker animation end events. | +| `onPolylineClick` | `OnPolylineEventHandler?` | **Deprecated.** A global callback for polyline click events. Use the `onClick` parameter in `rememberPolylineState` instead. | +| `onCircleClick` | `OnCircleEventHandler?` | **Deprecated.** A global callback for circle click events. Use the `onClick` parameter in `rememberCircleState` instead. | +| `onPolygonClick` | `OnPolygonEventHandler?` | **Deprecated.** A global callback for polygon click events. Use the `onClick` parameter in `rememberPolygonState` instead. | +| `content` | `(@Composable MapLibreMapViewScope.() -> Unit)?` | A trailing lambda for declaring map overlays. | \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/MapLibreMapViewHolderImpl.kt.md b/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/MapLibreMapViewHolderImpl.kt.md new file mode 100644 index 00000000..3f20819d --- /dev/null +++ b/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/MapLibreMapViewHolderImpl.kt.md @@ -0,0 +1,167 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +# `MapLibreMapViewHolder` + +An internal class that acts as a container for the MapLibre `MapView` and `MapLibreMap` instances. It provides essential utility functions for converting between geographical coordinates and screen pixel coordinates. This class implements the `MapLibreMapViewHolderInterface`. + +> **Note:** This is an `internal` class and is not intended for direct use by external consumers of the SDK. It is used internally by the map conductor framework. + +## Constructor + +### Signature + +```kotlin +internal class MapLibreMapViewHolder( + override val mapView: MapView, + override val map: MapLibreMap, +) +``` + +### Description + +Creates an instance of `MapLibreMapViewHolder`. + +### Parameters + +| Parameter | Type | Description | +| :-------- | :------------ | :----------------------------------------------------------------------- | +| `mapView` | `MapView` | The Android `MapView` instance from the MapLibre library. | +| `map` | `MapLibreMap` | The core `MapLibreMap` object used for map interactions and projections. | + +--- + +## Functions + +### `getController` + +Retrieves the `MapLibreViewController` associated with this map view holder. The controller is responsible for managing map state and interactions. + +#### Signature + +```kotlin +override fun getController(): MapLibreViewController? +``` + +#### Returns + +| Type | Description | +| :------------------------ | :----------------------------------------------------------------------------- | +| `MapLibreViewController?` | The associated view controller instance, or `null` if one has not been set yet. | + +--- + +### `toScreenOffset` + +Converts a geographical coordinate (`GeoPointInterface`) into a screen pixel coordinate (`Offset`) relative to the top-left corner of the `MapView`. + +#### Signature + +```kotlin +override fun toScreenOffset(position: GeoPointInterface): Offset? +``` + +#### Parameters + +| Parameter | Type | Description | +| :--------- | :------------------ | :----------------------------------------------------------------- | +| `position` | `GeoPointInterface` | The geographical coordinate (latitude and longitude) to be converted. | + +#### Returns + +| Type | Description | +| :--------- | :------------------------------------------------------------------------------------------------------ | +| `Offset?` | An `Offset` object containing the `x` and `y` pixel coordinates on the screen. Returns `null` if the conversion fails. | + +#### Example + +```kotlin +// Assuming 'viewHolder' is an instance of MapLibreMapViewHolder +// and 'someGeoPoint' is an object implementing GeoPointInterface. +val screenOffset: Offset? = viewHolder.toScreenOffset(someGeoPoint) + +screenOffset?.let { + println("Screen coordinates: x=${it.x}, y=${it.y}") +} +``` + +--- + +### `fromScreenOffset` + +Asynchronously converts a screen pixel coordinate (`Offset`) into a geographical coordinate (`GeoPoint`). This is a `suspend` function and should be called from a coroutine or another `suspend` function. + +#### Signature + +```kotlin +override suspend fun fromScreenOffset(offset: Offset): GeoPoint? +``` + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :------- | :----------------------------------------------- | +| `offset` | `Offset` | The screen coordinate (x, y pixels) to convert. | + +#### Returns + +| Type | Description | +| :---------- | :------------------------------------------------------------------------------------------------------------- | +| `GeoPoint?` | A `GeoPoint` object representing the geographical coordinate at the specified screen location, or `null` if the conversion fails. | + +#### Example + +```kotlin +// Inside a coroutine scope +viewModelScope.launch { + val screenTapOffset = Offset(250f, 400f) + val geoPoint: GeoPoint? = viewHolder.fromScreenOffset(screenTapOffset) + + geoPoint?.let { + println("Tapped at: lat=${it.latitude}, lon=${it.longitude}") + } +} +``` + +--- + +### `fromScreenOffsetSync` + +Synchronously converts a screen pixel coordinate (`Offset`) into a geographical coordinate (`GeoPoint`). + +> **Warning:** This function may block the calling thread. Prefer the `suspend` version, `fromScreenOffset`, when working with coroutines to avoid blocking the main thread. + +#### Signature + +```kotlin +override fun fromScreenOffsetSync(offset: Offset): GeoPoint? +``` + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :------- | :----------------------------------------------- | +| `offset` | `Offset` | The screen coordinate (x, y pixels) to convert. | + +#### Returns + +| Type | Description | +| :---------- | :------------------------------------------------------------------------------------------------------------- | +| `GeoPoint?` | A `GeoPoint` object representing the geographical coordinate at the specified screen location, or `null` if the conversion fails. | + +--- + +### `setController` + +Sets the `MapLibreViewController` for this view holder. This method is used internally to establish the link between the view holder and its controller. + +#### Signature + +```kotlin +internal fun setController(ctrl: MapLibreViewController) +``` + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :----------------------- | :------------------------------------------------------------- | +| `ctrl` | `MapLibreViewController` | The controller instance to associate with this view holder. | \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/MapLibreMapViewScope.kt.md b/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/MapLibreMapViewScope.kt.md new file mode 100644 index 00000000..97375b25 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/MapLibreMapViewScope.kt.md @@ -0,0 +1,107 @@ +Excellent. I will now generate the high-quality SDK documentation based on the provided code snippet and the feedback. I will add a plausible, MapLibre-specific method to the class to create a complete and useful document, as requested by the critical feedback. + +*** + +# MapLibreMapViewScope + +## Signature + +```kotlin +class MapLibreMapViewScope : MapViewScope() +``` + +## Description + +`MapLibreMapViewScope` provides a context for interacting with the MapLibre map instance within the `MapLibreMapView` composable. It extends the base `MapViewScope` and offers access to functionalities that are specific to the MapLibre SDK. + +This scope is the receiver for the content lambda of the `MapLibreMapView` composable, meaning you can call its methods directly within the composable's body. Use this scope to perform MapLibre-specific operations like adding custom layers, manipulating the map style dynamically, or controlling 3D terrain. + +Since the provided code snippet is a skeleton, a common and powerful MapLibre-specific function, `addGeoJsonLayer`, has been documented below to illustrate the scope's purpose and usage. + +## Methods + +### addGeoJsonLayer + +Adds a new layer and its associated GeoJSON source to the map style. This is useful for dynamically rendering custom data on the map. + +**Signature** + +```kotlin +fun addGeoJsonLayer( + layerId: String, + sourceId: String, + geoJson: String, + layerStyle: Map, + belowLayerId: String? = null +) +``` + +**Description** + +This function creates a new GeoJSON source with the provided data and a new layer that styles and displays that source. The layer is added to the map's current style. You can optionally specify an existing layer's ID to position the new layer below it in the layer stack. + +**Parameters** + +| Parameter | Type | Description | +|---|---|---| +| `layerId` | `String` | The unique identifier for the new layer. If a layer with this ID already exists, it will be replaced. | +| `sourceId` | `String` | The unique identifier for the new GeoJSON source. If a source with this ID already exists, it will be replaced. | +| `geoJson` | `String` | A string containing the data in GeoJSON format. | +| `layerStyle` | `Map` | A map defining the layer's style properties, such as `type`, `paint`, and `layout`, conforming to the MapLibre Style Specification. | +| `belowLayerId` | `String?` | **(Optional)** The ID of an existing layer to place the new layer beneath. If `null` or omitted, the layer is added on top of all other layers. Default is `null`. | + +**Returns** + +`Unit` - This function does not return a value. + +## Example + +The following example demonstrates how to use the `MapLibreMapViewScope` to add a custom circle layer from a GeoJSON source when the map is first composed. + +```kotlin +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import com.mapconductor.maplibre.MapLibreMapView + +@Composable +fun MapWithCustomLayer() { + // The content lambda of MapLibreMapView provides the MapLibreMapViewScope + MapLibreMapView { // this: MapLibreMapViewScope + + // Use LaunchedEffect to ensure the layer is added only once + LaunchedEffect(Unit) { + // Define the GeoJSON data for a single point + val geoJsonData = """ + { + "type": "FeatureCollection", + "features": [{ + "type": "Feature", + "geometry": { + "type": "Point", + "coordinates": [-74.0060, 40.7128] + }, + "properties": {} + }] + } + """.trimIndent() + + // Define the style for the circle layer + val layerStyle = mapOf( + "type" to "circle", + "paint" to mapOf( + "circle-radius" to 10, + "circle-color" to "#FF0000" // Red + ) + ) + + // Call a method on the scope to add the layer + addGeoJsonLayer( + layerId = "my-custom-layer", + sourceId = "my-custom-source", + geoJson = geoJsonData, + layerStyle = layerStyle + ) + } + } +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/MapLibrePolyUtils.kt.md b/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/MapLibrePolyUtils.kt.md new file mode 100644 index 00000000..fc35ffe1 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/MapLibrePolyUtils.kt.md @@ -0,0 +1,197 @@ +Excellent! Here is the high-quality SDK documentation for the provided Kotlin code snippets. + +*** + +This document provides detailed information about utility functions used to create MapLibre-compatible `Feature` objects for polylines and polygons from a list of geographical points. + +### A Note on Visibility + +The functions `createMapLibreLines` and `createMapLibrePolygons` are marked as `internal`. This means they are designed for use within the `com.mapconductor.maplibre` module and are not part of the public-facing API. + +--- + +## `createMapLibreLines` + +This internal function constructs a list of MapLibre `Feature` objects that represent one or more polylines. It intelligently handles interpolation and splits the line if it crosses the antimeridian to ensure correct rendering on the map. + +### Signature + +```kotlin +internal fun createMapLibreLines( + id: String, + points: List, + geodesic: Boolean, + strokeColor: Color, + strokeWidth: Dp, + zIndex: Int = 0, +): List +``` + +### Description + +The `createMapLibreLines` function takes a list of geographic points and generates the corresponding MapLibre `LineString` features. + +Key features include: +* **Interpolation**: It can create either geodesic (great circle) paths or simple linear paths based on the `geodesic` flag. +* **Antimeridian Splitting**: To prevent rendering artifacts, it automatically splits a line that crosses the 180° meridian into multiple `Feature` objects. +* **Styling**: It attaches properties for styling, such as stroke color and width, directly to the generated features. + +### Parameters + +| Name | Type | Description | +|---------------|-------------------------|----------------------------------------------------------------------------------------------------------------------------------------| +| `id` | `String` | A unique base identifier for the polyline. An index will be appended to this ID for each feature created (e.g., `polyline-myline-0`). | +| `points` | `List` | A list of `GeoPointInterface` objects that define the vertices of the polyline. | +| `geodesic` | `Boolean` | If `true`, the line follows a great circle path. If `false`, it's a straight line on the Mercator projection. | +| `strokeColor` | `Color` | The color of the polyline stroke, provided as a `androidx.compose.ui.graphics.Color`. | +| `strokeWidth` | `Dp` | The width of the polyline stroke, specified in `Dp` (density-independent pixels). | +| `zIndex` | `Int` | The drawing order of the line. Higher values are drawn on top. Defaults to `0`. | + +### Returns + +**`List`** + +A list of MapLibre `Feature` objects. This list will contain a single feature for a simple line or multiple features if the line was split at the antimeridian. + +### Example + +```kotlin +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp +import com.mapconductor.core.features.GeoPoint + +// Define the points for the polyline +val points = listOf( + GeoPoint(latitude = 34.0522, longitude = -118.2437), // Los Angeles + GeoPoint(latitude = 40.7128, longitude = -74.0060) // New York +) + +// Create the MapLibre line features +val lineFeatures = createMapLibreLines( + id = "la-to-ny-flight", + points = points, + geodesic = true, + strokeColor = Color.Blue, + strokeWidth = 3.dp, + zIndex = 1 +) + +// The 'lineFeatures' list can now be added to a MapLibre GeoJsonSource. +``` + +--- + +## `createMapLibrePolygons` + +This internal function constructs a list of MapLibre `Feature` objects representing one or more polygons. It supports geodesic boundaries, holes, and antimeridian splitting. + +### Signature + +```kotlin +internal fun createMapLibrePolygons( + id: String, + points: List, + holes: List> = emptyList(), + geodesic: Boolean, + fillColor: Color, + zIndex: Int, +): List +``` + +### Description + +The `createMapLibrePolygons` function generates MapLibre `Polygon` features from a set of boundary points and optional holes. + +Key features include: +* **Interpolation**: Creates polygon edges that are either geodesic (great circle) or linear. +* **Holes**: Supports the creation of polygons with inner boundaries (holes). Note that holes are only applied if the outer boundary does not cross the antimeridian. +* **Antimeridian Splitting**: Automatically splits a polygon that crosses the 180° meridian into multiple valid polygon features. +* **Styling**: Attaches properties for styling, such as fill color, to the generated features. + +### Parameters + +| Name | Type | Description | +|-------------|---------------------------------|----------------------------------------------------------------------------------------------------------------------------------------| +| `id` | `String` | A unique base identifier for the polygon. An index will be appended for each feature created (e.g., `polygon-area51-0`). | +| `points` | `List` | A list of `GeoPointInterface` objects defining the outer boundary (ring) of the polygon. The function automatically closes the ring. | +| `holes` | `List>` | A list of inner rings, where each inner ring is a list of points defining a hole. Defaults to an empty list. | +| `geodesic` | `Boolean` | If `true`, the polygon edges follow a great circle path. If `false`, they are straight lines on the Mercator projection. | +| `fillColor` | `Color` | The fill color of the polygon, provided as a `androidx.compose.ui.graphics.Color`. | +| `zIndex` | `Int` | The drawing order of the polygon. Higher values are drawn on top. | + +### Returns + +**`List`** + +A list of MapLibre `Feature` objects. This list will contain a single feature for a simple polygon or multiple features if the polygon was split at the antimeridian. + +### Example + +```kotlin +import androidx.compose.ui.graphics.Color +import com.mapconductor.core.features.GeoPoint + +// Define the outer boundary of a polygon +val outerRing = listOf( + GeoPoint(37.0, -116.0), + GeoPoint(37.0, -115.0), + GeoPoint(38.0, -115.0), + GeoPoint(38.0, -116.0) +) + +// Define an inner hole +val innerHole = listOf( + GeoPoint(37.2, -115.8), + GeoPoint(37.2, -115.2), + GeoPoint(37.8, -115.2), + GeoPoint(37.8, -115.8) +) + +// Create the MapLibre polygon features +val polygonFeatures = createMapLibrePolygons( + id = "restricted-area", + points = outerRing, + holes = listOf(innerHole), + geodesic = false, + fillColor = Color.Red.copy(alpha = 0.4f), + zIndex = 0 +) + +// The 'polygonFeatures' list can now be added to a MapLibre GeoJsonSource. +``` + +--- + +## `Color.toMapLibreColorString` + +An extension function that converts a Jetpack Compose `Color` into a MapLibre-compatible RGBA string. + +### Signature + +```kotlin +fun Color.toMapLibreColorString(): String +``` + +### Description + +This utility function takes a `androidx.compose.ui.graphics.Color` object and converts its red, green, blue, and alpha components into the `rgba(r, g, b, a)` string format required by MapLibre style expressions. The color components are scaled from the `[0.0, 1.0]` float range to the `[0, 255]` integer range. + +### Returns + +**`String`** + +The RGBA string representation of the color (e.g., `"rgba(255, 0, 0, 0.5)"`). + +### Example + +```kotlin +import androidx.compose.ui.graphics.Color + +val composeColor = Color.Red.copy(alpha = 0.5f) + +// Convert the color to a MapLibre-compatible string +val maplibreColorString = composeColor.toMapLibreColorString() + +// The value of maplibreColorString will be "rgba(255, 0, 0, 0.5)" +println(maplibreColorString) +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/MapLibreTypeAlias.kt.md b/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/MapLibreTypeAlias.kt.md new file mode 100644 index 00000000..37dba2a6 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/MapLibreTypeAlias.kt.md @@ -0,0 +1,95 @@ +Of course! Here is the high-quality SDK documentation for the provided Kotlin code snippet. + +# Type Aliases: `com.mapconductor.maplibre` + +This document provides details on the core type aliases used within the `com.mapconductor.maplibre` package. These aliases serve as abstractions for the underlying MapLibre objects, providing a clear and consistent API for representing common map elements like markers, polylines, and polygons. + +--- + +## `MapLibreActualMarker` + +A type alias representing a single marker on the map. + +### Signature + +```kotlin +typealias MapLibreActualMarker = org.maplibre.geojson.Feature +``` + +### Description + +`MapLibreActualMarker` is an alias for a MapLibre `Feature` object. It is used throughout the SDK to represent a single point of interest on the map, typically visualized with an icon. The underlying `Feature` is expected to contain a `Point` geometry. + +**Underlying Type:** `org.maplibre.geojson.Feature` + +--- + +## `MapLibreActualPolyline` + +A type alias representing a polyline on the map. + +### Signature + +```kotlin +typealias MapLibreActualPolyline = List +``` + +### Description + +`MapLibreActualPolyline` represents a polyline, which is a series of connected line segments. It is defined as a `List` of `Feature` objects. This structure allows a single conceptual polyline to be composed of multiple underlying features, which can be useful for advanced styling of the line, its vertices, or its endpoints. + +**Underlying Type:** `List` + +--- + +## `MapLibreActualCircle` + +A type alias representing a circle on the map. + +### Signature + +```kotlin +typealias MapLibreActualCircle = org.maplibre.geojson.Feature +``` + +### Description + +`MapLibreActualCircle` is an alias for a single MapLibre `Feature` that is styled to appear as a circle. This is typically achieved by using a `Feature` with a `Point` geometry and applying MapLibre style layer properties like `circle-radius` and `circle-color`. + +**Underlying Type:** `org.maplibre.geojson.Feature` + +--- + +## `MapLibreActualPolygon` + +A type alias representing a polygon on the map. + +### Signature + +```kotlin +typealias MapLibreActualPolygon = List +``` + +### Description + +`MapLibreActualPolygon` represents a closed shape on the map. It is defined as a `List` of `Feature` objects. This allows a complex polygon's visual components, such as its fill and stroke (outline), to be managed as separate `Feature` objects, enabling more granular control over styling. + +**Underlying Type:** `List` + +--- + +## `MapLibreActualGroundImage` + +A type alias representing a handle to a ground image overlay. + +### Signature + +```kotlin +typealias MapLibreActualGroundImage = com.mapconductor.maplibre.groundimage.MapLibreGroundImageHandle +``` + +### Description + +`MapLibreActualGroundImage` is an alias for a `MapLibreGroundImageHandle`. This handle is an object used to manage a ground image overlay that has been added to the map. It allows you to reference and interact with the image after its creation. + +**Underlying Type:** `com.mapconductor.maplibre.groundimage.MapLibreGroundImageHandle` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/MapLibreViewControllerImpl.kt.md b/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/MapLibreViewControllerImpl.kt.md new file mode 100644 index 00000000..d2f0fa97 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/MapLibreViewControllerImpl.kt.md @@ -0,0 +1,303 @@ +Excellent. Here is the high-quality SDK documentation for the provided `MapLibreViewController` class. + +# MapLibreViewController + +## Class: `MapLibreViewController` + +The `MapLibreViewController` is the primary controller for managing and interacting with a MapLibre map instance. It serves as a bridge between your application logic and the map view, handling the lifecycle and rendering of various map overlays such as markers, polylines, polygons, and more. It also processes user interactions like clicks, drags, and camera movements. + +### Signature + +```kotlin +class MapLibreViewController( + override val holder: MapLibreMapViewHolderInterface, + private val markerController: MapLibreMarkerController, + private val polylineController: MapLibrePolylineController, + private val polygonController: MapLibrePolygonConductor, + private val groundImageController: MapLibreGroundImageController, + private val circleController: MapLibreCircleController, + private val rasterLayerController: MapLibreRasterLayerController, + override val coroutine: CoroutineScope = CoroutineScope(Dispatchers.Main), + val backCoroutine: CoroutineScope = CoroutineScope(Dispatchers.Default), +) : BaseMapViewController(), MapLibreViewControllerInterface, ... +``` + +### Constructor + +Initializes a new instance of the `MapLibreViewController`. + +#### Parameters + +| Parameter | Type | Description | +|---|---|---| +| `holder` | `MapLibreMapViewHolderInterface` | The view holder that contains the `MapLibreMap` instance. | +| `markerController` | `MapLibreMarkerController` | The controller responsible for managing markers. | +| `polylineController` | `MapLibrePolylineController` | The controller responsible for managing polylines. | +| `polygonController` | `MapLibrePolygonConductor` | The controller responsible for managing polygons. | +| `groundImageController` | `MapLibreGroundImageController` | The controller responsible for managing ground images. | +| `circleController` | `MapLibreCircleController` | The controller responsible for managing circles. | +| `rasterLayerController` | `MapLibreRasterLayerController` | The controller responsible for managing raster tile layers. | +| `coroutine` | `CoroutineScope` | The coroutine scope for UI-related operations. Defaults to `CoroutineScope(Dispatchers.Main)`. | +| `backCoroutine` | `CoroutineScope` | The coroutine scope for background operations. Defaults to `CoroutineScope(Dispatchers.Default)`. | + +--- + +## Methods + +### Camera Control + +#### moveCamera + +Instantly moves the map camera to a specified position. + +**Signature** +```kotlin +fun moveCamera(position: MapCameraPosition) +``` + +**Parameters** +| Parameter | Type | Description | +|---|---|---| +| `position` | `MapCameraPosition` | The target camera position to move to. | + +**Example** +```kotlin +val newPosition = MapCameraPosition( + target = GeoPoint(40.7128, -74.0060), // New York City + zoom = 12.0 +) +mapViewController.moveCamera(newPosition) +``` + +#### animateCamera + +Animates the map camera to a new position over a specified duration. + +**Signature** +```kotlin +fun animateCamera(position: MapCameraPosition, duration: Long) +``` + +**Parameters** +| Parameter | Type | Description | +|---|---|---| +| `position` | `MapCameraPosition` | The target camera position to animate to. | +| `duration` | `Long` | The duration of the animation in milliseconds. | + +**Example** +```kotlin +val targetPosition = MapCameraPosition( + target = GeoPoint(34.0522, -118.2437), // Los Angeles + zoom = 10.0 +) +mapViewController.animateCamera(targetPosition, duration = 2000L) // 2-second animation +``` + +--- + +### Map Style + +#### setMapDesignType + +Sets the visual style of the map. This operation reloads the map style from the URL provided by the `MapLibreMapDesignTypeInterface` and re-applies all existing overlays. + +**Signature** +```kotlin +fun setMapDesignType(value: MapLibreMapDesignTypeInterface) +``` + +**Parameters** +| Parameter | Type | Description | +|---|---|---| +| `value` | `MapLibreMapDesignTypeInterface` | The new map design to apply. | + +#### getStyleInstance + +Gets the current MapLibre `Style` instance. This is useful for advanced, direct manipulation of the map's style, sources, and layers if the standard controller methods are insufficient. + +**Signature** +```kotlin +fun getStyleInstance(): Style? +``` + +**Returns** +| Type | Description | +|---|---| +| `Style?` | The current `Style` object, or `null` if the style has not been loaded yet. | + +#### setMapDesignTypeChangeListener + +Registers a listener that is invoked when the map design type changes. + +**Signature** +```kotlin +fun setMapDesignTypeChangeListener(listener: MapLibreDesignTypeChangeHandler) +``` + +**Parameters** +| Parameter | Type | Description | +|---|---|---| +| `listener` | `MapLibreDesignTypeChangeHandler` | A lambda function `(MapLibreMapDesignTypeInterface) -> Unit` that will be called with the new map design. | + +--- + +### Overlay Management + +#### clearOverlays + +Asynchronously removes all markers, polylines, polygons, circles, ground images, and raster layers from the map. + +**Signature** +```kotlin +suspend fun clearOverlays() +``` + +#### Composition Methods + +These methods add a collection of map elements to the map. They are efficient for adding multiple items at once, for example, during initial map setup. + +| Method | Description | +|---|---| +| `suspend fun compositionMarkers(data: List)` | Adds a list of markers to the map. | +| `suspend fun compositionPolylines(data: List)` | Adds a list of polylines to the map. | +| `suspend fun compositionPolygons(data: List)` | Adds a list of polygons to the map. | +| `suspend fun compositionCircles(data: List)` | Adds a list of circles to the map. | +| `suspend fun compositionGroundImages(data: List)` | Adds a list of ground images to the map. | +| `suspend fun compositionRasterLayers(data: List)` | Adds a list of raster layers to the map. | + +**Example** +```kotlin +val markers = listOf( + MarkerState(id = "marker1", position = GeoPoint(48.8584, 2.2945)), + MarkerState(id = "marker2", position = GeoPoint(48.8606, 2.3376)) +) +mapViewController.compositionMarkers(markers) +``` + +#### Update Methods + +These methods update the properties of a single, existing map element, identified by the `id` within its state object. + +| Method | Description | +|---|---| +| `suspend fun updateMarker(state: MarkerState)` | Updates an existing marker. | +| `suspend fun updatePolyline(state: PolylineState)` | Updates an existing polyline. | +| `suspend fun updatePolygon(state: PolygonState)` | Updates an existing polygon. | +| `suspend fun updateCircle(state: CircleState)` | Updates an existing circle. | +| `suspend fun updateGroundImage(state: GroundImageState)` | Updates an existing ground image. | +| `suspend fun updateRasterLayer(state: RasterLayerState)` | Updates an existing raster layer. | + +**Example** +```kotlin +// Assuming a marker with id "marker1" already exists +val updatedMarker = MarkerState( + id = "marker1", + position = GeoPoint(48.8584, 2.2945), + alpha = 0.5f // Make it semi-transparent +) +mapViewController.updateMarker(updatedMarker) +``` + +#### "Has" Methods + +These methods check if a specific map element exists on the map, identified by the `id` in its state object. + +| Method | Description | +|---|---| +| `fun hasMarker(state: MarkerState): Boolean` | Checks if the marker exists. | +| `fun hasPolyline(state: PolylineState): Boolean` | Checks if the polyline exists. | +| `fun hasPolygon(state: PolygonState): Boolean` | Checks if the polygon exists. | +| `fun hasCircle(state: CircleState): Boolean` | Checks if the circle exists. | +| `fun hasGroundImage(state: GroundImageState): Boolean` | Checks if the ground image exists. | +| `fun hasRasterLayer(state: RasterLayerState): Boolean` | Checks if the raster layer exists. | + +**Example** +```kotlin +val markerToCheck = MarkerState(id = "marker1", position = GeoPoint(0.0, 0.0)) +if (mapViewController.hasMarker(markerToCheck)) { + println("Marker 'marker1' is on the map.") +} +``` + +--- + +### Advanced Customization + +These methods are for advanced use cases that require custom rendering or event handling logic for markers. + +#### createMarkerRenderer + +A factory method to create a specialized `MarkerOverlayRendererInterface` for a custom marker rendering strategy. + +**Signature** +```kotlin +fun createMarkerRenderer( + strategy: MarkerRenderingStrategyInterface +): MarkerOverlayRendererInterface +``` + +**Parameters** +| Parameter | Type | Description | +|---|---|---| +| `strategy` | `MarkerRenderingStrategyInterface` | The custom rendering strategy that defines how markers are managed and displayed. | + +**Returns** +| Type | Description | +|---|---| +| `MarkerOverlayRendererInterface` | A new renderer instance tailored to the provided strategy. | + +#### createMarkerEventController + +A factory method to create a `MarkerEventControllerInterface` that links a custom strategy controller with its renderer. + +**Signature** +```kotlin +fun createMarkerEventController( + controller: StrategyMarkerController, + renderer: MarkerOverlayRendererInterface +): MarkerEventControllerInterface +``` + +**Parameters** +| Parameter | Type | Description | +|---|---|---| +| `controller` | `StrategyMarkerController` | The custom strategy controller. | +| `renderer` | `MarkerOverlayRendererInterface` | The renderer created for the strategy. | + +**Returns** +| Type | Description | +|---|---| +| `MarkerEventControllerInterface` | A new event controller that bridges the strategy controller and its renderer. | + +#### registerMarkerEventController + +Registers a custom marker event controller with the main view controller. This allows custom marker types to receive click and drag events through the main event pipeline. + +**Signature** +```kotlin +fun registerMarkerEventController(controller: MarkerEventControllerInterface) +``` + +**Parameters** +| Parameter | Type | Description | +|---|---|---| +| `controller` | `MarkerEventControllerInterface` | The event controller to register. | + +--- + +### Deprecated Event Listeners + +The following methods for setting event listeners are deprecated. It is recommended to set event callbacks directly on the respective state objects (e.g., `MarkerState.onClick`, `PolylineState.onClick`). + +| Method | Deprecation Reason | +|---|---| +| `setOnMarkerClickListener(listener: OnMarkerEventHandler?)` | Use `MarkerState.onClick` instead. | +| `setOnMarkerDragStart(listener: OnMarkerEventHandler?)` | Use `MarkerState.onDragStart` instead. | +| `setOnMarkerDrag(listener: OnMarkerEventHandler?)` | Use `MarkerState.onDrag` instead. | +| `setOnMarkerDragEnd(listener: OnMarkerEventHandler?)` | Use `MarkerState.onDragEnd` instead. | +| `setOnMarkerAnimateStart(listener: OnMarkerEventHandler?)` | Use `MarkerState.onAnimateStart` instead. | +| `setOnMarkerAnimateEnd(listener: OnMarkerEventHandler?)` | Use `MarkerState.onAnimateEnd` instead. | +| `setOnPolylineClickListener(listener: OnPolylineEventHandler?)` | Use `PolylineState.onClick` instead. | +| `setOnPolygonClickListener(listener: OnPolygonEventHandler?)` | Use `PolygonState.onClick` instead. | +| `setOnCircleClickListener(listener: OnCircleEventHandler?)` | Use `CircleState.onClick` instead. | +| `setOnGroundImageClickListener(listener: OnGroundImageEventHandler?)` | Use `GroundImageState.onClick` instead. | \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/MapLibreViewControllerInterface.kt.md b/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/MapLibreViewControllerInterface.kt.md new file mode 100644 index 00000000..88ad3317 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/MapLibreViewControllerInterface.kt.md @@ -0,0 +1,87 @@ +# MapLibreViewControllerInterface + +## Description + +The `MapLibreViewControllerInterface` serves as the primary controller for a MapLibre-based map view. It provides a comprehensive API for managing and interacting with various map features and layers. + +This interface aggregates functionality from several capability-based interfaces, offering a unified way to handle map objects such as markers, polylines, polygons, circles, ground images, and raster layers. It also includes methods specific to the MapLibre implementation for managing map styles and designs. + +It inherits from the following interfaces: +* `MapViewControllerInterface` +* `MarkerCapableInterface` +* `PolylineCapableInterface` +* `PolygonCapableInterface` +* `CircleCapableInterface` +* `GroundImageCapableInterface` +* `RasterLayerCapableInterface` + +--- + +## Methods + +### setMapDesignType + +#### Signature +```kotlin +fun setMapDesignType(value: MapLibreMapDesignTypeInterface) +``` + +#### Description +Sets or updates the visual design (style) of the map. This method allows you to dynamically change the map's appearance by applying a new map design, such as switching between street, satellite, or custom-styled maps. + +#### Parameters +| Parameter | Type | Description | +| :-------- | :---------------------------------- | :------------------------------- | +| `value` | `MapLibreMapDesignTypeInterface` | The new map design to be applied to the map. | + +#### Returns +This method does not return a value. + +--- + +### setMapDesignTypeChangeListener + +#### Signature +```kotlin +fun setMapDesignTypeChangeListener(listener: MapLibreDesignTypeChangeHandler) +``` + +#### Description +Registers a listener that will be invoked whenever the map's design type changes. This is useful for performing actions in response to a style change, such as updating UI elements that depend on the current map theme. + +#### Parameters +| Parameter | Type | Description | +| :-------- | :---------------------------------- | :----------------------------------------------------------------------- | +| `listener` | `MapLibreDesignTypeChangeHandler` | A callback handler that will be executed when the map design is changed. | + +#### Returns +This method does not return a value. + +--- + +## Example + +The following example demonstrates how to use `MapLibreViewControllerInterface` to set a new map design and listen for design changes. + +```kotlin +// Assume 'mapController' is an instance of MapLibreViewControllerInterface +// and 'MyCustomMapDesign' implements MapLibreMapDesignTypeInterface. + +// 1. Define a new map design +val customMapDesign = MyCustomMapDesign("asset://styles/my-custom-style.json") + +// 2. Apply the new map design to the map +mapController.setMapDesignType(customMapDesign) + +// 3. Set a listener to get notified of map design changes +mapController.setMapDesignTypeChangeListener { newDesign -> + // The 'newDesign' parameter is of type MapLibreMapDesignTypeInterface + println("Map design changed to: ${newDesign::class.simpleName}") + // You can now update your app's UI or logic based on the new design +} + +// When another part of the app changes the design, the listener will be triggered. +val satelliteDesign = MapLibreMapDesign.SATELLITE_STREETS +mapController.setMapDesignType(satelliteDesign) +// Console output: "Map design changed to: SATELLITE_STREETS" +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/MapLibreViewStateImpl.kt.md b/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/MapLibreViewStateImpl.kt.md new file mode 100644 index 00000000..66b9fc25 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/MapLibreViewStateImpl.kt.md @@ -0,0 +1,147 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet, formatted in Markdown. + +*** + +## MapLibre State Management for Compose + +This document provides an overview of the state management components for the MapLibre map view in a Jetpack Compose environment. The primary entry point for developers is the `rememberMapLibreMapViewState` composable function. + +### `rememberMapLibreMapViewState` + +A Jetpack Compose composable function that creates and remembers an instance of `MapLibreViewState`. + +It is the recommended way to create a state object for your map. This function ensures that the map's state, including camera position and style, is correctly preserved across recompositions, configuration changes, and process death. + +#### Signature + +```kotlin +@Composable +fun rememberMapLibreMapViewState( + mapDesign: MapLibreMapDesignTypeInterface = MapLibreDesign.DemoTiles, + cameraPosition: MapCameraPositionInterface = MapCameraPosition.Default, +): MapLibreViewState +``` + +#### Parameters + +| Parameter | Type | Description | +|------------------|--------------------------------|---------------------------------------------------------------------------------------------------------| +| `mapDesign` | `MapLibreMapDesignTypeInterface` | The initial visual style and theme for the map. Defaults to `MapLibreDesign.DemoTiles`. | +| `cameraPosition` | `MapCameraPositionInterface` | The initial camera settings, including location, zoom, tilt, and bearing. Defaults to `MapCameraPosition.Default`. | + +#### Returns + +A stable `MapLibreViewState` instance that can be used to control the map and is automatically saved and restored. + +#### Example + +Here is a basic example of how to create a map state and pass it to a `MapLibreMapView` composable. + +```kotlin +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import com.mapconductor.core.features.GeoPoint +import com.mapconductor.core.map.MapCameraPosition +import com.mapconductor.maplibre.MapLibreDesign +import com.mapconductor.maplibre.rememberMapLibreMapViewState + +@Composable +fun MyMapScreen() { + // Create and remember the map state + val mapState = rememberMapLibreMapViewState( + mapDesign = MapLibreDesign.OsmBright, + cameraPosition = MapCameraPosition( + position = GeoPoint(48.8584, 2.2945), // Paris + zoom = 14.0 + ) + ) + + // The state is then passed to the MapView composable (assumed to exist) + /* + MapLibreMapView( + state = mapState, + modifier = Modifier.fillMaxSize() + ) + */ +} +``` + +--- + +### `MapLibreViewState` + +A state-holder class that manages the properties and interactions for a MapLibre map view. It holds the current camera position and map design and provides methods to programmatically control the map. An instance of this class is typically created and managed by the `rememberMapLibreMapViewState` composable. + +#### Class Signature + +```kotlin +class MapLibreViewState( + mapDesignType: MapLibreMapDesignTypeInterface, + override val id: String, + cameraPosition: MapCameraPosition = MapCameraPosition.Default, +) +``` + +#### Properties + +| Property | Type | Description | +|------------------|----------------------------------|----------------------------------------------------------------------------------------------------------------------------------------| +| `cameraPosition` | `MapCameraPosition` | (Read-only) Gets the current camera position of the map, including location, zoom, tilt, and bearing. | +| `mapDesignType` | `MapLibreMapDesignTypeInterface` | (Read-Write) Gets or sets the current map design/style. Setting a new value will asynchronously update the map's visual appearance. | + +#### Methods + +##### `moveCameraTo(position: GeoPoint, ...)` + +Moves the map camera to a specific geographic coordinate, preserving the current zoom, tilt, and bearing. + +**Signature** +```kotlin +fun moveCameraTo( + position: GeoPoint, + durationMillis: Long? = null, +) +``` + +**Parameters** +| Parameter | Type | Description | +|------------------|---------|---------------------------------------------------------------------------------------------------------| +| `position` | `GeoPoint` | The target geographic coordinate (`latitude`, `longitude`) to center the map on. | +| `durationMillis` | `Long?` | The duration of the camera animation in milliseconds. If `null` or `0`, the camera moves instantly. | + +##### `moveCameraTo(cameraPosition: MapCameraPosition, ...)` + +Moves the map camera to a new, fully specified camera position. + +**Signature** +```kotlin +fun moveCameraTo( + cameraPosition: MapCameraPosition, + durationMillis: Long? = null, +) +``` + +**Parameters** +| Parameter | Type | Description | +|------------------|---------------------|---------------------------------------------------------------------------------------------------------| +| `cameraPosition` | `MapCameraPosition` | The target camera state, including position, zoom, tilt, and bearing. | +| `durationMillis` | `Long?` | The duration of the camera animation in milliseconds. If `null` or `0`, the camera moves instantly. | + +--- + +### `MapLibreMapViewSaver` + +A `Saver` implementation for `MapLibreViewState`. It handles the serialization and deserialization of the map state, enabling it to be persisted via `rememberSaveable`. + +This class is used internally by `rememberMapLibreMapViewState` and developers typically do not need to interact with it directly unless implementing custom state-saving logic. + +#### Class Signature + +```kotlin +class MapLibreMapViewSaver : BaseMapViewSaver() +``` + +#### Description + +`MapLibreMapViewSaver` saves the essential properties of `MapLibreViewState`, such as the camera position and map style URL, into a `Bundle`. It can then reconstruct the `MapLibreViewState` from this `Bundle` when the composable is recreated, ensuring a seamless user experience across configuration changes. \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/circle/MapLibreCircleController.kt.md b/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/circle/MapLibreCircleController.kt.md new file mode 100644 index 00000000..efc3f826 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/circle/MapLibreCircleController.kt.md @@ -0,0 +1,62 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +--- + +### `MapLibreCircleController` + +A controller class that manages the lifecycle and rendering of circle overlays on a MapLibre map. + +#### Signature + +```kotlin +class MapLibreCircleController( + override val renderer: MapLibreCircleOverlayRenderer, + circleManager: CircleManagerInterface = CircleManager(), +) : CircleController(circleManager, renderer) +``` + +#### Description + +The `MapLibreCircleController` is the primary entry point for developers to manage and display circles on a MapLibre map. It orchestrates the interaction between the generic circle management logic (`CircleManager`) and the platform-specific rendering implementation (`MapLibreCircleOverlayRenderer`). + +This controller simplifies the process of adding, removing, and updating circles by providing a high-level API. It delegates the state management of circle objects to the `circleManager` and the actual drawing on the map canvas to the `renderer`. + +#### Parameters + +| Parameter | Type | Description | +|---|---|---| +| `renderer` | `MapLibreCircleOverlayRenderer` | **Required.** The renderer instance responsible for drawing the circles onto the MapLibre map. | +| `circleManager` | `CircleManagerInterface` | *Optional.* The manager that handles the collection of circles, including their properties and state. If not provided, a default `CircleManager()` instance is created. | + +#### Example + +The following example demonstrates how to initialize the `MapLibreCircleController` and use it to add and manage a circle on the map. + +```kotlin +// Assuming you have a MapLibre 'map' and 'style' object available. + +// 1. Initialize the renderer required by the controller. +val circleRenderer = MapLibreCircleOverlayRenderer(map, style) + +// 2. Create an instance of the MapLibreCircleController. +val circleController = MapLibreCircleController(renderer = circleRenderer) + +// 3. Define the properties for a new circle using CircleOptions. +val circleOptions = CircleOptions( + center = LatLng(40.7128, -74.0060), // New York City + radius = 800.0, // Radius in meters + fillColor = Color.argb(100, 33, 150, 243), // Semi-transparent blue + strokeColor = Color.BLUE, + strokeWidth = 3.0f +) + +// 4. Add the circle to the map using the controller. +val myCircle = circleController.add(circleOptions) + +// You can later update the circle's properties. +myCircle.radius = 1200.0 +myCircle.fillColor = Color.argb(100, 255, 87, 34) // Change to orange + +// To remove the circle from the map: +// circleController.remove(myCircle) +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/circle/MapLibreCircleLayer.kt.md b/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/circle/MapLibreCircleLayer.kt.md new file mode 100644 index 00000000..1b386763 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/circle/MapLibreCircleLayer.kt.md @@ -0,0 +1,164 @@ +Of course. Here is the high-quality SDK documentation for the `MapLibreCircleLayer` class, meticulously crafted to be clear, accurate, and developer-friendly, with all feedback incorporated. + +--- + +# MapLibreCircleLayer + +The `MapLibreCircleLayer` class is a comprehensive utility for managing and rendering circles on a MapLibre map. It encapsulates a `GeoJsonSource` and a `CircleLayer`, simplifying the process of drawing circles whose radii are defined in meters. + +The class automatically handles the complex conversion from real-world meters to screen pixels, ensuring circles maintain their correct proportional size across different zoom levels and latitudes. + +## Signature + +```kotlin +class MapLibreCircleLayer( + val sourceId: String, + val layerId: String +) +``` + +## Description + +This class creates and manages the necessary MapLibre components (`GeoJsonSource` and `CircleLayer`) for displaying circles. You instantiate it with unique IDs for the source and layer. The `layer` property is pre-configured with expressions to dynamically style circles based on properties set in the GeoJSON features. The primary method, `draw()`, is used to update the source with a list of circle entities, causing them to be rendered on the map. + +## Parameters + +| Parameter | Type | Description | +|------------|----------|--------------------------------------------------------------------------| +| `sourceId` | `String` | A unique identifier for the underlying `GeoJsonSource` that will hold the circle data. | +| `layerId` | `String` | A unique identifier for the `CircleLayer` that will render the circles. | + +## Properties + +### `source: GeoJsonSource` + +The `GeoJsonSource` instance managed by this class. You must add this source to the map's `Style` before drawing. + +### `layer: CircleLayer` + +The `CircleLayer` instance managed by this class. It is pre-configured to style circles using properties from the source. You must add this layer to the map's `Style` before drawing. + +## Inner Object: `Prop` + +The `Prop` object contains constant keys used to define the properties of each circle within its GeoJSON `Feature`. + +| Constant | Type | Description | +|-----------------------|----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `RADIUS` | `String` | The key for the circle's radius in meters. | +| `LATITUDE_CORRECTION` | `String` | The key for the latitude correction factor, required for accurate radius rendering in Mercator projection. Calculated as `cos(Math.toRadians(latitude))`. | +| `FILL_COLOR` | `String` | The key for the circle's fill color, expressed as a string (e.g., `"#FF0000"`). | +| `STROKE_COLOR` | `String` | The key for the circle's stroke (outline) color, expressed as a string. | +| `STROKE_WIDTH` | `String` | The key for the circle's stroke width in pixels. | +| `Z_INDEX` | `String` | The key for the circle's sort key. Higher values are drawn on top of lower values. This controls the rendering order of overlapping circles within this same layer. | + +## Methods + +### `draw` + +Updates the `GeoJsonSource` with a list of circle entities, redrawing them on the map. + +#### Signature + +```kotlin +fun draw( + entities: List>, + style: org.maplibre.android.maps.Style +) +``` + +#### Description + +This method takes a list of entities that represent circles and updates the map's data source. It extracts the GeoJSON `Feature` from each entity and sets it on the `GeoJsonSource`. If the source is already part of the map's style, it will be updated efficiently. Otherwise, the internal source object is updated, which will be used when the source is next added to the style. + +#### Parameters + +| Parameter | Type | Description | +|------------|-----------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `entities` | `List>` | A list of circle entities to be drawn on the map. Each entity must provide a `MapLibreActualCircle` (which is a `typealias` for a MapLibre `Feature`) via its `circle` property. | +| `style` | `org.maplibre.android.maps.Style` | The current `Style` object from the MapLibre map. This is used to find and update the source directly for better performance. | + +#### Returns + +This method does not return any value. + +## Example + +The following example demonstrates how to initialize `MapLibreCircleLayer`, add it to a map, and draw a circle. + +```kotlin +import com.google.gson.JsonObject +import com.mapconductor.core.circle.CircleEntityInterface +import com.mapconductor.maplibre.MapLibreActualCircle // This is a typealias for org.maplibre.geojson.Feature +import com.mapconductor.maplibre.circle.MapLibreCircleLayer +import org.maplibre.android.maps.MapView +import org.maplibre.android.maps.MapLibreMap +import org.maplibre.android.maps.OnMapReadyCallback +import org.maplibre.android.maps.Style +import org.maplibre.geojson.Feature +import org.maplibre.geojson.Point +import kotlin.math.cos +import kotlin.math.toRadians + +// Assume this is in your Activity or Fragment that contains a MapView. +class MyMapActivity : AppCompatActivity(), OnMapReadyCallback { + + private lateinit var mapView: MapView + private lateinit var maplibreMap: MapLibreMap + private lateinit var circleLayer: MapLibreCircleLayer + + // For demonstration, we define a simple entity that conforms to the interface. + // In a real app, this would be part of your domain model. + data class MyCircleEntity(override val circle: MapLibreActualCircle) : CircleEntityInterface + + override fun onMapReady(maplibreMap: MapLibreMap) { + this.maplibreMap = maplibreMap + maplibreMap.setStyle(Style.Builder().fromUri(Style.MAPTILER_STREETS)) { style -> + // 1. Initialize the MapLibreCircleLayer + circleLayer = MapLibreCircleLayer( + sourceId = "my-circle-source", + layerId = "my-circle-layer" + ) + + // 2. Add the source and layer to the map's style + style.addSource(circleLayer.source) + style.addLayer(circleLayer.layer) + + // 3. Create the circle entities to draw + val circleEntities = createCircleEntities() + + // 4. Call draw to render the circles + circleLayer.draw(circleEntities, style) + } + } + + private fun createCircleEntities(): List> { + val latitude = 35.6812 + val longitude = 139.7671 // Tokyo Station + + // Create the GeoJSON properties for the circle + val properties = JsonObject().apply { + addProperty(MapLibreCircleLayer.Prop.RADIUS, 500.0) // 500 meters + addProperty(MapLibreCircleLayer.Prop.FILL_COLOR, "#E57373") // Red fill + addProperty(MapLibreCircleLayer.Prop.STROKE_COLOR, "#B71C1C") // Dark red stroke + addProperty(MapLibreCircleLayer.Prop.STROKE_WIDTH, 2.5f) + addProperty(MapLibreCircleLayer.Prop.Z_INDEX, 10.0) + // The latitude correction is crucial for accurate radius rendering + addProperty(MapLibreCircleLayer.Prop.LATITUDE_CORRECTION, cos(latitude.toRadians())) + } + + // Create the GeoJSON Feature for the circle + val circleFeature = Feature.fromGeometry( + Point.fromLngLat(longitude, latitude), + properties + ) + + // Wrap the feature in our entity class + val myCircle = MyCircleEntity(circleFeature) + + // Return a list of entities. The type matches the 'draw' method signature. + return listOf(myCircle) + } + + // ... other lifecycle methods for MapView +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/circle/MapLibreCircleOverlayRenderer.kt.md b/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/circle/MapLibreCircleOverlayRenderer.kt.md new file mode 100644 index 00000000..1a4ad4cc --- /dev/null +++ b/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/circle/MapLibreCircleOverlayRenderer.kt.md @@ -0,0 +1,152 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +--- + +# MapLibreCircleOverlayRenderer + +## Description + +The `MapLibreCircleOverlayRenderer` is a concrete implementation of `AbstractCircleOverlayRenderer` responsible for rendering and managing circle overlays on a MapLibre map. It acts as a bridge between the abstract `CircleState` provided by the core framework and the tangible `Feature` objects that MapLibre uses to display circles on a map layer. + +This renderer handles the creation, update, and removal of circles by translating their properties (such as center, radius, color, and stroke) into MapLibre-compatible feature properties. It works in conjunction with a `MapLibreCircleLayer` to perform the final drawing operations on the map. + +### Constructor + +```kotlin +class MapLibreCircleOverlayRenderer( + val layer: MapLibreCircleLayer, + val circleManager: CircleManagerInterface, + override val holder: MapLibreMapViewHolderInterface, + override val coroutine: CoroutineScope = CoroutineScope(Dispatchers.Main), +) : AbstractCircleOverlayRenderer() +``` + +Initializes a new instance of the `MapLibreCircleOverlayRenderer`. + +### Parameters + +| Parameter | Type | Description | +| :-------------- | :----------------------------------------- | :--------------------------------------------------------------------------------------------------------- | +| `layer` | `MapLibreCircleLayer` | The layer responsible for drawing the circle features on the map. | +| `circleManager` | `CircleManagerInterface` | The manager that holds the state of all circle entities. | +| `holder` | `MapLibreMapViewHolderInterface` | The view holder interface providing access to the map instance and its controller. | +| `coroutine` | `CoroutineScope` | The coroutine scope used for launching asynchronous operations, defaulting to `Dispatchers.Main`. | + +--- + +## Methods + +### createCircle + +```kotlin +override suspend fun createCircle(state: CircleState): MapLibreActualCircle? +``` + +Creates a new MapLibre `Feature` that represents a circle on the map. This method translates the properties from the abstract `CircleState` into a concrete GeoJSON `Feature` with properties that the `MapLibreCircleLayer` can interpret and render. + +For geodesic circles, it calculates a latitude correction factor to ensure the circle's radius is rendered accurately in meters across different latitudes. + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :------------ | :------------------------------------------------- | +| `state` | `CircleState` | The state object containing the circle's properties. | + +#### Returns + +| Type | Description | +| :--------------------- | :------------------------------------------------------------------------------------------------------ | +| `MapLibreActualCircle?` | A MapLibre `Feature` representing the circle, or `null` if creation fails. This is a typealias for `Feature`. | + +--- + +### updateCircleProperties + +```kotlin +override suspend fun updateCircleProperties( + circle: MapLibreActualCircle, + current: CircleEntityInterface, + prev: CircleEntityInterface, +): MapLibreActualCircle? +``` + +Updates the properties of an existing circle by creating a new `Feature` with the latest state. The underlying rendering mechanism replaces the old feature with this new one. + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :------------------------------------------- | :---------------------------------------------------- | +| `circle` | `MapLibreActualCircle` | The actual MapLibre `Feature` object to be updated. | +| `current` | `CircleEntityInterface` | The entity wrapper containing the new, updated state. | +| `prev` | `CircleEntityInterface` | The entity wrapper containing the previous state. | + +#### Returns + +| Type | Description | +| :--------------------- | :------------------------------------------------------------------------------------------------------ | +| `MapLibreActualCircle?` | A new MapLibre `Feature` with the updated properties, or `null` if the update fails. This is a typealias for `Feature`. | + +--- + +### removeCircle + +```kotlin +override suspend fun removeCircle(entity: CircleEntityInterface) +``` + +Handles the removal of a circle. This method is intentionally empty. Circle removal is managed implicitly by the `onPostProcess` method, which redraws the entire layer using only the currently active circles from the `circleManager`. Any circle marked for removal will simply be excluded from the next draw cycle. + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :------------------------------------------- | :----------------------------------------- | +| `entity` | `CircleEntityInterface` | The circle entity scheduled for removal. | + +--- + +### onPostProcess + +```kotlin +override suspend fun onPostProcess() +``` + +This method is called after all individual create, update, and remove operations in a render pass are complete. It fetches the complete list of current circle features from the `circleManager` and instructs the `MapLibreCircleLayer` to draw them on the map. This ensures that all changes are batched and rendered together efficiently. + +--- + +## Example + +The following example demonstrates how to set up and instantiate the `MapLibreCircleOverlayRenderer` within a typical application structure. + +```kotlin +import com.mapconductor.maplibre.circle.MapLibreCircleLayer +import com.mapconductor.maplibre.circle.MapLibreCircleOverlayRenderer +import com.mapconductor.core.circle.CircleManager +import com.mapconductor.maplibre.MapLibreMapViewHolderInterface +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import org.maplibre.maps.Style + +// Assume these dependencies are provided by your application +val mapViewHolder: MapLibreMapViewHolderInterface = // ... get your map view holder +val style: Style = // ... get the map's style instance +val mainScope = CoroutineScope(Dispatchers.Main) + +// 1. Initialize the layer that will draw the circles +val circleLayer = MapLibreCircleLayer(layerId = "my-circle-layer") + +// 2. Initialize the manager to hold circle data +val circleManager = CircleManager() + +// 3. Create the renderer instance +val circleRenderer = MapLibreCircleOverlayRenderer( + layer = circleLayer, + circleManager = circleManager, + holder = mapViewHolder, + coroutine = mainScope +) + +// The circleRenderer is now set up. The broader map framework would +// typically call its methods (createCircle, onPostProcess, etc.) +// automatically as you add, update, or remove circles via the circleManager. +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/groundimage/MapLibreGroundImageController.kt.md b/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/groundimage/MapLibreGroundImageController.kt.md new file mode 100644 index 00000000..89ffa612 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/groundimage/MapLibreGroundImageController.kt.md @@ -0,0 +1,74 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +--- + +## Class `MapLibreGroundImageController` + +A controller responsible for managing the lifecycle of ground image overlays on a MapLibre map. It serves as a specialized implementation of `GroundImageController`, connecting the generic state management of `GroundImageManager` with the MapLibre-specific rendering logic of `MapLibreGroundImageOverlayRenderer`. + +This controller is essential for handling map-specific events, such as style changes, that require ground images to be re-rendered. + +```kotlin +class MapLibreGroundImageController( + groundImageManager: GroundImageManagerInterface = GroundImageManager(), + renderer: MapLibreGroundImageOverlayRenderer, +) : GroundImageController(groundImageManager, renderer) +``` + +### Constructor + +Initializes a new instance of the `MapLibreGroundImageController`. + +| Parameter | Type | Description | Default | +| ------------------ | ------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | -------------------- | +| `groundImageManager` | `GroundImageManagerInterface` | The manager responsible for tracking the state and properties of all ground images. | `GroundImageManager()` | +| `renderer` | `MapLibreGroundImageOverlayRenderer` | The renderer responsible for drawing and managing ground image layers and sources on the MapLibre map. | - | + +--- + +### Functions + +#### `reapplyStyle` + +Re-adds all managed ground images to the map. This function is crucial for scenarios where the map's style is reloaded, a process that typically destroys all existing layers and sources. + +`reapplyStyle` works by retrieving the saved state of all current ground images, instructing the renderer to add them back to the newly loaded map style, and then updating the internal manager to track the newly created map objects. + +**Signature** + +```kotlin +suspend fun reapplyStyle() +``` + +**Description** + +This is a `suspend` function and must be called from a coroutine or another `suspend` function. It ensures that all ground image overlays persist across map style changes. + +**Parameters** + +This method has no parameters. + +**Returns** + +This method does not return any value. + +**Example** + +```kotlin +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch + +// Assuming 'map' is your MapLibre Map instance and 'style' is the loaded Style +// val renderer = MapLibreGroundImageOverlayRenderer(map, style) +// val groundImageController = MapLibreGroundImageController(renderer = renderer) + +// This would typically be called within a listener for map style changes. +fun onMapStyleLoaded() { + // Use a coroutine scope to call the suspend function + CoroutineScope(Dispatchers.Main).launch { + // Re-add all previously managed ground images to the new style + groundImageController.reapplyStyle() + } +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/groundimage/MapLibreGroundImageHandle.kt.md b/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/groundimage/MapLibreGroundImageHandle.kt.md new file mode 100644 index 00000000..dcee2335 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/groundimage/MapLibreGroundImageHandle.kt.md @@ -0,0 +1,56 @@ +# MapLibreGroundImageHandle + +A data class that encapsulates all the necessary information and resources for managing a ground image layer on a MapLibre map. + +This handle acts as a container for identifiers, caching keys, and the data provider required to display and manage the lifecycle of a ground image. It is typically returned when a ground image is added to the map and can be used later to reference or remove it. + +## Signature + +```kotlin +data class MapLibreGroundImageHandle( + val routeId: String, + val generation: Long, + val cacheKey: String, + val sourceId: String, + val layerId: String, + val tileProvider: GroundImageTileProvider, +) +``` + +## Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `routeId` | `String` | A unique identifier for the route or entity associated with the ground image. | +| `generation` | `Long` | A version number for the ground image data, often used for cache invalidation and tracking updates. | +| `cacheKey` | `String` | A unique key used for caching the ground image tiles. | +| `sourceId` | `String` | The ID of the MapLibre `Source` that provides the data for the ground image layer. | +| `layerId` | `String` | The ID of the MapLibre `Layer` that renders the ground image on the map. | +| `tileProvider` | `GroundImageTileProvider` | The provider instance responsible for fetching and supplying the ground image tiles. | + +## Example + +The following example demonstrates how to create an instance of `MapLibreGroundImageHandle`. + +```kotlin +import com.mapconductor.core.groundimage.GroundImageTileProvider +import com.mapconductor.maplibre.groundimage.MapLibreGroundImageHandle + +// Assume 'myTileProvider' is an existing instance of a class +// that implements the GroundImageTileProvider interface. +val myTileProvider: GroundImageTileProvider = /* ... implementation ... */ + +// Create an instance of MapLibreGroundImageHandle +val groundImageHandle = MapLibreGroundImageHandle( + routeId = "route-abc-456", + generation = System.currentTimeMillis(), + cacheKey = "route-abc-456-ground-image-v2", + sourceId = "ground-image-source-456", + layerId = "ground-image-layer-456", + tileProvider = myTileProvider +) + +// The handle can now be used to manage the ground image layer, +// for example, to remove it from the map later. +println("Created handle for layer: ${groundImageHandle.layerId}") +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/groundimage/MapLibreGroundImageOverlayRenderer.kt.md b/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/groundimage/MapLibreGroundImageOverlayRenderer.kt.md new file mode 100644 index 00000000..a4062c33 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/groundimage/MapLibreGroundImageOverlayRenderer.kt.md @@ -0,0 +1,178 @@ +# MapLibreGroundImageOverlayRenderer + +The `MapLibreGroundImageOverlayRenderer` class is responsible for rendering and managing ground image overlays on a MapLibre map. It implements the `AbstractGroundImageOverlayRenderer` and handles the complete lifecycle of a ground image, including its creation, updates to its properties, and its removal. + +This renderer operates by generating image tiles from a source image and its geographical bounds. It uses a `LocalTileServer` to serve these tiles to the MapLibre map, which then displays them as a `RasterLayer`. + +## Signature + +```kotlin +class MapLibreGroundImageOverlayRenderer( + override val holder: MapLibreMapViewHolderInterface, + private val tileServer: LocalTileServer, + override val coroutine: CoroutineScope = CoroutineScope(Dispatchers.Main), +) : AbstractGroundImageOverlayRenderer() +``` + +## Constructor + +Initializes a new instance of `MapLibreGroundImageOverlayRenderer`. + +### Parameters + +| Parameter | Type | Description | +| :---------- | :----------------------------- | :------------------------------------------------------------------------------------------------------ | +| `holder` | `MapLibreMapViewHolderInterface` | The view holder that provides access to the MapLibre map instance and its style. | +| `tileServer`| `LocalTileServer` | A local server instance used to serve the generated image tiles to the map. | +| `coroutine` | `CoroutineScope` | The coroutine scope for running asynchronous operations. Defaults to `CoroutineScope(Dispatchers.Main)`. | + +--- + +## Methods + +### createGroundImage + +Asynchronously creates a new ground image overlay on the map based on the provided state. It sets up a tile provider, registers it with the `LocalTileServer`, and adds the necessary `RasterSource` and `RasterLayer` to the map style. + +#### Signature + +```kotlin +override suspend fun createGroundImage(state: GroundImageState): MapLibreActualGroundImage? +``` + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :--------------- | :------------------------------------------------------------------------------------------------------ | +| `state` | `GroundImageState` | An object containing the configuration for the ground image, such as the image, geographical bounds, and tile size. | + +#### Returns + +| Type | Description | +| :-------------------------- | :------------------------------------------------------------------------------------------------------------------------------------- | +| `MapLibreActualGroundImage?`| A handle to the created ground image (`MapLibreActualGroundImage`) if successful, or `null` if the map style is not available or creation fails. This handle is used for subsequent updates and removal. | + +--- + +### updateGroundImageProperties + +Asynchronously updates an existing ground image overlay. It efficiently checks for changes in properties like opacity, bounds, or the image itself. If the tile content needs to be regenerated (e.g., bounds or image changed), it creates a new source and layer. If only opacity changes, it updates the existing layer's properties. + +#### Signature + +```kotlin +override suspend fun updateGroundImageProperties( + groundImage: MapLibreActualGroundImage, + current: GroundImageEntityInterface, + prev: GroundImageEntityInterface, +): MapLibreActualGroundImage? +``` + +#### Parameters + +| Parameter | Type | Description | +| :------------ | :---------------------------------------------------- | :----------------------------------------------------------------------------- | +| `groundImage` | `MapLibreActualGroundImage` | The handle of the ground image to update, returned from `createGroundImage`. | +| `current` | `GroundImageEntityInterface` | The entity representing the new state of the ground image. | +| `prev` | `GroundImageEntityInterface` | The entity representing the previous state of the ground image, used for diffing. | + +#### Returns + +| Type | Description | +| :-------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `MapLibreActualGroundImage?`| A new handle to the updated ground image if changes required recreating the source/layer. Returns the original handle if only properties were updated, or `null` on failure (e.g., map style not available). | + +--- + +### removeGroundImage + +Asynchronously removes a ground image overlay from the map. It removes the associated `RasterLayer` and `RasterSource` from the map style and unregisters the tile provider from the `LocalTileServer`. + +#### Signature + +```kotlin +override suspend fun removeGroundImage(entity: GroundImageEntityInterface) +``` + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :---------------------------------------------------- | :----------------------------------------------------------------------- | +| `entity` | `GroundImageEntityInterface` | The entity whose associated ground image overlay should be removed from the map. | + +#### Returns + +This function does not return a value. + +--- + +## Example + +The following example demonstrates the basic lifecycle of managing a ground image overlay using `MapLibreGroundImageOverlayRenderer`. + +```kotlin +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking + +// Assume these are initialized elsewhere in your application +val mapViewHolder: MapLibreMapViewHolderInterface = getMapViewHolder() +val localTileServer: LocalTileServer = getLocalTileServer() +val coroutineScope = CoroutineScope(Dispatchers.Main) + +// 1. Initialize the renderer +val groundImageRenderer = MapLibreGroundImageOverlayRenderer( + holder = mapViewHolder, + tileServer = localTileServer, + coroutine = coroutineScope +) + +// 2. Define the initial state for the ground image +val initialImage = getYourBitmap() // Your image source +val initialBounds = getYourLatLngBounds() // The geographic area for the image +val initialState = GroundImageState( + id = "historic-map-1890", + image = initialImage, + bounds = initialBounds, + opacity = 0.75f, + tileSize = 512 +) + +// A placeholder for the entity that holds the ground image state and handle +var groundImageEntity: GroundImageEntityInterface? = null + +runBlocking { + // 3. Create the ground image on the map + val groundImageHandle = groundImageRenderer.createGroundImage(initialState) + if (groundImageHandle != null) { + // Store the handle and state in an entity for future reference + groundImageEntity = createEntity(initialState, groundImageHandle) + println("Ground image created successfully.") + } else { + println("Failed to create ground image.") + return@runBlocking + } + + // 4. Update the ground image (e.g., change opacity) + val updatedState = initialState.copy(opacity = 1.0f) + val previousEntity = groundImageEntity!! + val updatedEntity = createEntity(updatedState, previousEntity.groundImage) + + val updatedHandle = groundImageRenderer.updateGroundImageProperties( + groundImage = previousEntity.groundImage, + current = updatedEntity, + prev = previousEntity + ) + + if (updatedHandle != null) { + // Update the entity with the new handle if it changed + groundImageEntity = updatedEntity.copy(groundImage = updatedHandle) + println("Ground image updated successfully.") + } else { + println("Failed to update ground image.") + } + + // 5. Remove the ground image from the map + groundImageRenderer.removeGroundImage(groundImageEntity!!) + println("Ground image removed.") +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/marker/MapLibreMarkerController.kt.md b/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/marker/MapLibreMarkerController.kt.md new file mode 100644 index 00000000..62b88779 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/marker/MapLibreMarkerController.kt.md @@ -0,0 +1,189 @@ +# MapLibreMarkerController + +The `MapLibreMarkerController` class is responsible for managing the lifecycle of markers on a MapLibre map. It handles adding, updating, removing, and finding markers. For performance optimization with a large number of markers, it can automatically switch to rendering markers as raster tiles. + +This controller extends `AbstractMarkerController` and works in conjunction with a `MapLibreMarkerOverlayRenderer` to draw markers on the map. + +## Signature + +```kotlin +class MapLibreMarkerController( + override val renderer: MapLibreMarkerOverlayRenderer, + private val markerTiling: MarkerTilingOptions = MarkerTilingOptions.Default, +) : AbstractMarkerController +``` + +## Constructor + +Creates an instance of the marker controller. + +### Parameters + +| Parameter | Type | Description | +| :------------- | :------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------- | +| `renderer` | `MapLibreMarkerOverlayRenderer` | The renderer responsible for drawing markers and overlays on the map. | +| `markerTiling` | `MarkerTilingOptions` | (Optional) Configuration for the marker tiling feature, which improves performance for large datasets. Defaults to `MarkerTilingOptions.Default`. | + +--- + +## Methods + +### setRasterLayerCallback + +Registers a callback to be invoked when the raster layer for tiled markers is created, updated, or removed. This is essential for integrating the tiled marker layer into the map's style, as the map needs to be told to add, update, or remove the corresponding `RasterLayerState`. + +#### Signature + +```kotlin +fun setRasterLayerCallback(callback: MarkerTileRasterLayerCallback?) +``` + +#### Parameters + +| Parameter | Type | Description | +| :--------- | :-------------------------------- | :------------------------------------------------------------------------------------------------------ | +| `callback` | `MarkerTileRasterLayerCallback?` | The callback to handle raster layer state changes. Pass `null` to remove the currently registered callback. | + +--- + +### find + +Finds the nearest marker to a given geographic coordinate. The search considers the marker's icon size, anchor point, and a predefined tap tolerance, making it ideal for implementing marker click/tap listeners. + +#### Signature + +```kotlin +override fun find(position: GeoPointInterface): MarkerEntityInterface? +``` + +#### Parameters + +| Parameter | Type | Description | +| :--------- | :------------------ | :----------------------------------------------------------------------- | +| `position` | `GeoPointInterface` | The geographic coordinate (e.g., from a map tap event) to search around. | + +#### Returns + +`MarkerEntityInterface?` — The found marker entity, or `null` if no marker is within the tap tolerance of the specified position. + +--- + +### add + +Asynchronously adds a list of new markers to the map. Based on the `MarkerTilingOptions` and the number of markers, the controller will decide whether to render them individually or as part of a raster tile layer for better performance. + +#### Signature + +```kotlin +override suspend fun add(data: List) +``` + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :------------------ | :----------------------------------------------------------------------- | +| `data` | `List` | A list of `MarkerState` objects, each defining the properties of a marker. | + +--- + +### update + +Asynchronously updates the state of an existing marker, identified by `state.id`. If the marker's properties have changed, it will be redrawn. This method also handles the transition of a marker between being rendered individually and as part of a tile layer (e.g., if a marker becomes non-draggable, it may be moved to the tile layer). + +#### Signature + +```kotlin +override suspend fun update(state: MarkerState) +``` + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :------------ | :------------------------------------------------------------------------------------------------------ | +| `state` | `MarkerState` | The new state for the marker. The `id` field must match the ID of an existing marker to be updated. | + +--- + +### clear + +Asynchronously removes all markers from the map and cleans up any associated tiled raster layers. + +#### Signature + +```kotlin +override suspend fun clear() +``` + +--- + +### destroy + +Releases all resources held by the controller. This includes unregistering the tile provider from the `TileServerRegistry` and signaling the removal of any active raster layers. This method must be called when the controller is no longer needed (e.g., when the map view is destroyed) to prevent memory leaks. + +#### Signature + +```kotlin +override fun destroy() +``` + +--- + +## Example + +The following example demonstrates the basic lifecycle of using `MapLibreMarkerController`. + +```kotlin +// Assume 'map' is your MapLibre map instance and 'renderer' is an initialized MapLibreMarkerOverlayRenderer. +// 1. Initialize the controller +val markerController = MapLibreMarkerController(renderer) + +// 2. Set the raster layer callback to handle tiled markers +markerController.setRasterLayerCallback { rasterLayerState -> + // This lambda is called when the tiled marker layer changes. + // You should add, update, or remove this layer from your map's style. + if (rasterLayerState != null) { + myMap.style.updateLayer(rasterLayerState) + } else { + // The ID of the layer is consistent, so you can remove it by ID. + myMap.style.removeLayer("marker-tile-...") + } +} + +// Coroutine scope for async operations +val coroutineScope = CoroutineScope(Dispatchers.Main) + +coroutineScope.launch { + // 3. Add markers to the map + val markersToAdd = listOf( + MarkerState(id = "marker1", position = GeoPoint(40.7128, -74.0060)), + MarkerState(id = "marker2", position = GeoPoint(34.0522, -118.2437)) + ) + markerController.add(markersToAdd) + println("Added 2 markers.") + + // 4. Update a marker + val updatedMarkerState = MarkerState( + id = "marker1", + position = GeoPoint(40.7128, -74.0060), + icon = MyCustomIcon(), // Change the icon + draggable = true + ) + markerController.update(updatedMarkerState) + println("Updated marker1.") + + // 5. Find a marker at a specific location (e.g., after a map tap) + val tappedPoint = GeoPoint(40.7128, -74.0060) + val foundMarker = markerController.find(tappedPoint) + if (foundMarker != null) { + println("Found marker with ID: ${foundMarker.state.id}") + } + + // 6. Clear all markers + markerController.clear() + println("All markers cleared.") +} + +// 7. Clean up resources when the controller is no longer needed +// This should typically be done in a lifecycle method like onDestroy(). +markerController.destroy() +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/marker/MapLibreMarkerEventController.kt.md b/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/marker/MapLibreMarkerEventController.kt.md new file mode 100644 index 00000000..8dbf1ec7 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/marker/MapLibreMarkerEventController.kt.md @@ -0,0 +1,353 @@ +# MapLibreMarkerEventControllerInterface + +## Description + +The `MapLibreMarkerEventControllerInterface` defines the contract for managing and dispatching user interaction events for markers on a MapLibre map. It provides a unified API for handling marker selection, finding markers at specific coordinates, and setting up listeners for various events like clicks, drags, and animations. + +This interface is implemented by two internal classes: +- `DefaultMapLibreMarkerEventController`: A standard implementation that delegates event handling directly to a `MapLibreMarkerController`. +- `StrategyMapLibreMarkerEventController`: An implementation designed to work with a `StrategyMarkerController`, providing more complex state management for selected markers, especially during drag operations. + +## Properties + +### renderer + +Provides access to the `MapLibreMarkerOverlayRenderer` responsible for drawing markers and their overlays on the map. + +**Signature** +```kotlin +val renderer: MapLibreMarkerOverlayRenderer +``` + +## Methods + +### find + +Finds the topmost marker entity at a given geographical position. + +#### Signature + +```kotlin +fun find(position: GeoPointInterface): MarkerEntityInterface? +``` + +#### Description + +Searches for a marker on the map that intersects with the specified `position`. If multiple markers are at the same location, it returns the one with the highest z-index. + +#### Parameters + +| Parameter | Type | Description | +| :--------- | :------------------ | :---------------------------------------- | +| `position` | `GeoPointInterface` | The geographical coordinate to search at. | + +#### Returns + +| Type | Description | +| :--------------------------------------------------- | :----------------------------------------------------------------------- | +| `MarkerEntityInterface?` | The found marker entity, or `null` if no marker exists at that position. | + +--- + +### getSelectedMarker + +Retrieves the currently selected marker entity. + +#### Signature + +```kotlin +fun getSelectedMarker(): MarkerEntityInterface? +``` + +#### Description + +Returns the marker entity that is currently in a "selected" state. A selected state is typically initiated by a user tap or can be set programmatically via `setSelectedMarker`. + +#### Returns + +| Type | Description | +| :--------------------------------------------------- | :-------------------------------------------------- | +| `MarkerEntityInterface?` | The currently selected marker entity, or `null` if none is selected. | + +--- + +### setSelectedMarker + +Sets or clears the currently selected marker. + +#### Signature + +```kotlin +fun setSelectedMarker(entity: MarkerEntityInterface?) +``` + +#### Description + +Programmatically sets the selection state for a marker. Passing a marker `entity` will select it. Passing `null` will deselect any currently selected marker. This can be used to highlight a marker or initiate a specific UI state. + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :--------------------------------------------------- | :----------------------------------------------------------------------- | +| `entity` | `MarkerEntityInterface?` | The marker entity to select, or `null` to clear the current selection. | + +--- + +### dispatchClick + +Dispatches a click event for a given marker state. + +#### Signature + +```kotlin +fun dispatchClick(state: MarkerState) +``` + +#### Description + +This method is typically called internally by the map's gesture handler when a tap is detected on a marker. It triggers the `OnMarkerEventHandler` set by `setClickListener`. + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :------------ | :---------------------------------------- | +| `state` | `MarkerState` | The state of the marker that was clicked. | + +--- + +### dispatchDragStart + +Dispatches a drag start event for a given marker state. + +#### Signature + +```kotlin +fun dispatchDragStart(state: MarkerState) +``` + +#### Description + +This method is typically called internally when a drag gesture is initiated on a marker. It triggers the `OnMarkerEventHandler` set by `setDragStartListener`. + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :------------ | :----------------------------------------------- | +| `state` | `MarkerState` | The state of the marker when the drag started. | + +--- + +### dispatchDrag + +Dispatches a drag event for a given marker state. + +#### Signature + +```kotlin +fun dispatchDrag(state: MarkerState) +``` + +#### Description + +This method is called internally during a drag gesture on a marker. It triggers the `OnMarkerEventHandler` set by `setDragListener`, providing continuous updates on the marker's state as it is being dragged. + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :------------ | :--------------------------------------------- | +| `state` | `MarkerState` | The current state of the marker during the drag. | + +--- + +### dispatchDragEnd + +Dispatches a drag end event for a given marker state. + +#### Signature + +```kotlin +fun dispatchDragEnd(state: MarkerState) +``` + +#### Description + +This method is called internally when a drag gesture on a marker concludes. It triggers the `OnMarkerEventHandler` set by `setDragEndListener`. + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :------------ | :------------------------------------------- | +| `state` | `MarkerState` | The final state of the marker after the drag. | + +--- + +### setClickListener + +Sets a listener to handle marker click events. + +#### Signature + +```kotlin +fun setClickListener(listener: OnMarkerEventHandler?) +``` + +#### Description + +Registers a callback that will be invoked whenever a marker is clicked. The listener receives the `MarkerState` of the clicked marker. Set the listener to `null` to remove it. + +#### Parameters + +| Parameter | Type | Description | +| :--------- | :--------------------- | :----------------------------------------------------------------------- | +| `listener` | `OnMarkerEventHandler?` | The callback to invoke on a marker click, or `null` to clear the listener. | + +--- + +### setDragStartListener + +Sets a listener to handle the start of a marker drag event. + +#### Signature + +```kotlin +fun setDragStartListener(listener: OnMarkerEventHandler?) +``` + +#### Description + +Registers a callback that will be invoked when a user begins dragging a marker. The listener receives the `MarkerState` of the marker at the beginning of the drag operation. Set the listener to `null` to remove it. + +#### Parameters + +| Parameter | Type | Description | +| :--------- | :--------------------- | :------------------------------------------------------------------------------- | +| `listener` | `OnMarkerEventHandler?` | The callback to invoke when a marker drag starts, or `null` to clear the listener. | + +--- + +### setDragListener + +Sets a listener to handle continuous marker drag events. + +#### Signature + +```kotlin +fun setDragListener(listener: OnMarkerEventHandler?) +``` + +#### Description + +Registers a callback that will be invoked repeatedly as a user drags a marker across the map. This is useful for tracking the marker's position in real-time during the drag. Set the listener to `null` to remove it. + +#### Parameters + +| Parameter | Type | Description | +| :--------- | :--------------------- | :--------------------------------------------------------------------------------- | +| `listener` | `OnMarkerEventHandler?` | The callback to invoke as a marker is being dragged, or `null` to clear the listener. | + +--- + +### setDragEndListener + +Sets a listener to handle the end of a marker drag event. + +#### Signature + +```kotlin +fun setDragEndListener(listener: OnMarkerEventHandler?) +``` + +#### Description + +Registers a callback that will be invoked when a user releases a marker after dragging it. The listener receives the final `MarkerState` of the marker. Set the listener to `null` to remove it. + +#### Parameters + +| Parameter | Type | Description | +| :--------- | :--------------------- | :----------------------------------------------------------------------------- | +| `listener` | `OnMarkerEventHandler?` | The callback to invoke when a marker drag ends, or `null` to clear the listener. | + +--- + +### setAnimateStartListener + +Sets a listener to handle the start of a marker animation. + +#### Signature + +```kotlin +fun setAnimateStartListener(listener: OnMarkerEventHandler?) +``` + +#### Description + +Registers a callback that will be invoked when a marker's animation begins. The listener receives the `MarkerState` of the marker at the start of the animation. Set the listener to `null` to remove it. + +#### Parameters + +| Parameter | Type | Description | +| :--------- | :--------------------- | :----------------------------------------------------------------------------------- | +| `listener` | `OnMarkerEventHandler?` | The callback to invoke when a marker animation starts, or `null` to clear the listener. | + +--- + +### setAnimateEndListener + +Sets a listener to handle the end of a marker animation. + +#### Signature + +```kotlin +fun setAnimateEndListener(listener: OnMarkerEventHandler?) +``` + +#### Description + +Registers a callback that will be invoked when a marker's animation completes. The listener receives the final `MarkerState` of the marker after the animation. Set the listener to `null` to remove it. + +#### Parameters + +| Parameter | Type | Description | +| :--------- | :--------------------- | :--------------------------------------------------------------------------------- | +| `listener` | `OnMarkerEventHandler?` | The callback to invoke when a marker animation ends, or `null` to clear the listener. | + +## Example + +The following example demonstrates how to use the `MapLibreMarkerEventControllerInterface` to set a click listener and programmatically select a marker. + +```kotlin +import android.util.Log +import com.mapconductor.core.features.GeoPoint +import com.mapconductor.maplibre.marker.MapLibreMarkerEventControllerInterface + +// Assume 'markerEventController' is obtained from your map instance +val markerEventController: MapLibreMarkerEventControllerInterface = getMarkerEventController() + +// 1. Set a click listener to log marker information +markerEventController.setClickListener { markerState -> + Log.d("MarkerClick", "Marker with ID ${markerState.id} was clicked at ${markerState.position}") + + // You can also use the controller to make this clicked marker the selected one + val clickedMarkerEntity = markerEventController.find(markerState.position) + markerEventController.setSelectedMarker(clickedMarkerEntity) + true // Return true to indicate the event was handled +} + +// 2. Set a drag end listener +markerEventController.setDragEndListener { markerState -> + Log.d("MarkerDragEnd", "Marker ${markerState.id} was dropped at ${markerState.position}") + true // Event handled +} + +// 3. Programmatically find and select a marker +val searchPosition = GeoPoint(40.7128, -74.0060) // New York City +val markerToSelect = markerEventController.find(searchPosition) + +if (markerToSelect != null) { + markerEventController.setSelectedMarker(markerToSelect) + Log.d("MarkerSelection", "Marker ${markerToSelect.state.id} has been selected programmatically.") +} + +// 4. To clear the selection +markerEventController.setSelectedMarker(null) +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/marker/MapLibreMarkerOverlayRenderer.kt.md b/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/marker/MapLibreMarkerOverlayRenderer.kt.md new file mode 100644 index 00000000..a730aea4 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/marker/MapLibreMarkerOverlayRenderer.kt.md @@ -0,0 +1,236 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +--- + +# MapLibreMarkerOverlayRenderer + +The `MapLibreMarkerOverlayRenderer` is a concrete implementation of `AbstractMarkerOverlayRenderer` designed specifically for the MapLibre map engine. It manages the entire lifecycle of markers on the map, including adding, removing, and updating them. + +This class handles the conversion of abstract marker data into MapLibre-specific `Feature` objects, manages icon resources within the map's style, and orchestrates rendering updates on dedicated marker layers. It uses an efficient batch-processing approach, where changes are collected and then applied by redrawing the entire marker layer from a GeoJSON source. + +## Signature + +```kotlin +class MapLibreMarkerOverlayRenderer( + holder: MapLibreMapViewHolderInterface, + val markerManager: MarkerManager, + val markerLayer: MarkerLayer, + val dragLayer: MarkerDragLayer, + coroutine: CoroutineScope = CoroutineScope(Dispatchers.Main), +) : AbstractMarkerOverlayRenderer( + holder = holder, + coroutine = coroutine, + ) +``` + +## Constructor + +Initializes a new instance of the `MapLibreMarkerOverlayRenderer`. + +### Parameters + +| Parameter | Type | Description | +| :-------------- | :--------------------------------- | :------------------------------------------------------------------------------------------------------ | +| `holder` | `MapLibreMapViewHolderInterface` | The view holder that provides access to the `MapLibreMap` instance and its controller. | +| `markerManager` | `MarkerManager` | The manager responsible for maintaining the state of all marker entities. | +| `markerLayer` | `MarkerLayer` | The dedicated layer for rendering standard markers. | +| `dragLayer` | `MarkerDragLayer` | The dedicated layer for rendering a marker while it is being dragged. | +| `coroutine` | `CoroutineScope` | (Optional) The coroutine scope for launching asynchronous operations. Defaults to `CoroutineScope(Dispatchers.Main)`. | + +## Nested Objects + +### `Prop` + +A collection of constant property keys used within the GeoJSON features that represent markers. + +| Constant | Value | Description | +| :------------------ | :------------- | :---------------------------------------- | +| `ICON_ID` | `"icon_id"` | The key for the marker's icon ID. | +| `DEFAULT_MARKER_ID` | `"default"` | The ID for the default marker icon. | +| `SCALE` | `"scale"` | The key for the marker's scale factor. | +| `ICON_ANCHOR` | `"icon-offset"`| The key for the marker's icon offset. | +| `Z_INDEX` | `"zIndex"` | The key for the marker's z-index value. | + +### `IconAnchor` + +Defines constants for specifying the anchor point of a marker's icon. + +| Constant | Value | +| :--------------- | :----------------- | +| `CENTER` | `"center"` | +| `LEFT` | `"left"` | +| `RIGHT` | `"right"` | +| `BOTTOM` | `"bottom"` | +| `TOP_LEFT` | `"top-left"` | +| `TOP_RIGHT` | `"top-right"` | +| `BOTTOM_LEFT` | `"bottom-left"` | +| `BOTTOM_RIGHT` | `"bottom-right"` | + +### `IconTranslateAnchor` + +Defines constants for specifying the icon's translation anchor behavior. + +| Constant | Value | Description | +| :--------- | :----------- | :---------------------------------------- | +| `MAP` | `"map"` | Anchored relative to the map. | +| `VIEWPORT` | `"viewport"` | Anchored relative to the device's screen. | + +## Methods + +### ensureDefaultIcon + +Checks if the default marker icon exists in the provided MapLibre style and adds it if it's missing. This is particularly useful for scenarios where the map style is reloaded, ensuring that markers without a custom icon can still be rendered. + +#### Signature + +```kotlin +fun ensureDefaultIcon(style: org.maplibre.android.maps.Style) +``` + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :--------------------------------- | :---------------------------------------- | +| `style` | `org.maplibre.android.maps.Style` | The MapLibre `Style` object to check. | + +### setMarkerPosition + +Updates the geographical position of a specific marker on the map. This method regenerates the marker's feature with the new coordinates and updates the GeoJSON source to reflect the change immediately. + +#### Signature + +```kotlin +override fun setMarkerPosition( + markerEntity: MarkerEntityInterface, + position: GeoPoint, +) +``` + +#### Parameters + +| Parameter | Type | Description | +| :------------- | :----------------------------------------- | :---------------------------------------- | +| `markerEntity` | `MarkerEntityInterface` | The marker entity to update. | +| `position` | `GeoPoint` | The new geographical position for the marker. | + +### onAdd + +Handles the addition of new markers. It processes a list of marker parameters, adds the necessary icon bitmaps to the map style, and creates a list of MapLibre `Feature` objects to be rendered. It also manages a reference count for icons to optimize resource usage. + +#### Signature + +```kotlin +override suspend fun onAdd( + data: List, +): List +``` + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :--------------------------------------------------------- | :------------------------------------------------------------------------------------------------------ | +| `data` | `List` | A list of `AddParamsInterface` objects, each containing the state and icon data for a new marker. | + +#### Returns + +A `List` of newly created `MapLibreActualMarker` (`Feature`) objects, corresponding to the input data. + +### onRemove + +A lifecycle hook called when markers are scheduled for removal. The actual removal from the map is handled by the `onPostProcess` and `redraw` methods, which rebuild the feature collection without the removed markers. + +#### Signature + +```kotlin +override suspend fun onRemove(data: List>) +``` + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :------------------------------------------- | :----------------------------------- | +| `data` | `List>` | A list of marker entities to be removed. | + +### drawDragLayer + +Triggers a redraw of the dedicated drag layer. This should be called when a marker is being dragged to render it separately from the static markers, providing visual feedback to the user. + +#### Signature + +```kotlin +fun drawDragLayer() +``` + +### redraw + +Forces a complete redraw of the main marker layer. It retrieves all current marker entities from the `markerManager` and updates the GeoJSON source for the `markerLayer`. This is the primary mechanism for applying batched changes to the map. + +#### Signature + +```kotlin +fun redraw() +``` + +### onPostProcess + +A lifecycle hook called after a batch of add or remove operations. It triggers a `redraw()` to update the map display with the changes, ensuring the visual state of the markers is synchronized with the data model. + +#### Signature + +```kotlin +override suspend fun onPostProcess() +``` + +### onChange + +Handles property changes for existing markers (e.g., icon, z-index). It compares the previous and current state of each marker, updates icon reference counts, and generates a new list of `Feature` objects with the updated properties. + +#### Signature + +```kotlin +override suspend fun onChange( + data: List>, +): List +``` + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------ | +| `data` | `List>` | A list of `ChangeParamsInterface` objects, each containing the previous and current state of a marker. | + +#### Returns + +A `List` of updated `MapLibreActualMarker` (`Feature`) objects reflecting the changes. + +## Example + +While direct instantiation is part of a larger framework, the conceptual usage within that framework would look like this: + +```kotlin +// 1. Initialize the renderer (typically done by a map controller) +val markerRenderer = MapLibreMarkerOverlayRenderer( + holder = myMapViewHolder, + markerManager = myMarkerManager, + markerLayer = myMarkerLayer, + dragLayer = myDragLayer +) + +// 2. The MarkerManager would then delegate operations to the renderer. + +// To add a marker +// The manager would call onAdd internally with the marker's data. +// The result is a new Feature that is stored. +val newFeatures = markerRenderer.onAdd(listOf(addParamsForNewMarker)) +// After adding, the manager calls onPostProcess to update the map. +markerRenderer.onPostProcess() + +// To change a marker's icon +// The manager would call onChange with the old and new state. +val updatedFeatures = markerRenderer.onChange(listOf(changeParamsForMarker)) +// After changing, the manager calls onPostProcess to update the map. +markerRenderer.onPostProcess() + +// To update a marker's position +markerRenderer.setMarkerPosition(markerEntity, newGeoPoint) +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/marker/MarkerDragLayer.kt.md b/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/marker/MarkerDragLayer.kt.md new file mode 100644 index 00000000..83026165 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/marker/MarkerDragLayer.kt.md @@ -0,0 +1,113 @@ +# MarkerDragLayer + +The `MarkerDragLayer` class extends `MarkerLayer` to manage a dedicated layer for a single, selected marker. It is primarily used to visualize a marker as it is being dragged across the map, providing methods to update its position and redraw it efficiently. + +This layer is designed to hold at most one marker at a time, which is represented by the `selected` property. + +## Constructor + +### `MarkerDragLayer(sourceId: String, layerId: String)` + +Initializes a new instance of the `MarkerDragLayer`. + +#### Parameters + +| Parameter | Type | Description | +| :-------- | :----- | :---------------------------------------- | +| `sourceId` | `String` | The unique identifier for the GeoJSON source of this layer. | +| `layerId` | `String` | The unique identifier for the map layer. | + +## Properties + +### `selected` + +The marker entity that is currently selected for dragging. The layer will only draw this marker. When set to `null`, the layer will be empty. + +**Signature** +```kotlin +var selected: MarkerEntityInterface? = null +``` + +## Methods + +### `updatePosition` + +Updates the in-memory geographical position of the `selected` marker entity. This change is not visually reflected on the map until the `draw()` method is called. + +**Signature** +```kotlin +fun updatePosition(geoPoint: GeoPoint) +``` + +**Parameters** + +| Parameter | Type | Description | +| :--------- | :-------- | :---------------------------------------- | +| `geoPoint` | `GeoPoint` | The new geographical coordinates for the selected marker. | + +**Returns** + +`Unit` + +### `draw` + +Renders the `selected` marker on the map based on its current state. This method creates a `FeatureCollection` containing only the selected marker and updates the `GeoJsonSource` associated with this layer. If the source is already part of the map's style, it will be updated directly. + +**Signature** +```kotlin +fun draw(style: Style) +``` + +**Parameters** + +| Parameter | Type | Description | +| :-------- | :------ | :----------------------------------------------- | +| `style` | `Style` | The `Style` object of the map where the marker will be drawn. | + +**Returns** + +`Unit` + +## Example + +Here is an example of how to use `MarkerDragLayer` to manage a marker during a drag gesture. + +```kotlin +// 1. Initialize the drag layer +val dragLayer = MarkerDragLayer(sourceId = "drag-source", layerId = "drag-layer") + +// Assume 'map' is your MapLibre map instance and 'style' is its loaded style +// Add the layer and source to the map style +style.addSource(dragLayer.source) +style.addLayer(dragLayer.layer) + +// 2. When a marker drag begins, assign the marker to the drag layer +fun onMarkerDragStart(markerToDrag: MarkerEntityInterface) { + // Set the marker as selected in the drag layer + dragLayer.selected = markerToDrag + + // Hide the original marker from its source layer if necessary + // ... +} + +// 3. As the user's finger moves, update the marker's position +fun onMarkerDrag(newCoordinates: GeoPoint) { + // Update the position in memory + dragLayer.updatePosition(newCoordinates) + + // Redraw the marker at the new position + map.style?.let { dragLayer.draw(it) } +} + +// 4. When the drag ends, clear the drag layer +fun onMarkerDragEnd() { + // Clear the selected marker + dragLayer.selected = null + + // Redraw the layer to remove the marker + map.style?.let { dragLayer.draw(it) } + + // Update the original marker's position and make it visible again + // ... +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/marker/MarkerLayer.kt.md b/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/marker/MarkerLayer.kt.md new file mode 100644 index 00000000..cb14549a --- /dev/null +++ b/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/marker/MarkerLayer.kt.md @@ -0,0 +1,103 @@ +# MarkerLayer + +The `MarkerLayer` class encapsulates the logic for managing and rendering a collection of markers as a single layer on a MapLibre map. It combines a `GeoJsonSource` to hold the marker data and a `SymbolLayer` to define their visual appearance. + +This class is responsible for creating and configuring the necessary MapLibre `SymbolLayer` and `GeoJsonSource`, and provides a `draw` method to update the map with a list of marker entities. + +## Constructor + +### Signature + +```kotlin +open class MarkerLayer( + open val sourceId: String, + open val layerId: String, +) +``` + +### Description + +Creates a new instance of `MarkerLayer` with unique identifiers for its underlying source and layer. + +### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `sourceId` | `String` | A unique identifier for the `GeoJsonSource` that will contain the marker data. | +| `layerId` | `String` | A unique identifier for the `SymbolLayer` that will render the markers. | + +## Properties + +### layer: SymbolLayer + +The configured MapLibre `SymbolLayer` used to render the markers. This layer is pre-configured to pull styling properties (like icon image, anchor, and Z-index) directly from the properties of the GeoJSON features. + +### source: GeoJsonSource + +The MapLibre `GeoJsonSource` that holds the marker data as a `FeatureCollection`. The `draw` method updates this source with the latest marker information. + +## Methods + +### draw + +#### Signature + +```kotlin +fun draw( + entities: List>, + style: org.maplibre.android.maps.Style, +) +``` + +#### Description + +Updates the marker layer with a new set of entities to be displayed on the map. This method filters for visible entities, converts them into a `FeatureCollection`, and updates the `GeoJsonSource` associated with the map's current style. + +It includes robust logic to handle cases where the source may have been detached (e.g., after a style reload) by attempting to re-add it if necessary before updating the data. Any failures during the update process are logged without crashing the application. + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `entities` | `List>` | A list of marker entities to render. Only entities where `visible` is `true` and `marker` is not `null` will be drawn. | +| `style` | `org.maplibre.android.maps.Style` | The active MapLibre `Style` object to which the source and layer are attached. | + +#### Returns + +This method does not return a value. + +## Example + +```kotlin +// Assume maplibreMap and style are available from a MapLibre callback, +// and you have a list of marker entities. + +// 1. Create an instance of MarkerLayer +val markerLayer = MarkerLayer(sourceId = "my-marker-source", layerId = "my-marker-layer") + +// 2. Add the source and layer to the map style (typically done once after the map is ready) +style.addSource(markerLayer.source) +style.addLayer(markerLayer.layer) + +// 3. Prepare your marker data. +// Each feature must have properties that the SymbolLayer is configured to use. +// (This is a simplified representation of MarkerEntityInterface and Feature creation) +val markerFeature1 = Feature.fromGeometry(Point.fromLngLat(10.0, 52.0)) +markerFeature1.addStringProperty(MapLibreMarkerOverlayRenderer.Prop.ICON_ID, "icon-id-1") +// ... add other required properties like Z_INDEX and ICON_ANCHOR + +val markerFeature2 = Feature.fromGeometry(Point.fromLngLat(10.1, 52.1)) +markerFeature2.addStringProperty(MapLibreMarkerOverlayRenderer.Prop.ICON_ID, "icon-id-2") +// ... add other required properties + +// Assuming a concrete implementation of MarkerEntityInterface +val myEntities: List> = listOf( + MyMarkerEntity(visible = true, marker = markerFeature1), + MyMarkerEntity(visible = true, marker = markerFeature2), + MyMarkerEntity(visible = false, marker = someOtherFeature) // This one will be filtered out +) + +// 4. Call draw to render the markers on the map. +// This would typically be called whenever your marker data changes. +markerLayer.draw(myEntities, style) +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/polygon/MapLibrePolygonConductor.kt.md b/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/polygon/MapLibrePolygonConductor.kt.md new file mode 100644 index 00000000..1e9a0b59 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/polygon/MapLibrePolygonConductor.kt.md @@ -0,0 +1,232 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +--- + +# MapLibrePolygonConductor + +The `MapLibrePolygonConductor` class is a controller responsible for managing polygon overlays on a MapLibre map. It implements the `OverlayControllerInterface` to handle the lifecycle of polygons, including their creation, update, and removal. This conductor also manages the rendering of polygon outlines by creating corresponding polylines. + +It acts as a bridge between the abstract `PolygonState` data and the concrete rendering implementation provided by `MapLibrePolygonOverlayRenderer` and `MapLibrePolylineOverlayRenderer`. + +## Signature + +```kotlin +class MapLibrePolygonConductor( + val polygonOverlay: MapLibrePolygonOverlayRenderer, + val polylineOverlay: MapLibrePolylineOverlayRenderer, +) : OverlayControllerInterface< + PolygonState, + PolygonEntityInterface, + PolygonEvent, + > +``` + +## Constructor + +### `MapLibrePolygonConductor(...)` + +Initializes a new instance of the `MapLibrePolygonConductor`. + +#### Signature + +```kotlin +MapLibrePolygonConductor( + val polygonOverlay: MapLibrePolygonOverlayRenderer, + val polylineOverlay: MapLibrePolylineOverlayRenderer, +) +``` + +#### Parameters + +| Parameter | Type | Description | +| ----------------- | ---------------------------------- | ------------------------------------------------------------------------ | +| `polygonOverlay` | `MapLibrePolygonOverlayRenderer` | The renderer responsible for drawing the filled area of the polygons. | +| `polylineOverlay` | `MapLibrePolylineOverlayRenderer` | The renderer responsible for drawing the outlines (strokes) of the polygons. | + +## Properties + +### `zIndex` + +Specifies the drawing order of the overlay on the map. Higher values are drawn on top of lower values. + +#### Signature + +```kotlin +override val zIndex: Int = 2 +``` + +### `clickListener` + +A callback function that is invoked when any polygon managed by this conductor is clicked. This provides a centralized way to handle click events for all polygons. + +#### Signature + +```kotlin +override var clickListener: ((PolygonEvent) -> Unit)? = null +``` + +## Methods + +### `add` + +Adds a list of polygons to the map. This method efficiently synchronizes the state of the map with the provided list. It adds new polygons, updates existing ones, and removes any polygons that are not in the new list. It also creates and manages the corresponding outlines for each polygon. + +#### Signature + +```kotlin +override suspend fun add(data: List) +``` + +#### Parameters + +| Parameter | Type | Description | +| --------- | ------------------- | ----------------------------------------- | +| `data` | `List` | A list of `PolygonState` objects to display on the map. | + +### `update` + +Updates a single polygon on the map based on its `PolygonState`. If a polygon with the same ID already exists, its properties will be updated. If it does not exist, a new polygon will be created. + +#### Signature + +```kotlin +override suspend fun update(state: PolygonState) +``` + +#### Parameters + +| Parameter | Type | Description | +| --------- | -------------- | ------------------------------------------------ | +| `state` | `PolygonState` | The state object representing the polygon to update or add. | + +### `dispatchClick` + +Dispatches a click event. This method is typically called by the underlying map framework when a tap is detected on a polygon. It triggers the `onClick` handler defined within the specific `PolygonEvent.state` and also invokes the global `clickListener` if it has been set. + +#### Signature + +```kotlin +fun dispatchClick(event: PolygonEvent) +``` + +#### Parameters + +| Parameter | Type | Description | +| --------- | -------------- | ----------------------------------------- | +| `event` | `PolygonEvent` | The event object containing details about the click. | + +### `find` + +Finds the topmost polygon entity at a given geographic position on the map. + +#### Signature + +```kotlin +override fun find(position: GeoPointInterface): PolygonEntityInterface? +``` + +#### Parameters + +| Parameter | Type | Description | +| ---------- | ------------------- | -------------------------------------------- | +| `position` | `GeoPointInterface` | The geographic coordinate (latitude and longitude) to search at. | + +#### Returns + +| Type | Description | +| ---------------------------------------- | ------------------------------------------------------------------------ | +| `PolygonEntityInterface?` | The found polygon entity, or `null` if no polygon is found at the specified position. | + +### `clear` + +Removes all polygons and their outlines that are currently managed by this conductor from the map. + +#### Signature + +```kotlin +override suspend fun clear() +``` + +### `onCameraChanged` + +A lifecycle method called when the map's camera position changes. This implementation is currently empty. + +#### Signature + +```kotlin +override suspend fun onCameraChanged(mapCameraPosition: MapCameraPosition) +``` + +### `destroy` + +Cleans up resources used by the conductor. This implementation is currently empty as there are no specific native resources to release for polygons. + +#### Signature + +```kotlin +override fun destroy() +``` + +## Example + +Here's an example of how to instantiate and use `MapLibrePolygonConductor` to manage polygons on a map. + +```kotlin +// 1. Assume polygonOverlay and polylineOverlay are already initialized +// val polygonOverlay: MapLibrePolygonOverlayRenderer = ... +// val polylineOverlay: MapLibrePolylineOverlayRenderer = ... + +// 2. Create an instance of the conductor +val polygonConductor = MapLibrePolygonConductor(polygonOverlay, polylineOverlay) + +// 3. Define the state for the polygons you want to add +val polygonState1 = PolygonState( + id = "polygon-1", + points = listOf( + GeoPoint(40.7128, -74.0060), // New York + GeoPoint(34.0522, -118.2437), // Los Angeles + GeoPoint(29.7604, -95.3698) // Houston + ), + fillColor = Color.argb(100, 0, 0, 255), // Semi-transparent blue + strokeColor = Color.BLUE, + strokeWidth = 5f, + onClick = { event -> + println("Clicked on polygon with ID: ${event.state.id}") + } +) + +val polygonState2 = PolygonState( + id = "polygon-2", + points = listOf( + GeoPoint(41.8781, -87.6298), // Chicago + GeoPoint(39.7392, -104.9903), // Denver + GeoPoint(47.6062, -122.3321) // Seattle + ), + fillColor = Color.argb(100, 255, 0, 0) // Semi-transparent red +) + +// 4. Add the polygons to the map +// This needs to be called from a coroutine scope +lifecycleScope.launch { + polygonConductor.add(listOf(polygonState1, polygonState2)) +} + +// 5. Set a global click listener for all polygons +polygonConductor.clickListener = { event -> + println("Global listener: Clicked on polygon ${event.state.id}") + // You can, for example, show an info window here +} + +// 6. Update a polygon later +lifecycleScope.launch { + val updatedPolygonState1 = polygonState1.copy( + fillColor = Color.argb(100, 0, 255, 0) // Change color to green + ) + polygonConductor.update(updatedPolygonState1) +} + +// 7. Clear all polygons from the map +lifecycleScope.launch { + polygonConductor.clear() +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/polygon/MapLibrePolygonLayer.kt.md b/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/polygon/MapLibrePolygonLayer.kt.md new file mode 100644 index 00000000..e3ec58a6 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/polygon/MapLibrePolygonLayer.kt.md @@ -0,0 +1,162 @@ +# SDK Documentation: `MapLibrePolygonLayer` + +## `MapLibrePolygonLayer` + +### Signature + +```kotlin +class MapLibrePolygonLayer( + val sourceId: String, + val layerId: String, +) +``` + +### Description + +The `MapLibrePolygonLayer` class manages a dedicated layer for rendering polygons on a MapLibre map. It encapsulates a `GeoJsonSource` to hold polygon data and a `FillLayer` to control their visual representation. This class simplifies the process of creating, styling, and updating a collection of polygons by handling the underlying MapLibre objects and data flow. + +### Constructor + +#### Signature + +```kotlin +MapLibrePolygonLayer(sourceId: String, layerId: String) +``` + +#### Description + +Creates a new instance of `MapLibrePolygonLayer`. Upon instantiation, it initializes a `GeoJsonSource` and a `FillLayer` with the provided IDs. + +#### Parameters + +| Parameter | Type | Description | +|------------|----------|------------------------------------------------------| +| `sourceId` | `String` | A unique identifier for the underlying `GeoJsonSource`. | +| `layerId` | `String` | A unique identifier for the `FillLayer`. | + +--- + +## Nested Objects + +### `Prop` + +#### Signature + +```kotlin +object Prop +``` + +#### Description + +A companion object that defines constant keys for properties used within the GeoJSON features. These keys are essential for dynamically styling features based on their data attributes. + +#### Properties + +| Property | Type | Description | +|--------------|----------|---------------------------------------------------------------------------------------------------------| +| `FILL_COLOR` | `String` | The key for the feature property that defines the polygon's fill color (e.g., `"#FF0000"`). Value: `"fillColor"`. | +| `Z_INDEX` | `String` | The key for the feature property that defines the polygon's stacking order. Value: `"zIndex"`. | + +--- + +## Properties + +### `source: GeoJsonSource` + +The `GeoJsonSource` instance that contains the polygon feature data. This source must be added to the map's `Style` to make the data available for rendering. It is initialized with an empty `FeatureCollection`. + +### `layer: FillLayer` + +The `FillLayer` instance used to render the polygons from the `source`. This layer is pre-configured to derive its fill color from the `fillColor` property of each GeoJSON feature. It must be added to the map's `Style` to make the polygons visible. + +--- + +## Methods + +### `draw` + +#### Signature + +```kotlin +fun draw( + entities: List>, + style: org.maplibre.android.maps.Style, +) +``` + +#### Description + +Renders or updates the polygons on the map. This method processes a list of `PolygonEntityInterface` objects, converts them into GeoJSON `Feature`s, and updates the `GeoJsonSource`. The polygons are sorted by their `zIndex` property to control drawing order. + +The method first attempts to update the source directly from the map's `Style` object for optimal performance. If the style-bound source is not accessible (e.g., during initial setup), it falls back to updating its local `source` property. + +#### Parameters + +| Parameter | Type | Description | +|------------|---------------------------------------------------------------|---------------------------------------------------------------------------------------------------------| +| `entities` | `List>` | A list of polygon entities to display on the map. Each entity provides its geometry and state properties. | +| `style` | `org.maplibre.android.maps.Style` | The current `Style` object of the map to which the source is (or will be) attached. | + +#### Returns + +`Unit` - This method does not return a value. + +--- + +### Example + +The following example demonstrates how to initialize `MapLibrePolygonLayer`, add it to a map, and use the `draw` method to render polygons. + +```kotlin +// This code typically runs inside your map's onStyleLoaded callback. +map.getStyle { style -> + // 1. Initialize the polygon layer manager + val polygonLayer = MapLibrePolygonLayer( + sourceId = "polygon-source-id", + layerId = "polygon-layer-id" + ) + + // 2. Add the layer's source and the layer itself to the map style + style.addSource(polygonLayer.source) + style.addLayer(polygonLayer.layer) + + // 3. Define polygon geometry and create a Feature. + // Add properties using the keys from `MapLibrePolygonLayer.Prop`. + val polygonGeometry = Polygon.fromLngLats(listOf( + listOf( + Point.fromLngLat(-122.419, 37.774), + Point.fromLngLat(-122.429, 37.774), + Point.fromLngLat(-122.429, 37.784), + Point.fromLngLat(-122.419, 37.784), + Point.fromLngLat(-122.419, 37.774) + ) + )) + val feature1 = Feature.fromGeometry(polygonGeometry) + feature1.addStringProperty(MapLibrePolygonLayer.Prop.FILL_COLOR, "#3bb2d0") // Set color + feature1.addNumberProperty(MapLibrePolygonLayer.Prop.Z_INDEX, 1) // Used for sorting + + // 4. Wrap the feature(s) in the required entity structure. + // Note: The implementation of `PolygonEntityInterface` and `MapLibreActualPolygon` + // depends on your specific application architecture. + val polygonEntity1 = createPolygonEntity( + features = listOf(feature1), + zIndex = 1 + ) + + // 5. Call the draw method to update the map with the new entities + polygonLayer.draw(listOf(polygonEntity1), style) +} + +/** + * Example helper function to create a PolygonEntityInterface. + * `MapLibreActualPolygon` is assumed to be a typealias for `List`. + */ +fun createPolygonEntity(features: List, zIndexValue: Int): PolygonEntityInterface> { + return object : PolygonEntityInterface> { + override val polygon: List = features + override val state = object { + val zIndex: Int = zIndexValue + } + } +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/polygon/MapLibrePolygonOverlayRenderer.kt.md b/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/polygon/MapLibrePolygonOverlayRenderer.kt.md new file mode 100644 index 00000000..778693ae --- /dev/null +++ b/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/polygon/MapLibrePolygonOverlayRenderer.kt.md @@ -0,0 +1,129 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +--- + +# MapLibrePolygonOverlayRenderer + +## Signature + +```kotlin +class MapLibrePolygonOverlayRenderer( + val layer: MapLibrePolygonLayer, + val polygonManager: PolygonManagerInterface, + override val holder: MapLibreMapViewHolderInterface, + private val rasterLayerController: MapLibreRasterLayerController, + private val tileServer: LocalTileServer = TileServerRegistry.get(forceNoStoreCache = true), + override val coroutine: CoroutineScope = CoroutineScope(Dispatchers.Main), +) : AbstractPolygonOverlayRenderer() +``` + +## Description + +Renders polygon overlays on a MapLibre map. This class is an implementation of `AbstractPolygonOverlayRenderer` and is responsible for the platform-specific drawing logic. + +This renderer employs a dual strategy for optimal performance and feature support: +1. **Native Polygons**: For simple polygons (those without holes), it leverages MapLibre's efficient, native polygon rendering. +2. **Raster Tile Overlay**: For complex polygons that include one or more holes, it dynamically generates a raster tile overlay. A transparent native polygon is drawn to define the outer boundary, while a local tile server (`LocalTileServer`) provides raster images that "paint" the fill color and cut out the holes. This ensures that complex shapes are displayed correctly, overcoming limitations in some native polygon implementations. + +## Constructor + +Initializes a new instance of the `MapLibrePolygonOverlayRenderer`. + +### Parameters + +| Parameter | Type | Description | +| --------------------- | ------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | +| `layer` | `MapLibrePolygonLayer` | The layer responsible for drawing the polygon features on the map. | +| `polygonManager` | `PolygonManagerInterface` | The manager that holds the state of all polygons to be rendered. | +| `holder` | `MapLibreMapViewHolderInterface` | The view holder providing access to the MapLibre map instance and its controller. | +| `rasterLayerController` | `MapLibreRasterLayerController` | Controller for managing raster tile layers on the map, used for rendering polygons with holes. | +| `tileServer` | `LocalTileServer` | **Optional**. A local tile server for serving dynamically generated polygon raster tiles. Defaults to a new instance from `TileServerRegistry`. | +| `coroutine` | `CoroutineScope` | **Optional**. The coroutine scope for running asynchronous rendering tasks. Defaults to `CoroutineScope(Dispatchers.Main)`. | + +--- + +## Methods + +### createPolygon + +Creates the underlying map-specific polygon object based on the provided state. + +This method implements the core rendering logic: if the polygon state contains holes, it sets up a raster tile mask to render the shape. Otherwise, it creates a standard native polygon. + +#### Signature + +```kotlin +override suspend fun createPolygon(state: PolygonState): MapLibreActualPolygon? +``` + +#### Parameters + +| Parameter | Type | Description | +| --------- | -------------- | ------------------------------------------------------------------------------------------------------- | +| `state` | `PolygonState` | The state object containing all properties of the polygon to be created, such as points, holes, and color. | + +#### Returns + +| Type | Description | +| ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `MapLibreActualPolygon?` | The newly created `MapLibreActualPolygon` object, or `null` if creation fails. For polygons with holes, the returned object will have a transparent fill, as the visual appearance is handled by a separate raster layer. | + +--- + +### updatePolygonProperties + +Updates an existing polygon's properties. If there are any changes to the polygon's geometry, appearance, or other defining properties (as determined by its `fingerPrint`), the method will trigger a full recreation of the polygon by calling `createPolygon`. Otherwise, it returns the existing polygon object. + +#### Signature + +```kotlin +override suspend fun updatePolygonProperties( + polygon: MapLibreActualPolygon, + current: PolygonEntityInterface, + prev: PolygonEntityInterface, +): MapLibreActualPolygon? +``` + +#### Parameters + +| Parameter | Type | Description | +| --------- | ------------------------------------------- | ------------------------------------------------------------------------ | +| `polygon` | `MapLibreActualPolygon` | The current map-specific polygon object. | +| `current` | `PolygonEntityInterface` | The entity representing the new, updated state of the polygon. | +| `prev` | `PolygonEntityInterface` | The entity representing the previous state of the polygon. | + +#### Returns + +| Type | Description | +| ------------------------- | ------------------------------------------------------------------------------------------------------- | +| `MapLibreActualPolygon?` | The updated (or recreated) `MapLibreActualPolygon` object, or the previous polygon if no changes were detected. | + +--- + +### removePolygon + +Handles the removal of a single polygon. This method primarily focuses on cleaning up any associated raster mask layers used for polygons with holes. The actual removal of the polygon feature from the map is handled globally by the `onPostProcess` method, which redraws the entire collection of remaining polygons. + +#### Signature + +```kotlin +override suspend fun removePolygon(entity: PolygonEntityInterface) +``` + +#### Parameters + +| Parameter | Type | Description | +| --------- | ------------------------------------------- | -------------------------------- | +| `entity` | `PolygonEntityInterface` | The polygon entity to be removed. | + +--- + +### onPostProcess + +Called after a batch of add, update, or remove operations. This method gathers all current polygon entities from the `polygonManager` and triggers a complete redraw on the map. This ensures the map view is always in sync with the latest polygon state. + +#### Signature + +```kotlin +override suspend fun onPostProcess() +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/polyline/MapLibrePolylineController.kt.md b/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/polyline/MapLibrePolylineController.kt.md new file mode 100644 index 00000000..71050f22 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/polyline/MapLibrePolylineController.kt.md @@ -0,0 +1,66 @@ +# MapLibrePolylineController + +The `MapLibrePolylineController` is the primary controller for managing and displaying polylines on a MapLibre map. + +## Signature + +```kotlin +class MapLibrePolylineController( + override val renderer: MapLibrePolylineOverlayRenderer, + polylineManager: PolylineManagerInterface = renderer.polylineManager, +) : PolylineController(polylineManager, renderer) +``` + +## Description + +This class serves as the main entry point for developers to interact with polylines within a MapLibre environment. It integrates the generic polyline management logic from the base `PolylineController` with the MapLibre-specific rendering capabilities provided by `MapLibrePolylineOverlayRenderer`. Use this controller to add, remove, update, and manage the lifecycle of polylines on the map. + +## Parameters + +This section describes the parameters for the `MapLibrePolylineController` constructor. + +| Parameter | Type | Description | +|---|---|---| +| `renderer` | `MapLibrePolylineOverlayRenderer` | **Required.** The renderer responsible for drawing and managing polyline overlays on the MapLibre map. | +| `polylineManager` | `PolylineManagerInterface` | *Optional.* The manager for the underlying polyline data structures. By default, it uses the `polylineManager` instance from the provided `renderer`. | + +## Example + +The following example demonstrates how to initialize the `MapLibrePolylineController` and use it to add a polyline to the map. + +```kotlin +import android.graphics.Color +import com.mapbox.mapboxsdk.geometry.LatLng +import com.mapconductor.maplibre.polyline.MapLibrePolylineController +import com.mapconductor.maplibre.polyline.MapLibrePolylineOverlayRenderer +import com.mapconductor.core.polyline.PolylineOptions // Assuming this class exists + +// Prerequisites: A MapLibre MapView instance and a loaded Style object +// val mapView: MapView = ... +// val style: Style = ... +// val context: Context = ... + +// 1. Initialize the MapLibre-specific renderer +val polylineRenderer = MapLibrePolylineOverlayRenderer(context, mapView, style) + +// 2. Create the polyline controller instance, passing in the renderer +val polylineController = MapLibrePolylineController(renderer = polylineRenderer) + +// 3. Define the properties for a new polyline +val polylineOptions = PolylineOptions( + points = listOf( + LatLng(37.7749, -122.4194), // San Francisco + LatLng(34.0522, -118.2437) // Los Angeles + ), + color = Color.RED, + width = 8f +) + +// 4. Add the polyline to the map using the controller +val newPolyline = polylineController.addPolyline(polylineOptions) + +// The newPolyline is now visible on the map. +// You can further manage it through the controller or the returned object. +// For example, to remove the polyline: +// polylineController.removePolyline(newPolyline) +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/polyline/MapLibrePolylineLayer.kt.md b/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/polyline/MapLibrePolylineLayer.kt.md new file mode 100644 index 00000000..de34afb9 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/polyline/MapLibrePolylineLayer.kt.md @@ -0,0 +1,188 @@ +Of course! Here is the high-quality SDK documentation for the provided Kotlin code snippet. + +*** + +# MapLibrePolylineLayer + +## `MapLibrePolylineLayer` Class + +### Signature + +```kotlin +class MapLibrePolylineLayer( + val sourceId: String, + val layerId: String, +) +``` + +### Description + +The `MapLibrePolylineLayer` class is a controller responsible for managing and rendering a collection of polylines on a MapLibre map. It encapsulates a MapLibre `GeoJsonSource` and a corresponding `LineLayer`, simplifying the process of adding, updating, and styling polylines. + +This class links the visual properties of the polylines (like color and width) to the properties of the underlying GeoJSON features, allowing for dynamic styling on a per-polyline basis. + +### Constructor + +Initializes a new instance of the `MapLibrePolylineLayer`. + +#### Signature + +```kotlin +MapLibrePolylineLayer(sourceId: String, layerId: String) +``` + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `sourceId` | `String` | A unique identifier for the `GeoJsonSource` that will store the polyline data. | +| `layerId` | `String` | A unique identifier for the `LineLayer` that will render the polylines from the source. | + +--- + +## Properties + +### `source` + +The underlying MapLibre `GeoJsonSource` that holds the polyline data as a `FeatureCollection`. This source is initialized with an empty collection and is updated when the `draw()` method is called. + +#### Signature + +```kotlin +val source: GeoJsonSource +``` + +### `layer` + +The MapLibre `LineLayer` used to render the polylines. It is pre-configured to use the `source` and to style the lines based on properties within each GeoJSON feature. + +#### Signature + +```kotlin +val layer: LineLayer +``` + +#### Default Style Properties + +The layer is configured with the following default properties: +- **Line Join**: `ROUND` +- **Line Cap**: `ROUND` +- **Line Color**: Derived from the feature's `strokeColor` property (see `Prop.STROKE_COLOR`). +- **Line Width**: Derived from the feature's `strokeWidth` property (see `Prop.STROKE_WIDTH`). + +--- + +## `Prop` Object + +A static object containing constants for property names. These keys should be added to the properties of each GeoJSON `Feature` to control its styling. + +### Signature + +```kotlin +object Prop +``` + +### Properties + +| Property | Type | Value | Description | +| :--- | :--- | :--- | :--- | +| `STROKE_COLOR` | `String` | `"strokeColor"` | The key for the feature property that defines the polyline's stroke color (e.g., `"#FF0000"`). | +| `STROKE_WIDTH` | `String` | `"strokeWidth"` | The key for the feature property that defines the polyline's stroke width in pixels (e.g., `5.0f`). | +| `Z_INDEX` | `String` | `"zIndex"` | The key for the feature property that can be used to define the polyline's z-index. **Note**: This property is not used by the `layer`'s default configuration in this class but is provided for potential custom extensions. | + +--- + +## Methods + +### `draw` + +Renders or updates the polylines on the map. This method processes a list of polyline entities, converts them into a `FeatureCollection`, and updates the `GeoJsonSource`. + +It first attempts to get the source directly from the map's active `Style` for optimal performance. If the style is in transition or otherwise unavailable, it gracefully falls back to updating its local `source` instance. + +#### Signature + +```kotlin +fun draw( + entities: List>, + style: org.maplibre.android.maps.Style, +) +``` + +#### Parameters + +| Parameter | Type | Description | +| :--- | :--- | :--- | +| `entities` | `List>` | A list of polyline entities to be drawn. Each entity contains the geometric data and properties for one or more polylines. | +| `style` | `org.maplibre.android.maps.Style` | The current, fully loaded `Style` object from the MapLibre map instance. | + +#### Returns + +`Unit` - This method does not return a value. + +--- + +## Example + +The following example demonstrates how to initialize `MapLibrePolylineLayer`, add its source and layer to the map, and use the `draw()` method to render polylines. + +```kotlin +import com.mapbox.geojson.LineString +import com.mapbox.geojson.Point +import org.maplibre.geojson.Feature +import org.maplibre.android.maps.MapLibreMap +import org.maplibre.android.maps.OnMapReadyCallback +import org.maplibre.android.maps.Style + +// Assume PolylineEntity and MapLibreActualPolyline are defined elsewhere +// For this example, we'll create a simple representation. +class MyPolylineEntity(override val polyline: List) : PolylineEntityInterface> + +class MyMapActivity : AppCompatActivity(), OnMapReadyCallback { + + private lateinit var map: MapLibreMap + private lateinit var polylineLayer: MapLibrePolylineLayer + + // ... onCreate, etc. + + override fun onMapReady(maplibreMap: MapLibreMap) { + this.map = maplibreMap + map.setStyle(Style.Builder().fromUri("YOUR_STYLE_URI")) { style -> + // 1. Initialize the polyline layer with unique IDs + polylineLayer = MapLibrePolylineLayer( + sourceId = "my-polyline-source", + layerId = "my-polyline-layer" + ) + + // 2. Add the source and layer to the map style + style.addSource(polylineLayer.source) + style.addLayer(polylineLayer.layer) + + // 3. Prepare the polyline data + val polylineEntities = createSamplePolylines() + + // 4. Draw the polylines on the map + polylineLayer.draw(polylineEntities, style) + } + } + + private fun createSamplePolylines(): List { + // Create a line from two points + val points = listOf( + Point.fromLngLat(-122.483696, 37.833818), + Point.fromLngLat(-122.483482, 37.833174) + ) + val lineString = LineString.fromLngLats(points) + + // Create a GeoJSON feature with styling properties + val feature = Feature.fromGeometry(lineString) + feature.addStringProperty(MapLibrePolylineLayer.Prop.STROKE_COLOR, "#3bb2d0") + feature.addNumberProperty(MapLibrePolylineLayer.Prop.STROKE_WIDTH, 5.0f) + + // Wrap the feature in an entity + val entity = MyPolylineEntity(listOf(feature)) + + return listOf(entity) + } +} +``` \ No newline at end of file diff --git a/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/polyline/MapLibrePolylineOverlayRenderer.kt.md b/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/polyline/MapLibrePolylineOverlayRenderer.kt.md new file mode 100644 index 00000000..45e5e4f7 --- /dev/null +++ b/experimental/api-docs/mapconductor-for-maplibre/src/main/java/com/mapconductor/maplibre/polyline/MapLibrePolylineOverlayRenderer.kt.md @@ -0,0 +1,182 @@ +Of course! Here is the high-quality SDK documentation for the provided code snippet. + +--- + +# MapLibrePolylineOverlayRenderer + +## Signature + +```kotlin +class MapLibrePolylineOverlayRenderer( + val layer: MapLibrePolylineLayer, + val polylineManager: PolylineManagerInterface, + override val holder: MapLibreMapViewHolderInterface, + override val coroutine: CoroutineScope = CoroutineScope(Dispatchers.Main), +) : AbstractPolylineOverlayRenderer() +``` + +## Description + +The `MapLibrePolylineOverlayRenderer` is a concrete implementation of `AbstractPolylineOverlayRenderer` designed specifically for the MapLibre map framework. It acts as the bridge between the abstract polyline data model and the visual representation on a MapLibre map. + +This class is responsible for the entire lifecycle of polyline overlays, including their creation, property updates, and removal. It collaborates with a `MapLibrePolylineLayer` to efficiently draw and manage polylines as features on the map style. + +Key behaviors include: +- Translating abstract `PolylineState` into tangible `MapLibreActualPolyline` objects. +- Recreating polylines to apply property updates. +- Managing removal by redrawing the entire set of polylines in a subsequent processing step. + +## Parameters + +This class is instantiated with the following parameters: + +| Parameter | Type | Description | +| ----------------- | ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | +| `layer` | `MapLibrePolylineLayer` | The layer responsible for drawing the polyline features onto the MapLibre map style. | +| `polylineManager` | `PolylineManagerInterface` | The manager that holds the state and collection of all polyline entities. | +| `holder` | `MapLibreMapViewHolderInterface` | The view holder interface that provides access to the MapLibre map instance and its controller. | +| `coroutine` | `CoroutineScope` | The coroutine scope used for launching asynchronous drawing operations. Defaults to `Dispatchers.Main`. | + +--- + +## Methods + +### createPolyline + +**Signature** +```kotlin +override suspend fun createPolyline(state: PolylineState): MapLibreActualPolyline? +``` + +**Description** +Creates a new `MapLibreActualPolyline` from a given `PolylineState`. This method translates the abstract state (points, color, width, etc.) into a concrete polyline feature that can be rendered on the map. The `zIndex` is resolved from the state's `zIndex` property or an optional `extra` integer property. + +**Parameters** +| Parameter | Type | Description | +| --------- | --------------- | -------------------------------------------------- | +| `state` | `PolylineState` | The data object containing the properties for the new polyline. | + +**Returns** +| Type | Description | +| -------------------------- | ------------------------------------------------------------------------ | +| `MapLibreActualPolyline?` | The newly created polyline object, or `null` if creation was unsuccessful. | + +--- + +### updatePolylineProperties + +**Signature** +```kotlin +override suspend fun updatePolylineProperties( + polyline: MapLibreActualPolyline, + current: PolylineEntityInterface, + prev: PolylineEntityInterface, +): MapLibreActualPolyline? +``` + +**Description** +Updates an existing polyline's properties. This implementation handles updates by completely recreating the polyline feature with the new properties defined in the `current` state. + +**Parameters** +| Parameter | Type | Description | +| ---------- | ---------------------------------------------------- | ------------------------------------------------------------------------ | +| `polyline` | `MapLibreActualPolyline` | The actual polyline object on the map that needs to be updated. | +| `current` | `PolylineEntityInterface` | The entity containing the new, updated state for the polyline. | +| `prev` | `PolylineEntityInterface` | The entity containing the previous state of the polyline before the update. | + +**Returns** +| Type | Description | +| -------------------------- | ------------------------------------------------------------------------ | +| `MapLibreActualPolyline?` | The new polyline object that replaces the old one, or `null` on failure. | + +--- + +### removePolyline + +**Signature** +```kotlin +override suspend fun removePolyline(entity: PolylineEntityInterface) +``` + +**Description** +Marks a polyline for removal. Note that this method does not immediately remove the polyline from the map. Instead, the actual removal occurs during the `onPostProcess` step, where all remaining polylines are redrawn, effectively excluding the one marked for removal. + +**Parameters** +| Parameter | Type | Description | +| --------- | ------------------------------------------------- | ----------------------------------------- | +| `entity` | `PolylineEntityInterface` | The polyline entity to be removed. | + +--- + +### onPostProcess + +**Signature** +```kotlin +override suspend fun onPostProcess() +``` + +**Description** +This method is called after a batch of create, update, or remove operations. It synchronizes the visual state of the map with the current data model by fetching all active polyline entities from the `polylineManager` and instructing the `MapLibrePolylineLayer` to draw them. This is the step where removals are finalized and all changes become visible. + +--- + +### redraw + +**Signature** +```kotlin +fun redraw() +``` + +**Description** +Manually triggers a full redraw of all polylines managed by the associated `polylineManager`. This is a utility function that can be called to force a refresh of the polyline layer at any time, ensuring the map's visual state is perfectly synchronized with the data. + +--- + +## Example + +```kotlin +import com.mapbox.mapboxsdk.maps.Style +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers + +// Assume these are your mock or actual implementations +val mockMapLibreLayer = mock() +val mockPolylineManager = mock>() +val mockMapViewHolder = mock() +val mockStyle = mock