diff --git a/.github/workflows/deploy-basic-example.yml b/.github/workflows/deploy-basic-example.yml new file mode 100644 index 0000000..8a10099 --- /dev/null +++ b/.github/workflows/deploy-basic-example.yml @@ -0,0 +1,64 @@ +name: Deploy basic example + +on: + push: + branches: + - main + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: pages + cancel-in-progress: false + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v7 + with: + submodules: recursive + + - name: Setup pnpm + uses: pnpm/action-setup@v6 + + - name: Setup Node + uses: actions/setup-node@v6 + with: + cache: pnpm + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Build basic example + run: pnpm --filter @mapconductor/example-basic build + env: + VITE_BASE_PATH: /${{ github.event.repository.name }}/ + VITE_GOOGLE_MAPS_API_KEY: ${{ secrets.VITE_GOOGLE_MAPS_API_KEY }} + + - name: Add SPA fallback + run: cp examples/basic/dist/index.html examples/basic/dist/404.html + + - name: Setup Pages + uses: actions/configure-pages@v5 + + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + path: examples/basic/dist + + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + needs: build + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.gitmodules b/.gitmodules index db60de9..e79c27b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -19,3 +19,15 @@ [submodule "reactnative-for-maplibre"] path = reactnative-for-maplibre url = ./reactnative-for-maplibre +[submodule "react-geojson-layer"] + path = react-geojson-layer + url = ./react-geojson-layer +[submodule "react-heatmap"] + path = react-heatmap + url = ./react-heatmap +[submodule "react-icons"] + path = react-icons + url = ./react-icons +[submodule "react-marker-clustering"] + path = react-marker-clustering + url = ./react-marker-clustering diff --git a/examples/basic/package.json b/examples/basic/package.json index a8beb9b..68ff014 100644 --- a/examples/basic/package.json +++ b/examples/basic/package.json @@ -13,7 +13,11 @@ "@mapconductor/js-sdk-core": "workspace:*", "@mapconductor/react-for-googlemaps": "workspace:*", "@mapconductor/react-for-maplibre": "workspace:*", + "@mapconductor/react-icons": "workspace:*", + "@mapconductor/react-marker-clustering": "workspace:*", "@mapconductor/js-sdk-react": "workspace:*", + "@mapconductor/react-geojson-layer": "workspace:*", + "@mapconductor/react-heatmap": "workspace:*", "react": "^19.2.7", "react-dom": "^19.2.7", "react-router-dom": "^7.18.0" diff --git a/examples/basic/public/geojson/sample-railway.geojson b/examples/basic/public/geojson/sample-railway.geojson new file mode 100644 index 0000000..86b5167 --- /dev/null +++ b/examples/basic/public/geojson/sample-railway.geojson @@ -0,0 +1,149 @@ +{ + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "id": "yamanote-1", + "geometry": { + "type": "LineString", + "coordinates": [ + [139.7744, 35.6812], + [139.7703, 35.6818], + [139.7664, 35.6830], + [139.7627, 35.6855], + [139.7599, 35.6882], + [139.7587, 35.6916], + [139.7592, 35.6950], + [139.7614, 35.6980], + [139.7649, 35.6999], + [139.7692, 35.7007], + [139.7732, 35.6998], + [139.7764, 35.6977], + [139.7785, 35.6947], + [139.7784, 35.6913], + [139.7766, 35.6882], + [139.7744, 35.6812] + ] + }, + "properties": { + "N02_002": "普通鉄道", + "N02_003": "幹線鉄道", + "N02_004": "山手線", + "N02_005": "東日本旅客鉄道" + } + }, + { + "type": "Feature", + "id": "chuo-1", + "geometry": { + "type": "LineString", + "coordinates": [ + [139.7320, 35.6980], + [139.7420, 35.6980], + [139.7520, 35.6974], + [139.7620, 35.6967], + [139.7665, 35.6958], + [139.7744, 35.6812] + ] + }, + "properties": { + "N02_002": "普通鉄道", + "N02_003": "幹線鉄道", + "N02_004": "中央線快速", + "N02_005": "東日本旅客鉄道" + } + }, + { + "type": "Feature", + "id": "ginza-1", + "geometry": { + "type": "LineString", + "coordinates": [ + [139.7353, 35.6673], + [139.7432, 35.6727], + [139.7516, 35.6753], + [139.7595, 35.6762], + [139.7659, 35.6773], + [139.7707, 35.6804], + [139.7744, 35.6812], + [139.7760, 35.6775], + [139.7781, 35.6742], + [139.7822, 35.6719] + ] + }, + "properties": { + "N02_002": "普通鉄道", + "N02_003": "地下鉄", + "N02_004": "銀座線", + "N02_005": "東京地下鉄" + } + }, + { + "type": "Feature", + "id": "sobu-1", + "geometry": { + "type": "LineString", + "coordinates": [ + [139.7744, 35.6812], + [139.7817, 35.6856], + [139.7891, 35.6892], + [139.7972, 35.6909], + [139.8063, 35.6934], + [139.8133, 35.6979] + ] + }, + "properties": { + "N02_002": "普通鉄道", + "N02_003": "幹線鉄道", + "N02_004": "総武線", + "N02_005": "東日本旅客鉄道" + } + }, + { + "type": "Feature", + "id": "keihin-1", + "geometry": { + "type": "LineString", + "coordinates": [ + [139.7744, 35.6812], + [139.7712, 35.6770], + [139.7687, 35.6726], + [139.7668, 35.6682], + [139.7649, 35.6621], + [139.7635, 35.6560] + ] + }, + "properties": { + "N02_002": "普通鉄道", + "N02_003": "幹線鉄道", + "N02_004": "京浜東北線", + "N02_005": "東日本旅客鉄道" + } + }, + { + "type": "Feature", + "id": "hibiya-1", + "geometry": { + "type": "LineString", + "coordinates": [ + [139.7150, 35.6450], + [139.7243, 35.6511], + [139.7327, 35.6572], + [139.7411, 35.6634], + [139.7500, 35.6697], + [139.7600, 35.6750], + [139.7687, 35.6795], + [139.7744, 35.6812], + [139.7800, 35.6844], + [139.7851, 35.6876] + ] + }, + "properties": { + "N02_002": "普通鉄道", + "N02_003": "地下鉄", + "N02_004": "日比谷線", + "N02_005": "東京地下鉄" + } + } + ] +} diff --git a/examples/basic/public/postoffice/cluster_red.png b/examples/basic/public/postoffice/cluster_red.png new file mode 100644 index 0000000..7fd5c60 Binary files /dev/null and b/examples/basic/public/postoffice/cluster_red.png differ diff --git a/examples/basic/public/postoffice/postoffice.webp b/examples/basic/public/postoffice/postoffice.webp new file mode 100644 index 0000000..a8f16aa Binary files /dev/null and b/examples/basic/public/postoffice/postoffice.webp differ diff --git a/examples/basic/src/App.tsx b/examples/basic/src/App.tsx index 057b42c..a9c4a45 100644 --- a/examples/basic/src/App.tsx +++ b/examples/basic/src/App.tsx @@ -3,7 +3,7 @@ import { Routes, Route, Navigate, useNavigate, useLocation, useParams } from 're import { PageNav } from './components/PageNav'; import { CirclePage } from './pages/CirclePage'; import { MapPage } from './pages/map/basic/StoreMapPage'; -import { MarkerPage } from './pages/MarkerPage'; +import { MarkerIconsPage } from './pages/marker/icons/MarkerIconsPage'; import { PolygonPage } from './pages/PolygonPage'; import { PolylinePage } from './pages/PolylinePage'; import { GroundImagePage } from './pages/groundimage/GroundImagePage'; @@ -15,25 +15,49 @@ import { MapDesignPage } from './pages/map/design/MapDesignPage'; import { FlyToPage } from './pages/map/flyto/FlyToPage'; import { TiltPage } from './pages/map/tilt/TiltPage'; import { VisibleRegionPage } from './pages/map/visibleregion/VisibleRegionPage'; +import { CameraSyncPage } from './pages/map/camerasync/CameraSyncPage'; import { MarkerAnimationPage } from './pages/marker/animation/MarkerAnimationPage'; import { PostOfficePage } from './pages/marker/postoffice/PostOfficePage'; +import { PostOfficeClusterPage } from './pages/marker/postofficecluster/PostOfficeClusterPage'; import { PolygonClickPage } from './pages/polygon/click/PolygonClickPage'; import { PolygonGeodesicPage } from './pages/polygon/geodesic/PolygonGeodesicPage'; import { PolygonHolePage } from './pages/polygon/hole/PolygonHolePage'; import { PolylineClickPage } from './pages/polyline/click/PolylineClickPage'; import { RasterLayerPage } from './pages/rasterlayer/RasterLayerPage'; -import { UnsupportedSamplePage } from './pages/unsupported/UnsupportedSamplePage'; -import { DEFAULT_SAMPLE_PAGE, SAMPLE_PAGES, isKnownSamplePage } from './sampleRegistry'; +import { HeatmapLayerPage } from './pages/heatmaplayer/HeatmapLayerPage'; +import { BasicGeoJSONPage } from './pages/geojson/basic/BasicGeoJSONPage'; +import { GeoJSONLayerPage } from './pages/geojson/layer/GeoJSONLayerPage'; +import { DEFAULT_SAMPLE_PAGE, getSamplePageDefinition, isKnownSamplePage } from './sampleRegistry'; type MapProvider = 'maplibre' | 'google' | 'google-3d'; +function providerRouteId(provider: string | undefined): string { + return provider ?? ''; +} + +function isPageUnavailableOnProvider(page: string | undefined, provider: string | undefined): boolean { + const definition = getSamplePageDefinition(page); + return definition?.unavailableProviders?.includes(providerRouteId(provider)) ?? false; +} + +function ProviderUnavailableOverlay() { + return ( +
+
+ This feature is not available on this provider +
+
+ ); +} + function pageContent(page: string | undefined) { switch (page) { case 'map-design': return ; case 'fly-to': return ; case 'tilt': return ; case 'visible-region': return ; - case 'marker': return ; + case 'camera-sync': return ; + case 'marker': return ; case 'marker-animation': return ; case 'post-office': return ; case 'circle': return ; @@ -49,12 +73,10 @@ function pageContent(page: string | undefined) { case 'info-bubble-styled': return ; case 'info-bubble-multiple': return ; case 'info-bubble-rich': return ; - case 'post-office-cluster': - case 'geojson-basic': - case 'geojson-layer': - case 'heatmap-layer': - case 'camera-sync': - return item.id === page)?.label ?? 'Unsupported'} />; + case 'post-office-cluster': return ; + case 'heatmap-layer': return ; + case 'geojson-basic': return ; + case 'geojson-layer': return ; default: return ; } } @@ -68,7 +90,14 @@ function ProviderPageRoute() { return ; } - return pageContent(page); + const isUnavailable = isPageUnavailableOnProvider(requestedPage, provider); + + return ( + <> + {isUnavailable ? : pageContent(page)} + {isUnavailable && } + + ); } function App() { @@ -81,6 +110,8 @@ function App() { location.pathname.startsWith('/google-maps-3d') ? 'google-3d' : location.pathname.startsWith('/google-maps') ? 'google' : null; + const currentPage = location.pathname.split('/').filter(Boolean)[1] || DEFAULT_SAMPLE_PAGE; + const showProviderSelector = getSamplePageDefinition(currentPage)?.showProviderSelector ?? true; const switchProvider = (provider: MapProvider) => { const pathParts = location.pathname.split('/').filter(Boolean); @@ -111,17 +142,19 @@ function App() { - + {showProviderSelector && ( + + )} diff --git a/examples/basic/src/MapViewContainer.tsx b/examples/basic/src/MapViewContainer.tsx index 01abe4a..b7385a1 100644 --- a/examples/basic/src/MapViewContainer.tsx +++ b/examples/basic/src/MapViewContainer.tsx @@ -1,5 +1,5 @@ import { useEffect, useRef } from 'react'; -import { useNavigate, useLocation } from 'react-router-dom'; +import { useLocation } from 'react-router-dom'; import { MapLibreDesign, MapLibreView, useMapLibreViewState, type MapLibreViewState } from '@mapconductor/react-for-maplibre'; import { GoogleMapDesign, GoogleMapsView, GoogleMapsView2D, useGoogleMapViewState, type GoogleMapViewState } from '@mapconductor/react-for-googlemaps'; import { createGeoPoint, createMapCameraPosition, MarkerTilingOptions, type GeoPoint, type MapCameraPosition, type MapViewStateInterface, type MapDesignTypeInterface } from '@mapconductor/js-sdk-core'; @@ -12,6 +12,8 @@ export { DEFAULT_CAMERA }; interface MapViewContainerProps { children?: React.ReactNode; onMapClick?: (point: GeoPoint) => void; + onCameraMoveStart?: (cam: MapCameraPosition) => void; + onCameraMove?: (cam: MapCameraPosition) => void; onCameraMoveEnd?: (cam: MapCameraPosition) => void; markerTilingOptions?: MarkerTilingOptions; state: MapViewStateInterface>; @@ -38,18 +40,26 @@ export function useSampleMapViewState(initialCamera: InitialCamera = DEFAULT_CAM return mapLibreState; } -function MapLibreContainer({ children, onMapClick, onCameraMoveEnd, markerTilingOptions, state }: MapViewContainerProps) { +function MapLibreContainer({ children, onMapClick, onCameraMoveStart, onCameraMove, onCameraMoveEnd, markerTilingOptions, state }: MapViewContainerProps) { const mapState = state as MapLibreViewState; - const isActive = useRef(true); - useEffect(() => () => { isActive.current = false; }, []); + const isActive = useRef(false); + useEffect(() => () => { isActive.current = true; }, []); return ( { + if (!isActive.current) return; + onCameraMoveStart?.(newCam); + }} + onCameraMove={(newCam: any) => { + if (!isActive.current) return; + onCameraMove?.(newCam); + }} onCameraMoveEnd={(newCam: any) => { if (!isActive.current) return; onCameraMoveEnd?.(newCam); @@ -60,12 +70,11 @@ function MapLibreContainer({ children, onMapClick, onCameraMoveEnd, markerTiling ); } -function GoogleMapsContainer2D({ children, onMapClick, onCameraMoveEnd, markerTilingOptions, state }: MapViewContainerProps) { - const navigate = useNavigate(); +function GoogleMapsContainer2D({ children, onMapClick, onCameraMoveStart, onCameraMove, onCameraMoveEnd, markerTilingOptions, state }: MapViewContainerProps) { const mapState = state as GoogleMapViewState; - const isActive = useRef(true); - useEffect(() => () => { isActive.current = false; }, []); + const isActive = useRef(false); + useEffect(() => () => { isActive.current = true; }, []); const apiKey = import.meta.env.VITE_GOOGLE_MAPS_API_KEY || ''; if (!apiKey || apiKey === 'your_api_key_here') { @@ -86,7 +95,16 @@ function GoogleMapsContainer2D({ children, onMapClick, onCameraMoveEnd, markerTi apiKey={apiKey} mapId={'DEMO_MAP_ID'} version='alpha' + libraries={'map3d'} // for demo markerTilingOptions={markerTilingOptions} + onCameraMoveStart={(newCam: any) => { + if (!isActive.current) return; + onCameraMoveStart?.(newCam); + }} + onCameraMove={(newCam: any) => { + if (!isActive.current) return; + onCameraMove?.(newCam); + }} onMapClick={onMapClick} onCameraMoveEnd={(newCam: any) => { if (!isActive.current) return; @@ -98,11 +116,11 @@ function GoogleMapsContainer2D({ children, onMapClick, onCameraMoveEnd, markerTi ); } -function GoogleMapsContainer3D({ children, onMapClick, onCameraMoveEnd, state }: MapViewContainerProps) { +function GoogleMapsContainer3D({ children, onMapClick, onCameraMoveStart, onCameraMove, onCameraMoveEnd, state }: MapViewContainerProps) { const mapState = state as GoogleMapViewState; - const isActive = useRef(true); - useEffect(() => () => { isActive.current = false; }, []); + const isActive = useRef(false); + useEffect(() => () => { isActive.current = true; }, []); const apiKey = import.meta.env.VITE_GOOGLE_MAPS_API_KEY || ''; if (!apiKey || apiKey === 'your_api_key_here') { @@ -124,6 +142,14 @@ function GoogleMapsContainer3D({ children, onMapClick, onCameraMoveEnd, state }: mapId={'DEMO_MAP_ID'} version='alpha' onMapClick={onMapClick} + onCameraMoveStart={(newCam: any) => { + if (!isActive.current) return; + onCameraMoveStart?.(newCam); + }} + onCameraMove={(newCam: any) => { + if (!isActive.current) return; + onCameraMove?.(newCam); + }} onCameraMoveEnd={(newCam: any) => { if (!isActive.current) return; onCameraMoveEnd?.(newCam); @@ -134,7 +160,7 @@ function GoogleMapsContainer3D({ children, onMapClick, onCameraMoveEnd, state }: ); } -export function MapViewContainer({ children, onMapClick, onCameraMoveEnd, markerTilingOptions, state }: MapViewContainerProps) { +export function MapViewContainer({ children, onMapClick, onCameraMoveStart, onCameraMove, onCameraMoveEnd, markerTilingOptions, state }: MapViewContainerProps) { const location = useLocation(); const isGoogle3D = location.pathname.startsWith('/google-maps-3d'); const isGoogle2D = !isGoogle3D && location.pathname.startsWith('/google-maps'); @@ -142,6 +168,8 @@ export function MapViewContainer({ children, onMapClick, onCameraMoveEnd, marker if (isGoogle3D) { return {children} @@ -150,6 +178,8 @@ export function MapViewContainer({ children, onMapClick, onCameraMoveEnd, marker if (isGoogle2D) { return @@ -158,6 +188,8 @@ export function MapViewContainer({ children, onMapClick, onCameraMoveEnd, marker } return diff --git a/examples/basic/src/hooks/useNavigation.ts b/examples/basic/src/hooks/useNavigation.ts deleted file mode 100644 index 35240ce..0000000 --- a/examples/basic/src/hooks/useNavigation.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { useState, useCallback } from 'react'; -import { createGeoPoint, createMapCameraPosition } from '@mapconductor/js-sdk-core'; -import { useCamera } from '@mapconductor/js-sdk-react'; - -/** - * Custom hook for handling map navigation with loading state - */ -export function useNavigation() { - const { moveCamera } = useCamera(); - const [isNavigating, setIsNavigating] = useState(false); - - const flyToLocation = useCallback( - async (lat: number, lng: number) => { - setIsNavigating(true); - try { - await moveCamera(createMapCameraPosition({ - position: createGeoPoint({ latitude: lat, longitude: lng }), - zoom: 10, - })); - } catch (error) { - console.error('Navigation error:', error); - } finally { - setIsNavigating(false); - } - }, - [moveCamera] - ); - - return { - flyToLocation, - isNavigating, - }; -} diff --git a/examples/basic/src/index.css b/examples/basic/src/index.css index 700b1fb..eac03a5 100644 --- a/examples/basic/src/index.css +++ b/examples/basic/src/index.css @@ -1,4 +1,5 @@ @import './pages/map/design/map-design-page.css'; +@import './pages/map/camerasync/camera-sync-page.css'; * { margin: 0; @@ -196,6 +197,57 @@ body, color: white; } +.simple-text-bubble { + color: #2563eb; + padding: 4px; +} + +.marker-icon-info-bubble { + max-width: min(360px, 72vw); + white-space: pre-wrap; + color: #111827; + font: 12px/1.35 ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', monospace; +} + +.right-tail-info-bubble { + position: relative; + width: max-content; + max-width: 220px; + padding: 8px; + margin-left: 8px; + border: 2px solid #000000; + border-radius: 4px; + background: #ffffff; + color: #2563eb; + font-size: 13px; + line-height: 1.35; + box-shadow: 0 3px 14px rgba(15, 23, 42, 0.18); +} + +.right-tail-info-bubble::before, +.right-tail-info-bubble::after { + content: ''; + position: absolute; + top: 50%; + transform: translateY(-50%); + width: 0; + height: 0; +} + +.right-tail-info-bubble::before { + left: -10px; + border-top: 7px solid transparent; + border-bottom: 7px solid transparent; + border-right: 10px solid #000000; +} + +.right-tail-info-bubble::after { + left: -7px; + border-top: 5px solid transparent; + border-bottom: 5px solid transparent; + border-right: 8px solid #ffffff; +} + .rich-bubble { display: grid; gap: 6px; @@ -208,6 +260,62 @@ body, font-size: 13px; } +.rich-location-bubble { + display: grid; + gap: 8px; + width: 200px; + color: #111827; + font-size: 13px; +} + +.rich-location-bubble strong { + font-size: 18px; + line-height: 1.2; +} + +.rich-location-bubble p { + color: #6b7280; + line-height: 1.35; +} + +.rating-row { + display: flex; + align-items: center; + gap: 4px; + font-size: 12px; +} + +.rating-stars { + display: flex; + gap: 1px; +} + +.rating-stars span { + color: #9ca3af; +} + +.rating-stars .filled { + color: #facc15; +} + +.multi-bubble-content { + display: grid; + gap: 2px; + min-height: 0; + padding: 0; + border: 0; + background: transparent; + color: #111827; + cursor: pointer; + text-align: left; + font: inherit; +} + +.multi-bubble-content span { + color: #6b7280; + font-size: 12px; +} + .map-container { flex: 1; min-width: 0; @@ -262,6 +370,23 @@ body, margin-top: 8px; } +.circle-radius-label { + position: absolute; + z-index: 1200; + transform: translate(-50%, -50%); + color: #dc2626; + font-size: 14px; + font-weight: 700; + line-height: 1; + text-shadow: + -2px -2px 0 #ffffff, + 2px -2px 0 #ffffff, + -2px 2px 0 #ffffff, + 2px 2px 0 #ffffff, + 0 0 5px rgba(255, 255, 255, 0.95); + pointer-events: none; +} + .toast-stack { position: absolute; bottom: 80px; @@ -284,6 +409,31 @@ body, white-space: pre-line; } +.provider-unavailable-overlay { + position: absolute; + inset: 0; + z-index: 2500; + display: grid; + place-items: center; + padding: 24px; + background: rgba(15, 23, 42, 0.42); + pointer-events: auto; +} + +.provider-unavailable-box { + width: min(420px, 100%); + padding: 18px 22px; + border: 1px solid rgba(148, 163, 184, 0.45); + border-radius: 8px; + background: rgba(255, 255, 255, 0.96); + color: #111827; + box-shadow: 0 18px 42px rgba(15, 23, 42, 0.3); + font-size: 0.98rem; + font-weight: 600; + line-height: 1.35; + text-align: center; +} + @media (max-width: 760px) { .header { min-height: 58px; diff --git a/examples/basic/src/main.tsx b/examples/basic/src/main.tsx index a814b52..4f982c8 100644 --- a/examples/basic/src/main.tsx +++ b/examples/basic/src/main.tsx @@ -6,7 +6,7 @@ import './index.css'; ReactDOM.createRoot(document.getElementById('root')!).render( - + diff --git a/examples/basic/src/pages/CirclePage.tsx b/examples/basic/src/pages/CirclePage.tsx index 53b2ab3..9951f20 100644 --- a/examples/basic/src/pages/CirclePage.tsx +++ b/examples/basic/src/pages/CirclePage.tsx @@ -1,49 +1,160 @@ -import { useMemo, useState } from 'react'; -import { createCircleState } from '@mapconductor/js-sdk-core'; -import { Circle, MarkerFromProps } from '@mapconductor/js-sdk-react'; +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { + ColorDefaultIcon, + Spherical, + calculatePositionAtDistance, + computeDistanceBetween, + createCircleState, + createGeoPoint, + type GeoPoint, + type MarkerState, + type Offset, +} from '@mapconductor/js-sdk-core'; +import { Circle, Marker, Polyline } from '@mapconductor/js-sdk-react'; import { ControlPanel, SliderControl } from '../components/ControlPanel'; import { Toast, useToast } from '../components/Toast'; -import { CIRCLE_CENTER } from '../data/storeData'; import { MapViewContainer, useSampleMapViewState } from '../MapViewContainer'; -const INIT_CAMERA = { lat: 21.3825, lng: -157.9330, zoom: 12 }; +const CIRCLE_CENTER = createGeoPoint({ latitude: 21.382314, longitude: -157.933097 }); +const INIT_CAMERA = { lat: CIRCLE_CENTER.latitude, lng: CIRCLE_CENTER.longitude, zoom: 12 }; +const INITIAL_RADIUS_METERS = 1000; +const SUPPRESS_CIRCLE_CLICK_AFTER_MARKER_DRAG_MS = 300; +const CIRCLE_COLORS = ['#0000ff', '#ff0000', '#008000', '#00ffff', '#d3d3d3', '#ff00ff']; + +function rgba(hex: string, alpha: number): string { + const value = hex.replace('#', ''); + const r = parseInt(value.slice(0, 2), 16); + const g = parseInt(value.slice(2, 4), 16); + const b = parseInt(value.slice(4, 6), 16); + return `rgba(${r}, ${g}, ${b}, ${alpha})`; +} + +function radiusLabelPosition( + center: GeoPoint, + edge: GeoPoint, + mapViewState: ReturnType, +): Offset | null | Promise { + const holder = mapViewState.getMapViewHolder(); + if (!holder) return null; + const midpoint = Spherical.linearInterpolate({ from: center, to: edge, fraction: 0.5 }); + return holder.toScreenOffset(midpoint); +} export function CirclePage() { const mapViewState = useSampleMapViewState(INIT_CAMERA); - const [radius, setRadius] = useState(1000); + const [edgePosition, setEdgePosition] = useState(() => + calculatePositionAtDistance({ + center: CIRCLE_CENTER, + distanceMeters: INITIAL_RADIUS_METERS, + bearingDegrees: 90, + }), + ); + const [colorIndex, setColorIndex] = useState(0); const [fillOpacity, setFillOpacity] = useState(0.3); const [strokeWidth, setStrokeWidth] = useState(3); + const [labelOffset, setLabelOffset] = useState(null); + const [cameraTick, setCameraTick] = useState(0); + const suppressCircleClickUntilRef = useRef(0); const { messages, showToast, dismissToast } = useToast(); + const radius = useMemo( + () => computeDistanceBetween(CIRCLE_CENTER, edgePosition), + [edgePosition], + ); + + const updateLabelPosition = useCallback(() => { + const next = radiusLabelPosition(CIRCLE_CENTER, edgePosition, mapViewState); + if (next instanceof Promise) { + next.then(setLabelOffset).catch(() => setLabelOffset(null)); + } else { + setLabelOffset(next); + } + }, [edgePosition, mapViewState]); + + useEffect(() => { + updateLabelPosition(); + const raf = requestAnimationFrame(updateLabelPosition); + return () => cancelAnimationFrame(raf); + }, [cameraTick, updateLabelPosition]); + + const handleMarkerMove = useCallback((dragged: MarkerState) => { + setEdgePosition(dragged.position); + }, []); + + const handleMarkerDragEnd = useCallback((dragged: MarkerState) => { + suppressCircleClickUntilRef.current = + Date.now() + SUPPRESS_CIRCLE_CLICK_AFTER_MARKER_DRAG_MS; + setEdgePosition(dragged.position); + }, []); const circleState = useMemo( () => createCircleState({ - id: 'demo-circle', + id: 'circle', center: CIRCLE_CENTER, radiusMeters: radius, - fillColor: `rgba(0, 100, 255, ${fillOpacity})`, - strokeColor: '#0064ff', + fillColor: rgba(CIRCLE_COLORS[colorIndex], fillOpacity), + strokeColor: 'rgba(0, 0, 255, 0.5)', strokeWidth, clickable: true, - onClick: () => showToast(`Circle clicked - radius: ${radius.toFixed(0)}m`), + onClick: () => { + if (Date.now() < suppressCircleClickUntilRef.current) return; + setColorIndex((index) => (index + 1) % CIRCLE_COLORS.length); + showToast(`Circle clicked - Radius: ${radius.toFixed(0)}m`); + }, }), - [radius, fillOpacity, strokeWidth, showToast] + [colorIndex, fillOpacity, radius, showToast, strokeWidth], + ); + + const centerIcon = useMemo( + () => new ColorDefaultIcon('#ff0000', { strokeColor: '#ffffff', label: 'C' }), + [], + ); + const edgeIcon = useMemo( + () => new ColorDefaultIcon('#008000', { strokeColor: '#ffffff', label: 'E' }), + [], ); return ( - + setCameraTick((tick) => tick + 1)} + onCameraMoveEnd={() => setCameraTick((tick) => tick + 1)} + > - + + + + {labelOffset && ( +
+ {radius.toFixed(0)} m +
+ )} - `${value.toFixed(0)}m`} - onChange={setRadius} - /> `${value.toFixed(1)}px`} onChange={setStrokeWidth} /> diff --git a/examples/basic/src/pages/common/sampleHelpers.ts b/examples/basic/src/pages/common/sampleHelpers.ts index 184dd22..c46af5e 100644 --- a/examples/basic/src/pages/common/sampleHelpers.ts +++ b/examples/basic/src/pages/common/sampleHelpers.ts @@ -1,11 +1,8 @@ import { ColorDefaultIcon, createGeoPoint, - createMapCameraPosition, createMarkerState, - type GeoPoint, } from '@mapconductor/js-sdk-core'; -import { useMapViewController } from '@mapconductor/js-sdk-react'; export const HONOLULU = createGeoPoint({ latitude: 21.382314, longitude: -157.933097 }); export const SAPPORO = createGeoPoint({ latitude: 43.06417, longitude: 141.34694 }); @@ -18,16 +15,3 @@ export function cityMarker(id: string, label: string, latitude: number, longitud icon: new ColorDefaultIcon('#2563eb', { label: label.slice(0, 1), labelTextColor: '#ffffff' }), }); } - -export function useCameraActions() { - const controller = useMapViewController(); - - return { - getBounds: () => controller?.getBounds() ?? null, - getCameraPosition: () => controller?.getCameraPosition() ?? null, - animateCamera: (center: GeoPoint, duration = 800, zoom = 13, bearing = 0, tilt = 0) => { - if (!controller) return; - void controller.animateCamera(createMapCameraPosition({ position: center, zoom, bearing, tilt }), { duration }); - }, - }; -} diff --git a/examples/basic/src/pages/geojson/basic/BasicGeoJSONPage.tsx b/examples/basic/src/pages/geojson/basic/BasicGeoJSONPage.tsx new file mode 100644 index 0000000..f2a4b70 --- /dev/null +++ b/examples/basic/src/pages/geojson/basic/BasicGeoJSONPage.tsx @@ -0,0 +1,83 @@ +import { useMemo, useState, useEffect } from 'react'; +import { + GeoJSONLayer, + GeoJSONLayerState, + GeoJSONParser, + colorArgb, +} from '@mapconductor/react-geojson-layer'; +import type { GeoJSONFeatureData } from '@mapconductor/react-geojson-layer'; +import { ControlPanel } from '../../../components/ControlPanel'; +import { MapViewContainer, useSampleMapViewState } from '../../../MapViewContainer'; + +const INIT_CAMERA = { lat: 25.255377, lng: 55.3089185, zoom: 13 }; + +const BASIC_GEOJSON = `{ + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [55.30122473231012, 25.26476622289597], + [55.29743486255916, 25.25827212207261], + [55.28978863411328, 25.251356725509737], + [55.300027931336984, 25.246425506635504], + [55.307474692951274, 25.244200378933655], + [55.31212891895635, 25.256408010450187], + [55.30774064871093, 25.26266169122738], + [55.301357710197806, 25.264946609615492], + [55.30122473231012, 25.26476622289597] + ], + [ + [55.30084858315658, 25.256531695820797], + [55.298280197635705, 25.252243254705405], + [55.30163885563897, 25.250501032248863], + [55.304059065092645, 25.254700192612702], + [55.30084858315658, 25.256531695820797] + ], + [ + [55.30173763969924, 25.262517391695198], + [55.301095543307355, 25.26122200491396], + [55.30396028103232, 25.259479911263526], + [55.30489872958182, 25.261132667394975], + [55.30173763969924, 25.262517391695198] + ] + ] + }, + "properties": {} + } + ] +}`; + +export function BasicGeoJSONPage() { + const mapViewState = useSampleMapViewState(INIT_CAMERA); + + const layerState = useMemo( + () => + new GeoJSONLayerState({ + fillColor: colorArgb(127, 0x3b, 0xb2, 0xd0), + strokeColor: colorArgb(255, 0x1d, 0x70, 0x82), + strokeWidth: 2, + }), + [], + ); + + const [features, setFeatures] = useState([]); + + useEffect(() => { + setFeatures(GeoJSONParser.parseFeatures(BASIC_GEOJSON)); + }, []); + + return ( + + + +

+ GeoJSON ポリゴン(ホール付き)を表示しています。 +

+
+
+ ); +} diff --git a/examples/basic/src/pages/geojson/layer/GeoJSONLayerPage.tsx b/examples/basic/src/pages/geojson/layer/GeoJSONLayerPage.tsx new file mode 100644 index 0000000..979ed21 --- /dev/null +++ b/examples/basic/src/pages/geojson/layer/GeoJSONLayerPage.tsx @@ -0,0 +1,142 @@ +import { useCallback, useMemo, useState, useEffect } from 'react'; +import { createGeoPoint, type GeoPoint } from '@mapconductor/js-sdk-core'; +import { InfoBubbleAtPosition } from '@mapconductor/js-sdk-react'; +import { + GeoJSONLayer, + GeoJSONLayerState, + GeoJSONParser, + colorArgb, +} from '@mapconductor/react-geojson-layer'; +import type { GeoJSONFeatureData } from '@mapconductor/react-geojson-layer'; +import { ControlPanel } from '../../../components/ControlPanel'; +import { MapViewContainer, useSampleMapViewState } from '../../../MapViewContainer'; + +const INIT_CAMERA = { lat: 35.68, lng: 139.77, zoom: 13 }; + +interface SelectedFeature { + position: GeoPoint; + properties: Record; +} + +const PROPERTY_LABELS: Record = { + N02_002: '鉄道区分', + N02_003: '事業者種別', + N02_004: '路線名', + N02_005: '運営会社', +}; + +export function GeoJSONLayerPage() { + const mapViewState = useSampleMapViewState(INIT_CAMERA); + const [features, setFeatures] = useState([]); + const [isLoading, setIsLoading] = useState(true); + const [error, setError] = useState(null); + const [selected, setSelected] = useState(null); + + const layerState = useMemo( + () => + new GeoJSONLayerState({ + strokeColor: colorArgb(200, 250, 36, 29), + strokeWidth: 6, + onClick: (feature, position) => { + setSelected({ + position: createGeoPoint({ latitude: position.latitude, longitude: position.longitude }), + properties: feature.properties ?? {}, + }); + }, + }), + [], + ); + + useEffect(() => { + fetch(`${import.meta.env.BASE_URL}geojson/sample-railway.geojson`) + .then(r => { + if (!r.ok) throw new Error(`HTTP ${r.status}`); + return r.text(); + }) + .then(text => { + setFeatures(GeoJSONParser.parseFeatures(text)); + setIsLoading(false); + }) + .catch(err => { + setError(String(err)); + setIsLoading(false); + }); + }, []); + + const handleMapClick = useCallback( + (point: GeoPoint) => { + if (!layerState.processClick(point)) { + setSelected(null); + } + }, + [layerState], + ); + + if (error) { + return ( +
+

データの読み込みに失敗しました: {error}

+
+ ); + } + + return ( + + + + {selected && ( + + + + )} + + + {isLoading ? ( +

GeoJSON 読み込み中...

+ ) : ( +

+ 路線をタップするとプロパティが表示されます。 +

+ )} +
+
+ ); +} + +function PropertyTable({ properties }: { properties: Record }) { + const entries = Object.entries(properties); + if (entries.length === 0) return

プロパティなし

; + + return ( + + + + + + + + + {entries.map(([key, value]) => ( + + + + + ))} + +
プロパティ
{PROPERTY_LABELS[key] ?? key}{value == null ? '' : String(value)}
+ ); +} + +const thStyle: React.CSSProperties = { + border: '1px solid #bbb', + padding: '4px 8px', + textAlign: 'left', + fontWeight: 600, + color: '#333', +}; + +const tdStyle: React.CSSProperties = { + border: '1px solid #bbb', + padding: '4px 8px', + color: '#222', +}; diff --git a/examples/basic/src/pages/groundimage/GroundImagePage.tsx b/examples/basic/src/pages/groundimage/GroundImagePage.tsx index 071e3f6..5006a7f 100644 --- a/examples/basic/src/pages/groundimage/GroundImagePage.tsx +++ b/examples/basic/src/pages/groundimage/GroundImagePage.tsx @@ -12,8 +12,8 @@ import { Toast, useToast } from '../../components/Toast'; import { MapViewContainer, useSampleMapViewState } from '../../MapViewContainer'; const INIT_CAMERA = { lat: 40.7410, lng: -74.1758, zoom: 11 }; -const GROUND_IMAGE_URL = '/newark_nj_1922_0.webp'; -const CLICKED_GROUND_IMAGE_URL = '/newark_nj_1922_1.webp'; +const GROUND_IMAGE_URL = `${import.meta.env.BASE_URL}newark_nj_1922_0.webp`; +const CLICKED_GROUND_IMAGE_URL = `${import.meta.env.BASE_URL}newark_nj_1922_1.webp`; export function GroundImagePage() { const mapViewState = useSampleMapViewState(INIT_CAMERA); diff --git a/examples/basic/src/pages/heatmaplayer/HeatmapLayerPage.tsx b/examples/basic/src/pages/heatmaplayer/HeatmapLayerPage.tsx new file mode 100644 index 0000000..963b09f --- /dev/null +++ b/examples/basic/src/pages/heatmaplayer/HeatmapLayerPage.tsx @@ -0,0 +1,95 @@ +import { useEffect, useState } from 'react'; +import { useLocation } from 'react-router-dom'; +import { createGeoPoint, createMapCameraPosition } from '@mapconductor/js-sdk-core'; +import { GoogleMapDesign, useGoogleMapViewState } from '@mapconductor/react-for-googlemaps'; +import { MapLibreDesign, useMapLibreViewState } from '@mapconductor/react-for-maplibre'; +import { HeatmapOverlay, HeatmapPoints, HeatmapPointState } from '@mapconductor/react-heatmap'; +import { ControlPanel } from '../../components/ControlPanel'; +import { MapViewContainer } from '../../MapViewContainer'; +import type { MapDesignTypeInterface, MapViewStateInterface } from '@mapconductor/js-sdk-core'; + +const INIT_CAMERA_POSITION = createMapCameraPosition({ + position: createGeoPoint({ latitude: 35.68049, longitude: 139.76669 }), + zoom: 10, +}); + +function HeatmapLayerPageContent({ + mapViewState, +}: { + mapViewState: MapViewStateInterface>; +}) { + const [heatmapPoints, setHeatmapPoints] = useState([]); + const [isLoading, setIsLoading] = useState(true); + const [error, setError] = useState(null); + + useEffect(() => { + fetch(`${import.meta.env.BASE_URL}postoffice/postoffices.json`) + .then(r => { + if (!r.ok) throw new Error(`HTTP ${r.status}`); + return r.json() as Promise<[number, number, string, string][]>; + }) + .then(data => { + const points = data.map(([lat, lng], i) => + new HeatmapPointState({ + id: `po-${i}`, + position: createGeoPoint({ latitude: lat, longitude: lng }), + }), + ); + setHeatmapPoints(points); + setIsLoading(false); + }) + .catch(err => { + setError(String(err)); + setIsLoading(false); + }); + }, []); + + if (error) { + return ( +
+

データの読み込みに失敗しました: {error}

+
+ ); + } + + return ( + + + + + + + {isLoading ? ( +

データ読み込み中...

+ ) : ( +

+ 郵便局データをヒートマップで表示しています。 +

+ )} +
+
+ ); +} + +function GoogleHeatmapLayerPage() { + const mapViewState = useGoogleMapViewState({ + mapDesignType: GoogleMapDesign.Normal, + cameraPosition: INIT_CAMERA_POSITION, + }); + return ; +} + +function MapLibreHeatmapLayerPage() { + const mapViewState = useMapLibreViewState({ + mapDesignType: MapLibreDesign.MapTilerTonerEn, + cameraPosition: INIT_CAMERA_POSITION, + }); + return ; +} + +export function HeatmapLayerPage() { + const location = useLocation(); + return location.pathname.startsWith('/google-maps') + ? + : ; +} diff --git a/examples/basic/src/pages/infobubble/MultipleBubblesPage.tsx b/examples/basic/src/pages/infobubble/MultipleBubblesPage.tsx index e17d6dd..b32926d 100644 --- a/examples/basic/src/pages/infobubble/MultipleBubblesPage.tsx +++ b/examples/basic/src/pages/infobubble/MultipleBubblesPage.tsx @@ -1,26 +1,75 @@ -import { useMemo } from 'react'; +import { useMemo, useState } from 'react'; +import { ColorDefaultIcon, createGeoPoint, createMarkerState } from '@mapconductor/js-sdk-core'; import { InfoBubble, Markers } from '@mapconductor/js-sdk-react'; -import { cityMarker } from '../common/sampleHelpers'; import { MapViewContainer, useSampleMapViewState } from '../../MapViewContainer'; -const INIT_CAMERA = { lat: 43.065, lng: 141.345, zoom: 12 }; +const INIT_CAMERA = { lat: 37.7749, lng: -122.4194, zoom: 15, pitch: 45 }; export function MultipleBubblesPage() { const mapViewState = useSampleMapViewState(INIT_CAMERA); - const markers = useMemo(() => [ - cityMarker('bubble-1', 'One', 43.06417, 141.34694), - cityMarker('bubble-2', 'Two', 43.07, 141.36), - cityMarker('bubble-3', 'Three', 43.055, 141.33), - ], []); + const [selectedMarkerIds, setSelectedMarkerIds] = useState>( + () => new Set(['marker_0', 'marker_1', 'marker_2']) + ); + const markers = useMemo( + () => + [ + { + position: createGeoPoint({ latitude: 37.7749, longitude: -122.4194 }), + name: 'Restaurant A', + color: '#ef4444', + }, + { + position: createGeoPoint({ latitude: 37.7849, longitude: -122.4094 }), + name: 'Hotel B', + color: '#2563eb', + }, + { + position: createGeoPoint({ latitude: 37.7649, longitude: -122.4294 }), + name: 'Shop C', + color: '#22c55e', + }, + ].map((data, index) => + createMarkerState({ + id: `marker_${index}`, + position: data.position, + icon: new ColorDefaultIcon(data.color, { label: `${index + 1}`, labelTextColor: '#ffffff' }), + extra: data.name, + onClick: state => { + setSelectedMarkerIds(prev => { + const next = new Set(prev); + if (next.has(state.id)) next.delete(state.id); + else next.add(state.id); + return next; + }); + }, + }) + ), + [] + ); return ( - + setSelectedMarkerIds(new Set())}> - {markers.map(marker => ( - -
{marker.extra as string}
-
- ))} + {markers.map(marker => + selectedMarkerIds.has(marker.id) ? ( + + + + ) : null + )}
); } diff --git a/examples/basic/src/pages/infobubble/RichContentBubblePage.tsx b/examples/basic/src/pages/infobubble/RichContentBubblePage.tsx index e50968d..c1774f1 100644 --- a/examples/basic/src/pages/infobubble/RichContentBubblePage.tsx +++ b/examples/basic/src/pages/infobubble/RichContentBubblePage.tsx @@ -1,23 +1,60 @@ -import { useMemo } from 'react'; -import { InfoBubbleCustom, Marker } from '@mapconductor/js-sdk-react'; -import { cityMarker } from '../common/sampleHelpers'; +import { useMemo, useState } from 'react'; +import { ColorDefaultIcon, createGeoPoint, createMarkerState } from '@mapconductor/js-sdk-core'; +import { InfoBubble, Marker } from '@mapconductor/js-sdk-react'; import { MapViewContainer, useSampleMapViewState } from '../../MapViewContainer'; -const INIT_CAMERA = { lat: 43.0642, lng: 141.3469, zoom: 13 }; +interface LocationInfo extends Record { + name: string; + description: string; + rating: number; +} + +const INIT_CAMERA = { lat: 37.7749, lng: -122.4194, zoom: 10 }; export function RichContentBubblePage() { const mapViewState = useSampleMapViewState(INIT_CAMERA); - const marker = useMemo(() => cityMarker('rich-bubble', 'Rich', 43.06417, 141.34694), []); + const [selectedMarkerId, setSelectedMarkerId] = useState('golden-gate-park'); + const marker = useMemo(() => { + const locationInfo: LocationInfo = { + name: 'Golden Gate Park', + description: 'A large urban park with gardens, museums, and recreational areas.', + rating: 4.5, + }; + return createMarkerState({ + id: 'golden-gate-park', + position: createGeoPoint({ latitude: 37.7694, longitude: -122.4862 }), + icon: new ColorDefaultIcon('#22c55e', { label: '🌳' }), + extra: locationInfo, + onClick: state => setSelectedMarkerId(state.id), + }); + }, []); + const info = marker.extra as LocationInfo; + return ( - + setSelectedMarkerId(null)}> - -
- Rich content - Status: open - -
-
+ {selectedMarkerId === marker.id && ( + +
+ {info.name} +

{info.description}

+
+ + {Array.from({ length: 5 }, (_, index) => ( + + ))} + + {info.rating}/5 +
+
+
+ )}
); } diff --git a/examples/basic/src/pages/infobubble/SimpleInfoBubblePage.tsx b/examples/basic/src/pages/infobubble/SimpleInfoBubblePage.tsx index 1a0c3fa..0b8ccb0 100644 --- a/examples/basic/src/pages/infobubble/SimpleInfoBubblePage.tsx +++ b/examples/basic/src/pages/infobubble/SimpleInfoBubblePage.tsx @@ -1,19 +1,32 @@ -import { useMemo } from 'react'; +import { useMemo, useState } from 'react'; +import { ColorDefaultIcon, createGeoPoint, createMarkerState } from '@mapconductor/js-sdk-core'; import { InfoBubble, Marker } from '@mapconductor/js-sdk-react'; -import { cityMarker } from '../common/sampleHelpers'; import { MapViewContainer, useSampleMapViewState } from '../../MapViewContainer'; -const INIT_CAMERA = { lat: 43.0642, lng: 141.3469, zoom: 13 }; +const SAN_FRANCISCO = createGeoPoint({ latitude: 37.7749, longitude: -122.4194 }); +const INIT_CAMERA = { lat: 37.7749, lng: -122.4194, zoom: 10 }; export function SimpleInfoBubblePage() { const mapViewState = useSampleMapViewState(INIT_CAMERA); - const marker = useMemo(() => cityMarker('simple-bubble', 'Sapporo', 43.06417, 141.34694), []); + const [selectedMarkerId, setSelectedMarkerId] = useState('simple-text-bubble'); + const marker = useMemo(() => createMarkerState({ + id: 'simple-text-bubble', + position: SAN_FRANCISCO, + icon: new ColorDefaultIcon('#2563eb', { label: 'SF', labelTextColor: '#ffffff' }), + extra: 'San Francisco - The Golden Gate City', + onClick: state => setSelectedMarkerId(state.id), + }), []); + return ( - + setSelectedMarkerId(null)}> - -
Simple text bubble
-
+ {selectedMarkerId === marker.id && ( + +
+ {marker.extra as string} +
+
+ )}
); } diff --git a/examples/basic/src/pages/infobubble/StyledInfoBubblePage.tsx b/examples/basic/src/pages/infobubble/StyledInfoBubblePage.tsx index 01256b1..c06db05 100644 --- a/examples/basic/src/pages/infobubble/StyledInfoBubblePage.tsx +++ b/examples/basic/src/pages/infobubble/StyledInfoBubblePage.tsx @@ -1,29 +1,52 @@ -import { useState } from 'react'; -import { ColorDefaultIcon, createMarkerState } from '@mapconductor/js-sdk-core'; -import { InfoBubble, Marker } from '@mapconductor/js-sdk-react'; -import { SAPPORO } from '../common/sampleHelpers'; +import { useMemo, useState } from 'react'; +import { ColorDefaultIcon, createGeoPoint, createMarkerState } from '@mapconductor/js-sdk-core'; +import { InfoBubbleCustom, Markers } from '@mapconductor/js-sdk-react'; import { MapViewContainer, useSampleMapViewState } from '../../MapViewContainer'; -const INIT_CAMERA = { lat: 43.0642, lng: 141.3469, zoom: 13 }; +const INIT_CAMERA = { lat: 37.7849, lng: -122.4094, zoom: 15 }; export function StyledInfoBubblePage() { const mapViewState = useSampleMapViewState(INIT_CAMERA); - const [marker] = useState(() => createMarkerState({ - id: 'styled-bubble', - position: SAPPORO, - draggable: true, - icon: new ColorDefaultIcon('#7c3aed', { label: 'S', labelTextColor: '#ffffff' }), - })); + const [selectedMarkerId, setSelectedMarkerId] = useState('marker1'); + const markers = useMemo(() => { + const marker1 = createMarkerState({ + id: 'marker1', + position: createGeoPoint({ latitude: 37.7749, longitude: -122.4194 }), + icon: new ColorDefaultIcon('#2563eb', { + label: '1', + labelTextColor: '#ffffff', + infoAnchor: { x: 0.5, y: 0.25 }, + }), + draggable: true, + onClick: state => setSelectedMarkerId(state.id), + onDragStart: state => console.log(`マーカーのドラッグを開始: ${state.id}`), + onDrag: state => console.log('マーカーをドラッグ中:', state.position), + onDragEnd: state => console.log(`マーカーのドラッグが終了: ${state.id}`), + }); + const marker2 = createMarkerState({ + id: 'marker2', + position: createGeoPoint({ latitude: 37.7849, longitude: -122.4094 }), + icon: new ColorDefaultIcon('#ef4444', { + label: '2', + labelTextColor: '#ffffff', + infoAnchor: { x: 0.5, y: 0.25 }, + }), + onClick: state => setSelectedMarkerId(state.id), + }); + return [marker1, marker2]; + }, []); + const activeMarker = markers.find(marker => marker.id === selectedMarkerId); return ( - - - -
- Styled bubble - Drag the marker. -
-
+ setSelectedMarkerId(null)}> + + {activeMarker && ( + +
+ {activeMarker.position.toUrlValue(6)} +
+
+ )}
); } diff --git a/examples/basic/src/pages/map/basic/StoreMapPage.tsx b/examples/basic/src/pages/map/basic/StoreMapPage.tsx index b7f75ca..96650fa 100644 --- a/examples/basic/src/pages/map/basic/StoreMapPage.tsx +++ b/examples/basic/src/pages/map/basic/StoreMapPage.tsx @@ -13,10 +13,10 @@ import { StoreInfoView } from './StoreInfoView'; const INIT_CAMERA = { lat: 21.382314, lng: -157.933097, zoom: 10 }; const STORE_ICON_URLS: Record = { - coffee_bean: '/store-icons/coffee_bean.webp', - honolulu_coffee: '/store-icons/honolulu_coffee.webp', - coffee_extra: '/store-icons/coffee_extra.webp', - starbucks: '/store-icons/starbucks.webp', + coffee_bean: `${import.meta.env.BASE_URL}store-icons/coffee_bean.webp`, + honolulu_coffee: `${import.meta.env.BASE_URL}store-icons/honolulu_coffee.webp`, + coffee_extra: `${import.meta.env.BASE_URL}store-icons/coffee_extra.webp`, + starbucks: `${import.meta.env.BASE_URL}store-icons/starbucks.webp`, }; export function MapPage() { diff --git a/examples/basic/src/pages/map/camerasync/CameraSyncPage.tsx b/examples/basic/src/pages/map/camerasync/CameraSyncPage.tsx new file mode 100644 index 0000000..adb310e --- /dev/null +++ b/examples/basic/src/pages/map/camerasync/CameraSyncPage.tsx @@ -0,0 +1,555 @@ +import { useMemo, useRef, useState } from 'react'; +import { + createGeoPoint, + createMapCameraPosition, + createPolygonState, + createPolylineState, + type GeoPoint, + type MapCameraPosition, + type MapDesignTypeInterface, + type MapViewStateInterface, + type PolygonState, + type PolylineState, +} from '@mapconductor/js-sdk-core'; +import { Polygon, Polyline } from '@mapconductor/js-sdk-react'; +import { + GoogleMapDesign, + GoogleMapsView, + GoogleMapsView2D, + useGoogleMapViewState, + type GoogleMapViewState, +} from '@mapconductor/react-for-googlemaps'; +import { + MapLibreDesign, + MapLibreView, + useMapLibreViewState, + type MapLibreViewState, +} from '@mapconductor/react-for-maplibre'; + +type PaneId = 'left' | 'right'; +type PaneProvider = 'maplibre' | 'google-maps' | 'google-maps-3d'; + +interface CameraLocationInfo { + name: string; + bounds: { + southWest: GeoPoint; + northEast: GeoPoint; + }; + center: GeoPoint; + zoom: number; +} + +interface ProgrammaticMoveState { + key: number | null; + target: MapCameraPosition | null; + untilMs: number; + sinceMs: number; +} + +interface PairedFlyToState { + active: boolean; + untilMs: number; + leftEnded: boolean; + rightEnded: boolean; +} + +interface PaneState { + provider: PaneProvider; + mapState: MapViewStateInterface>; +} + +const INITIAL_CAMERA = createMapCameraPosition({ + position: createGeoPoint({ latitude: 35.6812, longitude: 139.7671, altitude: 0 }), + zoom: 12, + bearing: 0, + tilt: 0, +}); + +const PROGRAMMATIC_TTL_MS = 1200; +const PROGRAMMATIC_GRACE_MS = 250; +const MOVE_SYNC_INTERVAL_MS = 33; +const FLY_TO_DURATION_MS = 1000; + +function cameraKey(camera: MapCameraPosition): number { + const latE5 = Math.trunc(camera.position.latitude * 1e5); + const lonE5 = Math.trunc(camera.position.longitude * 1e5); + const zoom100 = Math.trunc(camera.zoom * 100); + const bearing10 = Math.trunc(camera.bearing * 10); + return (((latE5 * 31 + lonE5) * 31 + zoom100) * 31 + bearing10); +} + +function bearingDeltaDeg(a: number, b: number): number { + const d = ((a - b) % 360 + 360) % 360; + return d > 180 ? 360 - d : d; +} + +function isCloseToTarget(camera: MapCameraPosition, target: MapCameraPosition): boolean { + return ( + Math.abs(camera.position.latitude - target.position.latitude) < 0.0012 && + Math.abs(camera.position.longitude - target.position.longitude) < 0.0012 && + Math.abs(camera.zoom - target.zoom) < 0.75 && + bearingDeltaDeg(camera.bearing, target.bearing) < 12 && + Math.abs(camera.tilt - target.tilt) < 6 + ); +} + +function markProgrammaticMove( + ref: React.MutableRefObject, + target: MapCameraPosition, + nowMs: number, + ttlMs = PROGRAMMATIC_TTL_MS, +) { + ref.current = { + key: cameraKey(target), + target, + sinceMs: nowMs, + untilMs: nowMs + ttlMs, + }; +} + +function clearProgrammaticMove(ref: React.MutableRefObject) { + ref.current = { key: null, target: null, sinceMs: 0, untilMs: 0 }; +} + +function clearPairedFlyTo(ref: React.MutableRefObject) { + ref.current = { active: false, untilMs: 0, leftEnded: false, rightEnded: false }; +} + +function isProgrammaticMove( + ref: React.MutableRefObject, + camera: MapCameraPosition, + nowMs: number, +): boolean { + const state = ref.current; + if (state.key == null || nowMs > state.untilMs) return false; + if (cameraKey(camera) === state.key) return true; + return state.target ? isCloseToTarget(camera, state.target) : false; +} + +function defaultLocations(): CameraLocationInfo[] { + return [ + { + name: 'Tokyo', + bounds: { + southWest: createGeoPoint({ latitude: 35.62, longitude: 139.7, altitude: 0 }), + northEast: createGeoPoint({ latitude: 35.74, longitude: 139.84, altitude: 0 }), + }, + center: createGeoPoint({ latitude: 35.6812, longitude: 139.7671, altitude: 0 }), + zoom: 12, + }, + { + name: 'French Southern and Antarctic Lands', + bounds: { + southWest: createGeoPoint({ latitude: -49.5, longitude: 50, altitude: 0 }), + northEast: createGeoPoint({ latitude: -37.5, longitude: 77, altitude: 0 }), + }, + center: createGeoPoint({ latitude: -43.5, longitude: 63.5, altitude: 0 }), + zoom: 4, + }, + { + name: 'Finland', + bounds: { + southWest: createGeoPoint({ latitude: 59.8, longitude: 19.1, altitude: 0 }), + northEast: createGeoPoint({ latitude: 70.1, longitude: 31.6, altitude: 0 }), + }, + center: createGeoPoint({ latitude: 64.95, longitude: 25.35, altitude: 0 }), + zoom: 5, + }, + { + name: 'Iceland', + bounds: { + southWest: createGeoPoint({ latitude: 63.3, longitude: -24.5, altitude: 0 }), + northEast: createGeoPoint({ latitude: 66.6, longitude: -13.5, altitude: 0 }), + }, + center: createGeoPoint({ latitude: 64.95, longitude: -19, altitude: 0 }), + zoom: 6, + }, + { + name: 'Kiribati', + bounds: { + southWest: createGeoPoint({ latitude: -11.5, longitude: -174.5, altitude: 0 }), + northEast: createGeoPoint({ latitude: 5, longitude: -147, altitude: 0 }), + }, + center: createGeoPoint({ latitude: -3.25, longitude: -160.75, altitude: 0 }), + zoom: 4.5, + }, + { + name: 'Oahu Island', + bounds: { + southWest: createGeoPoint({ latitude: 21.25, longitude: -158.3, altitude: 0 }), + northEast: createGeoPoint({ latitude: 21.7, longitude: -157.65, altitude: 0 }), + }, + center: createGeoPoint({ latitude: 21.475, longitude: -157.975, altitude: 0 }), + zoom: 9.5, + }, + ]; +} + +function boundsPolyline(location: CameraLocationInfo, index: number): PolylineState { + const sw = location.bounds.southWest; + const ne = location.bounds.northEast; + return createPolylineState({ + id: `camera_sync_bounds_${index}`, + points: [ + sw, + createGeoPoint({ latitude: sw.latitude, longitude: ne.longitude, altitude: 0 }), + ne, + createGeoPoint({ latitude: ne.latitude, longitude: sw.longitude, altitude: 0 }), + sw, + ], + strokeColor: '#dc2626', + strokeWidth: 3, + geodesic: true, + }); +} + +function referenceRectangles(locations: CameraLocationInfo[]): PolygonState[] { + const size = 1; + return locations.map((location, index) => { + const lat = location.center.latitude; + const lng = location.center.longitude; + return createPolygonState({ + id: `camera_sync_reference_${index}`, + points: [ + createGeoPoint({ latitude: lat - size / 2, longitude: lng - size / 2, altitude: 0 }), + createGeoPoint({ latitude: lat - size / 2, longitude: lng + size / 2, altitude: 0 }), + createGeoPoint({ latitude: lat + size / 2, longitude: lng + size / 2, altitude: 0 }), + createGeoPoint({ latitude: lat + size / 2, longitude: lng - size / 2, altitude: 0 }), + createGeoPoint({ latitude: lat - size / 2, longitude: lng - size / 2, altitude: 0 }), + ], + strokeColor: '#2563eb', + strokeWidth: 2, + fillColor: 'rgba(37, 99, 235, 0.1)', + geodesic: false, + zIndex: 1, + }); + }); +} + +function usePaneState( + provider: PaneProvider, + mapLibreState: MapLibreViewState, + google2DState: GoogleMapViewState, + google3DState: GoogleMapViewState, +): PaneState { + if (provider === 'google-maps') { + return { provider, mapState: google2DState as MapViewStateInterface> }; + } + if (provider === 'google-maps-3d') { + return { provider, mapState: google3DState as MapViewStateInterface> }; + } + return { provider, mapState: mapLibreState as MapViewStateInterface> }; +} + +function providerLabel(provider: PaneProvider): string { + if (provider === 'google-maps') return 'Google Maps'; + if (provider === 'google-maps-3d') return 'Google Maps 3D'; + return 'MapLibre'; +} + +function CameraInfoCard({ label, position }: { label: string; position: MapCameraPosition }) { + return ( +
+ {label} + Lat: {position.position.latitude.toFixed(5)} + Lng: {position.position.longitude.toFixed(5)} + Zoom: {position.zoom.toFixed(2)} + Tilt: {position.tilt.toFixed(1)}deg + Bearing: {position.bearing.toFixed(1)}deg + Alt: {(position.position.altitude ?? 0).toFixed(0)} m +
+ ); +} + +function CameraSyncMapView({ + paneState, + children, + onCameraMove, + onCameraMoveEnd, +}: { + paneState: PaneState; + children: React.ReactNode; + onCameraMove: (camera: MapCameraPosition) => void; + onCameraMoveEnd: (camera: MapCameraPosition) => void; +}) { + if (paneState.provider === 'google-maps' || paneState.provider === 'google-maps-3d') { + const apiKey = import.meta.env.VITE_GOOGLE_MAPS_API_KEY || ''; + if (!apiKey || apiKey === 'your_api_key_here') { + return ( +
+

Google Maps API Key is Missing

+

Add VITE_GOOGLE_MAPS_API_KEY to examples/basic/.env, or switch this pane to MapLibre.

+
+ ); + } + + if (paneState.provider === 'google-maps-3d') { + return ( + + {children} + + ); + } + + return ( + + {children} + + ); + } + + return ( + + {children} + + ); +} + +function CameraSyncMapPane({ + label, + paneState, + selectedProvider, + onProviderChange, + cameraPosition, + onCameraMove, + onCameraMoveEnd, + boundsPolylines, + referenceRectangles, +}: { + label: string; + paneState: PaneState; + selectedProvider: PaneProvider; + onProviderChange: (provider: PaneProvider) => void; + cameraPosition: MapCameraPosition; + onCameraMove: (position: MapCameraPosition) => void; + onCameraMoveEnd: (position: MapCameraPosition) => void; + boundsPolylines: PolylineState[]; + referenceRectangles: PolygonState[]; +}) { + return ( +
+ + {boundsPolylines.map(polyline => )} + {referenceRectangles.map(polygon => )} + + + + + +
+ ); +} + +export function CameraSyncPage() { + const locations = useMemo(defaultLocations, []); + const boundsPolylines = useMemo(() => locations.map(boundsPolyline), [locations]); + const rectangles = useMemo(() => referenceRectangles(locations), [locations]); + + const leftMapLibreState = useMapLibreViewState({ + id: 'camera-sync-left-maplibre', + mapDesignType: MapLibreDesign.OsmBrightJa, + cameraPosition: INITIAL_CAMERA, + }); + const rightMapLibreState = useMapLibreViewState({ + id: 'camera-sync-right-maplibre', + mapDesignType: MapLibreDesign.MapTilerBasicJa, + cameraPosition: INITIAL_CAMERA, + }); + const leftGoogle2DState = useGoogleMapViewState({ + id: 'camera-sync-left-google-2d', + mapDesignType: GoogleMapDesign.Normal, + cameraPosition: INITIAL_CAMERA, + }); + const rightGoogle2DState = useGoogleMapViewState({ + id: 'camera-sync-right-google-2d', + mapDesignType: GoogleMapDesign.Normal, + cameraPosition: INITIAL_CAMERA, + }); + const leftGoogle3DState = useGoogleMapViewState({ + id: 'camera-sync-left-google-3d', + mapDesignType: GoogleMapDesign.Normal, + cameraPosition: INITIAL_CAMERA, + }); + const rightGoogle3DState = useGoogleMapViewState({ + id: 'camera-sync-right-google-3d', + mapDesignType: GoogleMapDesign.Normal, + cameraPosition: INITIAL_CAMERA, + }); + + const [leftProvider, setLeftProvider] = useState('maplibre'); + const [rightProvider, setRightProvider] = useState('google-maps-3d'); + const [leftCameraPosition, setLeftCameraPosition] = useState(INITIAL_CAMERA); + const [rightCameraPosition, setRightCameraPosition] = useState(INITIAL_CAMERA); + + const leftPaneState = usePaneState(leftProvider, leftMapLibreState, leftGoogle2DState, leftGoogle3DState); + const rightPaneState = usePaneState(rightProvider, rightMapLibreState, rightGoogle2DState, rightGoogle3DState); + + const leftProgrammaticRef = useRef({ key: null, target: null, sinceMs: 0, untilMs: 0 }); + const rightProgrammaticRef = useRef({ key: null, target: null, sinceMs: 0, untilMs: 0 }); + const pairedFlyToRef = useRef({ + active: false, + untilMs: 0, + leftEnded: false, + rightEnded: false, + }); + const lastLeftMoveSyncAtMs = useRef(0); + const lastRightMoveSyncAtMs = useRef(0); + + function programmaticRefFor(pane: PaneId) { + return pane === 'left' ? leftProgrammaticRef : rightProgrammaticRef; + } + + function selectedPaneState(pane: PaneId) { + return pane === 'left' ? leftPaneState : rightPaneState; + } + + function setCameraForPane(pane: PaneId, camera: MapCameraPosition) { + if (pane === 'left') setLeftCameraPosition(camera); + else setRightCameraPosition(camera); + } + + function syncFromPane(source: PaneId, position: MapCameraPosition, fromMove: boolean) { + const now = performance.now(); + + if (pairedFlyToRef.current.active) { + if (now > pairedFlyToRef.current.untilMs) { + clearPairedFlyTo(pairedFlyToRef); + clearProgrammaticMove(leftProgrammaticRef); + clearProgrammaticMove(rightProgrammaticRef); + } else { + setCameraForPane(source, position); + if (!fromMove) { + if (source === 'left') pairedFlyToRef.current.leftEnded = true; + else pairedFlyToRef.current.rightEnded = true; + + if (pairedFlyToRef.current.leftEnded && pairedFlyToRef.current.rightEnded) { + clearPairedFlyTo(pairedFlyToRef); + clearProgrammaticMove(leftProgrammaticRef); + clearProgrammaticMove(rightProgrammaticRef); + } + } + return; + } + } + + const sourceRef = programmaticRefFor(source); + + if (sourceRef.current.key != null) { + if (now > sourceRef.current.untilMs) { + clearProgrammaticMove(sourceRef); + } else { + const age = now - sourceRef.current.sinceMs; + if (age <= PROGRAMMATIC_GRACE_MS || isProgrammaticMove(sourceRef, position, now)) { + setCameraForPane(source, position); + return; + } + clearProgrammaticMove(sourceRef); + } + } + + const lastSyncRef = source === 'left' ? lastLeftMoveSyncAtMs : lastRightMoveSyncAtMs; + if (fromMove && now - lastSyncRef.current < MOVE_SYNC_INTERVAL_MS) return; + lastSyncRef.current = now; + + const target: PaneId = source === 'left' ? 'right' : 'left'; + const targetRef = programmaticRefFor(target); + setCameraForPane(source, position); + setCameraForPane(target, position); + markProgrammaticMove(targetRef, position, now); + selectedPaneState(target).mapState.moveCameraTo(position, 0); + } + + function flyToLocation(location: CameraLocationInfo) { + const now = performance.now(); + const position = createMapCameraPosition({ + position: location.center, + zoom: location.zoom, + bearing: 0, + tilt: 0, + }); + + pairedFlyToRef.current = { + active: true, + untilMs: now + FLY_TO_DURATION_MS + PROGRAMMATIC_TTL_MS, + leftEnded: false, + rightEnded: false, + }; + + leftPaneState.mapState.moveCameraTo(position, FLY_TO_DURATION_MS); + rightPaneState.mapState.moveCameraTo(position, FLY_TO_DURATION_MS); + setLeftCameraPosition(position); + setRightCameraPosition(position); + markProgrammaticMove(leftProgrammaticRef, position, now, FLY_TO_DURATION_MS + PROGRAMMATIC_TTL_MS); + markProgrammaticMove(rightProgrammaticRef, position, now, FLY_TO_DURATION_MS + PROGRAMMATIC_TTL_MS); + } + + return ( +
+
+

Camera Sync

+
+ {locations.map(location => ( + + ))} +
+
+ +
+ syncFromPane('left', position, true)} + onCameraMoveEnd={position => syncFromPane('left', position, false)} + boundsPolylines={boundsPolylines} + referenceRectangles={rectangles} + /> + syncFromPane('right', position, true)} + onCameraMoveEnd={position => syncFromPane('right', position, false)} + boundsPolylines={boundsPolylines} + referenceRectangles={rectangles} + /> +
+
+ ); +} diff --git a/examples/basic/src/pages/map/camerasync/camera-sync-page.css b/examples/basic/src/pages/map/camerasync/camera-sync-page.css new file mode 100644 index 0000000..57b6b37 --- /dev/null +++ b/examples/basic/src/pages/map/camerasync/camera-sync-page.css @@ -0,0 +1,174 @@ +.camera-sync-page { + height: 100%; + min-height: 0; + display: grid; + grid-template-rows: auto minmax(0, 1fr); + background: #f7f8fb; +} + +.camera-sync-toolbar { + display: grid; + gap: 10px; + padding: 10px; + border-bottom: 1px solid #d9dde7; + background: #ffffff; + z-index: 20; +} + +.camera-sync-toolbar h2 { + color: #172033; + font-size: 1rem; + font-weight: 700; + line-height: 1.2; +} + +.camera-sync-location-list { + display: flex; + flex-wrap: wrap; + gap: 8px; +} + +.camera-sync-location-list button { + min-height: 32px; + max-width: 220px; + padding: 0.35rem 0.65rem; + border: 1px solid #cbd5e1; + border-radius: 4px; + background: #f8fafc; + color: #1f2937; + cursor: pointer; + font-size: 0.82rem; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.camera-sync-location-list button:hover { + background: #e2e8f0; +} + +.camera-sync-grid { + min-height: 0; + display: grid; + grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); + gap: 1px; + background: #94a3b8; +} + +.camera-sync-pane { + position: relative; + min-width: 0; + min-height: 0; + background: #e5e7eb; + overflow: hidden; +} + +.camera-sync-pane > .maplibregl-map, +.camera-sync-pane > div:first-child { + width: 100%; + height: 100%; +} + +.camera-sync-provider { + position: absolute; + top: 10px; + left: 10px; + z-index: 1000; + width: min(72%, 320px); + display: grid; + gap: 4px; + padding: 8px; + border: 1px solid rgba(148, 163, 184, 0.7); + border-radius: 6px; + background: rgba(255, 255, 255, 0.94); + box-shadow: 0 2px 10px rgba(15, 23, 42, 0.18); + color: #1f2937; + font-size: 0.78rem; + font-weight: 700; +} + +.camera-sync-provider select { + min-width: 0; + min-height: 32px; + padding: 0.32rem 0.45rem; + border: 1px solid #cbd5e1; + border-radius: 4px; + background: #ffffff; + color: #1f2937; + font-size: 0.86rem; +} + +.camera-sync-info { + position: absolute; + left: 10px; + bottom: 10px; + z-index: 1000; + min-width: 150px; + display: grid; + gap: 3px; + padding: 9px 10px; + border: 1px solid rgba(148, 163, 184, 0.7); + border-radius: 6px; + background: rgba(255, 255, 255, 0.94); + box-shadow: 0 2px 10px rgba(15, 23, 42, 0.18); + color: #1f2937; + font-size: 0.76rem; + line-height: 1.25; +} + +.camera-sync-info strong { + margin-bottom: 2px; + color: #111827; + font-size: 0.8rem; +} + +.camera-sync-missing-key { + height: 100%; + display: grid; + align-content: center; + justify-items: center; + gap: 8px; + padding: 24px; + text-align: center; + background: #f8fafc; + color: #1f2937; +} + +.camera-sync-missing-key h2 { + font-size: 1rem; +} + +.camera-sync-missing-key p { + max-width: 440px; + color: #475569; + font-size: 0.88rem; + line-height: 1.45; +} + +@media (max-width: 860px), (orientation: portrait) { + .camera-sync-grid { + grid-template-columns: minmax(0, 1fr); + grid-template-rows: minmax(0, 1fr) minmax(0, 1fr); + } + + .camera-sync-toolbar { + max-height: 155px; + overflow-y: auto; + } +} + +@media (max-width: 560px) { + .camera-sync-provider { + width: min(86%, 300px); + } + + .camera-sync-info { + max-width: calc(100% - 20px); + grid-template-columns: repeat(2, minmax(0, auto)); + column-gap: 10px; + } + + .camera-sync-info strong { + grid-column: 1 / -1; + } +} diff --git a/examples/basic/src/pages/map/flyto/FlyToPage.tsx b/examples/basic/src/pages/map/flyto/FlyToPage.tsx index 5a0ff3f..61732f7 100644 --- a/examples/basic/src/pages/map/flyto/FlyToPage.tsx +++ b/examples/basic/src/pages/map/flyto/FlyToPage.tsx @@ -1,13 +1,17 @@ import { useMemo } from 'react'; +import { + createMapCameraPosition, + type MapDesignTypeInterface, + type MapViewStateInterface, +} from '@mapconductor/js-sdk-core'; import { Markers } from '@mapconductor/js-sdk-react'; import { ControlPanel } from '../../../components/ControlPanel'; -import { cityMarker, useCameraActions } from '../../common/sampleHelpers'; +import { cityMarker } from '../../common/sampleHelpers'; import { MapViewContainer, useSampleMapViewState } from '../../../MapViewContainer'; const INIT_CAMERA = { lat: 35.0, lng: 0.0, zoom: 3 }; -function FlyToContent() { - const { animateCamera } = useCameraActions(); +function FlyToContent({ mapViewState }: { mapViewState: MapViewStateInterface> }) { const markers = useMemo(() => [ cityMarker('tokyo', 'Tokyo', 35.6812, 139.7671), cityMarker('sapporo', 'Sapporo', 43.0642, 141.3469), @@ -21,7 +25,13 @@ function FlyToContent() {
{markers.map(marker => ( - ))} @@ -35,7 +45,7 @@ export function FlyToPage() { const mapViewState = useSampleMapViewState(INIT_CAMERA); return ( - + ); } diff --git a/examples/basic/src/pages/map/tilt/TiltPage.tsx b/examples/basic/src/pages/map/tilt/TiltPage.tsx index 15e59aa..79b4b30 100644 --- a/examples/basic/src/pages/map/tilt/TiltPage.tsx +++ b/examples/basic/src/pages/map/tilt/TiltPage.tsx @@ -1,12 +1,11 @@ import { useState } from 'react'; +import type { MapDesignTypeInterface, MapViewStateInterface } from '@mapconductor/js-sdk-core'; import { ControlPanel, SliderControl } from '../../../components/ControlPanel'; -import { HONOLULU, useCameraActions } from '../../common/sampleHelpers'; import { MapViewContainer, useSampleMapViewState } from '../../../MapViewContainer'; const INIT_CAMERA = { lat: 21.3069, lng: -157.8583, zoom: 14 }; -function TiltContent() { - const { animateCamera, getCameraPosition } = useCameraActions(); +function TiltContent({ mapViewState }: { mapViewState: MapViewStateInterface> }) { const [tilt, setTilt] = useState(0); return ( @@ -20,8 +19,7 @@ function TiltContent() { format={value => `${value.toFixed(0)}°`} onChange={value => { setTilt(value); - const camera = getCameraPosition(); - animateCamera(camera?.center ?? HONOLULU, 400, camera?.zoom ?? 14, camera?.bearing ?? 0, value); + mapViewState.moveCameraTo(mapViewState.cameraPosition.copy({ tilt: value }), 400); }} /> @@ -32,7 +30,7 @@ export function TiltPage() { const mapViewState = useSampleMapViewState(INIT_CAMERA); return ( - + ); } diff --git a/examples/basic/src/pages/map/visibleregion/VisibleRegionPage.tsx b/examples/basic/src/pages/map/visibleregion/VisibleRegionPage.tsx index d2d1e52..d81924e 100644 --- a/examples/basic/src/pages/map/visibleregion/VisibleRegionPage.tsx +++ b/examples/basic/src/pages/map/visibleregion/VisibleRegionPage.tsx @@ -1,32 +1,61 @@ import { useState } from 'react'; +import type { GeoPoint, MapCameraPosition } from '@mapconductor/js-sdk-core'; import { ControlPanel } from '../../../components/ControlPanel'; -import { useCameraActions } from '../../common/sampleHelpers'; import { MapViewContainer, useSampleMapViewState } from '../../../MapViewContainer'; const INIT_CAMERA = { lat: 21.3069, lng: -157.8583, zoom: 10 }; -function VisibleRegionContent() { - const { getBounds } = useCameraActions(); - const [summary, setSummary] = useState('Move the map, then read the visible region.'); +function formatPoint(point: GeoPoint | null): string { + return point?.toUrlValue(5) ?? 'Unavailable'; +} + +function VisibleRegionContent({ cameraPosition }: { cameraPosition: MapCameraPosition | null }) { + const visibleRegion = cameraPosition?.visibleRegion ?? null; + const bounds = visibleRegion?.bounds ?? null; return ( - -

{summary}

+

+ Move the map to update the current camera and visible region. +

+

+ Center: {formatPoint(cameraPosition?.position ?? null)} +

+

+ Zoom: {cameraPosition?.zoom.toFixed(2) ?? 'Unavailable'} +

+

+ Bearing: {cameraPosition?.bearing.toFixed(1) ?? 'Unavailable'} deg +

+

+ Tilt: {cameraPosition?.tilt.toFixed(1) ?? 'Unavailable'} deg +

+

+ Bounds: {bounds?.toUrlValue(5) ?? 'Unavailable'} +

+

+ Near Left: {formatPoint(visibleRegion?.nearLeft ?? null)} +

+

+ Near Right: {formatPoint(visibleRegion?.nearRight ?? null)} +

+

+ Far Left: {formatPoint(visibleRegion?.farLeft ?? null)} +

+

+ Far Right: {formatPoint(visibleRegion?.farRight ?? null)} +

); } export function VisibleRegionPage() { const mapViewState = useSampleMapViewState(INIT_CAMERA); + const [cameraPosition, setCameraPosition] = useState(null); + return ( - - + + ); } diff --git a/examples/basic/src/pages/marker/animation/MarkerAnimationPage.tsx b/examples/basic/src/pages/marker/animation/MarkerAnimationPage.tsx index a8fc9e1..828b44e 100644 --- a/examples/basic/src/pages/marker/animation/MarkerAnimationPage.tsx +++ b/examples/basic/src/pages/marker/animation/MarkerAnimationPage.tsx @@ -9,24 +9,35 @@ const INIT_CAMERA = { lat: 21.3825, lng: -157.9330, zoom: 14 }; export function MarkerAnimationPage() { const mapViewState = useSampleMapViewState(INIT_CAMERA); - const [bounce, setBounce] = useState(false); + const [animation, setAnimation] = useState(null); + + const triggerAnimation = (nextAnimation: MarkerAnimation) => { + setAnimation(nextAnimation); + window.setTimeout(() => setAnimation(null), 900); + }; + const marker = useMemo(() => createMarkerState({ id: 'animated-marker', position: HONOLULU, - icon: new ColorDefaultIcon('#e74c3c', { label: bounce ? 'B' : 'M', labelTextColor: '#ffffff' }), - animation: bounce ? MarkerAnimation.Bounce : null, + icon: new ColorDefaultIcon('#e74c3c', { + label: animation === MarkerAnimation.Drop ? 'D' : animation === MarkerAnimation.Bounce ? 'B' : 'M', + labelTextColor: '#ffffff', + }), + animation, onClick: state => { state.animate(MarkerAnimation.Bounce); - setBounce(true); - window.setTimeout(() => setBounce(false), 800); + triggerAnimation(MarkerAnimation.Bounce); }, - }), [bounce]); + }), [animation]); return ( - +
+ + +

Tap the marker or the button to trigger the sample animation state.

diff --git a/examples/basic/src/pages/marker/icons/MarkerIconsPage.tsx b/examples/basic/src/pages/marker/icons/MarkerIconsPage.tsx new file mode 100644 index 0000000..56482cc --- /dev/null +++ b/examples/basic/src/pages/marker/icons/MarkerIconsPage.tsx @@ -0,0 +1,362 @@ +import { useEffect, useMemo, useState } from 'react'; +import { + ColorDefaultIcon, + ImageDefaultIcon, + ImageIcon, + MarkerIconSize, + createGeoPoint, + createMarkerState, + type MarkerIcon, + type MarkerState, +} from '@mapconductor/js-sdk-core'; +import { CircleIcon, FlagIcon, RightTailInfoBubbleIcon, RoundInfoBubbleIcon } from '@mapconductor/react-icons'; +import { InfoBubble, Markers } from '@mapconductor/js-sdk-react'; +import { MapViewContainer, useSampleMapViewState } from '../../../MapViewContainer'; + +const INIT_CAMERA = { lat: 0.014, lng: 0.008, zoom: 15 }; + +const WEATHER_ICON_URL = svgDataUrl(` + + + + + + + +`); + +const HUMAN_ICON_URL = svgDataUrl(` + + + + + +`); + +const LAUNCHER_ICON_URL = svgDataUrl(` + + + + + + + +`); + +const DEFAULT_MARKER_ICON_URL = svgDataUrl(` + + + + +`); + +interface MarkerDefinition { + id: string; + lat: number; + lng: number; + icon?: MarkerIcon | null; + extra: string; +} + +export function MarkerIconsPage() { + const mapViewState = useSampleMapViewState(INIT_CAMERA); + const weatherImage = useLoadedImage(WEATHER_ICON_URL); + const humanImage = useLoadedImage(HUMAN_ICON_URL); + const launcherImage = useLoadedImage(LAUNCHER_ICON_URL); + const weatherPngUrl = useRasterizedDataUrl(WEATHER_ICON_URL); + const defaultMarkerPngUrl = useRasterizedDataUrl(DEFAULT_MARKER_ICON_URL); + const labelImage = useLabeledMarkerImage('Label'); + const [selected, setSelected] = useState(null); + + const markerDefinitions = useMemo(() => { + const definitions: MarkerDefinition[] = [ + { + id: 'default-scale-0-7', + lat: 0.018, + lng: 0.004, + icon: new ColorDefaultIcon('#ff0000', { scale: 0.7, label: '0.7', debug: true }), + extra: `DefaultIcon( + scale = 0.7, + label = "0.7", + debug = true, +)`, + }, + { + id: 'default-scale-1-0', + lat: 0.018, + lng: 0.006, + icon: new ColorDefaultIcon('#ff0000', { scale: 1, label: '1.0', debug: true }), + extra: `DefaultIcon( + scale = 1.0, + label = "1.0", + debug = true, +)`, + }, + { + id: 'default-scale-1-4', + lat: 0.018, + lng: 0.009, + icon: new ColorDefaultIcon('#ff0000', { scale: 1.4, label: '1.4', debug: true }), + extra: `DefaultIcon( + scale = 1.4, + label = "1.4", + debug = true, +)`, + }, + { + id: 'default-scale-2-1', + lat: 0.018, + lng: 0.013, + icon: new ColorDefaultIcon('#ff0000', { scale: 2.1, label: '2.1', debug: true }), + extra: `DefaultIcon( + scale = 2.1, + label = "2.1", + debug = true, +)`, + }, + { + id: 'default', + lat: 0.014, + lng: 0.004, + icon: null, + extra: 'DefaultIcon()', + }, + { + id: 'default-yellow', + lat: 0.014, + lng: 0.008, + icon: new ColorDefaultIcon('#ffff00', { strokeColor: '#000000', strokeWidth: 2 }), + extra: `DefaultIcon( + fillColor = Color.Yellow, + strokeColor = Color.Black, + strokeWidth = 2.dp, +)`, + }, + { + id: 'default-labeled', + lat: 0.014, + lng: 0.012, + icon: new ColorDefaultIcon('#2ef527', { + strokeColor: '#fc225c', + label: 'AB', + labelTextColor: '#ffffff', + labelStrokeColor: '#000000', + }), + extra: `DefaultIcon( + fillColor = Color(red = 0x2E, green = 0xF5, blue = 0x27), + strokeColor = Color(red = 0xFC, green = 0x22, blue = 0x5C), + label = "AB", + labelTextColor = Color.White, + labelStrokeColor = Color.Black, +)`, + }, + { + id: 'circle-icon', + lat: 0.006, + lng: 0.004, + icon: new CircleIcon('#0000ff', { strokeColor: '#ffffff', strokeWidth: 2 }), + extra: `CircleIcon( + fillColor = Color.Blue, + strokeColor = Color.White, + strokeWidth = 2.dp, +)`, + }, + { + id: 'flag-icon', + lat: 0.006, + lng: 0.007, + icon: new FlagIcon('#008000', { strokeColor: '#808080', strokeWidth: 1 }), + extra: `FlagIcon( + fillColor = Color.Green, + strokeColor = Color.Gray, + strokeWidth = 1.dp, +)`, + }, + { + id: 'round-info-bubble-icon', + lat: 0.006, + lng: 0.012, + icon: new RoundInfoBubbleIcon(defaultMarkerPngUrl ?? DEFAULT_MARKER_ICON_URL, '$197', { + fillColor: '#ffffff', + scale: 1, + iconSize: MarkerIconSize.Small, + }), + extra: `RoundInfoBubbleIcon( + label = "$197", + fillColor = Color.White, + scale = 1, + iconSize = MarkerIconSize.Small, +)`, + }, + { + id: 'right-tail-info-bubble-icon', + lat: 0, + lng: 0.004, + icon: new RightTailInfoBubbleIcon(weatherPngUrl ?? WEATHER_ICON_URL, '5時間37分', '304マイル', { + fillColor: '#ffffff', + labelTextColor: '#000000', + scale: 0.8, + }), + extra: `RightTailInfoBubbleIcon( + label = "5 Hours and 37 minutes", + snippet = "304 miles.", + fillColor = Color.White, + labelTextColor = Color.Black, + scale = 0.8, +)`, + }, + ]; + + if (humanImage) { + definitions.splice(7, 0, { + id: 'drawable-human', + lat: 0.01, + lng: 0.004, + icon: new ImageDefaultIcon(humanImage), + extra: `DrawableDefaultIcon( + backgroundDrawable = icon, +)`, + }); + } + + if (launcherImage) { + definitions.splice(8, 0, { + id: 'drawable-launcher', + lat: 0.01, + lng: 0.006, + icon: new ImageDefaultIcon(launcherImage, { strokeColor: '#000000', scale: 1.5 }), + extra: `DrawableDefaultIcon( + backgroundDrawable = icon, + strokeColor = Color.Black, + scale = 1.5, +)`, + }); + } + + if (weatherImage) { + definitions.splice(9, 0, { + id: 'image-weather', + lat: 0.01, + lng: 0.009, + icon: new ImageIcon(weatherImage, { debug: true, anchor: { x: 0.5, y: 1 } }), + extra: `ImageIcon( + drawable = icon, + debug = true, + anchor = PointF(0.5, 1.0), +)`, + }); + } + + if (labelImage) { + definitions.splice(10, 0, { + id: 'image-label', + lat: 0.01, + lng: 0.012, + icon: new ImageIcon(labelImage, { anchor: { x: 0.5, y: 1 } }), + extra: `ImageIcon( + drawable = createMarkerWithLabelIcon(label), + anchor = Offset(0.5, 1.0), +)`, + }); + } + + return definitions; + }, [defaultMarkerPngUrl, humanImage, labelImage, launcherImage, weatherImage, weatherPngUrl]); + + const markers = useMemo( + () => + markerDefinitions.map(({ id, lat, lng, icon, extra }) => + createMarkerState({ + id, + position: createGeoPoint({ latitude: lat, longitude: lng }), + icon, + extra, + onClick: setSelected, + }) + ), + [markerDefinitions] + ); + + return ( + setSelected(null)}> + + {typeof selected?.extra === 'string' && ( + +
{selected.extra}
+
+ )} +
+ ); +} + +function useLoadedImage(url: string): HTMLImageElement | null { + const [image, setImage] = useState(null); + + useEffect(() => { + const nextImage = new Image(); + nextImage.onload = () => setImage(nextImage); + nextImage.src = url; + return () => { + nextImage.onload = null; + }; + }, [url]); + + return image; +} + +function useRasterizedDataUrl(url: string): string | null { + const image = useLoadedImage(url); + const [rasterizedUrl, setRasterizedUrl] = useState(null); + + useEffect(() => { + if (!image) return; + const canvas = document.createElement('canvas'); + canvas.width = image.naturalWidth || image.width || 64; + canvas.height = image.naturalHeight || image.height || 64; + const ctx = canvas.getContext('2d'); + if (!ctx) return; + ctx.drawImage(image, 0, 0, canvas.width, canvas.height); + setRasterizedUrl(canvas.toDataURL('image/png')); + }, [image]); + + return rasterizedUrl; +} + +function useLabeledMarkerImage(label: string): HTMLCanvasElement | null { + const [canvas, setCanvas] = useState(null); + + useEffect(() => { + const nextCanvas = document.createElement('canvas'); + nextCanvas.width = 96; + nextCanvas.height = 144; + const ctx = nextCanvas.getContext('2d'); + if (!ctx) return; + + ctx.fillStyle = '#2563eb'; + ctx.strokeStyle = '#ffffff'; + ctx.lineWidth = 5; + ctx.beginPath(); + ctx.moveTo(48, 138); + ctx.bezierCurveTo(41, 112, 18, 91, 18, 56); + ctx.bezierCurveTo(18, 34, 31, 20, 48, 20); + ctx.bezierCurveTo(65, 20, 78, 34, 78, 56); + ctx.bezierCurveTo(78, 91, 55, 112, 48, 138); + ctx.closePath(); + ctx.fill(); + ctx.stroke(); + + ctx.fillStyle = '#ffffff'; + ctx.font = '700 22px sans-serif'; + ctx.textAlign = 'center'; + ctx.textBaseline = 'middle'; + ctx.fillText(label, 48, 68); + + setCanvas(nextCanvas); + }, [label]); + + return canvas; +} + +function svgDataUrl(svg: string): string { + return `data:image/svg+xml;charset=UTF-8,${encodeURIComponent(svg.trim())}`; +} diff --git a/examples/basic/src/pages/marker/postoffice/PostOfficePage.tsx b/examples/basic/src/pages/marker/postoffice/PostOfficePage.tsx index 3f0d8c4..61c12e6 100644 --- a/examples/basic/src/pages/marker/postoffice/PostOfficePage.tsx +++ b/examples/basic/src/pages/marker/postoffice/PostOfficePage.tsx @@ -1,6 +1,7 @@ -import { useEffect, useMemo, useState } from 'react'; +import { useCallback, useEffect, useMemo, useState } from 'react'; import { useLocation } from 'react-router-dom'; import { + ImageIcon, MarkerTilingOptions, createGeoPoint, createMapCameraPosition, @@ -22,7 +23,6 @@ interface PostOfficeExtra { } // ─── InfoBubble with zoom-in on click ────────────────────────────────────────── -// Must be rendered inside MapViewContainer so useMapViewController can find the context. function PostOfficeInfoBubble({ extra, marker, @@ -63,9 +63,7 @@ const INIT_CAMERA_POSITION = createMapCameraPosition({ const MARKER_TILING_OPTIONS: MarkerTilingOptions = { ...MarkerTilingOptions.Default, iconScaleCallback: (_state, zoom) => { - if (zoom > 12) return 2.0; - if (zoom > 10) return 1.0; - if (zoom > 8) return 0.8; + if (zoom > 10) return 0.8; if (zoom > 5) return 0.5; return 0.2; }, @@ -79,15 +77,24 @@ function PostOfficePageContent({ const [raw, setRaw] = useState<[number, number, string, string][] | null>(null); const [error, setError] = useState(null); const [selected, setSelected] = useState(null); + const [icon, setIcon] = useState(null); // Load post office data useEffect(() => { - fetch('/postoffice/postoffices.json') + fetch(`${import.meta.env.BASE_URL}postoffice/postoffices.json`) .then((r) => { if (!r.ok) throw new Error(`HTTP ${r.status}`); return r.json() as Promise<[number, number, string, string][]>; }) .then(setRaw) + .then(() => new Promise((resolve, reject) => { + const url = `${import.meta.env.BASE_URL}postoffice/postoffice.webp`; + const img = new Image(); + img.onload = () => resolve(img); + img.onerror = () => reject(new Error(`Failed to load icon: ${url}`)); + img.src = url; + })) + .then((img) => setIcon(new ImageIcon(img))) .catch((err) => setError(String(err))); }, []); @@ -99,12 +106,15 @@ function PostOfficePageContent({ id: `po-${i}`, position: createGeoPoint({ latitude: lat, longitude: lng }), extra: { name, address } satisfies PostOfficeExtra, + icon, onClick: (state) => setSelected(state), }), ), - [raw], + [icon], ); + const clearSelect = useCallback(() => setSelected(null), []) + if (error) { return (
@@ -117,6 +127,7 @@ function PostOfficePageContent({ diff --git a/examples/basic/src/pages/marker/postofficecluster/PostOfficeClusterPage.tsx b/examples/basic/src/pages/marker/postofficecluster/PostOfficeClusterPage.tsx new file mode 100644 index 0000000..bf91ce6 --- /dev/null +++ b/examples/basic/src/pages/marker/postofficecluster/PostOfficeClusterPage.tsx @@ -0,0 +1,255 @@ +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { useLocation } from 'react-router-dom'; +import { + ImageIcon, + createGeoPoint, + createMapCameraPosition, + createMarkerState, + type MapDesignTypeInterface, + type MapViewStateInterface, + type MarkerState, +} from '@mapconductor/js-sdk-core'; +import { InfoBubble } from '@mapconductor/js-sdk-react'; +import { GoogleMapDesign, useGoogleMapViewState } from '@mapconductor/react-for-googlemaps'; +import { MapLibreDesign, useMapLibreViewState } from '@mapconductor/react-for-maplibre'; +import { + MarkerClusterGroup, + type ClusterIconProvider, + type MarkerCluster, +} from '@mapconductor/react-marker-clustering'; +import { ControlPanel } from '../../../components/ControlPanel'; +import { MapViewContainer } from '../../../MapViewContainer'; + +// ─── Data types ──────────────────────────────────────────────────────────────── +interface PostOfficeExtra { + name: string; + address: string; +} + +// ─── Cluster icon helpers ────────────────────────────────────────────────────── +function clusterCountLabel(count: number): string { + if (count > 1_000) return '1k+'; + if (count > 200) return '200+'; + if (count > 100) return '100+'; + return String(count); +} + +function drawClusterCanvas(background: HTMLImageElement, label: string): HTMLCanvasElement { + const canvas = document.createElement('canvas'); + canvas.width = background.naturalWidth; + canvas.height = background.naturalHeight; + const ctx = canvas.getContext('2d')!; + ctx.drawImage(background, 0, 0); + // The bubble occupies roughly the top 38% of the image; its center is at ~20%. + const bubbleCenterY = Math.floor(background.naturalHeight * 0.22); + const fontSize = Math.floor(background.naturalHeight * 0.35); + ctx.font = `bold ${fontSize}px sans-serif`; + ctx.fillStyle = 'white'; + ctx.textAlign = 'center'; + ctx.textBaseline = 'middle'; + ctx.fillText(label, background.naturalWidth / 2, bubbleCenterY); + return canvas; +} + +function buildClusterIconProvider(background: HTMLImageElement): ClusterIconProvider { + const cache = new Map(); + return (count: number) => { + const label = clusterCountLabel(count); + const hit = cache.get(label); + if (hit) return hit; + const canvas = drawClusterCanvas(background, label); + const icon = new ImageIcon(canvas, { anchor: { x: 0.5, y: 0.5 } }); + cache.set(label, icon); + return icon; + }; +} + +// ─── InfoBubble ──────────────────────────────────────────────────────────────── +function PostOfficeInfoBubble({ + extra, + marker, + mapViewState, +}: { + extra: PostOfficeExtra; + marker: MarkerState; + mapViewState: MapViewStateInterface>; +}) { + const handleClick = () => { + mapViewState.moveCameraTo( + createMapCameraPosition({ position: marker.position, zoom: 18, tilt: 30 }), + 2000, + ); + }; + return ( + +
+ {extra.name} + {extra.address} +
+
+ ); +} + +// ─── Constants ───────────────────────────────────────────────────────────────── +const INIT_CAMERA_POSITION = createMapCameraPosition({ + position: createGeoPoint({ latitude: 35.68049, longitude: 139.76669 }), + zoom: 10, +}); + +// ─── Page content ────────────────────────────────────────────────────────────── +function PostOfficeClusterPageContent({ + mapViewState, +}: { + mapViewState: MapViewStateInterface>; +}) { + const [raw, setRaw] = useState<[number, number, string, string][] | null>(null); + const [error, setError] = useState(null); + const [selected, setSelected] = useState(null); + const [markerIcon, setMarkerIcon] = useState(null); + const [clusterIconProvider, setClusterIconProvider] = useState(null); + const [debugHullPolygons, setDebugHullPolygons] = useState(false); + + // Load post office data and icons in parallel. + useEffect(() => { + Promise.all([ + fetch(`${import.meta.env.BASE_URL}postoffice/postoffices.json`) + .then(r => { if (!r.ok) throw new Error(`HTTP ${r.status}`); return r.json() as Promise<[number, number, string, string][]>; }), + loadImage(`${import.meta.env.BASE_URL}postoffice/postoffice.webp`), + loadImage(`${import.meta.env.BASE_URL}postoffice/cluster_red.png`), + ]) + .then(([data, markerImg, clusterImg]) => { + setRaw(data); + // scale 0.5 matches the Android example (24dp on screen). + setMarkerIcon(new ImageIcon(markerImg, { scale: 0.5 })); + setClusterIconProvider(() => buildClusterIconProvider(clusterImg)); + }) + .catch(err => setError(String(err))); + }, []); + + const markerStates = useMemo( + () => + (raw ?? []).map(([lat, lng, name, address], i) => + createMarkerState({ + id: `po-${i}`, + position: createGeoPoint({ latitude: lat, longitude: lng }), + extra: { name, address } satisfies PostOfficeExtra, + icon: markerIcon, + onClick: (state) => setSelected(state), + }), + ), + [markerIcon], + ); + + // Fast O(1) lookup for cluster-click zoom. + const markerMapRef = useRef>(new Map()); + useEffect(() => { + markerMapRef.current = new Map(markerStates.map(s => [s.id, s])); + }, [markerStates]); + + const handleClusterClick = useCallback((cluster: MarkerCluster) => { + const map = markerMapRef.current; + let lat = 0, lon = 0, count = 0; + for (const id of cluster.markerIds) { + const s = map.get(id); + if (!s) continue; + lat += s.position.latitude; + lon += s.position.longitude; + count++; + } + if (count === 0) return; + const camera = mapViewState.cameraPosition; + mapViewState.moveCameraTo( + createMapCameraPosition({ + position: createGeoPoint({ latitude: lat / count, longitude: lon / count }), + zoom: Math.min((camera?.zoom ?? 10) + 2, 18), + }), + 600, + ); + }, [mapViewState]); + + const clearSelect = useCallback(() => setSelected(null), []); + + if (error) { + return ( +
+

データの読み込みに失敗しました: {error}

+
+ ); + } + + return ( + + + + {selected && !(selected.extra as unknown as MarkerCluster)?.markerIds && ( + + )} + + + + {!raw ? ( +

データ読み込み中...

+ ) : ( +

+ クラスターをクリックするとズームインします。
+ 個別マーカーをクリックすると郵便局情報が表示されます。 +

+ )} +
+
+ ); +} + +function loadImage(url: string): Promise { + return new Promise((resolve, reject) => { + const img = new Image(); + img.onload = () => resolve(img); + img.onerror = () => reject(new Error(`Failed to load image: ${url}`)); + img.src = url; + }); +} + +// ─── Provider-specific wrappers ──────────────────────────────────────────────── +function GooglePostOfficeClusterPage() { + const mapViewState = useGoogleMapViewState({ + mapDesignType: GoogleMapDesign.Normal, + cameraPosition: INIT_CAMERA_POSITION, + }); + return ; +} + +function MapLibrePostOfficeClusterPage() { + const mapViewState = useMapLibreViewState({ + mapDesignType: MapLibreDesign.OsmBrightJa, + cameraPosition: INIT_CAMERA_POSITION, + }); + return ; +} + +// ─── Page export ─────────────────────────────────────────────────────────────── +export function PostOfficeClusterPage() { + const location = useLocation(); + return location.pathname.startsWith('/google-maps') + ? + : ; +} diff --git a/examples/basic/src/pages/polygon/click/California.ts b/examples/basic/src/pages/polygon/click/California.ts new file mode 100644 index 0000000..f45f842 --- /dev/null +++ b/examples/basic/src/pages/polygon/click/California.ts @@ -0,0 +1,773 @@ +import { GeoPoint } from '@mapconductor/js-sdk-core'; + + +export const california: GeoPoint[][] = + [ + [ + GeoPoint.fromLatLong(37.77205, -123.00111), + GeoPoint.fromLatLong(37.77078, -122.99754), + GeoPoint.fromLatLong(37.76913, -122.99509), + GeoPoint.fromLatLong(37.76387, -122.98741), + GeoPoint.fromLatLong(37.75892, -122.98143), + GeoPoint.fromLatLong(37.75498, -122.9776), + GeoPoint.fromLatLong(37.75258, -122.97545), + GeoPoint.fromLatLong(37.74993, -122.97406), + GeoPoint.fromLatLong(37.74865, -122.97326), + GeoPoint.fromLatLong(37.74782, -122.97045), + GeoPoint.fromLatLong(37.7463, -122.96727), + GeoPoint.fromLatLong(37.74428, -122.9641), + GeoPoint.fromLatLong(37.74195, -122.96106), + GeoPoint.fromLatLong(37.73973, -122.95839), + GeoPoint.fromLatLong(37.73683, -122.95464), + GeoPoint.fromLatLong(37.73219, -122.94998), + GeoPoint.fromLatLong(37.72531, -122.94552), + GeoPoint.fromLatLong(37.72106, -122.94229), + GeoPoint.fromLatLong(37.71535, -122.93971), + GeoPoint.fromLatLong(37.70996, -122.93792), + GeoPoint.fromLatLong(37.70924, -122.93768), + GeoPoint.fromLatLong(37.70246, -122.93586), + GeoPoint.fromLatLong(37.69762, -122.93571), + GeoPoint.fromLatLong(37.69093, -122.93734), + GeoPoint.fromLatLong(37.68654, -122.93903), + GeoPoint.fromLatLong(37.68235, -122.93966), + GeoPoint.fromLatLong(37.67658, -122.94136), + GeoPoint.fromLatLong(37.66977, -122.94501), + GeoPoint.fromLatLong(37.66338, -122.94907), + GeoPoint.fromLatLong(37.65835, -122.95488), + GeoPoint.fromLatLong(37.65198, -122.96301), + GeoPoint.fromLatLong(37.64658, -122.97301), + GeoPoint.fromLatLong(37.6439, -122.98051), + GeoPoint.fromLatLong(37.64178, -122.98675), + GeoPoint.fromLatLong(37.64102, -122.99259), + GeoPoint.fromLatLong(37.63993, -123.00091), + GeoPoint.fromLatLong(37.63983, -123.00111), + GeoPoint.fromLatLong(37.64177, -123.00111), + GeoPoint.fromLatLong(37.64151, -123.0116), + GeoPoint.fromLatLong(37.64522, -123.02715), + GeoPoint.fromLatLong(37.65033, -123.0407), + GeoPoint.fromLatLong(37.65865, -123.05358), + GeoPoint.fromLatLong(37.66732, -123.06358), + GeoPoint.fromLatLong(37.67304, -123.06859), + GeoPoint.fromLatLong(37.68258, -123.07306), + GeoPoint.fromLatLong(37.69007, -123.07492), + GeoPoint.fromLatLong(37.69232, -123.07715), + GeoPoint.fromLatLong(37.69684, -123.08188), + GeoPoint.fromLatLong(37.70075, -123.08606), + GeoPoint.fromLatLong(37.70579, -123.08878), + GeoPoint.fromLatLong(37.71187, -123.0918), + GeoPoint.fromLatLong(37.71369, -123.09285), + GeoPoint.fromLatLong(37.71407, -123.0948), + GeoPoint.fromLatLong(37.71359, -123.09965), + GeoPoint.fromLatLong(37.71265, -123.10492), + GeoPoint.fromLatLong(37.71405, -123.11557), + GeoPoint.fromLatLong(37.71544, -123.11964), + GeoPoint.fromLatLong(37.71673, -123.12611), + GeoPoint.fromLatLong(37.71939, -123.1326), + GeoPoint.fromLatLong(37.72616, -123.14149), + GeoPoint.fromLatLong(37.73228, -123.14913), + GeoPoint.fromLatLong(37.73784, -123.15676), + GeoPoint.fromLatLong(37.74353, -123.16275), + GeoPoint.fromLatLong(37.74993, -123.1667), + GeoPoint.fromLatLong(37.75593, -123.16998), + GeoPoint.fromLatLong(37.7636, -123.17293), + GeoPoint.fromLatLong(37.77183, -123.17376), + GeoPoint.fromLatLong(37.77573, -123.17382), + GeoPoint.fromLatLong(37.77611, -123.17371), + GeoPoint.fromLatLong(37.79236, -123.16911), + GeoPoint.fromLatLong(37.79688, -123.16601), + GeoPoint.fromLatLong(37.802, -123.16094), + GeoPoint.fromLatLong(37.80707, -123.15386), + GeoPoint.fromLatLong(37.80744, -123.15333), + GeoPoint.fromLatLong(37.81195, -123.14721), + GeoPoint.fromLatLong(37.81672, -123.13944), + GeoPoint.fromLatLong(37.81968, -123.13227), + GeoPoint.fromLatLong(37.82166, -123.12611), + GeoPoint.fromLatLong(37.82306, -123.11829), + GeoPoint.fromLatLong(37.82254, -123.10826), + GeoPoint.fromLatLong(37.82188, -123.09526), + GeoPoint.fromLatLong(37.81815, -123.08165), + GeoPoint.fromLatLong(37.81383, -123.07049), + GeoPoint.fromLatLong(37.79982, -123.04882), + GeoPoint.fromLatLong(37.78888, -123.03873), + GeoPoint.fromLatLong(37.78043, -123.0351), + GeoPoint.fromLatLong(37.77837, -123.03384), + GeoPoint.fromLatLong(37.77715, -123.03144), + GeoPoint.fromLatLong(37.77709, -123.02585), + GeoPoint.fromLatLong(37.77607, -123.01697), + GeoPoint.fromLatLong(37.77493, -123.01059), + GeoPoint.fromLatLong(37.77352, -123.00615), + ], + [ + GeoPoint.fromLatLong(33.53589, -119.00093), + GeoPoint.fromLatLong(33.53582, -119.00093), + GeoPoint.fromLatLong(33.5342, -118.99784), + GeoPoint.fromLatLong(33.53271, -118.99445), + GeoPoint.fromLatLong(33.53185, -118.9928), + GeoPoint.fromLatLong(33.53029, -118.99005), + GeoPoint.fromLatLong(33.52743, -118.9862), + GeoPoint.fromLatLong(33.52426, -118.98215), + GeoPoint.fromLatLong(33.52065, -118.9791), + GeoPoint.fromLatLong(33.51768, -118.97654), + GeoPoint.fromLatLong(33.51384, -118.97289), + GeoPoint.fromLatLong(33.51066, -118.97101), + GeoPoint.fromLatLong(33.50556, -118.96831), + GeoPoint.fromLatLong(33.50002, -118.96602), + GeoPoint.fromLatLong(33.4953, -118.9649), + GeoPoint.fromLatLong(33.48521, -118.96276), + GeoPoint.fromLatLong(33.4733, -118.96273), + GeoPoint.fromLatLong(33.46683, -118.96375), + GeoPoint.fromLatLong(33.45792, -118.96676), + GeoPoint.fromLatLong(33.45289, -118.96983), + GeoPoint.fromLatLong(33.44317, -118.97268), + GeoPoint.fromLatLong(33.43111, -118.97848), + GeoPoint.fromLatLong(33.42859, -118.98051), + GeoPoint.fromLatLong(33.42344, -118.98873), + GeoPoint.fromLatLong(33.41996, -118.99598), + GeoPoint.fromLatLong(33.41849, -119.00093), + GeoPoint.fromLatLong(33.4213, -119.00093), + GeoPoint.fromLatLong(33.41677, -119.00952), + GeoPoint.fromLatLong(33.41505, -119.018), + GeoPoint.fromLatLong(33.41287, -119.02704), + GeoPoint.fromLatLong(33.41222, -119.03963), + GeoPoint.fromLatLong(33.41102, -119.05307), + GeoPoint.fromLatLong(33.41255, -119.06412), + GeoPoint.fromLatLong(33.4138, -119.07189), + GeoPoint.fromLatLong(33.41677, -119.07851), + GeoPoint.fromLatLong(33.42139, -119.08651), + GeoPoint.fromLatLong(33.42687, -119.09413), + GeoPoint.fromLatLong(33.43524, -119.10076), + GeoPoint.fromLatLong(33.44044, -119.10378), + GeoPoint.fromLatLong(33.44501, -119.10615), + GeoPoint.fromLatLong(33.45115, -119.10888), + GeoPoint.fromLatLong(33.45632, -119.1088), + GeoPoint.fromLatLong(33.46229, -119.10965), + GeoPoint.fromLatLong(33.46894, -119.10908), + GeoPoint.fromLatLong(33.47428, -119.10957), + GeoPoint.fromLatLong(33.48007, -119.1092), + GeoPoint.fromLatLong(33.48578, -119.10836), + GeoPoint.fromLatLong(33.49119, -119.10791), + GeoPoint.fromLatLong(33.49542, -119.10747), + GeoPoint.fromLatLong(33.49518, -119.10794), + GeoPoint.fromLatLong(33.50002, -119.10721), + GeoPoint.fromLatLong(33.50002, -119.11895), + GeoPoint.fromLatLong(33.5056, -119.11717), + GeoPoint.fromLatLong(33.50986, -119.11508), + GeoPoint.fromLatLong(33.51547, -119.11166), + GeoPoint.fromLatLong(33.51926, -119.10872), + GeoPoint.fromLatLong(33.52289, -119.10421), + GeoPoint.fromLatLong(33.52644, -119.0987), + GeoPoint.fromLatLong(33.5303, -119.09049), + GeoPoint.fromLatLong(33.53291, -119.08374), + GeoPoint.fromLatLong(33.53515, -119.0765), + GeoPoint.fromLatLong(33.53642, -119.07014), + GeoPoint.fromLatLong(33.53671, -119.06616), + GeoPoint.fromLatLong(33.53643, -119.06041), + GeoPoint.fromLatLong(33.53603, -119.05655), + GeoPoint.fromLatLong(33.53676, -119.05394), + GeoPoint.fromLatLong(33.53716, -119.0508), + GeoPoint.fromLatLong(33.53729, -119.04847), + GeoPoint.fromLatLong(33.53722, -119.04706), + GeoPoint.fromLatLong(33.53862, -119.04139), + GeoPoint.fromLatLong(33.53962, -119.03625), + GeoPoint.fromLatLong(33.54012, -119.03074), + GeoPoint.fromLatLong(33.54, -119.02394), + GeoPoint.fromLatLong(33.53919, -119.01735), + GeoPoint.fromLatLong(33.53834, -119.01213), + GeoPoint.fromLatLong(33.53755, -119.00807), + ], + [ + GeoPoint.fromLatLong(32.87504, -118.3289), + GeoPoint.fromLatLong(32.87173, -118.32571), + GeoPoint.fromLatLong(32.86377, -118.31739), + GeoPoint.fromLatLong(32.85347, -118.30489), + GeoPoint.fromLatLong(32.83639, -118.2924), + GeoPoint.fromLatLong(32.82083, -118.28878), + GeoPoint.fromLatLong(32.81053, -118.2899), + GeoPoint.fromLatLong(32.79826, -118.29584), + GeoPoint.fromLatLong(32.78885, -118.30285), + GeoPoint.fromLatLong(32.7768, -118.31853), + GeoPoint.fromLatLong(32.77057, -118.33197), + GeoPoint.fromLatLong(32.76581, -118.35349), + GeoPoint.fromLatLong(32.76708, -118.36427), + GeoPoint.fromLatLong(32.76885, -118.37386), + GeoPoint.fromLatLong(32.77023, -118.37838), + GeoPoint.fromLatLong(32.76808, -118.3812), + GeoPoint.fromLatLong(32.76356, -118.38535), + GeoPoint.fromLatLong(32.7585, -118.39256), + GeoPoint.fromLatLong(32.75523, -118.39952), + GeoPoint.fromLatLong(32.75191, -118.40832), + GeoPoint.fromLatLong(32.75059, -118.4162), + GeoPoint.fromLatLong(32.75004, -118.43654), + GeoPoint.fromLatLong(32.75103, -118.44091), + GeoPoint.fromLatLong(32.75511, -118.45381), + GeoPoint.fromLatLong(32.76018, -118.46471), + GeoPoint.fromLatLong(32.76682, -118.47391), + GeoPoint.fromLatLong(32.77472, -118.48167), + GeoPoint.fromLatLong(32.77916, -118.48497), + GeoPoint.fromLatLong(32.7855, -118.49421), + GeoPoint.fromLatLong(32.79203, -118.50091), + GeoPoint.fromLatLong(32.79688, -118.50765), + GeoPoint.fromLatLong(32.79957, -118.51581), + GeoPoint.fromLatLong(32.80472, -118.52481), + GeoPoint.fromLatLong(32.80877, -118.53178), + GeoPoint.fromLatLong(32.81277, -118.5382), + GeoPoint.fromLatLong(32.82068, -118.54673), + GeoPoint.fromLatLong(32.8288, -118.55319), + GeoPoint.fromLatLong(32.83735, -118.55714), + GeoPoint.fromLatLong(32.84944, -118.56253), + GeoPoint.fromLatLong(32.85456, -118.56735), + GeoPoint.fromLatLong(32.86573, -118.57505), + GeoPoint.fromLatLong(32.87152, -118.58132), + GeoPoint.fromLatLong(32.87884, -118.58794), + GeoPoint.fromLatLong(32.88383, -118.59114), + GeoPoint.fromLatLong(32.89047, -118.59693), + GeoPoint.fromLatLong(32.90356, -118.60352), + GeoPoint.fromLatLong(32.90661, -118.60433), + GeoPoint.fromLatLong(32.91243, -118.60552), + GeoPoint.fromLatLong(32.9182, -118.60547), + GeoPoint.fromLatLong(32.92612, -118.60817), + GeoPoint.fromLatLong(32.92931, -118.61079), + GeoPoint.fromLatLong(32.93685, -118.6186), + GeoPoint.fromLatLong(32.94723, -118.62592), + GeoPoint.fromLatLong(32.95363, -118.62957), + GeoPoint.fromLatLong(32.96221, -118.63387), + GeoPoint.fromLatLong(32.96909, -118.63649), + GeoPoint.fromLatLong(32.97536, -118.63771), + GeoPoint.fromLatLong(32.98261, -118.64049), + GeoPoint.fromLatLong(32.99284, -118.64343), + GeoPoint.fromLatLong(32.99777, -118.64433), + GeoPoint.fromLatLong(33.00003, -118.67017), + GeoPoint.fromLatLong(33.01214, -118.67484), + GeoPoint.fromLatLong(33.02636, -118.67855), + GeoPoint.fromLatLong(33.04935, -118.6756), + GeoPoint.fromLatLong(33.06685, -118.66385), + GeoPoint.fromLatLong(33.08188, -118.6413), + GeoPoint.fromLatLong(33.08566, -118.62592), + GeoPoint.fromLatLong(33.08632, -118.60834), + GeoPoint.fromLatLong(33.08711, -118.58953), + GeoPoint.fromLatLong(33.08222, -118.56492), + GeoPoint.fromLatLong(33.07863, -118.55263), + GeoPoint.fromLatLong(33.06786, -118.53419), + GeoPoint.fromLatLong(33.04989, -118.51497), + GeoPoint.fromLatLong(33.03788, -118.50766), + GeoPoint.fromLatLong(33.03059, -118.50091), + GeoPoint.fromLatLong(33.01792, -118.49113), + GeoPoint.fromLatLong(33.00818, -118.48714), + GeoPoint.fromLatLong(33.00462, -118.48475), + GeoPoint.fromLatLong(33.00003, -118.48141), + GeoPoint.fromLatLong(32.99578, -118.47779), + GeoPoint.fromLatLong(32.98912, -118.47119), + GeoPoint.fromLatLong(32.97525, -118.46018), + GeoPoint.fromLatLong(32.96797, -118.45454), + GeoPoint.fromLatLong(32.96175, -118.44886), + GeoPoint.fromLatLong(32.95795, -118.44215), + GeoPoint.fromLatLong(32.94958, -118.42769), + GeoPoint.fromLatLong(32.94211, -118.41874), + GeoPoint.fromLatLong(32.93647, -118.41127), + GeoPoint.fromLatLong(32.93179, -118.40604), + GeoPoint.fromLatLong(32.91784, -118.39079), + GeoPoint.fromLatLong(32.91508, -118.38644), + GeoPoint.fromLatLong(32.91185, -118.38157), + GeoPoint.fromLatLong(32.9096, -118.37762), + GeoPoint.fromLatLong(32.90869, -118.37591), + GeoPoint.fromLatLong(32.90507, -118.36992), + GeoPoint.fromLatLong(32.90012, -118.36252), + GeoPoint.fromLatLong(32.8945, -118.35639), + GeoPoint.fromLatLong(32.88968, -118.35031), + GeoPoint.fromLatLong(32.88443, -118.34358), + GeoPoint.fromLatLong(32.87975, -118.33746), + GeoPoint.fromLatLong(32.87678, -118.33197), + ], + [ + GeoPoint.fromLatLong(33.41669, -118.30868), + GeoPoint.fromLatLong(33.40972, -118.30713), + GeoPoint.fromLatLong(33.40363, -118.3041), + GeoPoint.fromLatLong(33.38984, -118.28581), + GeoPoint.fromLatLong(33.38074, -118.27721), + GeoPoint.fromLatLong(33.37502, -118.26818), + GeoPoint.fromLatLong(33.36413, -118.25771), + GeoPoint.fromLatLong(33.3534, -118.25138), + GeoPoint.fromLatLong(33.33834, -118.2451), + GeoPoint.fromLatLong(33.31303, -118.24105), + GeoPoint.fromLatLong(33.29106, -118.24594), + GeoPoint.fromLatLong(33.28047, -118.2509), + GeoPoint.fromLatLong(33.2725, -118.25918), + GeoPoint.fromLatLong(33.25909, -118.2807), + GeoPoint.fromLatLong(33.25101, -118.30284), + GeoPoint.fromLatLong(33.25003, -118.30872), + GeoPoint.fromLatLong(33.24847, -118.31862), + GeoPoint.fromLatLong(33.24861, -118.33475), + GeoPoint.fromLatLong(33.25003, -118.34377), + GeoPoint.fromLatLong(33.25507, -118.36331), + GeoPoint.fromLatLong(33.2623, -118.38149), + GeoPoint.fromLatLong(33.2652, -118.38913), + GeoPoint.fromLatLong(33.26794, -118.40256), + GeoPoint.fromLatLong(33.26596, -118.42752), + GeoPoint.fromLatLong(33.26746, -118.44046), + GeoPoint.fromLatLong(33.27289, -118.47146), + GeoPoint.fromLatLong(33.28407, -118.50091), + GeoPoint.fromLatLong(33.28966, -118.51039), + GeoPoint.fromLatLong(33.31518, -118.53488), + GeoPoint.fromLatLong(33.33105, -118.54252), + GeoPoint.fromLatLong(33.35065, -118.54961), + GeoPoint.fromLatLong(33.36935, -118.55119), + GeoPoint.fromLatLong(33.37503, -118.54858), + GeoPoint.fromLatLong(33.38023, -118.55338), + GeoPoint.fromLatLong(33.38103, -118.56363), + GeoPoint.fromLatLong(33.39087, -118.59108), + GeoPoint.fromLatLong(33.40619, -118.61857), + GeoPoint.fromLatLong(33.41507, -118.62592), + GeoPoint.fromLatLong(33.42868, -118.63694), + GeoPoint.fromLatLong(33.44531, -118.65506), + GeoPoint.fromLatLong(33.46733, -118.66592), + GeoPoint.fromLatLong(33.48594, -118.6682), + GeoPoint.fromLatLong(33.50003, -118.66371), + GeoPoint.fromLatLong(33.50576, -118.65992), + GeoPoint.fromLatLong(33.51082, -118.65573), + GeoPoint.fromLatLong(33.51821, -118.64786), + GeoPoint.fromLatLong(33.52403, -118.6384), + GeoPoint.fromLatLong(33.52898, -118.62592), + GeoPoint.fromLatLong(33.53185, -118.61216), + GeoPoint.fromLatLong(33.53232, -118.59976), + GeoPoint.fromLatLong(33.53198, -118.58972), + GeoPoint.fromLatLong(33.53088, -118.58582), + GeoPoint.fromLatLong(33.52841, -118.57914), + GeoPoint.fromLatLong(33.52926, -118.57529), + GeoPoint.fromLatLong(33.52947, -118.57152), + GeoPoint.fromLatLong(33.52948, -118.56719), + GeoPoint.fromLatLong(33.52929, -118.56317), + GeoPoint.fromLatLong(33.52833, -118.55775), + GeoPoint.fromLatLong(33.52921, -118.55502), + GeoPoint.fromLatLong(33.53019, -118.55093), + GeoPoint.fromLatLong(33.53084, -118.5466), + GeoPoint.fromLatLong(33.53126, -118.53894), + GeoPoint.fromLatLong(33.53144, -118.53364), + GeoPoint.fromLatLong(33.53118, -118.52938), + GeoPoint.fromLatLong(33.53015, -118.52324), + GeoPoint.fromLatLong(33.52869, -118.51765), + GeoPoint.fromLatLong(33.52692, -118.51238), + GeoPoint.fromLatLong(33.52491, -118.50676), + GeoPoint.fromLatLong(33.52267, -118.50261), + GeoPoint.fromLatLong(33.52163, -118.50091), + GeoPoint.fromLatLong(33.51838, -118.49671), + GeoPoint.fromLatLong(33.51573, -118.49333), + GeoPoint.fromLatLong(33.51615, -118.48788), + GeoPoint.fromLatLong(33.51606, -118.48262), + GeoPoint.fromLatLong(33.51543, -118.47728), + GeoPoint.fromLatLong(33.513, -118.46896), + GeoPoint.fromLatLong(33.51043, -118.46285), + GeoPoint.fromLatLong(33.50688, -118.45682), + GeoPoint.fromLatLong(33.50339, -118.4522), + GeoPoint.fromLatLong(33.50003, -118.44755), + GeoPoint.fromLatLong(33.48944, -118.43683), + GeoPoint.fromLatLong(33.48894, -118.43243), + GeoPoint.fromLatLong(33.48716, -118.42709), + GeoPoint.fromLatLong(33.48387, -118.42287), + GeoPoint.fromLatLong(33.48178, -118.42084), + GeoPoint.fromLatLong(33.48078, -118.41187), + GeoPoint.fromLatLong(33.47927, -118.40323), + GeoPoint.fromLatLong(33.47671, -118.39513), + GeoPoint.fromLatLong(33.47449, -118.38939), + GeoPoint.fromLatLong(33.47345, -118.38605), + GeoPoint.fromLatLong(33.47157, -118.37798), + GeoPoint.fromLatLong(33.4711, -118.37591), + GeoPoint.fromLatLong(33.469, -118.36922), + GeoPoint.fromLatLong(33.46492, -118.35939), + GeoPoint.fromLatLong(33.45938, -118.35255), + GeoPoint.fromLatLong(33.45782, -118.35092), + GeoPoint.fromLatLong(33.45388, -118.34206), + GeoPoint.fromLatLong(33.44804, -118.33277), + GeoPoint.fromLatLong(33.44198, -118.32465), + GeoPoint.fromLatLong(33.43414, -118.31635), + GeoPoint.fromLatLong(33.42514, -118.31095), + ], + [ + GeoPoint.fromLatLong(34.06867, -119.37595), + GeoPoint.fromLatLong(34.06506, -119.33093), + GeoPoint.fromLatLong(34.03658, -119.29764), + GeoPoint.fromLatLong(34.00001, -119.29647), + GeoPoint.fromLatLong(33.98654, -119.30409), + GeoPoint.fromLatLong(33.96543, -119.34154), + GeoPoint.fromLatLong(33.95636, -119.36976), + GeoPoint.fromLatLong(33.95332, -119.38467), + GeoPoint.fromLatLong(33.95378, -119.40293), + GeoPoint.fromLatLong(33.95591, -119.43296), + GeoPoint.fromLatLong(33.97065, -119.47265), + GeoPoint.fromLatLong(33.97634, -119.49695), + GeoPoint.fromLatLong(33.95492, -119.52331), + GeoPoint.fromLatLong(33.94496, -119.56329), + GeoPoint.fromLatLong(33.93808, -119.59594), + GeoPoint.fromLatLong(33.93588, -119.61853), + GeoPoint.fromLatLong(33.93626, -119.64054), + GeoPoint.fromLatLong(33.9264, -119.66396), + GeoPoint.fromLatLong(33.91844, -119.68239), + GeoPoint.fromLatLong(33.90963, -119.71883), + GeoPoint.fromLatLong(33.9114, -119.75096), + GeoPoint.fromLatLong(33.91061, -119.77831), + GeoPoint.fromLatLong(33.90132, -119.81022), + GeoPoint.fromLatLong(33.90749, -119.85522), + GeoPoint.fromLatLong(33.92265, -119.87823), + GeoPoint.fromLatLong(33.93967, -119.90476), + GeoPoint.fromLatLong(33.9239, -119.91102), + GeoPoint.fromLatLong(33.89438, -119.95029), + GeoPoint.fromLatLong(33.88525, -120.00096), + GeoPoint.fromLatLong(33.875, -120.01553), + GeoPoint.fromLatLong(33.86432, -120.04253), + GeoPoint.fromLatLong(33.85051, -120.08011), + GeoPoint.fromLatLong(33.84235, -120.12596), + GeoPoint.fromLatLong(33.85343, -120.15708), + GeoPoint.fromLatLong(33.86629, -120.183), + GeoPoint.fromLatLong(33.87598, -120.20238), + GeoPoint.fromLatLong(33.89233, -120.22963), + GeoPoint.fromLatLong(33.90844, -120.25097), + GeoPoint.fromLatLong(33.94336, -120.2777), + GeoPoint.fromLatLong(33.96224, -120.29187), + GeoPoint.fromLatLong(33.96637, -120.31739), + GeoPoint.fromLatLong(33.96139, -120.35863), + GeoPoint.fromLatLong(33.96743, -120.39564), + GeoPoint.fromLatLong(33.97495, -120.42259), + GeoPoint.fromLatLong(33.97627, -120.45579), + GeoPoint.fromLatLong(33.98428, -120.48815), + GeoPoint.fromLatLong(33.99735, -120.50308), + GeoPoint.fromLatLong(34.02282, -120.52314), + GeoPoint.fromLatLong(34.05169, -120.53106), + GeoPoint.fromLatLong(34.06864, -120.56699), + GeoPoint.fromLatLong(34.09856, -120.5819), + GeoPoint.fromLatLong(34.12647, -120.57509), + GeoPoint.fromLatLong(34.14642, -120.5487), + GeoPoint.fromLatLong(34.15134, -120.50717), + GeoPoint.fromLatLong(34.14508, -120.48691), + GeoPoint.fromLatLong(34.13929, -120.46529), + GeoPoint.fromLatLong(34.15748, -120.43272), + GeoPoint.fromLatLong(34.15873, -120.40117), + GeoPoint.fromLatLong(34.15242, -120.37598), + GeoPoint.fromLatLong(34.14145, -120.35049), + GeoPoint.fromLatLong(34.12328, -120.33918), + GeoPoint.fromLatLong(34.1138, -120.32434), + GeoPoint.fromLatLong(34.10412, -120.29779), + GeoPoint.fromLatLong(34.0818, -120.27428), + GeoPoint.fromLatLong(34.06508, -120.26241), + GeoPoint.fromLatLong(34.05803, -120.25285), + GeoPoint.fromLatLong(34.06114, -120.231), + GeoPoint.fromLatLong(34.06022, -120.20611), + GeoPoint.fromLatLong(34.06136, -120.1911), + GeoPoint.fromLatLong(34.0757, -120.15901), + GeoPoint.fromLatLong(34.07881, -120.13501), + GeoPoint.fromLatLong(34.07805, -120.11496), + GeoPoint.fromLatLong(34.07449, -120.10292), + GeoPoint.fromLatLong(34.0864, -120.08028), + GeoPoint.fromLatLong(34.09061, -120.04515), + GeoPoint.fromLatLong(34.08404, -120.01021), + GeoPoint.fromLatLong(34.06487, -119.99164), + GeoPoint.fromLatLong(34.09674, -119.97439), + GeoPoint.fromLatLong(34.11256, -119.96547), + GeoPoint.fromLatLong(34.12866, -119.93615), + GeoPoint.fromLatLong(34.12935, -119.90557), + GeoPoint.fromLatLong(34.12608, -119.87988), + GeoPoint.fromLatLong(34.125, -119.86883), + GeoPoint.fromLatLong(34.12019, -119.83796), + GeoPoint.fromLatLong(34.10638, -119.7926), + GeoPoint.fromLatLong(34.11194, -119.76903), + GeoPoint.fromLatLong(34.11092, -119.74522), + GeoPoint.fromLatLong(34.10175, -119.71804), + GeoPoint.fromLatLong(34.09031, -119.68607), + GeoPoint.fromLatLong(34.07122, -119.65493), + GeoPoint.fromLatLong(34.08673, -119.63523), + GeoPoint.fromLatLong(34.0927, -119.62597), + GeoPoint.fromLatLong(34.10564, -119.59056), + GeoPoint.fromLatLong(34.10811, -119.57412), + GeoPoint.fromLatLong(34.10778, -119.54825), + GeoPoint.fromLatLong(34.09871, -119.52459), + GeoPoint.fromLatLong(34.08584, -119.50095), + GeoPoint.fromLatLong(34.07183, -119.47731), + GeoPoint.fromLatLong(34.06474, -119.46004), + GeoPoint.fromLatLong(34.06769, -119.42373), + GeoPoint.fromLatLong(34.06604, -119.38975), + ], + [ + GeoPoint.fromLatLong(33.23455, -119.62594), + GeoPoint.fromLatLong(33.24497, -119.63128), + GeoPoint.fromLatLong(33.24891, -119.63163), + GeoPoint.fromLatLong(33.25322, -119.63154), + GeoPoint.fromLatLong(33.25902, -119.63284), + GeoPoint.fromLatLong(33.27304, -119.6363), + GeoPoint.fromLatLong(33.28596, -119.63479), + GeoPoint.fromLatLong(33.29934, -119.62908), + GeoPoint.fromLatLong(33.30925, -119.62165), + GeoPoint.fromLatLong(33.3148, -119.61453), + GeoPoint.fromLatLong(33.3204, -119.60482), + GeoPoint.fromLatLong(33.32582, -119.59252), + GeoPoint.fromLatLong(33.32779, -119.58268), + GeoPoint.fromLatLong(33.32828, -119.57491), + GeoPoint.fromLatLong(33.32764, -119.56493), + GeoPoint.fromLatLong(33.33118, -119.5546), + GeoPoint.fromLatLong(33.33416, -119.54354), + GeoPoint.fromLatLong(33.33526, -119.53418), + GeoPoint.fromLatLong(33.33486, -119.52531), + GeoPoint.fromLatLong(33.331, -119.50855), + GeoPoint.fromLatLong(33.32911, -119.50094), + GeoPoint.fromLatLong(33.32715, -119.49512), + GeoPoint.fromLatLong(33.32514, -119.49054), + GeoPoint.fromLatLong(33.31683, -119.4713), + GeoPoint.fromLatLong(33.30574, -119.44327), + GeoPoint.fromLatLong(33.30103, -119.43), + GeoPoint.fromLatLong(33.30033, -119.42872), + GeoPoint.fromLatLong(33.30003, -119.42816), + GeoPoint.fromLatLong(33.29894, -119.42618), + GeoPoint.fromLatLong(33.29877, -119.42594), + GeoPoint.fromLatLong(33.29623, -119.4225), + GeoPoint.fromLatLong(33.28951, -119.41337), + GeoPoint.fromLatLong(33.28351, -119.40539), + GeoPoint.fromLatLong(33.28007, -119.39991), + GeoPoint.fromLatLong(33.27438, -119.39098), + GeoPoint.fromLatLong(33.27092, -119.38522), + GeoPoint.fromLatLong(33.26763, -119.37973), + GeoPoint.fromLatLong(33.26476, -119.37594), + GeoPoint.fromLatLong(33.26324, -119.37594), + GeoPoint.fromLatLong(33.25578, -119.36908), + GeoPoint.fromLatLong(33.2534, -119.36723), + GeoPoint.fromLatLong(33.25003, -119.36472), + GeoPoint.fromLatLong(33.24522, -119.36212), + GeoPoint.fromLatLong(33.24206, -119.36093), + GeoPoint.fromLatLong(33.23487, -119.36013), + GeoPoint.fromLatLong(33.22832, -119.36018), + GeoPoint.fromLatLong(33.22145, -119.3607), + GeoPoint.fromLatLong(33.21482, -119.36244), + GeoPoint.fromLatLong(33.20851, -119.3654), + GeoPoint.fromLatLong(33.2026, -119.36845), + GeoPoint.fromLatLong(33.19763, -119.37196), + GeoPoint.fromLatLong(33.19503, -119.37386), + GeoPoint.fromLatLong(33.19219, -119.37594), + GeoPoint.fromLatLong(33.19071, -119.37899), + GeoPoint.fromLatLong(33.18948, -119.38146), + GeoPoint.fromLatLong(33.18628, -119.38614), + GeoPoint.fromLatLong(33.18084, -119.39454), + GeoPoint.fromLatLong(33.16699, -119.43372), + GeoPoint.fromLatLong(33.16527, -119.44497), + GeoPoint.fromLatLong(33.16461, -119.46836), + GeoPoint.fromLatLong(33.1644, -119.47223), + GeoPoint.fromLatLong(33.16364, -119.47526), + GeoPoint.fromLatLong(33.16349, -119.47649), + GeoPoint.fromLatLong(33.16367, -119.47874), + GeoPoint.fromLatLong(33.16391, -119.47959), + GeoPoint.fromLatLong(33.1666, -119.48944), + GeoPoint.fromLatLong(33.16701, -119.49142), + GeoPoint.fromLatLong(33.16739, -119.50094), + GeoPoint.fromLatLong(33.16739, -119.50803), + GeoPoint.fromLatLong(33.16742, -119.51025), + GeoPoint.fromLatLong(33.16776, -119.512), + GeoPoint.fromLatLong(33.169, -119.51688), + GeoPoint.fromLatLong(33.17089, -119.52286), + GeoPoint.fromLatLong(33.17266, -119.52671), + GeoPoint.fromLatLong(33.17325, -119.52901), + GeoPoint.fromLatLong(33.17392, -119.53224), + GeoPoint.fromLatLong(33.17647, -119.54126), + GeoPoint.fromLatLong(33.17882, -119.54676), + GeoPoint.fromLatLong(33.18028, -119.55007), + GeoPoint.fromLatLong(33.18061, -119.55154), + GeoPoint.fromLatLong(33.18126, -119.55319), + GeoPoint.fromLatLong(33.18152, -119.55495), + GeoPoint.fromLatLong(33.18248, -119.56224), + GeoPoint.fromLatLong(33.18462, -119.56932), + GeoPoint.fromLatLong(33.18648, -119.57419), + GeoPoint.fromLatLong(33.1885, -119.57832), + GeoPoint.fromLatLong(33.19019, -119.58097), + GeoPoint.fromLatLong(33.19553, -119.58726), + GeoPoint.fromLatLong(33.19673, -119.58909), + GeoPoint.fromLatLong(33.199, -119.59395), + GeoPoint.fromLatLong(33.20324, -119.60026), + GeoPoint.fromLatLong(33.20692, -119.60528), + GeoPoint.fromLatLong(33.21273, -119.61147), + GeoPoint.fromLatLong(33.21725, -119.61481), + GeoPoint.fromLatLong(33.22248, -119.61824), + GeoPoint.fromLatLong(33.22501, -119.61949), + GeoPoint.fromLatLong(33.22683, -119.61992), + GeoPoint.fromLatLong(33.22786, -119.62092), + GeoPoint.fromLatLong(33.22874, -119.62192), + GeoPoint.fromLatLong(33.23041, -119.62356), + GeoPoint.fromLatLong(33.23247, -119.62519), + ], + [ + GeoPoint.fromLatLong(34.87057, -114.63332), + GeoPoint.fromLatLong(34.86997, -114.63305), + GeoPoint.fromLatLong(34.79181, -114.56953), + GeoPoint.fromLatLong(34.71453, -114.48236), + GeoPoint.fromLatLong(34.64288, -114.44166), + GeoPoint.fromLatLong(34.47903, -114.38169), + GeoPoint.fromLatLong(34.41527, -114.29195), + GeoPoint.fromLatLong(34.31087, -114.14737), + GeoPoint.fromLatLong(34.17212, -114.26017), + GeoPoint.fromLatLong(34.12866, -114.35765), + GeoPoint.fromLatLong(34.04257, -114.4355), + GeoPoint.fromLatLong(33.96372, -114.49813), + GeoPoint.fromLatLong(33.91285, -114.51318), + GeoPoint.fromLatLong(33.84446, -114.52801), + GeoPoint.fromLatLong(33.6969, -114.49649), + GeoPoint.fromLatLong(33.58709, -114.5402), + GeoPoint.fromLatLong(33.47131, -114.61185), + GeoPoint.fromLatLong(33.39691, -114.72123), + GeoPoint.fromLatLong(33.23376, -114.68157), + GeoPoint.fromLatLong(33.03255, -114.62973), + GeoPoint.fromLatLong(32.97206, -114.48131), + GeoPoint.fromLatLong(32.87408, -114.46563), + GeoPoint.fromLatLong(32.73487, -114.58576), + GeoPoint.fromLatLong(32.73137, -114.63501), + GeoPoint.fromLatLong(32.73946, -114.69096), + GeoPoint.fromLatLong(32.71943, -114.71919), + GeoPoint.fromLatLong(32.71875, -114.71972), + GeoPoint.fromLatLong(32.70253, -114.9559), + GeoPoint.fromLatLong(32.66605, -115.47927), + GeoPoint.fromLatLong(32.66438, -115.50314), + GeoPoint.fromLatLong(32.64163, -115.80199), + GeoPoint.fromLatLong(32.6211, -116.0738), + GeoPoint.fromLatLong(32.61112, -116.19899), + GeoPoint.fromLatLong(32.59913, -116.3481), + GeoPoint.fromLatLong(32.58952, -116.46732), + GeoPoint.fromLatLong(32.57725, -116.61646), + GeoPoint.fromLatLong(32.56578, -116.75596), + GeoPoint.fromLatLong(32.55977, -116.82902), + GeoPoint.fromLatLong(32.54863, -116.95778), + GeoPoint.fromLatLong(32.54234, -117.02945), + GeoPoint.fromLatLong(32.5395, -117.06674), + GeoPoint.fromLatLong(32.6209, -117.22314), + GeoPoint.fromLatLong(32.65404, -117.30735), + GeoPoint.fromLatLong(32.83452, -117.34004), + GeoPoint.fromLatLong(33.07321, -117.37526), + GeoPoint.fromLatLong(33.3123, -117.57153), + GeoPoint.fromLatLong(33.40862, -117.71501), + GeoPoint.fromLatLong(33.49087, -117.81636), + GeoPoint.fromLatLong(33.55979, -117.94957), + GeoPoint.fromLatLong(33.63031, -118.06299), + GeoPoint.fromLatLong(33.66343, -118.34541), + GeoPoint.fromLatLong(33.93905, -118.51367), + GeoPoint.fromLatLong(33.98697, -118.62007), + GeoPoint.fromLatLong(33.97556, -118.74596), + GeoPoint.fromLatLong(33.98382, -118.87592), + GeoPoint.fromLatLong(34.07434, -119.22693), + GeoPoint.fromLatLong(34.23687, -119.33489), + GeoPoint.fromLatLong(34.34814, -119.56331), + GeoPoint.fromLatLong(34.34275, -119.73923), + GeoPoint.fromLatLong(34.36437, -119.90542), + GeoPoint.fromLatLong(34.41671, -120.12095), + GeoPoint.fromLatLong(34.41798, -120.24944), + GeoPoint.fromLatLong(34.39674, -120.42603), + GeoPoint.fromLatLong(34.4893, -120.56388), + GeoPoint.fromLatLong(34.51999, -120.6724), + GeoPoint.fromLatLong(34.60609, -120.70856), + GeoPoint.fromLatLong(34.7177, -120.683), + GeoPoint.fromLatLong(34.80895, -120.68251), + GeoPoint.fromLatLong(34.9015, -120.73438), + GeoPoint.fromLatLong(35.03457, -120.6942), + GeoPoint.fromLatLong(35.11198, -120.78292), + GeoPoint.fromLatLong(35.24362, -120.96025), + GeoPoint.fromLatLong(35.37806, -120.93094), + GeoPoint.fromLatLong(35.4377, -121.05951), + GeoPoint.fromLatLong(35.60068, -121.25103), + GeoPoint.fromLatLong(35.81771, -121.4561), + GeoPoint.fromLatLong(36.14645, -121.7489), + GeoPoint.fromLatLong(36.20183, -121.86514), + GeoPoint.fromLatLong(36.25918, -121.92971), + GeoPoint.fromLatLong(36.32734, -121.96481), + GeoPoint.fromLatLong(36.37682, -121.97196), + GeoPoint.fromLatLong(36.42509, -121.98576), + GeoPoint.fromLatLong(36.50891, -122.022), + GeoPoint.fromLatLong(36.58872, -122.04406), + GeoPoint.fromLatLong(36.87495, -122.06832), + GeoPoint.fromLatLong(37.02483, -122.27637), + GeoPoint.fromLatLong(37.14967, -122.4452), + GeoPoint.fromLatLong(37.31479, -122.47161), + GeoPoint.fromLatLong(37.47338, -122.56299), + GeoPoint.fromLatLong(37.62752, -122.57267), + GeoPoint.fromLatLong(37.82781, -122.63247), + GeoPoint.fromLatLong(37.89376, -122.78903), + GeoPoint.fromLatLong(37.97575, -122.89742), + GeoPoint.fromLatLong(37.98809, -123.08461), + GeoPoint.fromLatLong(38.28431, -123.12347), + GeoPoint.fromLatLong(38.40068, -123.18504), + GeoPoint.fromLatLong(38.49991, -123.3304), + GeoPoint.fromLatLong(38.66066, -123.49508), + GeoPoint.fromLatLong(38.84412, -123.7253), + GeoPoint.fromLatLong(39.03837, -123.76207), + GeoPoint.fromLatLong(39.1249, -123.80553), + GeoPoint.fromLatLong(39.21873, -123.84839), + GeoPoint.fromLatLong(39.34835, -123.89494), + GeoPoint.fromLatLong(39.44394, -123.88889), + GeoPoint.fromLatLong(39.55492, -123.83841), + GeoPoint.fromLatLong(39.68453, -123.87391), + GeoPoint.fromLatLong(39.82699, -123.9536), + GeoPoint.fromLatLong(39.95662, -124.06357), + GeoPoint.fromLatLong(40.09338, -124.2312), + GeoPoint.fromLatLong(40.27564, -124.42868), + GeoPoint.fromLatLong(40.45264, -124.47916), + GeoPoint.fromLatLong(40.71898, -124.33411), + GeoPoint.fromLatLong(40.9617, -124.1945), + GeoPoint.fromLatLong(41.03142, -124.2151), + GeoPoint.fromLatLong(41.09222, -124.24743), + GeoPoint.fromLatLong(41.13756, -124.25119), + GeoPoint.fromLatLong(41.18092, -124.21142), + GeoPoint.fromLatLong(41.25705, -124.17435), + GeoPoint.fromLatLong(41.29199, -124.19233), + GeoPoint.fromLatLong(41.31575, -124.23454), + GeoPoint.fromLatLong(41.36383, -124.23517), + GeoPoint.fromLatLong(41.38412, -124.14406), + GeoPoint.fromLatLong(41.51075, -124.15357), + GeoPoint.fromLatLong(41.6897, -124.22986), + GeoPoint.fromLatLong(41.78831, -124.41076), + GeoPoint.fromLatLong(41.85879, -124.31194), + GeoPoint.fromLatLong(41.99807, -124.32829), + GeoPoint.fromLatLong(41.99833, -124.32883), + GeoPoint.fromLatLong(41.99703, -124.11879), + GeoPoint.fromLatLong(41.99625, -123.96782), + GeoPoint.fromLatLong(41.99569, -123.79381), + GeoPoint.fromLatLong(41.99984, -123.62007), + GeoPoint.fromLatLong(42.00086, -123.51413), + GeoPoint.fromLatLong(42.00197, -123.2737), + GeoPoint.fromLatLong(42.00302, -123.03178), + GeoPoint.fromLatLong(42.00388, -122.78389), + GeoPoint.fromLatLong(42.00482, -122.64619), + GeoPoint.fromLatLong(42.00869, -122.40756), + GeoPoint.fromLatLong(42.00755, -122.18647), + GeoPoint.fromLatLong(42.00397, -122.00032), + GeoPoint.fromLatLong(42.00262, -121.81573), + GeoPoint.fromLatLong(42.00077, -121.70538), + GeoPoint.fromLatLong(41.99933, -121.6122), + GeoPoint.fromLatLong(41.99827, -121.51946), + GeoPoint.fromLatLong(41.99738, -121.43715), + GeoPoint.fromLatLong(41.99668, -121.36025), + GeoPoint.fromLatLong(41.99759, -121.26065), + GeoPoint.fromLatLong(41.99336, -120.97395), + GeoPoint.fromLatLong(41.99387, -120.76508), + GeoPoint.fromLatLong(41.99309, -120.60306), + GeoPoint.fromLatLong(41.99313, -120.30731), + GeoPoint.fromLatLong(41.99443, -120.19996), + GeoPoint.fromLatLong(41.99514, -120.00104), + GeoPoint.fromLatLong(41.99454, -119.99917), + GeoPoint.fromLatLong(41.97905, -119.99919), + GeoPoint.fromLatLong(41.26742, -120.00002), + GeoPoint.fromLatLong(40.86934, -119.99926), + GeoPoint.fromLatLong(40.39719, -119.99567), + GeoPoint.fromLatLong(40.08934, -119.99733), + GeoPoint.fromLatLong(39.79567, -120.00049), + GeoPoint.fromLatLong(39.57782, -120.0015), + GeoPoint.fromLatLong(39.37557, -120.00608), + GeoPoint.fromLatLong(38.98156, -119.9748), + GeoPoint.fromLatLong(38.83427, -119.76041), + GeoPoint.fromLatLong(38.60904, -119.43506), + GeoPoint.fromLatLong(38.30368, -119.00097), + GeoPoint.fromLatLong(37.96065, -118.51722), + GeoPoint.fromLatLong(37.75309, -118.22972), + GeoPoint.fromLatLong(37.6185, -118.04392), + GeoPoint.fromLatLong(37.43715, -117.79563), + GeoPoint.fromLatLong(37.08441, -117.31883), + GeoPoint.fromLatLong(36.75057, -116.87227), + GeoPoint.fromLatLong(36.37205, -116.37528), + GeoPoint.fromLatLong(36.14577, -116.08072), + GeoPoint.fromLatLong(36.0018, -115.89512), + GeoPoint.fromLatLong(35.81231, -115.65233), + GeoPoint.fromLatLong(35.59033, -115.36992), + GeoPoint.fromLatLong(35.38796, -115.11622), + GeoPoint.fromLatLong(35.15341, -114.82052), + GeoPoint.fromLatLong(35.00195, -114.63361), + GeoPoint.fromLatLong(35.00186, -114.63349), + ], + ] diff --git a/examples/basic/src/pages/polygon/click/PolygonClickPage.tsx b/examples/basic/src/pages/polygon/click/PolygonClickPage.tsx index 0026dcb..eda2603 100644 --- a/examples/basic/src/pages/polygon/click/PolygonClickPage.tsx +++ b/examples/basic/src/pages/polygon/click/PolygonClickPage.tsx @@ -1,51 +1,109 @@ -import { useMemo } from 'react'; -import { createGeoPoint, createPolygonState } from '@mapconductor/js-sdk-core'; -import { Polygon } from '@mapconductor/js-sdk-react'; +import { useCallback, useMemo, useRef, useState } from 'react'; +import { + ColorDefaultIcon, + MarkerAnimation, + PolygonManager, + createMarkerState, + createPolygonEntity, + createPolygonState, + type GeoPoint, +} from '@mapconductor/js-sdk-core'; +import { InfoBubble, Marker, Polygon } from '@mapconductor/js-sdk-react'; import { ControlPanel } from '../../../components/ControlPanel'; -import { Toast, useToast } from '../../../components/Toast'; import { MapViewContainer, useSampleMapViewState } from '../../../MapViewContainer'; +import { california } from './California'; -const INIT_CAMERA = { lat: 36.0, lng: -120.0, zoom: 7 }; +const INIT_CAMERA = { lat: 37.2, lng: -119.6, zoom: 5 }; + +interface ClickedMarker { + position: GeoPoint; + message: string; + sequence: number; +} export function PolygonClickPage() { const mapViewState = useSampleMapViewState(INIT_CAMERA); - const { messages, showToast, dismissToast } = useToast(); - const polygons = useMemo(() => [ - createPolygonState({ - id: 'sf-shape', - points: [ - createGeoPoint({ latitude: 37.82, longitude: -122.52 }), - createGeoPoint({ latitude: 37.83, longitude: -122.35 }), - createGeoPoint({ latitude: 37.70, longitude: -122.32 }), - createGeoPoint({ latitude: 37.68, longitude: -122.50 }), - ], - fillColor: 'rgba(37, 99, 235, 0.32)', - strokeColor: '#2563eb', - strokeWidth: 2, - onClick: event => showToast(`Blue polygon clicked\n${event.clicked.toUrlValue(5)}`), - }), - createPolygonState({ - id: 'la-shape', - points: [ - createGeoPoint({ latitude: 34.13, longitude: -118.52 }), - createGeoPoint({ latitude: 34.13, longitude: -118.18 }), - createGeoPoint({ latitude: 33.94, longitude: -118.16 }), - createGeoPoint({ latitude: 33.92, longitude: -118.48 }), - ], - fillColor: 'rgba(239, 68, 68, 0.28)', - strokeColor: '#ef4444', - strokeWidth: 2, - onClick: event => showToast(`Red polygon clicked\n${event.clicked.toUrlValue(5)}`), - }), - ], [showToast]); + const polygons = useMemo( + () => california.map((points, index) => + createPolygonState({ + id: `california-${index}`, + points, + fillColor: 'rgba(37, 99, 235, 0.28)', + strokeColor: '#2563eb', + strokeWidth: 2, + geodesic: true, + }), + ), + [], + ); + const polygonManager = useMemo(() => { + const manager = new PolygonManager(); + polygons.forEach((polygon) => { + manager.registerEntity(createPolygonEntity({ polygon: null, state: polygon })); + }); + return manager; + }, [polygons]); + const [clickedMarker, setClickedMarker] = useState(null); + const markerIcon = useMemo( + () => new ColorDefaultIcon('#ef4444', { label: 'P', labelTextColor: '#ffffff' }), + [], + ); + const marker = useMemo( + () => clickedMarker + ? createMarkerState({ + id: `polygon-click-${clickedMarker.sequence}`, + position: clickedMarker.position, + icon: markerIcon, + animation: MarkerAnimation.Drop, + }) + : null, + [clickedMarker, markerIcon], + ); + const lastPolygonClickRef = useRef<{ point: GeoPoint; time: number } | null>(null); + + const showClickedMarker = useCallback((clicked: GeoPoint) => { + const isInside = polygonManager.find(clicked) != null; + setClickedMarker((current) => ({ + position: clicked, + message: isInside ? `Inside\n${clicked.toUrlValue(5)}` : 'Outside', + sequence: (current?.sequence ?? 0) + 1, + })); + }, [polygonManager]); + const handleMapClick = useCallback((clicked: GeoPoint) => { + const lastPolygonClick = lastPolygonClickRef.current; + if ( + lastPolygonClick && + Date.now() - lastPolygonClick.time < 250 && + lastPolygonClick.point.equals(clicked) + ) { + return; + } + showClickedMarker(clicked); + }, [showClickedMarker]); + const handlePolygonClick = useCallback((clicked: GeoPoint) => { + lastPolygonClickRef.current = { point: clicked, time: Date.now() }; + showClickedMarker(clicked); + }, [showClickedMarker]); return ( - - {polygons.map(polygon => )} + + {polygons.map(polygon => ( + handlePolygonClick(event.clicked) })} + /> + ))} + {marker && ( + <> + + +
{clickedMarker?.message}
+
+ + )} -

Tap either polygon to show a toast with the clicked location.

+

Tap inside or outside California.

-
); } diff --git a/examples/basic/src/pages/polygon/geodesic/PolygonGeodesicPage.tsx b/examples/basic/src/pages/polygon/geodesic/PolygonGeodesicPage.tsx index dc8501a..c71d054 100644 --- a/examples/basic/src/pages/polygon/geodesic/PolygonGeodesicPage.tsx +++ b/examples/basic/src/pages/polygon/geodesic/PolygonGeodesicPage.tsx @@ -1,37 +1,90 @@ -import { useMemo } from 'react'; -import { createGeoPoint, createPolygonState } from '@mapconductor/js-sdk-core'; -import { Polygon } from '@mapconductor/js-sdk-react'; +import { useMemo, useState } from 'react'; +import { + ColorDefaultIcon, + GeoPoint, + MarkerAnimation, + createMarkerState, + createPolygonState, + type GeoPoint as GeoPointType, + type PolygonEvent, +} from '@mapconductor/js-sdk-core'; +import { InfoBubble, Marker, Polygon } from '@mapconductor/js-sdk-react'; import { ControlPanel } from '../../../components/ControlPanel'; import { MapViewContainer, useSampleMapViewState } from '../../../MapViewContainer'; const INIT_CAMERA = { lat: 30.0, lng: 20.0, zoom: 2 }; +interface ClickedPolygon { + position: GeoPointType; + label: string; + markerColor: string; + sequence: number; +} + export function PolygonGeodesicPage() { const mapViewState = useSampleMapViewState(INIT_CAMERA); + const [clickedPolygon, setClickedPolygon] = useState(null); const points = useMemo(() => [ - createGeoPoint({ latitude: 35.6812, longitude: 139.7671 }), - createGeoPoint({ latitude: 21.3069, longitude: -157.8583 }), - createGeoPoint({ latitude: 40.7128, longitude: -74.006 }), + GeoPoint.fromLongLat(23.66, 56.42, 5000), + GeoPoint.fromLongLat(13.39, 2.95, 5000), + GeoPoint.fromLongLat(-87.82, 38.58, 5000), + GeoPoint.fromLongLat(23.66, 56.42, 5000), ], []); + const handlePolygonClick = (label: string) => (event: PolygonEvent) => { + setClickedPolygon((current) => ({ + position: event.clicked, + label, + markerColor: event.state.strokeColor, + sequence: (current?.sequence ?? 0) + 1, + })); + }; + const polygons = useMemo(() => [ + createPolygonState({ + id: 'linear-triangle', + points, + fillColor: 'rgba(37, 99, 235, 0.5)', + strokeColor: '#2563eb', + strokeWidth: 2, + geodesic: false, + onClick: handlePolygonClick('Linear Triangle'), + }), + createPolygonState({ + id: 'geodesic-triangle', + points, + fillColor: 'rgba(245, 158, 11, 0.5)', + strokeColor: '#f59e0b', + strokeWidth: 2, + geodesic: true, + zIndex: 3, + onClick: handlePolygonClick('Geodesic Triangle'), + }), + ], [points]); + const marker = useMemo( + () => clickedPolygon + ? createMarkerState({ + id: `polygon-geodesic-click-${clickedPolygon.sequence}`, + position: clickedPolygon.position, + icon: new ColorDefaultIcon(clickedPolygon.markerColor, { + label: 'P', + labelTextColor: '#ffffff', + }), + animation: MarkerAnimation.Drop, + }) + : null, + [clickedPolygon], + ); return ( - - + {polygons.map(polygon => )} + {marker && ( + <> + + +
{clickedPolygon?.label}
+
+ + )}

Blue uses straight screen-space edges. Orange uses geodesic interpolation.

diff --git a/examples/basic/src/pages/polygon/hole/PolygonHolePage.tsx b/examples/basic/src/pages/polygon/hole/PolygonHolePage.tsx index 56fce6b..2f9caf2 100644 --- a/examples/basic/src/pages/polygon/hole/PolygonHolePage.tsx +++ b/examples/basic/src/pages/polygon/hole/PolygonHolePage.tsx @@ -1,49 +1,103 @@ -import { useMemo } from 'react'; -import { createGeoPoint, createPolygonState } from '@mapconductor/js-sdk-core'; -import { Polygon } from '@mapconductor/js-sdk-react'; +import { useMemo, useRef, useState } from 'react'; +import { + ColorDefaultIcon, + createGeoPoint, + createMarkerState, + createPolygonState, + type GeoPoint, + type MarkerState, +} from '@mapconductor/js-sdk-core'; +import { Markers, Polygon } from '@mapconductor/js-sdk-react'; import { ControlPanel } from '../../../components/ControlPanel'; import { MapViewContainer, useSampleMapViewState } from '../../../MapViewContainer'; const INIT_CAMERA = { lat: 43.0602, lng: 141.3195, zoom: 11 }; +const WORLD_POINTS = [ + createGeoPoint({ latitude: 85, longitude: 90 }), + createGeoPoint({ latitude: 85, longitude: 0.1 }), + createGeoPoint({ latitude: 85, longitude: -90 }), + createGeoPoint({ latitude: 85, longitude: -179.9 }), + createGeoPoint({ latitude: -85, longitude: -179.9 }), + createGeoPoint({ latitude: -85, longitude: -90 }), + createGeoPoint({ latitude: -85, longitude: 0.1 }), + createGeoPoint({ latitude: -85, longitude: 90 }), + createGeoPoint({ latitude: -85, longitude: 179.9 }), + createGeoPoint({ latitude: 85, longitude: 179.9 }), +]; +const INITIAL_HOLES = [ + [ + createGeoPoint({ latitude: 43.100869, longitude: 141.352909 }), + createGeoPoint({ latitude: 43.044443, longitude: 141.411895 }), + createGeoPoint({ latitude: 43.050601, longitude: 141.306563 }), + ], + [ + createGeoPoint({ latitude: 43.060351, longitude: 141.319905 }), + createGeoPoint({ latitude: 43.038285, longitude: 141.333247 }), + createGeoPoint({ latitude: 43.049062, longitude: 141.286901 }), + ], +]; +const HOLE_MARKER_COLORS = ['#2563eb', '#f97316']; export function PolygonHolePage() { const mapViewState = useSampleMapViewState(INIT_CAMERA); - const state = useMemo(() => createPolygonState({ - id: 'world-hole', - points: [ - createGeoPoint({ latitude: 85, longitude: 90 }), - createGeoPoint({ latitude: 85, longitude: 0.1 }), - createGeoPoint({ latitude: 85, longitude: -90 }), - createGeoPoint({ latitude: 85, longitude: -179.9 }), - createGeoPoint({ latitude: -85, longitude: -179.9 }), - createGeoPoint({ latitude: -85, longitude: -90 }), - createGeoPoint({ latitude: -85, longitude: 0.1 }), - createGeoPoint({ latitude: -85, longitude: 90 }), - createGeoPoint({ latitude: -85, longitude: 179.9 }), - createGeoPoint({ latitude: 85, longitude: 179.9 }), - ], - holes: [ - [ - createGeoPoint({ latitude: 43.100869, longitude: 141.352909 }), - createGeoPoint({ latitude: 43.044443, longitude: 141.411895 }), - createGeoPoint({ latitude: 43.050601, longitude: 141.306563 }), - ], - [ - createGeoPoint({ latitude: 43.060351, longitude: 141.319905 }), - createGeoPoint({ latitude: 43.038285, longitude: 141.333247 }), - createGeoPoint({ latitude: 43.049062, longitude: 141.286901 }), - ], - ], - fillColor: 'rgba(120, 120, 128, 0.8)', - strokeColor: '#ef4444', - strokeWidth: 2, - }), []); + const [holes, setHoles] = useState(() => INITIAL_HOLES.map(hole => [...hole])); + const setHolesRef = useRef(setHoles); + setHolesRef.current = setHoles; + + const state = useMemo( + () => + createPolygonState({ + id: 'world-hole', + points: WORLD_POINTS, + holes, + fillColor: 'rgba(120, 120, 128, 0.8)', + strokeColor: '#ef4444', + strokeWidth: 2, + }), + [holes] + ); + + const vertexMarkers = useMemo( + () => + holes.flatMap((hole, holeIndex) => + hole.map((point, vertexIndex) => { + const label = `${holeIndex + 1}-${vertexIndex + 1}`; + const updateHoleVertex = (markerState: MarkerState) => { + setHolesRef.current(prev => + prev.map((prevHole, prevHoleIndex) => + prevHoleIndex !== holeIndex + ? prevHole + : prevHole.map((prevPoint, prevVertexIndex) => + prevVertexIndex === vertexIndex ? markerState.position : prevPoint + ) + ) + ); + }; + + return createMarkerState({ + id: `hole-${holeIndex}-${vertexIndex}`, + position: point, + draggable: true, + clickable: false, + icon: new ColorDefaultIcon(HOLE_MARKER_COLORS[holeIndex] ?? '#64748b', { + label, + labelTextColor: '#ffffff', + strokeColor: '#ffffff', + }), + onDrag: updateHoleVertex, + onDragEnd: updateHoleVertex, + }); + }) + ), + [holes] + ); return ( + -

A world-covering polygon with two triangular holes near Sapporo.

+

Drag hole vertex markers to reshape the holes.

); diff --git a/examples/basic/src/pages/rasterlayer/RasterLayerPage.tsx b/examples/basic/src/pages/rasterlayer/RasterLayerPage.tsx index aee8841..ee2234f 100644 --- a/examples/basic/src/pages/rasterlayer/RasterLayerPage.tsx +++ b/examples/basic/src/pages/rasterlayer/RasterLayerPage.tsx @@ -5,6 +5,7 @@ import { ControlPanel, SliderControl } from '../../components/ControlPanel'; import { MapViewContainer, useSampleMapViewState } from '../../MapViewContainer'; const INIT_CAMERA = { lat: 35.6812, lng: 139.7671, zoom: 5 }; +const OSM_TILE_SIZE = 256; export function RasterLayerPage() { const mapViewState = useSampleMapViewState(INIT_CAMERA); @@ -13,7 +14,7 @@ export function RasterLayerPage() { id: 'osm-raster', source: RasterLayerSource.UrlTemplate({ template: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png', - tileSize: RasterLayerSource.DEFAULT_TILE_SIZE, + tileSize: OSM_TILE_SIZE, attribution: '© OpenStreetMap contributors', }), opacity, diff --git a/examples/basic/src/sampleRegistry.ts b/examples/basic/src/sampleRegistry.ts index 4df30a6..c9e539d 100644 --- a/examples/basic/src/sampleRegistry.ts +++ b/examples/basic/src/sampleRegistry.ts @@ -5,6 +5,8 @@ export interface SamplePageDefinition { label: string; group: string; status?: SampleStatus; + showProviderSelector?: boolean; + unavailableProviders?: string[]; } export const SAMPLE_PAGES: SamplePageDefinition[] = [ @@ -13,27 +15,27 @@ export const SAMPLE_PAGES: SamplePageDefinition[] = [ { id: 'fly-to', label: 'Fly To', group: 'Map' }, { id: 'tilt', label: 'Tilt', group: 'Map' }, { id: 'visible-region', label: 'Visible Region', group: 'Map' }, - { id: 'camera-sync', label: 'Camera Sync', group: 'Map', status: 'unsupported' }, + { id: 'camera-sync', label: 'Camera Sync', group: 'Map', showProviderSelector: false }, { id: 'marker', label: 'Marker Icons', group: 'Marker' }, { id: 'marker-animation', label: 'Marker Animation', group: 'Marker' }, - { id: 'post-office', label: 'Post Office', group: 'Marker' }, - { id: 'post-office-cluster', label: 'Post Office Cluster', group: 'Marker', status: 'unsupported' }, + { id: 'post-office', label: 'Post Office', group: 'Marker', unavailableProviders: ['google-maps-3d'] }, + { id: 'post-office-cluster', label: 'Post Office Cluster', group: 'Marker' }, { id: 'circle', label: 'Circle', group: 'Shape' }, { id: 'polyline', label: 'Polyline', group: 'Shape' }, { id: 'polyline-click', label: 'Polyline Click', group: 'Shape' }, { id: 'polygon', label: 'Polygon', group: 'Shape' }, { id: 'polygon-click', label: 'Polygon Click', group: 'Shape' }, { id: 'polygon-geodesic', label: 'Polygon Geodesic', group: 'Shape' }, - { id: 'polygon-hole', label: 'Polygon Hole', group: 'Shape' }, - { id: 'ground-image', label: 'Ground Image', group: 'Overlay' }, - { id: 'raster-layer', label: 'Raster Layer', group: 'Overlay' }, + { id: 'polygon-hole', label: 'Polygon Hole', group: 'Shape', unavailableProviders: ['google-maps-3d'] }, + { id: 'ground-image', label: 'Ground Image', group: 'Overlay', unavailableProviders: ['google-maps-3d'] }, + { id: 'raster-layer', label: 'Raster Layer', group: 'Overlay', unavailableProviders: ['google-maps-3d'] }, { id: 'info-bubble-simple', label: 'Simple Bubble', group: 'Info Bubble' }, { id: 'info-bubble-styled', label: 'Styled Bubble', group: 'Info Bubble' }, { id: 'info-bubble-multiple', label: 'Multiple Bubbles', group: 'Info Bubble' }, { id: 'info-bubble-rich', label: 'Rich Bubble', group: 'Info Bubble' }, - { id: 'geojson-basic', label: 'GeoJSON Basic', group: 'Extensions', status: 'unsupported' }, - { id: 'geojson-layer', label: 'GeoJSON Layer', group: 'Extensions', status: 'unsupported' }, - { id: 'heatmap-layer', label: 'Heatmap Layer', group: 'Extensions', status: 'unsupported' }, + { id: 'geojson-basic', label: 'GeoJSON Basic', group: 'Extensions', unavailableProviders: ['google-maps-3d'] }, + { id: 'geojson-layer', label: 'GeoJSON Layer', group: 'Extensions', unavailableProviders: ['google-maps-3d'] }, + { id: 'heatmap-layer', label: 'Heatmap Layer', group: 'Extensions', unavailableProviders: ['google-maps-3d'] }, ]; export const DEFAULT_SAMPLE_PAGE = 'map'; @@ -41,3 +43,7 @@ export const DEFAULT_SAMPLE_PAGE = 'map'; export function isKnownSamplePage(page: string | undefined): boolean { return SAMPLE_PAGES.some((item) => item.id === page); } + +export function getSamplePageDefinition(page: string | undefined): SamplePageDefinition | undefined { + return SAMPLE_PAGES.find((item) => item.id === page); +} diff --git a/examples/basic/vite.config.ts b/examples/basic/vite.config.ts index 81e3d0b..21dcd5e 100644 --- a/examples/basic/vite.config.ts +++ b/examples/basic/vite.config.ts @@ -43,6 +43,7 @@ function mapconductorTileServiceWorker(): Plugin { const useHttps = process.env.HTTPS === 'true'; export default defineConfig({ + base: process.env.VITE_BASE_PATH ?? '/', // mkcert() enables server.https itself (with a locally-trusted cert) when present; // no separate server.https config is needed. plugins: [react(), mapconductorTileServiceWorker(), ...(useHttps ? [mkcert()] : [])], diff --git a/js-sdk-core b/js-sdk-core index c3babf4..9b19a1d 160000 --- a/js-sdk-core +++ b/js-sdk-core @@ -1 +1 @@ -Subproject commit c3babf4ee6ce94638cb0edec446e7d34dbe7e73a +Subproject commit 9b19a1d1448210d0756efdf090d1124cedfbe872 diff --git a/js-sdk-react b/js-sdk-react index 7354460..06f3267 160000 --- a/js-sdk-react +++ b/js-sdk-react @@ -1 +1 @@ -Subproject commit 735446073c7e99120f5e33e97333205eaf459458 +Subproject commit 06f3267c7018efb13c852864248ce41d4caaec87 diff --git a/package.json b/package.json index 84b541d..98c2ae2 100644 --- a/package.json +++ b/package.json @@ -7,8 +7,11 @@ "js-sdk-core/", "js-sdk-react/", "js-sdk-reactnative/", + "react-icons/", "react-for-googlemaps/", "react-for-maplibre/", + "react-heatmap/", + "react-marker-clustering/", "reactnative-for-googlemaps/", "reactnative-for-maplibre/", "examples/*" @@ -16,7 +19,7 @@ "scripts": { "build": "pnpm -r --if-present run build", "dev": "pnpm run build && (pnpm run dev:packages & pnpm run dev:examples & wait)", - "dev:packages": "pnpm --parallel --filter @mapconductor/js-sdk-core --filter @mapconductor/js-sdk-react --filter @mapconductor/js-sdk-reactnative --filter @mapconductor/react-for-googlemaps --filter @mapconductor/react-for-maplibre --filter @mapconductor/reactnative-for-googlemaps --filter @mapconductor/reactnative-for-maplibre --if-present run dev --tsconfig ../tsconfig.dev.json", + "dev:packages": "pnpm --parallel --filter @mapconductor/js-sdk-core --filter @mapconductor/js-sdk-react --filter @mapconductor/js-sdk-reactnative --filter @mapconductor/react-icons --filter @mapconductor/react-for-googlemaps --filter @mapconductor/react-for-maplibre --filter @mapconductor/reactnative-for-googlemaps --filter @mapconductor/reactnative-for-maplibre --if-present run dev --tsconfig ../tsconfig.dev.json", "dev:examples": "pnpm --parallel --filter @mapconductor/example-basic --if-present run dev", "lint": "eslint . --ext .ts,.tsx", "lint:fix": "eslint . --ext .ts,.tsx --fix", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4363d3d..b87a1e1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -22,19 +22,19 @@ importers: version: 19.2.2(@types/react@19.2.2) '@typescript-eslint/eslint-plugin': specifier: ^6.15.0 - version: 6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3) + version: 6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1(supports-color@10.2.2))(supports-color@10.2.2)(typescript@5.9.3))(eslint@8.57.1(supports-color@10.2.2))(supports-color@10.2.2)(typescript@5.9.3) '@typescript-eslint/parser': specifier: ^6.15.0 - version: 6.21.0(eslint@8.57.1)(typescript@5.9.3) + version: 6.21.0(eslint@8.57.1(supports-color@10.2.2))(supports-color@10.2.2)(typescript@5.9.3) eslint: specifier: ^8.56.0 - version: 8.57.1 + version: 8.57.1(supports-color@10.2.2) eslint-plugin-react: specifier: ^7.33.2 - version: 7.37.5(eslint@8.57.1) + version: 7.37.5(eslint@8.57.1(supports-color@10.2.2)) eslint-plugin-react-hooks: specifier: ^4.6.0 - version: 4.6.2(eslint@8.57.1) + version: 4.6.2(eslint@8.57.1(supports-color@10.2.2)) prettier: specifier: ^3.1.1 version: 3.6.2 @@ -56,6 +56,18 @@ importers: '@mapconductor/react-for-maplibre': specifier: workspace:* version: link:../../react-for-maplibre + '@mapconductor/react-geojson-layer': + specifier: workspace:* + version: link:../../react-geojson-layer + '@mapconductor/react-heatmap': + specifier: workspace:* + version: link:../../react-heatmap + '@mapconductor/react-icons': + specifier: workspace:* + version: link:../../react-icons + '@mapconductor/react-marker-clustering': + specifier: workspace:* + version: link:../../react-marker-clustering react: specifier: ^19.2.7 version: 19.2.7 @@ -104,19 +116,19 @@ importers: version: link:../../reactnative-for-maplibre '@react-native-picker/picker': specifier: 2.11.4 - version: 2.11.4(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3))(react@19.2.3) + version: 2.11.4(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) expo: specifier: ~57.0.1 - version: 57.0.1(@babel/core@7.29.7)(@expo/dom-webview@57.0.0)(react-dom@19.2.7(react@19.2.3))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)(typescript@6.0.3) + version: 57.0.1(@babel/core@7.29.7)(@expo/dom-webview@57.0.0)(react-dom@19.2.7(react@19.2.3))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2)(typescript@6.0.3) expo-status-bar: specifier: ~57.0.0 - version: 57.0.0(expo@57.0.1)(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3))(react@19.2.3) + version: 57.0.0(expo@57.0.1)(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) react: specifier: 19.2.3 version: 19.2.3 react-native: specifier: 0.86.0 - version: 0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3) + version: 0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)(supports-color@10.2.2) devDependencies: '@babel/core': specifier: ^7.25.0 @@ -129,10 +141,14 @@ importers: version: 6.0.3 js-sdk-core: + dependencies: + polygon-clipping: + specifier: ^0.15.7 + version: 0.15.7 devDependencies: tsup: specifier: ^8.0.1 - version: 8.5.1(postcss@8.5.15)(typescript@5.9.3)(yaml@2.9.0) + version: 8.5.1(postcss@8.5.15)(supports-color@10.2.2)(typescript@5.9.3)(yaml@2.9.0) typescript: specifier: ^5.3.3 version: 5.9.3 @@ -148,7 +164,7 @@ importers: version: 18.3.1 tsup: specifier: ^8.0.1 - version: 8.5.1(postcss@8.5.15)(typescript@5.9.3)(yaml@2.9.0) + version: 8.5.1(postcss@8.5.15)(supports-color@10.2.2)(typescript@5.9.3)(yaml@2.9.0) typescript: specifier: ^5.3.3 version: 5.9.3 @@ -167,7 +183,7 @@ importers: devDependencies: tsup: specifier: ^8.5.1 - version: 8.5.1(postcss@8.5.15)(typescript@5.9.3)(yaml@2.9.0) + version: 8.5.1(postcss@8.5.15)(supports-color@10.2.2)(typescript@5.9.3)(yaml@2.9.0) typescript: specifier: ^5.9.3 version: 5.9.3 @@ -192,7 +208,7 @@ importers: version: 19.2.7 tsup: specifier: ^8.5.1 - version: 8.5.1(postcss@8.5.15)(typescript@5.9.3)(yaml@2.9.0) + version: 8.5.1(postcss@8.5.15)(supports-color@10.2.2)(typescript@5.9.3)(yaml@2.9.0) typescript: specifier: ^5.9.3 version: 5.9.3 @@ -214,7 +230,86 @@ importers: version: 19.2.7 tsup: specifier: ^8.5.0 - version: 8.5.1(postcss@8.5.15)(typescript@5.9.3)(yaml@2.9.0) + version: 8.5.1(postcss@8.5.15)(supports-color@10.2.2)(typescript@5.9.3)(yaml@2.9.0) + typescript: + specifier: ^5.9.3 + version: 5.9.3 + + react-geojson-layer: + dependencies: + '@mapconductor/js-sdk-core': + specifier: workspace:* + version: link:../js-sdk-core + '@mapconductor/js-sdk-react': + specifier: workspace:* + version: link:../js-sdk-react + devDependencies: + '@types/react': + specifier: ^19.2.0 + version: 19.2.17 + react: + specifier: ^19.2.0 + version: 19.2.7 + tsup: + specifier: ^8.5.1 + version: 8.5.1(postcss@8.5.15)(supports-color@10.2.2)(typescript@5.9.3)(yaml@2.9.0) + typescript: + specifier: ^5.9.3 + version: 5.9.3 + + react-heatmap: + dependencies: + '@mapconductor/js-sdk-core': + specifier: workspace:* + version: link:../js-sdk-core + '@mapconductor/js-sdk-react': + specifier: workspace:* + version: link:../js-sdk-react + devDependencies: + '@types/react': + specifier: ^19.2.0 + version: 19.2.17 + react: + specifier: ^19.2.0 + version: 19.2.7 + tsup: + specifier: ^8.5.1 + version: 8.5.1(postcss@8.5.15)(supports-color@10.2.2)(typescript@5.9.3)(yaml@2.9.0) + typescript: + specifier: ^5.9.3 + version: 5.9.3 + + react-icons: + dependencies: + '@mapconductor/js-sdk-core': + specifier: workspace:* + version: link:../js-sdk-core + devDependencies: + tsup: + specifier: ^8.5.1 + version: 8.5.1(postcss@8.5.15)(supports-color@10.2.2)(typescript@5.9.3)(yaml@2.9.0) + typescript: + specifier: ^5.9.3 + version: 5.9.3 + + react-marker-clustering: + dependencies: + '@mapconductor/js-sdk-core': + specifier: workspace:* + version: link:../js-sdk-core + '@mapconductor/js-sdk-react': + specifier: workspace:* + version: link:../js-sdk-react + devDependencies: + '@types/react': + specifier: ^19.2.0 + version: 19.2.17 + react: + specifier: ^19.2.0 + version: 19.2.7 + tsup: + specifier: ^8.5.1 + version: 8.5.1(postcss@8.5.15)(supports-color@10.2.2)(typescript@5.9.3)(yaml@2.9.0) typescript: specifier: ^5.9.3 version: 5.9.3 @@ -236,7 +331,7 @@ importers: devDependencies: tsup: specifier: ^8.5.1 - version: 8.5.1(postcss@8.5.15)(typescript@5.9.3)(yaml@2.9.0) + version: 8.5.1(postcss@8.5.15)(supports-color@10.2.2)(typescript@5.9.3)(yaml@2.9.0) typescript: specifier: ^5.9.3 version: 5.9.3 @@ -258,7 +353,7 @@ importers: devDependencies: tsup: specifier: ^8.5.1 - version: 8.5.1(postcss@8.5.15)(typescript@5.9.3)(yaml@2.9.0) + version: 8.5.1(postcss@8.5.15)(supports-color@10.2.2)(typescript@5.9.3)(yaml@2.9.0) typescript: specifier: ^5.9.3 version: 5.9.3 @@ -1191,131 +1286,157 @@ packages: resolution: {integrity: sha512-T1dMEQhXA/jkJ/jyMIw9IovK8bSUq7A8kLIlvZTb/6YIVsp2zLavr4F3oyllHWo7eIVJRyE5n3tUjQJEbE1IuQ==} cpu: [arm] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm-gnueabihf@4.62.2': resolution: {integrity: sha512-n1GJHPOvpIfhi3TmrCeh6S6URt9BFCt0KQE3qvexyGCTAKpR4Lg+eWvNZEqu7epxwus/8ElT3hacYEucm49SZg==} cpu: [arm] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm-musleabihf@4.62.0': resolution: {integrity: sha512-2as0LgT7qQpyceQq6VUJYnumUMUrgGQCWIiDIN9DE0/tglsk6o66uCB4f3djRawAltvfCNLyZZrsqbPA6inCsA==} cpu: [arm] os: [linux] + libc: [musl] '@rollup/rollup-linux-arm-musleabihf@4.62.2': resolution: {integrity: sha512-JqgflS8wEB+UXV/vS1RpRbifGBeN4D5lz8D8oOFbFZw4vedvdOgCFAjfBmIMdW3yL10XpQQ0Ambepw6MXrhOnA==} cpu: [arm] os: [linux] + libc: [musl] '@rollup/rollup-linux-arm64-gnu@4.62.0': resolution: {integrity: sha512-bVURMg+6eNN9C/yc0aVjooZcwTTtYF4YW3xta5pP0//r3o1V8gXEHXWCndj47w/HhwsFroZrFhR+6uQP5T0n0g==} cpu: [arm64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm64-gnu@4.62.2': resolution: {integrity: sha512-wnFJkogWvN4jm/hQRF2UBaeUmk20j5+DmHvoyWii2b8HJDyvz1MF2OU/6ynXt2KR63rbZLWkFpoytpdc/yBuSA==} cpu: [arm64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm64-musl@4.62.0': resolution: {integrity: sha512-Ful8pM/2yYI83PViWdFdpZhdI8HJ5qsXANe5atypbHDf+KIBBDsZsbyy8hbXnULVvW9NsTh5DHwbcBftyLTfiw==} cpu: [arm64] os: [linux] + libc: [musl] '@rollup/rollup-linux-arm64-musl@4.62.2': resolution: {integrity: sha512-HVu2bp0zhvJ8xHEV9+UUs7S90VadmBSY3LcIMvozbPo4AuMGDWlz3ymHLHZPX4hR67TKTt8Qp5PJ5RBg/i+RMQ==} cpu: [arm64] os: [linux] + libc: [musl] '@rollup/rollup-linux-loong64-gnu@4.62.0': resolution: {integrity: sha512-9Gp/DgrkzfUBmNPVTyPTvay+4xEP7M/clXpj3efXBcm6uTIVIgDg4rqUpqKXvLEuFRVuEpSAOkhgNeecvaZ4Cg==} cpu: [loong64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-loong64-gnu@4.62.2': resolution: {integrity: sha512-mQqqAV8QaoSgr9I2fKDLY2BAVvmKjWoGiu/cSYQonsLvtqwEn1E4QYfnCOcp5zoEqNhsDYin1s6jx/VJmrxlZg==} cpu: [loong64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-loong64-musl@4.62.0': resolution: {integrity: sha512-m9tsJz54LUXkSYM8+8PG81B9IKK5r+2T0clMq4QrS16xFosufU7firBDAZEsDheDs7wTlP7h3++S7lMsU955HA==} cpu: [loong64] os: [linux] + libc: [musl] '@rollup/rollup-linux-loong64-musl@4.62.2': resolution: {integrity: sha512-IxKLoxCQ2IWi6bT2akyDUBGsOImDKB+sPp4EsTmwFQ/fMwpCKm8uLSSgP/Kx/QYUgKis6SEZ5/Nlhup0DIA0PQ==} cpu: [loong64] os: [linux] + libc: [musl] '@rollup/rollup-linux-ppc64-gnu@4.62.0': resolution: {integrity: sha512-3UvJ5PNVU16aJf6M3tFI24pWzAl2/ynfbyRN3ICyQajK1lSkrnVYNnLz3v04J32qKa0FczJc22zeToc0lr2A3w==} cpu: [ppc64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-ppc64-gnu@4.62.2': resolution: {integrity: sha512-Mk5ha2RQSgyFfmYYLkBpPnUk8D8FriBxesO1u9O75X0mHgXL1UQcH5Itl2lurWL2tj0RxV9b9tJgipac0hRY9A==} cpu: [ppc64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-ppc64-musl@4.62.0': resolution: {integrity: sha512-vRWUAbYLGHBZS6Q8Msb2sfnf1fvJf+47t8l/TwOerM2qArzy+IeNMTHrYLHXh95h8MoatPHI5hhSZNs+mGXKPg==} cpu: [ppc64] os: [linux] + libc: [musl] '@rollup/rollup-linux-ppc64-musl@4.62.2': resolution: {integrity: sha512-CjvEnqJL/0/TQ3TXX3OPIJ/kmBellrWd4heXUmHeJlTnmwjKpSJzoehLaL6Xk0ZnMHBu9dZuFADNOrtjF4v+2w==} cpu: [ppc64] os: [linux] + libc: [musl] '@rollup/rollup-linux-riscv64-gnu@4.62.0': resolution: {integrity: sha512-c00T5SYENHAt86cfW47URaP3Us5vLC/4QO7GYud1G5VNRffCwwCuBspwqYrriuJB+5m0WFzClCn9wed0FBjKvg==} cpu: [riscv64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-riscv64-gnu@4.62.2': resolution: {integrity: sha512-1SiZbzwdkaDURsew/tSOrooKiYy7EQGT6m8ufavAi9NEyQb/6VuIxFXAL1fqa4iZe3g4NbNk4P7J32z2tw5Mgg==} cpu: [riscv64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-riscv64-musl@4.62.0': resolution: {integrity: sha512-krrCDilhXOwFkSkO3Wm9I/f9H0L92XHHwy2fwxjukxIbh0dem8gZqOW5Y8BsHrpJv5qwlRBV+Wl4ZFyRWhUpwg==} cpu: [riscv64] os: [linux] + libc: [musl] '@rollup/rollup-linux-riscv64-musl@4.62.2': resolution: {integrity: sha512-nQts12zJ3NQRoE6uYljOH89v7szzLDvG2JD/vsX+vGXU8w/At1GowTZ5/7qeFQ8m7L55rpR8Okugnuo5bgjy2Q==} cpu: [riscv64] os: [linux] + libc: [musl] '@rollup/rollup-linux-s390x-gnu@4.62.0': resolution: {integrity: sha512-7pfYFSTc4/rUC/FtAI0Qp6QthDBCIi6/AuP1xYqFk5vanI6KnL5dWKP60OM/05LOsbwTmIcvr6eXC4CJuJ75IA==} cpu: [s390x] os: [linux] + libc: [glibc] '@rollup/rollup-linux-s390x-gnu@4.62.2': resolution: {integrity: sha512-E9/ll019jhPIJgpzfZoIkBGhcz+kKNgVWYRY0zr9srBdPPFVpvOKW8VaJKUbeK+eZXyQF9ltME+Kk6affeaPgg==} cpu: [s390x] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-gnu@4.62.0': resolution: {integrity: sha512-7SDIalKeIpG0Ifogbbdn58HmSotYMlf23K3dCJEmiVd9Fg36Vmni82iPQec27N3wY4Bvbxftkxz6vSx9OcouTg==} cpu: [x64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-gnu@4.62.2': resolution: {integrity: sha512-5BqxR/pshjey51iliyzTD5Xi3EN0aLmQ2lZ3lvefVV9c82BvrLo2/6OT55iifpWBufs6kdwWbuOKS841DrmK9A==} cpu: [x64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-musl@4.62.0': resolution: {integrity: sha512-eRZevouTH2i1HeAVLqJuLnt256krQkGY0TN6WsTmsIhuzbh457HuWDMakKwmi0Cjadux983CoSr8Lim2QhUIFw==} cpu: [x64] os: [linux] + libc: [musl] '@rollup/rollup-linux-x64-musl@4.62.2': resolution: {integrity: sha512-uNN83XxQrRAh/w0/pmAfibcwyb6YWt4gP+dpnQKPVJshAloQ785ii8CT8ZCIxkGg9opVsvAlGhFitSm6D1Jjpg==} cpu: [x64] os: [linux] + libc: [musl] '@rollup/rollup-openbsd-x64@4.62.0': resolution: {integrity: sha512-3oVS7FLGa4U1qcvao9ylGxrjXZyUQqR8UwxEcnUEyPX53O/C/mKDZegNXTdHCP+h3e6ta/f1EN38Yif1mmZHYg==} @@ -2729,24 +2850,28 @@ packages: engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] + libc: [glibc] lightningcss-linux-arm64-musl@1.32.0: resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] + libc: [musl] lightningcss-linux-x64-gnu@1.32.0: resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] + libc: [glibc] lightningcss-linux-x64-musl@1.32.0: resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] + libc: [musl] lightningcss-win32-arm64-msvc@1.32.0: resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==} @@ -3155,6 +3280,9 @@ packages: resolution: {integrity: sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==} engines: {node: '>=4.0.0'} + polygon-clipping@0.15.7: + resolution: {integrity: sha512-nhfdr83ECBg6xtqOAJab1tbksbBAOMUltN60bU+llHVOL0e5Onm1WpAXXWXVB39L8AJFssoIhEVuy/S90MmotA==} + possible-typed-array-names@1.1.0: resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} engines: {node: '>= 0.4'} @@ -3373,6 +3501,9 @@ packages: deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true + robust-predicates@3.0.3: + resolution: {integrity: sha512-NS3levdsRIUOmiJ8FZWCP7LG3QpJyrs/TE0Zpf1yvZu8cAJJ6QMW92H1c7kWpdIHo8RvmLxN/o2JXTKHp74lUA==} + rollup@4.62.0: resolution: {integrity: sha512-nc72Wgq62I7rtDV4izT5/aaS0zxy3kttkinf9586ApknY3jZO9NYsmtc24fUckA0X7Q2v+ML4a15pdUlV5V/jA==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} @@ -3514,6 +3645,10 @@ packages: resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==} engines: {node: '>= 12'} + splaytree@3.2.3: + resolution: {integrity: sha512-7OXrNWzy6CK+r7Ch9OLPBDTKfB6XlWHjX4P0RU5B3IgFuWPeYN0XtRtlexGRjgbQxpfaUve6jTAwBGWuGntz/w==} + engines: {node: '>=18.20 || >=20'} + stackframe@1.3.4: resolution: {integrity: sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==} @@ -4038,7 +4173,7 @@ snapshots: regexpu-core: 6.4.0 semver: 6.3.1 - '@babel/helper-define-polyfill-provider@0.6.8(@babel/core@7.29.7)': + '@babel/helper-define-polyfill-provider@0.6.8(@babel/core@7.29.7)(supports-color@10.2.2)': dependencies: '@babel/core': 7.29.7 '@babel/helper-compilation-targets': 7.29.7 @@ -4369,12 +4504,12 @@ snapshots: '@babel/helper-annotate-as-pure': 7.29.7 '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-runtime@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-runtime@7.29.7(@babel/core@7.29.7)(supports-color@10.2.2)': dependencies: '@babel/core': 7.29.7 '@babel/helper-module-imports': 7.29.7 '@babel/helper-plugin-utils': 7.29.7 - babel-plugin-polyfill-corejs2: 0.4.17(@babel/core@7.29.7) + babel-plugin-polyfill-corejs2: 0.4.17(@babel/core@7.29.7)(supports-color@10.2.2) babel-plugin-polyfill-corejs3: 0.13.0(@babel/core@7.29.7) babel-plugin-polyfill-regenerator: 0.6.8(@babel/core@7.29.7) semver: 6.3.1 @@ -4516,14 +4651,14 @@ snapshots: '@esbuild/win32-x64@0.27.7': optional: true - '@eslint-community/eslint-utils@4.9.0(eslint@8.57.1)': + '@eslint-community/eslint-utils@4.9.0(eslint@8.57.1(supports-color@10.2.2))': dependencies: - eslint: 8.57.1 + eslint: 8.57.1(supports-color@10.2.2) eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.2': {} - '@eslint/eslintrc@2.1.4': + '@eslint/eslintrc@2.1.4(supports-color@10.2.2)': dependencies: ajv: 6.12.6 debug: 4.4.3(supports-color@10.2.2) @@ -4539,7 +4674,7 @@ snapshots: '@eslint/js@8.57.1': {} - '@expo/cli@57.0.3(@expo/dom-webview@57.0.0)(expo-constants@57.0.2(expo@57.0.1)(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)))(expo-font@57.0.0(expo@57.0.1)(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3))(react@19.2.3))(expo@57.0.1)(react-dom@19.2.7(react@19.2.3))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)(typescript@6.0.3)': + '@expo/cli@57.0.3(@expo/dom-webview@57.0.0)(expo-constants@57.0.2(expo@57.0.1)(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)(supports-color@10.2.2)))(expo-font@57.0.0(expo@57.0.1)(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3))(expo@57.0.1)(react-dom@19.2.7(react@19.2.3))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2)(typescript@6.0.3)': dependencies: '@expo/code-signing-certificates': 0.0.6 '@expo/config': 57.0.1(typescript@6.0.3) @@ -4549,21 +4684,21 @@ snapshots: '@expo/image-utils': 0.11.0(typescript@6.0.3) '@expo/inline-modules': 0.1.1(typescript@6.0.3) '@expo/json-file': 11.0.0 - '@expo/log-box': 57.0.0(@expo/dom-webview@57.0.0)(expo@57.0.1)(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3))(react@19.2.3) + '@expo/log-box': 57.0.0(@expo/dom-webview@57.0.0)(expo@57.0.1)(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) '@expo/metro': 56.0.0 '@expo/metro-config': 57.0.2(expo@57.0.1)(typescript@6.0.3) - '@expo/metro-file-map': 57.0.0 + '@expo/metro-file-map': 57.0.0(supports-color@10.2.2) '@expo/osascript': 2.7.0 '@expo/package-manager': 1.13.0 '@expo/plist': 0.8.0 - '@expo/prebuild-config': 57.0.3(typescript@6.0.3) + '@expo/prebuild-config': 57.0.3(supports-color@10.2.2)(typescript@6.0.3) '@expo/require-utils': 57.0.0(typescript@6.0.3) - '@expo/router-server': 57.0.1(expo-constants@57.0.2(expo@57.0.1)(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)))(expo-font@57.0.0(expo@57.0.1)(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3))(react@19.2.3))(expo-server@57.0.0)(expo@57.0.1)(react-dom@19.2.7(react@19.2.3))(react@19.2.3) + '@expo/router-server': 57.0.1(expo-constants@57.0.2(expo@57.0.1)(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)(supports-color@10.2.2)))(expo-font@57.0.0(expo@57.0.1)(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3))(expo-server@57.0.0)(expo@57.0.1)(react-dom@19.2.7(react@19.2.3))(react@19.2.3)(supports-color@10.2.2) '@expo/schema-utils': 57.0.0 '@expo/spawn-async': 1.8.0 '@expo/ws-tunnel': 2.0.0(ws@8.21.0) '@expo/xcpretty': 4.4.4 - '@react-native/dev-middleware': 0.86.0 + '@react-native/dev-middleware': 0.86.0(supports-color@10.2.2) accepts: 1.3.8 arg: 5.0.2 bplist-creator: 0.1.0 @@ -4574,7 +4709,7 @@ snapshots: connect: 3.7.0 debug: 4.4.3(supports-color@10.2.2) dnssd-advertise: 1.1.6 - expo: 57.0.1(@babel/core@7.29.7)(@expo/dom-webview@57.0.0)(react-dom@19.2.7(react@19.2.3))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)(typescript@6.0.3) + expo: 57.0.1(@babel/core@7.29.7)(@expo/dom-webview@57.0.0)(react-dom@19.2.7(react@19.2.3))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2)(typescript@6.0.3) expo-server: 57.0.0 fetch-nodeshim: 0.4.10 getenv: 2.0.0 @@ -4600,7 +4735,7 @@ snapshots: ws: 8.21.0 zod: 3.25.76 optionalDependencies: - react-native: 0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3) + react-native: 0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)(supports-color@10.2.2) transitivePeerDependencies: - '@expo/dom-webview' - '@expo/metro-runtime' @@ -4662,18 +4797,18 @@ snapshots: transitivePeerDependencies: - supports-color - '@expo/devtools@57.0.0(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)': + '@expo/devtools@57.0.0(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)': dependencies: chalk: 4.1.2 optionalDependencies: react: 19.2.3 - react-native: 0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3) + react-native: 0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)(supports-color@10.2.2) - '@expo/dom-webview@57.0.0(expo@57.0.1)(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)': + '@expo/dom-webview@57.0.0(expo@57.0.1)(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)': dependencies: - expo: 57.0.1(@babel/core@7.29.7)(@expo/dom-webview@57.0.0)(react-dom@19.2.7(react@19.2.3))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)(typescript@6.0.3) + expo: 57.0.1(@babel/core@7.29.7)(@expo/dom-webview@57.0.0)(react-dom@19.2.7(react@19.2.3))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2)(typescript@6.0.3) react: 19.2.3 - react-native: 0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3) + react-native: 0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)(supports-color@10.2.2) '@expo/env@2.4.0': dependencies: @@ -4685,7 +4820,7 @@ snapshots: '@expo/expo-modules-macros-plugin@0.3.0': {} - '@expo/fingerprint@0.20.1': + '@expo/fingerprint@0.20.1(supports-color@10.2.2)': dependencies: '@expo/env': 2.4.0 '@expo/spawn-async': 1.8.0 @@ -4734,13 +4869,13 @@ snapshots: - supports-color - typescript - '@expo/log-box@57.0.0(@expo/dom-webview@57.0.0)(expo@57.0.1)(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)': + '@expo/log-box@57.0.0(@expo/dom-webview@57.0.0)(expo@57.0.1)(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)': dependencies: - '@expo/dom-webview': 57.0.0(expo@57.0.1)(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3))(react@19.2.3) + '@expo/dom-webview': 57.0.0(expo@57.0.1)(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) anser: 1.4.10 - expo: 57.0.1(@babel/core@7.29.7)(@expo/dom-webview@57.0.0)(react-dom@19.2.7(react@19.2.3))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)(typescript@6.0.3) + expo: 57.0.1(@babel/core@7.29.7)(@expo/dom-webview@57.0.0)(react-dom@19.2.7(react@19.2.3))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2)(typescript@6.0.3) react: 19.2.3 - react-native: 0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3) + react-native: 0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)(supports-color@10.2.2) stacktrace-parser: 0.1.11 '@expo/metro-config@57.0.2(expo@57.0.1)(typescript@6.0.3)': @@ -4769,14 +4904,14 @@ snapshots: postcss: 8.5.15 resolve-from: 5.0.0 optionalDependencies: - expo: 57.0.1(@babel/core@7.29.7)(@expo/dom-webview@57.0.0)(react-dom@19.2.7(react@19.2.3))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)(typescript@6.0.3) + expo: 57.0.1(@babel/core@7.29.7)(@expo/dom-webview@57.0.0)(react-dom@19.2.7(react@19.2.3))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2)(typescript@6.0.3) transitivePeerDependencies: - bufferutil - supports-color - typescript - utf-8-validate - '@expo/metro-file-map@57.0.0': + '@expo/metro-file-map@57.0.0(supports-color@10.2.2)': dependencies: debug: 4.4.3(supports-color@10.2.2) fb-watchman: 2.0.2 @@ -4827,7 +4962,7 @@ snapshots: base64-js: 1.5.1 xmlbuilder: 15.1.1 - '@expo/prebuild-config@57.0.3(typescript@6.0.3)': + '@expo/prebuild-config@57.0.3(supports-color@10.2.2)(typescript@6.0.3)': dependencies: '@expo/config': 57.0.1(typescript@6.0.3) '@expo/config-plugins': 57.0.1(typescript@6.0.3) @@ -4853,12 +4988,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@expo/router-server@57.0.1(expo-constants@57.0.2(expo@57.0.1)(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)))(expo-font@57.0.0(expo@57.0.1)(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3))(react@19.2.3))(expo-server@57.0.0)(expo@57.0.1)(react-dom@19.2.7(react@19.2.3))(react@19.2.3)': + '@expo/router-server@57.0.1(expo-constants@57.0.2(expo@57.0.1)(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)(supports-color@10.2.2)))(expo-font@57.0.0(expo@57.0.1)(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3))(expo-server@57.0.0)(expo@57.0.1)(react-dom@19.2.7(react@19.2.3))(react@19.2.3)(supports-color@10.2.2)': dependencies: debug: 4.4.3(supports-color@10.2.2) - expo: 57.0.1(@babel/core@7.29.7)(@expo/dom-webview@57.0.0)(react-dom@19.2.7(react@19.2.3))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)(typescript@6.0.3) - expo-constants: 57.0.2(expo@57.0.1)(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)) - expo-font: 57.0.0(expo@57.0.1)(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3))(react@19.2.3) + expo: 57.0.1(@babel/core@7.29.7)(@expo/dom-webview@57.0.0)(react-dom@19.2.7(react@19.2.3))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2)(typescript@6.0.3) + expo-constants: 57.0.2(expo@57.0.1)(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)(supports-color@10.2.2)) + expo-font: 57.0.0(expo@57.0.1)(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) expo-server: 57.0.0 react: 19.2.3 optionalDependencies: @@ -4890,7 +5025,7 @@ snapshots: dependencies: '@types/google.maps': 3.65.2 - '@humanwhocodes/config-array@0.13.0': + '@humanwhocodes/config-array@0.13.0(supports-color@10.2.2)': dependencies: '@humanwhocodes/object-schema': 2.0.3 debug: 4.4.3(supports-color@10.2.2) @@ -5003,10 +5138,10 @@ snapshots: dependencies: playwright: 1.61.0 - '@react-native-picker/picker@2.11.4(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)': + '@react-native-picker/picker@2.11.4(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)': dependencies: react: 19.2.3 - react-native: 0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3) + react-native: 0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)(supports-color@10.2.2) '@react-native/assets-registry@0.86.0': {} @@ -5028,9 +5163,9 @@ snapshots: tinyglobby: 0.2.17 yargs: 17.7.3 - '@react-native/community-cli-plugin@0.86.0': + '@react-native/community-cli-plugin@0.86.0(supports-color@10.2.2)': dependencies: - '@react-native/dev-middleware': 0.86.0 + '@react-native/dev-middleware': 0.86.0(supports-color@10.2.2) debug: 4.4.3(supports-color@10.2.2) invariant: 2.2.4 metro: 0.84.4 @@ -5044,7 +5179,7 @@ snapshots: '@react-native/debugger-frontend@0.86.0': {} - '@react-native/debugger-shell@0.86.0': + '@react-native/debugger-shell@0.86.0(supports-color@10.2.2)': dependencies: cross-spawn: 7.0.6 debug: 4.4.3(supports-color@10.2.2) @@ -5052,11 +5187,11 @@ snapshots: transitivePeerDependencies: - supports-color - '@react-native/dev-middleware@0.86.0': + '@react-native/dev-middleware@0.86.0(supports-color@10.2.2)': dependencies: '@isaacs/ttlcache': 1.4.1 '@react-native/debugger-frontend': 0.86.0 - '@react-native/debugger-shell': 0.86.0 + '@react-native/debugger-shell': 0.86.0(supports-color@10.2.2) chrome-launcher: 0.15.2 chromium-edge-launcher: 0.3.0 connect: 3.7.0 @@ -5077,12 +5212,12 @@ snapshots: '@react-native/normalize-colors@0.86.0': {} - '@react-native/virtualized-lists@0.86.0(@types/react@19.2.17)(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)': + '@react-native/virtualized-lists@0.86.0(@types/react@19.2.17)(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)': dependencies: invariant: 2.2.4 nullthrows: 1.1.1 react: 19.2.3 - react-native: 0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3) + react-native: 0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)(supports-color@10.2.2) optionalDependencies: '@types/react': 19.2.17 @@ -5336,16 +5471,16 @@ snapshots: dependencies: '@types/yargs-parser': 21.0.3 - '@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3)': + '@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1(supports-color@10.2.2))(supports-color@10.2.2)(typescript@5.9.3))(eslint@8.57.1(supports-color@10.2.2))(supports-color@10.2.2)(typescript@5.9.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 6.21.0(eslint@8.57.1)(typescript@5.9.3) + '@typescript-eslint/parser': 6.21.0(eslint@8.57.1(supports-color@10.2.2))(supports-color@10.2.2)(typescript@5.9.3) '@typescript-eslint/scope-manager': 6.21.0 - '@typescript-eslint/type-utils': 6.21.0(eslint@8.57.1)(typescript@5.9.3) - '@typescript-eslint/utils': 6.21.0(eslint@8.57.1)(typescript@5.9.3) + '@typescript-eslint/type-utils': 6.21.0(eslint@8.57.1(supports-color@10.2.2))(supports-color@10.2.2)(typescript@5.9.3) + '@typescript-eslint/utils': 6.21.0(eslint@8.57.1(supports-color@10.2.2))(typescript@5.9.3) '@typescript-eslint/visitor-keys': 6.21.0 debug: 4.4.3(supports-color@10.2.2) - eslint: 8.57.1 + eslint: 8.57.1(supports-color@10.2.2) graphemer: 1.4.0 ignore: 5.3.2 natural-compare: 1.4.0 @@ -5356,14 +5491,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.9.3)': + '@typescript-eslint/parser@6.21.0(eslint@8.57.1(supports-color@10.2.2))(supports-color@10.2.2)(typescript@5.9.3)': dependencies: '@typescript-eslint/scope-manager': 6.21.0 '@typescript-eslint/types': 6.21.0 - '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.9.3) + '@typescript-eslint/typescript-estree': 6.21.0(supports-color@10.2.2)(typescript@5.9.3) '@typescript-eslint/visitor-keys': 6.21.0 debug: 4.4.3(supports-color@10.2.2) - eslint: 8.57.1 + eslint: 8.57.1(supports-color@10.2.2) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: @@ -5374,12 +5509,12 @@ snapshots: '@typescript-eslint/types': 6.21.0 '@typescript-eslint/visitor-keys': 6.21.0 - '@typescript-eslint/type-utils@6.21.0(eslint@8.57.1)(typescript@5.9.3)': + '@typescript-eslint/type-utils@6.21.0(eslint@8.57.1(supports-color@10.2.2))(supports-color@10.2.2)(typescript@5.9.3)': dependencies: - '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.9.3) - '@typescript-eslint/utils': 6.21.0(eslint@8.57.1)(typescript@5.9.3) + '@typescript-eslint/typescript-estree': 6.21.0(supports-color@10.2.2)(typescript@5.9.3) + '@typescript-eslint/utils': 6.21.0(eslint@8.57.1(supports-color@10.2.2))(typescript@5.9.3) debug: 4.4.3(supports-color@10.2.2) - eslint: 8.57.1 + eslint: 8.57.1(supports-color@10.2.2) ts-api-utils: 1.4.3(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 @@ -5388,7 +5523,7 @@ snapshots: '@typescript-eslint/types@6.21.0': {} - '@typescript-eslint/typescript-estree@6.21.0(typescript@5.9.3)': + '@typescript-eslint/typescript-estree@6.21.0(supports-color@10.2.2)(typescript@5.9.3)': dependencies: '@typescript-eslint/types': 6.21.0 '@typescript-eslint/visitor-keys': 6.21.0 @@ -5403,15 +5538,15 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@6.21.0(eslint@8.57.1)(typescript@5.9.3)': + '@typescript-eslint/utils@6.21.0(eslint@8.57.1(supports-color@10.2.2))(typescript@5.9.3)': dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@8.57.1) + '@eslint-community/eslint-utils': 4.9.0(eslint@8.57.1(supports-color@10.2.2)) '@types/json-schema': 7.0.15 '@types/semver': 7.7.1 '@typescript-eslint/scope-manager': 6.21.0 '@typescript-eslint/types': 6.21.0 - '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.9.3) - eslint: 8.57.1 + '@typescript-eslint/typescript-estree': 6.21.0(supports-color@10.2.2)(typescript@5.9.3) + eslint: 8.57.1(supports-color@10.2.2) semver: 7.7.3 transitivePeerDependencies: - supports-color @@ -5568,11 +5703,11 @@ snapshots: dependencies: possible-typed-array-names: 1.1.0 - babel-plugin-polyfill-corejs2@0.4.17(@babel/core@7.29.7): + babel-plugin-polyfill-corejs2@0.4.17(@babel/core@7.29.7)(supports-color@10.2.2): dependencies: '@babel/compat-data': 7.29.7 '@babel/core': 7.29.7 - '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.7) + '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.7)(supports-color@10.2.2) semver: 6.3.1 transitivePeerDependencies: - supports-color @@ -5580,7 +5715,7 @@ snapshots: babel-plugin-polyfill-corejs3@0.13.0(@babel/core@7.29.7): dependencies: '@babel/core': 7.29.7 - '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.7) + '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.7)(supports-color@10.2.2) core-js-compat: 3.49.0 transitivePeerDependencies: - supports-color @@ -5588,7 +5723,7 @@ snapshots: babel-plugin-polyfill-regenerator@0.6.8(@babel/core@7.29.7): dependencies: '@babel/core': 7.29.7 - '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.7) + '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.7)(supports-color@10.2.2) transitivePeerDependencies: - supports-color @@ -5612,7 +5747,7 @@ snapshots: transitivePeerDependencies: - '@babel/core' - babel-preset-expo@57.0.1(@babel/core@7.29.7)(@babel/runtime@7.29.7)(expo@57.0.1)(react-refresh@0.14.2): + babel-preset-expo@57.0.1(@babel/core@7.29.7)(@babel/runtime@7.29.7)(expo@57.0.1)(react-refresh@0.14.2)(supports-color@10.2.2): dependencies: '@babel/generator': 7.29.7 '@babel/helper-module-imports': 7.29.7 @@ -5646,7 +5781,7 @@ snapshots: '@babel/plugin-transform-react-jsx': 7.29.7(@babel/core@7.29.7) '@babel/plugin-transform-react-jsx-development': 7.29.7(@babel/core@7.29.7) '@babel/plugin-transform-react-pure-annotations': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-runtime': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-runtime': 7.29.7(@babel/core@7.29.7)(supports-color@10.2.2) '@babel/plugin-transform-typescript': 7.29.7(@babel/core@7.29.7) '@babel/plugin-transform-unicode-regex': 7.29.7(@babel/core@7.29.7) '@babel/preset-typescript': 7.29.7(@babel/core@7.29.7) @@ -5659,7 +5794,7 @@ snapshots: react-refresh: 0.14.2 optionalDependencies: '@babel/runtime': 7.29.7 - expo: 57.0.1(@babel/core@7.29.7)(@expo/dom-webview@57.0.0)(react-dom@19.2.7(react@19.2.3))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)(typescript@6.0.3) + expo: 57.0.1(@babel/core@7.29.7)(@expo/dom-webview@57.0.0)(react-dom@19.2.7(react@19.2.3))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2)(typescript@6.0.3) transitivePeerDependencies: - '@babel/core' - supports-color @@ -6106,11 +6241,11 @@ snapshots: escape-string-regexp@4.0.0: {} - eslint-plugin-react-hooks@4.6.2(eslint@8.57.1): + eslint-plugin-react-hooks@4.6.2(eslint@8.57.1(supports-color@10.2.2)): dependencies: - eslint: 8.57.1 + eslint: 8.57.1(supports-color@10.2.2) - eslint-plugin-react@7.37.5(eslint@8.57.1): + eslint-plugin-react@7.37.5(eslint@8.57.1(supports-color@10.2.2)): dependencies: array-includes: 3.1.9 array.prototype.findlast: 1.2.5 @@ -6118,7 +6253,7 @@ snapshots: array.prototype.tosorted: 1.1.4 doctrine: 2.1.0 es-iterator-helpers: 1.2.1 - eslint: 8.57.1 + eslint: 8.57.1(supports-color@10.2.2) estraverse: 5.3.0 hasown: 2.0.2 jsx-ast-utils: 3.3.5 @@ -6139,13 +6274,13 @@ snapshots: eslint-visitor-keys@3.4.3: {} - eslint@8.57.1: + eslint@8.57.1(supports-color@10.2.2): dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@8.57.1) + '@eslint-community/eslint-utils': 4.9.0(eslint@8.57.1(supports-color@10.2.2)) '@eslint-community/regexpp': 4.12.2 - '@eslint/eslintrc': 2.1.4 + '@eslint/eslintrc': 2.1.4(supports-color@10.2.2) '@eslint/js': 8.57.1 - '@humanwhocodes/config-array': 0.13.0 + '@humanwhocodes/config-array': 0.13.0(supports-color@10.2.2) '@humanwhocodes/module-importer': 1.0.1 '@nodelib/fs.walk': 1.2.8 '@ungap/structured-clone': 1.3.0 @@ -6204,40 +6339,40 @@ snapshots: event-target-shim@5.0.1: {} - expo-asset@57.0.2(expo@57.0.1)(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)(typescript@6.0.3): + expo-asset@57.0.2(expo@57.0.1)(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(typescript@6.0.3): dependencies: '@expo/image-utils': 0.11.0(typescript@6.0.3) - expo: 57.0.1(@babel/core@7.29.7)(@expo/dom-webview@57.0.0)(react-dom@19.2.7(react@19.2.3))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)(typescript@6.0.3) - expo-constants: 57.0.2(expo@57.0.1)(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)) + expo: 57.0.1(@babel/core@7.29.7)(@expo/dom-webview@57.0.0)(react-dom@19.2.7(react@19.2.3))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2)(typescript@6.0.3) + expo-constants: 57.0.2(expo@57.0.1)(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)(supports-color@10.2.2)) react: 19.2.3 - react-native: 0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3) + react-native: 0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)(supports-color@10.2.2) transitivePeerDependencies: - supports-color - typescript - expo-constants@57.0.2(expo@57.0.1)(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)): + expo-constants@57.0.2(expo@57.0.1)(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)(supports-color@10.2.2)): dependencies: '@expo/env': 2.4.0 - expo: 57.0.1(@babel/core@7.29.7)(@expo/dom-webview@57.0.0)(react-dom@19.2.7(react@19.2.3))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)(typescript@6.0.3) - react-native: 0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3) + expo: 57.0.1(@babel/core@7.29.7)(@expo/dom-webview@57.0.0)(react-dom@19.2.7(react@19.2.3))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2)(typescript@6.0.3) + react-native: 0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)(supports-color@10.2.2) transitivePeerDependencies: - supports-color - expo-file-system@57.0.0(expo@57.0.1)(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)): + expo-file-system@57.0.0(expo@57.0.1)(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)(supports-color@10.2.2)): dependencies: - expo: 57.0.1(@babel/core@7.29.7)(@expo/dom-webview@57.0.0)(react-dom@19.2.7(react@19.2.3))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)(typescript@6.0.3) - react-native: 0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3) + expo: 57.0.1(@babel/core@7.29.7)(@expo/dom-webview@57.0.0)(react-dom@19.2.7(react@19.2.3))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2)(typescript@6.0.3) + react-native: 0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)(supports-color@10.2.2) - expo-font@57.0.0(expo@57.0.1)(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3))(react@19.2.3): + expo-font@57.0.0(expo@57.0.1)(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3): dependencies: - expo: 57.0.1(@babel/core@7.29.7)(@expo/dom-webview@57.0.0)(react-dom@19.2.7(react@19.2.3))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)(typescript@6.0.3) + expo: 57.0.1(@babel/core@7.29.7)(@expo/dom-webview@57.0.0)(react-dom@19.2.7(react@19.2.3))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2)(typescript@6.0.3) fontfaceobserver: 2.3.0 react: 19.2.3 - react-native: 0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3) + react-native: 0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)(supports-color@10.2.2) expo-keep-awake@57.0.0(expo@57.0.1)(react@19.2.3): dependencies: - expo: 57.0.1(@babel/core@7.29.7)(@expo/dom-webview@57.0.0)(react-dom@19.2.7(react@19.2.3))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)(typescript@6.0.3) + expo: 57.0.1(@babel/core@7.29.7)(@expo/dom-webview@57.0.0)(react-dom@19.2.7(react@19.2.3))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2)(typescript@6.0.3) react: 19.2.3 expo-modules-autolinking@57.0.3(typescript@6.0.3): @@ -6250,54 +6385,54 @@ snapshots: - supports-color - typescript - expo-modules-core@57.0.1(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3))(react@19.2.3): + expo-modules-core@57.0.1(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3): dependencies: '@expo/expo-modules-macros-plugin': 0.3.0 - expo-modules-jsi: 57.0.0(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)) + expo-modules-jsi: 57.0.0(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)(supports-color@10.2.2)) invariant: 2.2.4 react: 19.2.3 - react-native: 0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3) + react-native: 0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)(supports-color@10.2.2) - expo-modules-jsi@57.0.0(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)): + expo-modules-jsi@57.0.0(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)(supports-color@10.2.2)): dependencies: - react-native: 0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3) + react-native: 0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)(supports-color@10.2.2) expo-server@57.0.0: {} - expo-status-bar@57.0.0(expo@57.0.1)(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3))(react@19.2.3): + expo-status-bar@57.0.0(expo@57.0.1)(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3): dependencies: - expo: 57.0.1(@babel/core@7.29.7)(@expo/dom-webview@57.0.0)(react-dom@19.2.7(react@19.2.3))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)(typescript@6.0.3) + expo: 57.0.1(@babel/core@7.29.7)(@expo/dom-webview@57.0.0)(react-dom@19.2.7(react@19.2.3))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2)(typescript@6.0.3) react: 19.2.3 - react-native: 0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3) + react-native: 0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)(supports-color@10.2.2) - expo@57.0.1(@babel/core@7.29.7)(@expo/dom-webview@57.0.0)(react-dom@19.2.7(react@19.2.3))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)(typescript@6.0.3): + expo@57.0.1(@babel/core@7.29.7)(@expo/dom-webview@57.0.0)(react-dom@19.2.7(react@19.2.3))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2)(typescript@6.0.3): dependencies: '@babel/runtime': 7.29.7 - '@expo/cli': 57.0.3(@expo/dom-webview@57.0.0)(expo-constants@57.0.2(expo@57.0.1)(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)))(expo-font@57.0.0(expo@57.0.1)(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3))(react@19.2.3))(expo@57.0.1)(react-dom@19.2.7(react@19.2.3))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)(typescript@6.0.3) + '@expo/cli': 57.0.3(@expo/dom-webview@57.0.0)(expo-constants@57.0.2(expo@57.0.1)(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)(supports-color@10.2.2)))(expo-font@57.0.0(expo@57.0.1)(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3))(expo@57.0.1)(react-dom@19.2.7(react@19.2.3))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(supports-color@10.2.2)(typescript@6.0.3) '@expo/config': 57.0.1(typescript@6.0.3) '@expo/config-plugins': 57.0.1(typescript@6.0.3) - '@expo/devtools': 57.0.0(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3))(react@19.2.3) - '@expo/fingerprint': 0.20.1 + '@expo/devtools': 57.0.0(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) + '@expo/fingerprint': 0.20.1(supports-color@10.2.2) '@expo/local-build-cache-provider': 57.0.1(typescript@6.0.3) - '@expo/log-box': 57.0.0(@expo/dom-webview@57.0.0)(expo@57.0.1)(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3))(react@19.2.3) + '@expo/log-box': 57.0.0(@expo/dom-webview@57.0.0)(expo@57.0.1)(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) '@expo/metro': 56.0.0 '@expo/metro-config': 57.0.2(expo@57.0.1)(typescript@6.0.3) '@ungap/structured-clone': 1.3.0 - babel-preset-expo: 57.0.1(@babel/core@7.29.7)(@babel/runtime@7.29.7)(expo@57.0.1)(react-refresh@0.14.2) - expo-asset: 57.0.2(expo@57.0.1)(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)(typescript@6.0.3) - expo-constants: 57.0.2(expo@57.0.1)(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)) - expo-file-system: 57.0.0(expo@57.0.1)(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)) - expo-font: 57.0.0(expo@57.0.1)(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3))(react@19.2.3) + babel-preset-expo: 57.0.1(@babel/core@7.29.7)(@babel/runtime@7.29.7)(expo@57.0.1)(react-refresh@0.14.2)(supports-color@10.2.2) + expo-asset: 57.0.2(expo@57.0.1)(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3)(typescript@6.0.3) + expo-constants: 57.0.2(expo@57.0.1)(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)(supports-color@10.2.2)) + expo-file-system: 57.0.0(expo@57.0.1)(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)(supports-color@10.2.2)) + expo-font: 57.0.0(expo@57.0.1)(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) expo-keep-awake: 57.0.0(expo@57.0.1)(react@19.2.3) expo-modules-autolinking: 57.0.3(typescript@6.0.3) - expo-modules-core: 57.0.1(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3))(react@19.2.3) + expo-modules-core: 57.0.1(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) pretty-format: 29.7.0 react: 19.2.3 - react-native: 0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3) + react-native: 0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)(supports-color@10.2.2) react-refresh: 0.14.2 whatwg-url-minimum: 0.1.2 optionalDependencies: - '@expo/dom-webview': 57.0.0(expo@57.0.1)(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3))(react@19.2.3) + '@expo/dom-webview': 57.0.0(expo@57.0.1)(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) react-dom: 19.2.7(react@19.2.3) transitivePeerDependencies: - '@babel/core' @@ -7344,6 +7479,11 @@ snapshots: pngjs@3.4.0: {} + polygon-clipping@0.15.7: + dependencies: + robust-predicates: 3.0.3 + splaytree: 3.2.3 + possible-typed-array-names@1.1.0: {} postcss-load-config@6.0.1(postcss@8.5.15)(yaml@2.9.0): @@ -7427,15 +7567,15 @@ snapshots: react-is@18.3.1: {} - react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3): + react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)(supports-color@10.2.2): dependencies: '@react-native/assets-registry': 0.86.0 '@react-native/codegen': 0.86.0(@babel/core@7.29.7) - '@react-native/community-cli-plugin': 0.86.0 + '@react-native/community-cli-plugin': 0.86.0(supports-color@10.2.2) '@react-native/gradle-plugin': 0.86.0 '@react-native/js-polyfills': 0.86.0 '@react-native/normalize-colors': 0.86.0 - '@react-native/virtualized-lists': 0.86.0(@types/react@19.2.17)(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3))(react@19.2.3) + '@react-native/virtualized-lists': 0.86.0(@types/react@19.2.17)(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@19.2.3)(supports-color@10.2.2))(react@19.2.3) abort-controller: 3.0.0 anser: 1.4.10 ansi-regex: 5.0.1 @@ -7476,7 +7616,7 @@ snapshots: dependencies: '@react-native/assets-registry': 0.86.0 '@react-native/codegen': 0.86.0(@babel/core@7.29.7) - '@react-native/community-cli-plugin': 0.86.0 + '@react-native/community-cli-plugin': 0.86.0(supports-color@10.2.2) '@react-native/gradle-plugin': 0.86.0 '@react-native/js-polyfills': 0.86.0 '@react-native/normalize-colors': 0.86.0 @@ -7624,6 +7764,8 @@ snapshots: dependencies: glob: 7.2.3 + robust-predicates@3.0.3: {} + rollup@4.62.0: dependencies: '@types/estree': 1.0.9 @@ -7839,6 +7981,8 @@ snapshots: source-map@0.7.6: {} + splaytree@3.2.3: {} + stackframe@1.3.4: {} stacktrace-parser@0.1.11: @@ -8022,7 +8166,7 @@ snapshots: v8-compile-cache-lib: 3.0.1 yn: 3.1.1 - tsup@8.5.1(postcss@8.5.15)(typescript@5.9.3)(yaml@2.9.0): + tsup@8.5.1(postcss@8.5.15)(supports-color@10.2.2)(typescript@5.9.3)(yaml@2.9.0): dependencies: bundle-require: 5.1.0(esbuild@0.27.7) cac: 6.7.14 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 37f53e2..7e6dd84 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -2,8 +2,14 @@ packages: - 'js-sdk-core' - 'js-sdk-react' - 'js-sdk-reactnative' + - 'react-icons' - 'react-for-googlemaps' - 'react-for-maplibre' + - 'react-geojson-layer' + - 'react-heatmap' + - 'react-marker-clustering' - 'reactnative-for-googlemaps' - 'reactnative-for-maplibre' - 'examples/*' +allowBuilds: + esbuild: true diff --git a/react-for-googlemaps b/react-for-googlemaps index 8f1851e..6b67cca 160000 --- a/react-for-googlemaps +++ b/react-for-googlemaps @@ -1 +1 @@ -Subproject commit 8f1851e4716cff980f2ed701269b250abdab469d +Subproject commit 6b67ccafcaaee68eb973df0d87232678c657f62e diff --git a/react-for-maplibre b/react-for-maplibre index 2cc14f9..9547ddb 160000 --- a/react-for-maplibre +++ b/react-for-maplibre @@ -1 +1 @@ -Subproject commit 2cc14f91de6a4de5aa77117f12c8145c655ae579 +Subproject commit 9547ddbcbf2c9e45a380b5b3ee1f3c7983d01126 diff --git a/react-geojson-layer b/react-geojson-layer new file mode 160000 index 0000000..957ff9b --- /dev/null +++ b/react-geojson-layer @@ -0,0 +1 @@ +Subproject commit 957ff9b8063972804b2c25e8578bc51f4295082d diff --git a/react-heatmap b/react-heatmap new file mode 160000 index 0000000..7c6bd04 --- /dev/null +++ b/react-heatmap @@ -0,0 +1 @@ +Subproject commit 7c6bd0496db8990258b95d3297ceab04b294e325 diff --git a/react-icons b/react-icons new file mode 160000 index 0000000..2bffc55 --- /dev/null +++ b/react-icons @@ -0,0 +1 @@ +Subproject commit 2bffc55b9493f082993e88985fbfb61b959c0887 diff --git a/react-marker-clustering b/react-marker-clustering new file mode 160000 index 0000000..092e3b7 --- /dev/null +++ b/react-marker-clustering @@ -0,0 +1 @@ +Subproject commit 092e3b7e1d4e34ada14dd834994098e98d60c660 diff --git a/scripts/commit-all.sh b/scripts/commit-all.sh index 11b4aa3..63825e5 100755 --- a/scripts/commit-all.sh +++ b/scripts/commit-all.sh @@ -33,7 +33,13 @@ git-claude-commit() { if [ -n "$msg" ]; then echo -e "\ncommit message:\n$msg\n" git commit -m "$msg" - git push + local branch + branch=$(git branch --show-current) + if [ -z "$branch" ]; then + echo "Error: Failed to detect current branch." + return $FALSE + fi + git push -u origin "$branch" else echo "Error: Failed to generate commit message." return $FALSE