Skip to content

Commit dc63dfd

Browse files
authored
V1.1.3 (#85)
* add auto-translation script using OpenAI API * commonalize code blocks * Componentize all code blocks across all JA documentations * prevent crashes * wip: fixing the documents * wip: fixing the documents * wip: fixing the documents * wip: fixing the documents * wip: fixing the documents * wip: fixing the documents * wip: fixing the documents * wip: fixing the documents * wip: fixing the documents * Implement the sdkInitialize callback property for ArcGISMapView * sample: sdkInitialize for ArcGISMapView * update: ArcGISMapViewHoler pages * import ArcGISOAuthHybridInitialize * fix: build error of the example-app * add the marker animation sections * creating event page * fix: altitudeToZoomLevel is incorrect * remove okhttp dependencies * remove event/onMapLoaded * wip * fix: GeoPoint * fix: Align the class name with others: "MapLibreDesignType" * add: some predefined map designs * feat: Polyline and Polygon components accepts GeoRectBounds * fix: Align the class name with others: "MapLibreDesignType" * fix: GeoRectBounds.union generates a new bounds * add: some predefined map designs * update UnionBoundsExample * fix: new map tile designs for MapLibre cause a crash * fix: the title in the sidebar becomes under the camera position * refactor: move overlay event callbacks to state objects * feat: MapCameraPosition accepts GeoPoint object for the position parameter * fix: changing marker.position does not work * update examples * fix: linter errors * feat: MarkerRenderingStrategy is renamed to MarkerRenderingGroup, and provides it as Composable function * Feature: Introducing MarkerClusterGroup (#86) * wip: marker clustering * wip: marker clustering * wip: marker clustering * feat: draw the circle for a clustered icon in order to represent approximately radius * Remove debug log code * Introduce MarkerClusterGroup for ArcGIS, Here, Mapbox, and MapLibre * Add MarkerCluster Example * fix: Linter errors * Add RasterLayer, TileServer-based HeatmapOverlay across providers (#87) * feat: introduce RasterLayer component * feat: introduce TileServer and HeatmapOverlay * feat: introduce TileServer and HeatmapOverlay * add ARCHITECTURE.md * fix: typo "durationMills" to "durationMillis" * fix: onGroundImageClick is not used * rename: DefaultIcon is renamed to DefaultMarkerIcon * refact: xxxImpl(実装) と xxx(インタフェース)を、xxx(実装)と xxxInterface(インタフェース)に変更 * split the core module into two modules: "core(jetpack runtime)" and "core-domain(pure logic)" * fix: ktLint errors * Get rid of the MarkerRenderingGroup and MarkerClusterGroup from each SDK module. They should be in the mapconductor-maker-clustering module only. * Add marker clustering demo * refactor: some spherical functions were moved into each separate file * feat: implement enableZoomAnimation for MarkerClusterGroup * remove old cluster marker after marker animation is done * remove old cluster marker after marker animation is done * optimize marker clustering performance on panning the map * optimize marker animation for marker clustering * optimize marker animation for marker clustering * Provide TileServer feature by default instead of isolated module * Put back the mapconductor-core-domain into the mapconductor-core module. * rename "bitmap" to "image" * remove unused code * fix: Can not load OSM raster tile * fix: Can not load OSM raster tile * introduce ChildCollector * fix: heatmap tile drawing issue * implement GroundImage for MapLibre * implement GroundImage for Mapbox * wip: heatmap optimization * wip: heatmap optimization * ignore .gradle-user-home * implement GroundImage for Here * feat: implement GroundImage for ArcGIS * V1.1.3 sync ios (#88) * remove from request url for tile server. * ZoomAltitudeConverter considers latitude and tilt distance = zoom0Altitude * cos(lat) / 2^zoom altitude = distance * cos(tilt) lat/tilt clamp + minCosLat/minCosTilt * googlemaps: adaptive geodesic interpolation (zoom-based segment length) + LRU cache for polyline/polygon * fix: InfoBubble ignores click event * adjust circle radius * fix: initial camera position is ignored on Here SDK sometimes * - fix(arcgis): correct zoom conversion (remove -1.0 bias) - fix(arcgis): prevent disposed controller from overwriting camera on rapid provider switching - fix(arcgis): restore requested camera when viewpoint is reset after scene/basemap updates * Use DefaultMapViewItems for GroundImageMapPage.kt * Replace Japanese labels with in English * improve: GroundImage on ArcGIS * insert debounce to improve redraw performance. * Android(core): tileserver: add cacheKey path segment to LocalTileServer URL templates * Android(MapLibre/Mapbox/HERE/ArcGIS GroundImage): groundimage: switch tile URL cache busting from query to cacheKey path * implement marker tile layer for GoogleMaps * improve performance and clickable * improve marker rendering layer performance * fix: marker click may causes crash * introduce markerState.autoScalable * keep the original scale when markerState.autoScalable=true * drawing markers with collect size * fix: missing tile when change zoom levels quickly * wip: tile missing problem * re-implement MarkerTileLayer using RasterLayer * MarkerTileLayer works fine on GoogleMaps * introduce MarkerTileLayer for HERE * introduce MarkerTileLayer for MapLibre * introduce MarkerTileLayer * introduce MarkerTileLayer for MapBox * introduce MarkerTileLayer for Google Maps * wip: update the index page * introduce MarkerTileLayer for ArcGIS * fix: zoom level between mapbox and arcgis is incorrect * align zoom levels for all maps SDKs * update the JA top page * - refactor: decouple clustering support via map service registry - refactor(core): add MapServiceRegistry and move MarkerRenderingSupport to core - refactor(marker-clustering): resolve rendering support from LocalMapServiceRegistry - refactor(for-*): register MarkerRenderingSupport plugin; drop controller inheritance - chore(build): remove marker-clustering dependency from for-* modules * (getting better): MarkerTileLayer is unstable * fix: vertical flipped drawing * wip: simplify MarkerRasterLayer * improve marker cluster algorithm * - fix: MarkerTileRendering renders incorrect tiles - Removed: MarkerStrategy and MarkerNativeStrategy modules are removed * fix: marker tiling ignore the anchor and the scale properties * fix: build errors * introduce MarkerTileRendering * fix: ArcGIS camera bearing calculation is incorrect * No longer necessary homography * feat: introduce the hole property for Polygon * feat: introduce the hole property for Polygon * feat: introduce the hole property for Polygon * feat: introduce the hole property for Polygon * refact: align the zoom levels * feat: add zIndex property to map overlays and improve camera callbacks - Add zIndex property to Marker, Circle, Polyline, and RasterLayer for controlling overlay draw order across all map SDKs - Improve camera move callbacks with debouncing for ArcGIS and HERE to support camera sync scenarios - Add tile server warmup functionality to reduce raster layer latency - Fix HERE URL template handling for templates with query parameters - Remove unused imports and fix code formatting * Remove unnecessary file * fix: Marker click detection does not work when markerState.icon is null * code cleanup * Adjust the ArcGIZ Optimized zoom0 value * Adjusting the ArcGIZ zoom levels
1 parent 4f24341 commit dc63dfd

1,176 files changed

Lines changed: 64607 additions & 43165 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.claude/settings.local.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@
2020
"Bash(cat:*)",
2121
"Read(//Users/masashi/.gradle/caches/**)",
2222
"Bash(xargs jar tf:*)",
23-
"Bash(npm run build:*)"
23+
"Bash(npm run build:*)",
24+
"WebFetch(domain:medium.com)",
25+
"WebSearch"
2426
],
2527
"deny": [],
2628
"ask": []

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,6 @@ compose-map-story/
2727
slides/
2828
*.secrets
2929
act.env
30+
.gradle-user-home
31+
arcgis-heatmap-issue.txt
32+
session.txt

CLAUDE.md

Lines changed: 0 additions & 103 deletions
This file was deleted.

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,11 @@ A unified mapping library that provides a common API for multiple map providers
2525

2626
### Key Components
2727

28-
- **MapViewController**: Abstract controller interface for all map providers
28+
- **MapViewControllerInterface**: Abstract controller interface for all map providers
2929
- **MapViewBase**: Generic Compose-based map view component
3030
- **Overlay Managers**: Separate managers for markers, circles, polylines, and polygons
31-
- **Projection Utilities**: WebMercator and WGS84 coordinate transformations
32-
- **HexGeocell**: Spatial indexing system for performance optimization
31+
- **ProjectionInterface Utilities**: WebMercator and WGS84 coordinate transformations
32+
- **HexGeocellInterface**: Spatial indexing system for performance optimization
3333

3434
## Quick Start
3535

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import Foundation
2+
3+
public struct GoogleMapMarkerTilingOptions: Equatable, Hashable, Sendable {
4+
public var enabled: Bool
5+
public var minMarkerCount: Int
6+
7+
public init(
8+
enabled: Bool = true,
9+
minMarkerCount: Int = 2000
10+
) {
11+
self.enabled = enabled
12+
self.minMarkerCount = minMarkerCount
13+
}
14+
15+
public static let disabled: GoogleMapMarkerTilingOptions = .init(enabled: false)
16+
}
17+
Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
import CoreGraphics
2+
import GoogleMaps
3+
import UIKit
4+
5+
internal final class GoogleMapTiledMarkerLayer: GMSTileLayer {
6+
internal struct RenderMarker {
7+
let id: String
8+
let latitude: Double
9+
let longitude: Double
10+
let visible: Bool
11+
let image: CGImage
12+
let anchorX: CGFloat
13+
let anchorY: CGFloat
14+
let drawWidth: Double
15+
let drawHeight: Double
16+
}
17+
18+
private let tileSizePx: Int
19+
private let renderScale: Int
20+
private let lock = NSLock()
21+
22+
private var markersById: [String: RenderMarker] = [:]
23+
private var indexedZoom: Int = -1
24+
private var tileToMarkerIds: [UInt64: [String]] = [:]
25+
26+
init(
27+
mapView: GMSMapView,
28+
tileSizePx: Int = 256,
29+
zIndex: Int32 = 0
30+
) {
31+
self.tileSizePx = max(1, tileSizePx)
32+
self.renderScale = min(2, max(1, Int(round(UIScreen.main.scale))))
33+
super.init()
34+
tileSize = self.tileSizePx
35+
self.zIndex = zIndex
36+
map = mapView
37+
}
38+
39+
func setMarkers(
40+
_ markers: [String: RenderMarker],
41+
zoom: Int
42+
) {
43+
lock.lock()
44+
markersById = markers
45+
rebuildIndexLocked(zoom: zoom)
46+
lock.unlock()
47+
clearTileCache()
48+
}
49+
50+
func setZoom(_ zoom: Int) {
51+
lock.lock()
52+
if zoom == indexedZoom {
53+
lock.unlock()
54+
return
55+
}
56+
rebuildIndexLocked(zoom: zoom)
57+
lock.unlock()
58+
clearTileCache()
59+
}
60+
61+
func remove() {
62+
map = nil
63+
}
64+
65+
override func tileFor(x: UInt, y: UInt, zoom: UInt) -> UIImage? {
66+
let requestedZoom = Int(zoom)
67+
let worldTileCount = 1 << requestedZoom
68+
if Int(y) < 0 || Int(y) >= worldTileCount { return nil }
69+
70+
let normalizedX = normalizeTileX(Int(x), worldTileCount: worldTileCount)
71+
let key = tileKey(x: normalizedX, y: Int(y))
72+
73+
lock.lock()
74+
let zoomIndex = (requestedZoom == indexedZoom) ? tileToMarkerIds : [:]
75+
let ids = zoomIndex[key] ?? []
76+
let markers = markersById
77+
lock.unlock()
78+
79+
if ids.isEmpty { return nil }
80+
81+
let renderTileSize = tileSizePx * renderScale
82+
let format = UIGraphicsImageRendererFormat.default()
83+
format.scale = 1
84+
format.opaque = false
85+
86+
let renderImage = UIGraphicsImageRenderer(size: CGSize(width: renderTileSize, height: renderTileSize), format: format).image { ctx in
87+
let context = ctx.cgContext
88+
context.setAllowsAntialiasing(true)
89+
context.setShouldAntialias(true)
90+
context.interpolationQuality = .high
91+
if renderScale != 1 {
92+
context.scaleBy(x: CGFloat(renderScale), y: CGFloat(renderScale))
93+
}
94+
95+
let worldPixelSize = Double(worldTileCount * tileSizePx)
96+
let tileOriginX = Double(normalizedX * tileSizePx)
97+
let tileOriginY = Double(Int(y) * tileSizePx)
98+
99+
for id in ids {
100+
guard let marker = markers[id] else { continue }
101+
if !marker.visible { continue }
102+
let pixel = mercatorPixel(latitude: marker.latitude, longitude: marker.longitude, worldPixelSize: worldPixelSize)
103+
let localX = pixel.x - tileOriginX
104+
let localY = pixel.y - tileOriginY
105+
106+
let left = Double(localX) - Double(marker.anchorX) * marker.drawWidth
107+
let top = Double(localY) - Double(marker.anchorY) * marker.drawHeight
108+
let rect = CGRect(x: left, y: top, width: marker.drawWidth, height: marker.drawHeight)
109+
context.draw(marker.image, in: rect)
110+
}
111+
}
112+
113+
if renderScale == 1 { return renderImage }
114+
115+
let finalImage = UIGraphicsImageRenderer(size: CGSize(width: tileSizePx, height: tileSizePx), format: format).image { ctx in
116+
ctx.cgContext.interpolationQuality = .high
117+
renderImage.draw(in: CGRect(x: 0, y: 0, width: tileSizePx, height: tileSizePx))
118+
}
119+
return finalImage
120+
}
121+
122+
private struct Pixel {
123+
let x: Double
124+
let y: Double
125+
}
126+
127+
private func mercatorPixel(
128+
latitude: Double,
129+
longitude: Double,
130+
worldPixelSize: Double
131+
) -> Pixel {
132+
let clampedLatitude = min(85.05112878, max(-85.05112878, latitude))
133+
let sinLatitude = min(0.9999, max(-0.9999, sin(clampedLatitude * .pi / 180.0)))
134+
let x = (longitude + 180.0) / 360.0
135+
let y = 0.5 - log((1.0 + sinLatitude) / (1.0 - sinLatitude)) / (4.0 * .pi)
136+
137+
let pixelX = normalizePixel(x * worldPixelSize, worldPixelSize: worldPixelSize)
138+
let pixelY = min(worldPixelSize - 1.0, max(0.0, y * worldPixelSize))
139+
return Pixel(x: pixelX, y: pixelY)
140+
}
141+
142+
private func normalizePixel(_ pixel: Double, worldPixelSize: Double) -> Double {
143+
let wrapped = pixel.truncatingRemainder(dividingBy: worldPixelSize)
144+
return wrapped < 0 ? wrapped + worldPixelSize : wrapped
145+
}
146+
147+
private func normalizeTileX(_ x: Int, worldTileCount: Int) -> Int {
148+
let wrapped = x % worldTileCount
149+
return wrapped < 0 ? wrapped + worldTileCount : wrapped
150+
}
151+
152+
private func tileKey(x: Int, y: Int) -> UInt64 {
153+
(UInt64(bitPattern: Int64(x)) << 32) ^ (UInt64(bitPattern: Int64(y)) & 0xffffffff)
154+
}
155+
156+
private func rebuildIndexLocked(zoom: Int) {
157+
if markersById.isEmpty {
158+
indexedZoom = zoom
159+
tileToMarkerIds = [:]
160+
return
161+
}
162+
163+
let worldTileCount = 1 << zoom
164+
let worldPixelSize = Double(worldTileCount * tileSizePx)
165+
var tiles: [UInt64: [String]] = [:]
166+
tiles.reserveCapacity(min(4096, markersById.count))
167+
168+
for marker in markersById.values {
169+
if !marker.visible { continue }
170+
let pixel = mercatorPixel(latitude: marker.latitude, longitude: marker.longitude, worldPixelSize: worldPixelSize)
171+
let left = pixel.x - Double(marker.anchorX) * marker.drawWidth
172+
let top = pixel.y - Double(marker.anchorY) * marker.drawHeight
173+
let right = left + marker.drawWidth
174+
let bottom = top + marker.drawHeight
175+
176+
let minTileX = Int(floor(left / Double(tileSizePx)))
177+
let maxTileX = Int(floor((right - 1.0) / Double(tileSizePx)))
178+
let minTileY = Int(floor(top / Double(tileSizePx)))
179+
let maxTileY = Int(floor((bottom - 1.0) / Double(tileSizePx)))
180+
181+
if minTileY >= worldTileCount || maxTileY < 0 { continue }
182+
for tileY in minTileY...maxTileY {
183+
if tileY < 0 || tileY >= worldTileCount { continue }
184+
for tileX in minTileX...maxTileX {
185+
let normalizedX = normalizeTileX(tileX, worldTileCount: worldTileCount)
186+
let key = tileKey(x: normalizedX, y: tileY)
187+
if tiles[key] == nil { tiles[key] = [] }
188+
tiles[key]?.append(marker.id)
189+
}
190+
}
191+
}
192+
193+
indexedZoom = zoom
194+
tileToMarkerIds = tiles
195+
}
196+
}
197+

build.gradle.kts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
// Top-level build file where you can add configuration options common to all sub-projects/modules.
1+
// Top-level build file where you can add configuration options common to all sub-projects/modules.
22
import java.util.Properties
33
plugins {
44
alias(libs.plugins.android.application) apply false
55
alias(libs.plugins.kotlin.android) apply false
66
alias(libs.plugins.kotlin.compose) apply false
7+
alias(libs.plugins.kotlin.jvm) apply false
78
alias(libs.plugins.android.library) apply false
89
alias(libs.plugins.jlleitschuh.ktlint) apply false
910
id("com.gradleup.nmcp") version "0.0.8"
@@ -60,7 +61,7 @@ tasks.register("allLintChecks") {
6061
val lintTasks =
6162
modules
6263
.filter { it != "mapconductor-bom" }
63-
.map { module ->
64+
.flatMap { module ->
6465
listOf(":$module:ktlintFormat", ":$module:lint")
6566
}
6667

@@ -121,3 +122,4 @@ nmcp {
121122
// All publications from all subprojects will be published
122123
}
123124
}
125+

0 commit comments

Comments
 (0)