From 39c96fa12b6fe8aa0877bdc3aacc9e427636fe01 Mon Sep 17 00:00:00 2001 From: Masashi Katsumata Date: Thu, 22 Oct 2026 18:53:56 -0700 Subject: [PATCH] Pick the marker drawn under the tap, label included, and size the info bubble by the icon's real extent --- src/ArcGISMapView.web.tsx | 6 +-- src/marker/ArcGISMarkerController.ts | 70 ++++++++++++++-------------- 2 files changed, 37 insertions(+), 39 deletions(-) diff --git a/src/ArcGISMapView.web.tsx b/src/ArcGISMapView.web.tsx index ba23749..3213e95 100644 --- a/src/ArcGISMapView.web.tsx +++ b/src/ArcGISMapView.web.tsx @@ -1,5 +1,5 @@ import { useEffect, useRef, useState } from 'react'; -import { mapViewStateInternal } from '@mapconductor/js-sdk-core'; +import { mapViewStateInternal, markerIconExtentPx } from '@mapconductor/js-sdk-core'; import '@arcgis/core/assets/esri/themes/light/main.css'; import Attribution from '@arcgis/core/widgets/Attribution'; import Zoom from '@arcgis/core/widgets/Zoom'; @@ -386,12 +386,12 @@ export function ArcGISMapView({ const screenOffset = holder.toScreenOffset(pos); if (!screenOffset) return null; const icon = entry.icon; - const iconPixelSize = icon ? icon.iconSize * icon.scale : 0; + const iconPixelSize = markerIconExtentPx(icon); return ( | null { - const hit = this.findHitAtScreen(screen, () => true); - if (hit) return hit; - if (!this.tileRenderer) return null; - const position = this.renderer.holder.fromScreenOffsetSync(screen); - return position ? this.findTiled(position, zoom) : null; + const regular = this.findHitAtScreen(screen, () => true); + if (regular?.exact) return regular.item; + // タイル化マーカーも同じ規則で比べる(指の下に描かれていれば、通常マーカーの近接より勝つ)。 + const position = this.tileRenderer ? this.renderer.holder.fromScreenOffsetSync(screen) : null; + const tiledHit = position ? this.tileRenderer?.findHit(position, MARKER_HIT_RADIUS_MOUSE_PX, zoom) ?? null : null; + const tiledEntity = tiledHit ? this.markerManager.getEntity(tiledHit.item.id) : null; + return preferMarkerHit>( + regular, + tiledHit && tiledEntity ? { ...tiledHit, item: tiledEntity } : null, + )?.item ?? null; } private findDraggableAtScreen(screen: Offset): MarkerEntity | null { - return this.findHitAtScreen(screen, state => state.draggable); + return this.findHitAtScreen(screen, state => state.draggable)?.item ?? null; } + // 選び方はコアの pickMarkerHit(android / iOS の MarkerHitTest.pickIndex)と同じ。指の下に + // アイコン(ラベル込み)が描かれているマーカーを、アンカーの近い別のマーカーより優先する。 private findHitAtScreen( screen: Offset, filter: (state: MarkerState) => boolean, - ): MarkerEntity | null { - let best: MarkerEntity | null = null; - let bestDist = Number.POSITIVE_INFINITY; - for (const entity of this.markerManager.allEntities()) { - const state = entity.state; - if (!filter(state) || !entity.marker) continue; - const anchorScreen = this.renderer.holder.toScreenOffset(state.position); - if (!anchorScreen) continue; - - const bitmapIcon = state.icon?.toBitmapIcon() ?? DEFAULT_BITMAP_ICON; - const width = bitmapIcon.size.width; - const height = bitmapIcon.size.height; - let hit: boolean; - if (width && height) { - const left = anchorScreen.x - bitmapIcon.anchor.x * width - MARKER_HIT_PADDING_PX; - const top = anchorScreen.y - bitmapIcon.anchor.y * height - MARKER_HIT_PADDING_PX; - hit = screen.x >= left && screen.x <= left + width + MARKER_HIT_PADDING_PX * 2 && - screen.y >= top && screen.y <= top + height + MARKER_HIT_PADDING_PX * 2; - } else { - hit = Math.hypot(anchorScreen.x - screen.x, anchorScreen.y - screen.y) <= MARKER_HIT_RADIUS_MOUSE_PX; - } - if (!hit) continue; - - const dist = Math.hypot(anchorScreen.x - screen.x, anchorScreen.y - screen.y); - if (dist < bestDist) { - best = entity; - bestDist = dist; - } - } - return best; + ): MarkerHit> | null { + return pickMarkerHit( + screen, + this.markerManager.allEntities().filter(entity => entity.marker && filter(entity.state)), + entity => { + const anchorScreen = this.renderer.holder.toScreenOffset(entity.state.position); + if (!anchorScreen) return null; + const bitmapIcon = entity.state.icon?.toBitmapIcon() ?? DEFAULT_BITMAP_ICON; + return { + anchorScreen, + width: bitmapIcon.size.width, + height: bitmapIcon.size.height, + anchor: bitmapIcon.anchor, + }; + }, + MARKER_HIT_PADDING_PX, + ); } + } function generateId(): string {