diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index a0b718bb..fb92af35 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -169,6 +169,44 @@ jobs: fi done + - name: Run unit tests + if: steps.detect.outputs.changed_modules != '' + run: | + echo "Running unit tests for modules: ${{ steps.detect.outputs.changed_modules }}" + for module in ${{ steps.detect.outputs.changed_modules }}; do + # サンプルアプリはユニットテストを持たない + case "$module" in + example-app|simple-map-app) continue ;; + esac + echo "Running testDebugUnitTest for $module" + if ./gradlew ":$module:testDebugUnitTest"; then + echo "✅ unit tests passed for $module" + else + echo "❌ unit tests failed for $module" + exit 1 + fi + done + + # 公開 API サーフェスの凍結ゲート。ドライバー層の共通化中、アプリ開発者向け API を + # 変えていないことを機械的に保証する。意図した変更なら ./gradlew ::apiDump で + # ベースラインを更新してコミットすること。 + - name: Check public API surface + if: steps.detect.outputs.changed_modules != '' + run: | + echo "Running apiCheck for modules: ${{ steps.detect.outputs.changed_modules }}" + for module in ${{ steps.detect.outputs.changed_modules }}; do + case "$module" in + example-app|simple-map-app|mapconductor-bom) continue ;; + esac + echo "Running apiCheck for $module" + if ./gradlew ":$module:apiCheck"; then + echo "✅ apiCheck passed for $module" + else + echo "❌ public API changed for $module" + exit 1 + fi + done + - name: Save hash cache if: success() uses: actions/cache/save@v5 diff --git a/.gitignore b/.gitignore index b091448c..d9727034 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,9 @@ arcgis-heatmap-issue.txt session.txt .idea google-services.json +app1786438676688i1648980499/ + +# Mappls auth config (account-specific, downloaded from Mappls console) +example-app/*.a.conf +example-app/*.a.olf +example-app/src/debug/assets/mappls-conf.txt diff --git a/.gitmodules b/.gitmodules index 23872897..d59e02e8 100644 --- a/.gitmodules +++ b/.gitmodules @@ -40,3 +40,27 @@ [submodule "android-for-longdo"] path = android-for-longdo url = git@github.com:MapConductor/android-for-longdo.git +[submodule "android-kml"] + path = android-kml + url = git@github.com:MapConductor/android-kml.git +[submodule "android-for-openmobilemaps"] + path = android-for-openmobilemaps + url = git@github.com:MapConductor/android-for-openmobilemaps.git +[submodule "android-for-template"] + path = android-for-template + url = git@github.com:MapConductor/android-for-template.git +[submodule "mapconductor-bom"] + path = mapconductor-bom + url = git@github.com:MapConductor/mapconductor-bom.git +[submodule "android-for-mappls"] + path = android-for-mappls + url = git@github.com:MapConductor/android-for-mappls.git +[submodule "android-icons-jp"] + path = android-icons-jp + url = git@github.com:MapConductor/android-icons-jp.git +[submodule "android-icons-us"] + path = android-icons-us + url = git@github.com:MapConductor/android-icons-us.git +[submodule "android-icons-weather"] + path = android-icons-weather + url = git@github.com:MapConductor/android-icons-weather.git diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000..c3625cf2 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,124 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## 守っている前提(変えないこと) + +- **同一文法・同一動作・同一実装。** 少人数で保守するので「3 プラットフォーム + (android-sdk / ios-sdk / react-sdk)が同じ構造で書かれている」ことが最優先。 + 地図 SDK ごとの差でやむを得ず変える場合も、構造は可能な限り揃える。 +- **アプリ開発者向けの公開 API は凍結。** 見た目の変更と、後方互換な省略可能引数の + 追加は可。それ以外は `apiCheck` が落とす(下記)。 +- **SDK は下げない。** 4.0 が出ているのに 3.3.0 を使う、はしない。 +- **基本的にすべての機能は一度動くことを確認してある。** 壊れて見えたら、まず + 自分の変更を疑う。 +- **端末/エミュレータでの確認を必ず行う。** スクリーンショットだけで済ませない。 +- **API キーを git 管理下のファイルに書かない。** プレースホルダだけを置き、実値は + `secrets.properties` / `local.properties`(git 管理外)から流し込む。 + +## リポジトリ構成 + +Gradle のマルチモジュール。各モジュールは独立した Maven 成果物として publish する。 + +``` +android-sdk-core/ # プロバイダ非依存のコア(com.mapconductor:core) +android-sdk-compose/ # Jetpack Compose 層(com.mapconductor:compose) +android-for-/ # プロバイダ実装(googlemaps / maplibre / mapbox / arcgis / + # here / longdo / maptiler / mappls / tomtom / + # openmobilemaps / template) +android-heatmap/ # 拡張: ヒートマップ +android-marker-clustering/ # 拡張: マーカークラスタリング +android-geojson-layer/ # 拡張: GeoJSON タイルレイヤ +android-kml/ # 拡張: KML +android-icons*/ # アイコン集(jp / us / weather) +gradle/api-surface.gradle.kts # 公開 API サーフェスの門番 +``` + +`android-for-template` は**実在の地図 SDK ではなく代役を描くだけ**の雛形。 +新しいプロバイダを足すときの出発点で、アプリ開発者向けの選択肢ではない。 + +## ビルドと検証 + +```bash +# Android Studio 同梱 JDK を明示しないと通らないことがある +export JAVA_HOME="/Applications/Android Studio.app/Contents/jbr/Contents/Home" + +./gradlew :android-for-maplibre:compileDebugKotlin +./gradlew apiCheck # 公開 API の差分検査(後述) +./gradlew apiDump # 意図した変更ならベースラインを更新 +``` + +### 公開 API サーフェス(apiDump / apiCheck) + +`gradle/api-surface.gradle.kts` が release AAR の `classes.jar` を `javap` で読み、 +`/api/.api` にテキストで記録する。`binary-compatibility-validator` は +`com.android.library` に対応していない(`apiDump` タスクが 1 つも生成されない)ため +この方式を採っている。 + +`@InternalMapConductorApi` を付けた宣言は記録しない。**ドライバー実装点には必ず +付けること。** 付け忘れるとアプリ向けの凍結 API に載ってしまう。 +iOS の `@_spi(MapConductorDriver)`、React の `/** @internal */` に対応する。 + +## React Native から使うときの結合(重要) + +`react-sdk/reactnative-for-*` はこのリポジトリを **MavenLocal 経由**で参照する。 +コピーはしない。 + +```bash +# android-sdk を変更したら必ず publish してから RN 側を確認する +./gradlew :android-for-maplibre:compileDebugKotlin :android-for-maplibre:publishToMavenLocal +``` + +publish を忘れると **RN 側は古い実装のまま静かに動く。** 「直したのに変わらない」の +ほとんどはこれ。各 RN パッケージの `android/build.gradle` にある +`com.mapconductor:*` のバージョンと MavenLocal を揃えること。 + +Kotlin は 2.3.20 に固定してある(上げると pika の上限に当たる)。 + +## アーキテクチャ + +### コントローラは Compose と RN で共有する + +プロバイダごとに `createXxxViewController()` というファクトリを持ち、 +**Compose 版も RN 版も同じファクトリを通る。** + +``` +Compose: XxxMapView.kt ─┐ + ├→ createXxxViewController() → XxxViewController +RN: XxxMapViewWrapper.kt ─┘ +``` + +新しいプロバイダを足すときも、Compose だけで動く経路を作らないこと。 +経路が 2 本あると、片方でしか露見しない不具合が入る(下の「タイル判定」参照)。 + +### マーカーは RN で Compose 層を通さない + +RN のマーカーは意図的に Compose を経由しない(大量マーカーで固まるため)。 +Compose ホストは、ヒートマップやマーカークラスタリングなど **Compose が要る拡張** +だけに限定してある。 + +## 踏んだ落とし穴 + +- **タイル判定を自分で書かない。** + `controller.useMarkerLayer = markerTiling != null` と手書きすると、RN の共通基底が + 常に非 null の `MarkerTilingOptions` を渡すので **全マーカーが消える**。 + Compose では各ページが null を渡すので露見しない。 + `MarkerTilingOptions.shouldUseTiles` / + `shouldUseMarkerLayer(count)`(`options.enabled && count >= options.minMarkerCount`) + を呼ぶこと。android-for-longdo と android-for-maptiler が同じ穴を順に踏んだ。 + +- **デザイン ID の受け渡し方はプロバイダごとに違う。** + `id` / `getValue()` / スタイル URI のどれが 3 プラットフォームで一致するかは + プロバイダによる(Longdo・MapTiler は `id`、Mapbox はスタイル URI)。 + **web・android・iOS の 3 つを実際に読んでから決めること。** iOS だけ見て web も + 同じと決めつけて、Mapbox のタイルが真っ白になったことがある。 + +- **`map.animateCamera(update, duration)` は素の ease ではない。** + MapLibre / Mappls では `Transform.animateCamera` → `NativeMap.flyTo`、つまり + van Wijk のズームアークに落ちる。ease が要るなら `easeCamera`。 + 名前から素の ease だと決めつけると iOS 側の対向実装を誤る。 + +## 対応するリポジトリ + +`ios-sdk`(Swift)と `react-sdk`(TypeScript + RN ブリッジ)が兄弟として並ぶ前提。 +RN の `:path` / MavenLocal 参照はこの配置に依存している。 diff --git a/LICENSE b/LICENSE index 74cc4bf5..44382875 100644 --- a/LICENSE +++ b/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2025 Masashi Katsumata + Copyright 2026 Masashi Katsumata Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/README.es-419.md b/README.es-419.md index b64d2364..096587ed 100644 --- a/README.es-419.md +++ b/README.es-419.md @@ -112,11 +112,11 @@ dependencies { Cada proveedor de mapas puede requerir su propia clave de API, token de acceso, configuración de Gradle o configuración en el manifiesto de Android. Por favor revisa la guía de configuración del proveedor que estés usando. -- [Configuración de Google Maps Android API](https://docs-android.mapconductor.com/setup/google-maps/) -- [Configuración de MapBox](https://docs-android.mapconductor.com/setup/mapbox/) -- [Configuración de HERE](https://docs-android.mapconductor.com/setup/here-maps/) -- [Configuración de ArcGIS](https://docs-android.mapconductor.com/setup/arcgis/) -- [Configuración de MapLibre](https://docs-android.mapconductor.com/setup/maplibre/) +- [Configuración de Google Maps Android API](https://mapconductor.com/setup/android/google-maps/) +- [Configuración de MapBox](https://mapconductor.com/setup/android/mapbox/) +- [Configuración de HERE](https://mapconductor.com/setup/android/here/) +- [Configuración de ArcGIS](https://mapconductor.com/setup/android/arcgis/) +- [Configuración de MapLibre](https://mapconductor.com/setup/android/maplibre/) --- @@ -354,7 +354,7 @@ Es especialmente útil cuando quieres que tu código de aplicación describa qu La documentación está disponible en: -https://docs-android.mapconductor.com/es-419/ +https://mapconductor.com/es-419/ La documentación incluye: diff --git a/README.ja.md b/README.ja.md index 9a2bcaa8..dd126c71 100644 --- a/README.ja.md +++ b/README.ja.md @@ -112,11 +112,11 @@ dependencies { 各マッププロバイダーは、それぞれ独自のAPIキー、アクセストークン、Gradleの設定、またはAndroidマニフェストの設定が必要な場合があります。 使用するプロバイダーのセットアップガイドを確認してください。 -- [Google Maps Android APIのセットアップ](https://docs-android.mapconductor.com/setup/google-maps/) -- [MapBoxのセットアップ](https://docs-android.mapconductor.com/setup/mapbox/) -- [HEREのセットアップ](https://docs-android.mapconductor.com/setup/here-maps/) -- [ArcGISのセットアップ](https://docs-android.mapconductor.com/setup/arcgis/) -- [MapLibreのセットアップ](https://docs-android.mapconductor.com/setup/maplibre/) +- [Google Maps Android APIのセットアップ](https://mapconductor.com/setup/android/google-maps/) +- [MapBoxのセットアップ](https://mapconductor.com/setup/android/mapbox/) +- [HEREのセットアップ](https://mapconductor.com/setup/android/here/) +- [ArcGISのセットアップ](https://mapconductor.com/setup/android/arcgis/) +- [MapLibreのセットアップ](https://mapconductor.com/setup/android/maplibre/) --- @@ -354,7 +354,7 @@ MapConductorは、以下のような方に役立ちます: ドキュメントは以下で利用できます: -https://docs-android.mapconductor.com/ja/ +https://mapconductor.com/ja/ ドキュメントには以下が含まれます: diff --git a/README.md b/README.md index 3474793b..032a9d56 100644 --- a/README.md +++ b/README.md @@ -112,11 +112,11 @@ dependencies { Each map provider may require its own API key, access token, Gradle setup, or Android manifest configuration. Please check the setup guide for the provider you are using. -- [Setup for Google Maps Android API](https://docs-android.mapconductor.com/setup/google-maps/) -- [Setup for MapBox](https://docs-android.mapconductor.com/setup/mapbox/) -- [Setup for HERE](https://docs-android.mapconductor.com/setup/here-maps/) -- [Setup for ArcGIS](https://docs-android.mapconductor.com/setup/arcgis/) -- [Setup for MapLibre](https://docs-android.mapconductor.com/setup/maplibre/) +- [Setup for Google Maps Android API](https://mapconductor.com/setup/android/google-maps/) +- [Setup for MapBox](https://mapconductor.com/setup/android/mapbox/) +- [Setup for HERE](https://mapconductor.com/setup/android/here/) +- [Setup for ArcGIS](https://mapconductor.com/setup/android/arcgis/) +- [Setup for MapLibre](https://mapconductor.com/setup/android/maplibre/) --- @@ -354,7 +354,7 @@ It is especially helpful when you want your application code to describe what sh Documentation is available at: -https://docs-android.mapconductor.com/ +https://mapconductor.com/ The documentation includes: diff --git a/android-for-arcgis b/android-for-arcgis index 9ce9a87e..195d5ed7 160000 --- a/android-for-arcgis +++ b/android-for-arcgis @@ -1 +1 @@ -Subproject commit 9ce9a87ee3f1f83138adef1f79e76e4e3598f84d +Subproject commit 195d5ed78fdf031659f56b83388873b679a9a69e diff --git a/android-for-googlemaps b/android-for-googlemaps index a4706649..5ccf17ab 160000 --- a/android-for-googlemaps +++ b/android-for-googlemaps @@ -1 +1 @@ -Subproject commit a4706649c2402528ad681ce83d6d0a78df695d63 +Subproject commit 5ccf17ab2c718002ef7f8f641c98f6f8a85c042f diff --git a/android-for-here b/android-for-here index 621b7d9a..4a4f24f1 160000 --- a/android-for-here +++ b/android-for-here @@ -1 +1 @@ -Subproject commit 621b7d9a8d438061d630b9f07612896ceed8f9e3 +Subproject commit 4a4f24f1cc6475b94aa54e1c6fc72f1c613d68a6 diff --git a/android-for-longdo b/android-for-longdo index 9fc60eef..5b7be301 160000 --- a/android-for-longdo +++ b/android-for-longdo @@ -1 +1 @@ -Subproject commit 9fc60eef6327e62a0c24bde79bc596b32c93e2fd +Subproject commit 5b7be301e707d8fc84835fe2bd39fef700abb759 diff --git a/android-for-mapbox b/android-for-mapbox index 36067a76..63d19374 160000 --- a/android-for-mapbox +++ b/android-for-mapbox @@ -1 +1 @@ -Subproject commit 36067a76f04ffd8bb458bfe4aa83ef9e867af300 +Subproject commit 63d19374498cf4810a45fb61e3f76bb8ecbce0d8 diff --git a/android-for-maplibre b/android-for-maplibre index f907de17..58db0dd7 160000 --- a/android-for-maplibre +++ b/android-for-maplibre @@ -1 +1 @@ -Subproject commit f907de17ba6ef7e71278c508e4b0b42cee501a00 +Subproject commit 58db0dd7fb9b35eb422e6a3d8c4ee0bcb1c93b5e diff --git a/android-for-mappls b/android-for-mappls new file mode 160000 index 00000000..6bc026af --- /dev/null +++ b/android-for-mappls @@ -0,0 +1 @@ +Subproject commit 6bc026af1985e379ef1d54cca3997e36cc9e959e diff --git a/android-for-maptiler b/android-for-maptiler index 629df0a7..0c14e264 160000 --- a/android-for-maptiler +++ b/android-for-maptiler @@ -1 +1 @@ -Subproject commit 629df0a71c51cfd9d92ca4a4e1b66624a2af8319 +Subproject commit 0c14e2648c6025ca6eff94abc2fac6a823a525dd diff --git a/android-for-openmobilemaps b/android-for-openmobilemaps new file mode 160000 index 00000000..83b738ca --- /dev/null +++ b/android-for-openmobilemaps @@ -0,0 +1 @@ +Subproject commit 83b738cafec719961e26c49f12529b5954f0911b diff --git a/android-for-template b/android-for-template new file mode 160000 index 00000000..f467eb06 --- /dev/null +++ b/android-for-template @@ -0,0 +1 @@ +Subproject commit f467eb06d7c7f67b59c980e402c396f8e45577d5 diff --git a/android-for-tomtom b/android-for-tomtom index d2baa2c3..7552cecd 160000 --- a/android-for-tomtom +++ b/android-for-tomtom @@ -1 +1 @@ -Subproject commit d2baa2c3a8edb67358d7a92c73292c3a2f808c80 +Subproject commit 7552cecde1da358777b2eb7d93198a4740cb85d5 diff --git a/android-geojson-layer b/android-geojson-layer index 25eb6558..1c2fbf07 160000 --- a/android-geojson-layer +++ b/android-geojson-layer @@ -1 +1 @@ -Subproject commit 25eb655813526dd14bafbeead2d1df54e1496202 +Subproject commit 1c2fbf07db2e63372df7f31c72404dd3c7d08878 diff --git a/android-heatmap b/android-heatmap index 2ddb086f..d660d441 160000 --- a/android-heatmap +++ b/android-heatmap @@ -1 +1 @@ -Subproject commit 2ddb086f3469fb7baf96de8b8eddf1af24285498 +Subproject commit d660d441a8f51101ef4e04a6c5b823f53ebe0acc diff --git a/android-icons b/android-icons index 2a3a759c..d1fdb0d9 160000 --- a/android-icons +++ b/android-icons @@ -1 +1 @@ -Subproject commit 2a3a759c47b46c0dac5cc8d9a41cddeae1d6940b +Subproject commit d1fdb0d9a8a4217e73e5263060543bc0d42df1a2 diff --git a/android-icons-jp b/android-icons-jp new file mode 160000 index 00000000..36dd2d08 --- /dev/null +++ b/android-icons-jp @@ -0,0 +1 @@ +Subproject commit 36dd2d08d4f6a41e749ae9f8f032491951ccf254 diff --git a/android-icons-us b/android-icons-us new file mode 160000 index 00000000..b85bf3f8 --- /dev/null +++ b/android-icons-us @@ -0,0 +1 @@ +Subproject commit b85bf3f8327c2237a8e1080f968c223f32bb2657 diff --git a/android-icons-weather b/android-icons-weather new file mode 160000 index 00000000..ac9847a2 --- /dev/null +++ b/android-icons-weather @@ -0,0 +1 @@ +Subproject commit ac9847a21fe1e6f26b87fa85447716b49dac7027 diff --git a/android-kml b/android-kml new file mode 160000 index 00000000..d712d28b --- /dev/null +++ b/android-kml @@ -0,0 +1 @@ +Subproject commit d712d28b191fdbfc966ec9eedcfa8acc6f18e4c0 diff --git a/android-kml/.gitignore b/android-kml/.gitignore deleted file mode 100644 index 90e8a32d..00000000 --- a/android-kml/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -build/ -.DS_Store -.gradle/ diff --git a/android-kml/README.md b/android-kml/README.md deleted file mode 100644 index 0be50898..00000000 --- a/android-kml/README.md +++ /dev/null @@ -1,103 +0,0 @@ -# MapConductor KML Layer - -`android-kml` adds a tile-rendered KML overlay to MapConductor map views. It parses OGC KML 2.2 -documents into feature models, renders them through MapConductor's raster tile layer pipeline, -and provides hit-testing for feature selection. - -It shares the tile-rendering architecture of `android-geojson-layer`: rendering is tile based and -parsed features are supplied as lightweight data objects, so it scales to large KML datasets. - -## Features - -- Parses `Point`, `LineString`, `LinearRing`, `Polygon` (with `innerBoundaryIs` holes), - and `MultiGeometry`. -- Traverses nested `` and `` containers. -- Resolves KML styling: `LineStyle` (color, width), `PolyStyle` (color, fill, outline), - and `IconStyle` (color), including shared `