@sakuzu/maplibre-gl-draw 1.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (541) hide show
  1. package/CHANGELOG.md +1109 -2
  2. package/README.ja.md +83 -50
  3. package/README.md +89 -53
  4. package/THIRD_PARTY_NOTICES.md +29 -0
  5. package/dist/api/datasets.d.ts +263 -0
  6. package/dist/api/document.d.ts +66 -0
  7. package/dist/api/draw.d.ts +137 -0
  8. package/dist/api/draw.js +20 -0
  9. package/dist/api/drawing.d.ts +87 -0
  10. package/dist/api/errors.d.ts +35 -0
  11. package/dist/api/errors.js +35 -0
  12. package/dist/api/events.d.ts +316 -0
  13. package/dist/api/extension/context.d.ts +247 -0
  14. package/dist/api/extension/feature-type.d.ts +129 -0
  15. package/dist/api/extension/mode.d.ts +123 -0
  16. package/dist/api/extension/plugin.d.ts +51 -0
  17. package/dist/api/extension/plugin.js +3 -0
  18. package/dist/api/extension/provider.d.ts +119 -0
  19. package/dist/api/extension/provider.js +3 -0
  20. package/dist/api/extension/render.d.ts +176 -0
  21. package/dist/api/extension/render.js +3 -0
  22. package/dist/api/extension/store.d.ts +190 -0
  23. package/dist/api/extension/store.js +3 -0
  24. package/dist/api/extensions.d.ts +306 -0
  25. package/dist/api/extensions.js +3 -0
  26. package/dist/api/features.d.ts +188 -0
  27. package/dist/api/features.js +3 -0
  28. package/dist/api/groups.d.ts +111 -0
  29. package/dist/api/groups.js +3 -0
  30. package/dist/api/hidden.d.ts +45 -0
  31. package/dist/api/hidden.js +3 -0
  32. package/dist/api/impl/adapters.js +463 -0
  33. package/dist/api/impl/contexts.js +466 -0
  34. package/dist/api/impl/create-draw.js +124 -0
  35. package/dist/api/impl/datasets.js +405 -0
  36. package/dist/api/impl/destroy-guard.js +74 -0
  37. package/dist/api/impl/document.js +280 -0
  38. package/dist/api/impl/drawing.js +180 -0
  39. package/dist/api/impl/engine-context.d.ts +7 -0
  40. package/dist/api/{context.js → impl/engine-context.js} +49 -38
  41. package/dist/{maplibre-gl-draw.js → api/impl/engine.js} +145 -177
  42. package/dist/api/impl/events.js +423 -0
  43. package/dist/api/impl/extension-host.js +364 -0
  44. package/dist/api/impl/extensions.js +229 -0
  45. package/dist/api/impl/features.js +445 -0
  46. package/dist/api/impl/geometry/apply.d.ts +32 -0
  47. package/dist/api/{geometry → impl/geometry}/apply.js +10 -27
  48. package/dist/api/{geometry → impl/geometry}/buffer.d.ts +4 -3
  49. package/dist/api/{geometry → impl/geometry}/buffer.js +12 -23
  50. package/dist/api/{geometry → impl/geometry}/split.d.ts +4 -3
  51. package/dist/api/{geometry → impl/geometry}/split.js +10 -21
  52. package/dist/api/{geometry → impl/geometry}/targets.d.ts +2 -20
  53. package/dist/api/{geometry → impl/geometry}/targets.js +8 -35
  54. package/dist/api/impl/geometry/types.js +3 -0
  55. package/dist/api/impl/groups.d.ts +3 -0
  56. package/dist/api/impl/groups.js +268 -0
  57. package/dist/api/impl/hidden.js +60 -0
  58. package/dist/api/impl/import-export/constants.d.ts +22 -0
  59. package/dist/api/impl/import-export/constants.js +24 -0
  60. package/dist/api/{import-export → impl/import-export}/embedded-file.js +2 -2
  61. package/dist/api/{import-export → impl/import-export}/format-detection.d.ts +1 -1
  62. package/dist/api/{import-export → impl/import-export}/format-detection.js +2 -1
  63. package/dist/api/impl/import-export/geojson-export.d.ts +19 -0
  64. package/dist/api/{import-export → impl/import-export}/geojson-export.js +71 -104
  65. package/dist/api/{import-export → impl/import-export}/geojson-import.d.ts +11 -7
  66. package/dist/api/{import-export → impl/import-export}/geojson-import.js +139 -97
  67. package/dist/api/{import-export → impl/import-export}/geometry-validation.d.ts +14 -0
  68. package/dist/api/{import-export → impl/import-export}/geometry-validation.js +45 -0
  69. package/dist/api/impl/import-export/image-import.d.ts +16 -0
  70. package/dist/api/impl/import-export/image-import.js +73 -0
  71. package/dist/api/impl/import-export/legacy-image-style.d.ts +16 -0
  72. package/dist/api/impl/import-export/legacy-image-style.js +49 -0
  73. package/dist/api/impl/import-export/native-format.d.ts +14 -0
  74. package/dist/api/{import-export → impl/import-export}/native-format.js +32 -22
  75. package/dist/api/impl/import-export/native-upgrade.d.ts +8 -0
  76. package/dist/api/impl/import-export/native-upgrade.js +139 -0
  77. package/dist/api/{import-export → impl/import-export}/native-validation.d.ts +1 -1
  78. package/dist/api/{import-export → impl/import-export}/native-validation.js +15 -14
  79. package/dist/api/impl/import-export/style-validation.d.ts +24 -0
  80. package/dist/api/impl/import-export/style-validation.js +86 -0
  81. package/dist/api/impl/import-export/types.d.ts +52 -0
  82. package/dist/api/impl/import-export/types.js +3 -0
  83. package/dist/api/impl/input.js +180 -0
  84. package/dist/api/impl/layers.js +307 -0
  85. package/dist/api/impl/metadata.d.ts +1 -0
  86. package/dist/api/impl/metadata.js +22 -0
  87. package/dist/api/impl/options.d.ts +1 -0
  88. package/dist/api/impl/options.js +851 -0
  89. package/dist/api/impl/placement.d.ts +1 -0
  90. package/dist/api/impl/placement.js +138 -0
  91. package/dist/api/impl/render-context.d.ts +13 -0
  92. package/dist/api/impl/render-context.js +272 -0
  93. package/dist/api/impl/rows.d.ts +1 -0
  94. package/dist/api/impl/rows.js +46 -0
  95. package/dist/api/impl/selection.d.ts +1 -0
  96. package/dist/api/impl/selection.js +186 -0
  97. package/dist/api/impl/shared.d.ts +64 -0
  98. package/dist/api/impl/shared.js +178 -0
  99. package/dist/api/impl/validate-extension.d.ts +1 -0
  100. package/dist/api/impl/validate-extension.js +159 -0
  101. package/dist/api/layers.d.ts +130 -0
  102. package/dist/api/layers.js +3 -0
  103. package/dist/api/metadata.d.ts +18 -0
  104. package/dist/api/metadata.js +3 -0
  105. package/dist/api/model.d.ts +487 -0
  106. package/dist/api/model.js +47 -0
  107. package/dist/api/options.d.ts +466 -0
  108. package/dist/api/options.js +3 -0
  109. package/dist/api/selection.d.ts +94 -0
  110. package/dist/api/selection.js +3 -0
  111. package/dist/api/state.d.ts +155 -0
  112. package/dist/api/state.js +12 -0
  113. package/dist/{display → dataset}/chunk.d.ts +1 -1
  114. package/dist/{display → dataset}/chunk.js +4 -3
  115. package/dist/{display → dataset}/dataset.js +21 -21
  116. package/dist/dataset/interaction.d.ts +1 -0
  117. package/dist/{display → dataset}/interaction.js +18 -6
  118. package/dist/dataset/manager.d.ts +1 -0
  119. package/dist/dataset/provider.d.ts +1 -0
  120. package/dist/{display → dataset}/retained.d.ts +1 -1
  121. package/dist/{display → dataset}/retained.js +8 -7
  122. package/dist/dataset/source.d.ts +1 -0
  123. package/dist/{display → dataset}/source.js +17 -4
  124. package/dist/dataset/spatial.d.ts +1 -0
  125. package/dist/{display → dataset}/spatial.js +1 -1
  126. package/dist/dataset/style.d.ts +1 -0
  127. package/dist/{display → dataset}/style.js +1 -1
  128. package/dist/dataset/table-source.d.ts +1 -0
  129. package/dist/{display/columnar/source.js → dataset/table-source.js} +53 -25
  130. package/dist/{display → dataset}/thinning.d.ts +0 -10
  131. package/dist/{display → dataset}/thinning.js +3 -2
  132. package/dist/dataset/triangulation.d.ts +1 -0
  133. package/dist/{display → dataset}/triangulation.js +0 -0
  134. package/dist/{display → dataset}/types.d.ts +84 -121
  135. package/dist/dataset/types.js +61 -0
  136. package/dist/dispatcher/hit-test/box-strategies.js +13 -13
  137. package/dist/dispatcher/hit-test/box-strategy.d.ts +2 -15
  138. package/dist/dispatcher/hit-test/globe-shape.js +5 -4
  139. package/dist/dispatcher/hit-test/service.d.ts +17 -0
  140. package/dist/dispatcher/hit-test/service.js +80 -16
  141. package/dist/dispatcher/hit-test/strategies/base.d.ts +13 -19
  142. package/dist/dispatcher/hit-test/strategies/circle.js +3 -2
  143. package/dist/dispatcher/hit-test/strategies/image.js +6 -18
  144. package/dist/dispatcher/hit-test/strategies/line.d.ts +0 -6
  145. package/dist/dispatcher/hit-test/strategies/line.js +9 -2
  146. package/dist/dispatcher/hit-test/strategies/multi.d.ts +0 -8
  147. package/dist/dispatcher/hit-test/strategies/multi.js +17 -8
  148. package/dist/dispatcher/hit-test/strategies/point.d.ts +9 -7
  149. package/dist/dispatcher/hit-test/strategies/point.js +8 -4
  150. package/dist/dispatcher/hit-test/strategies/polygon.d.ts +0 -7
  151. package/dist/dispatcher/hit-test/strategies/polygon.js +11 -3
  152. package/dist/dispatcher/hit-test/topmost.d.ts +1 -1
  153. package/dist/dispatcher/input-router.js +154 -70
  154. package/dist/dispatcher/normalizer.js +25 -4
  155. package/dist/dispatcher/types.d.ts +6 -1
  156. package/dist/extension/feature-handler.d.ts +1 -182
  157. package/dist/extension/index.d.ts +4 -7
  158. package/dist/extension/renderers.d.ts +1 -208
  159. package/dist/geometry/angle.d.ts +0 -8
  160. package/dist/geometry/angle.js +0 -8
  161. package/dist/geometry/bbox.d.ts +4 -12
  162. package/dist/geometry/bbox.js +4 -12
  163. package/dist/geometry/boolean.d.ts +0 -51
  164. package/dist/geometry/boolean.js +0 -51
  165. package/dist/geometry/buffer.d.ts +0 -26
  166. package/dist/geometry/buffer.js +0 -26
  167. package/dist/geometry/circle.d.ts +0 -9
  168. package/dist/geometry/circle.js +0 -9
  169. package/dist/geometry/coords.d.ts +0 -16
  170. package/dist/geometry/coords.js +0 -16
  171. package/dist/geometry/distance.d.ts +0 -24
  172. package/dist/geometry/distance.js +0 -24
  173. package/dist/geometry/errors.d.ts +21 -36
  174. package/dist/geometry/errors.js +49 -29
  175. package/dist/geometry/geojson.d.ts +85 -0
  176. package/dist/geometry/geojson.js +173 -0
  177. package/dist/geometry/index.d.ts +38 -23
  178. package/dist/geometry/index.js +43 -31
  179. package/dist/geometry/measure.d.ts +0 -27
  180. package/dist/geometry/measure.js +0 -27
  181. package/dist/geometry/predicates.d.ts +0 -9
  182. package/dist/geometry/predicates.js +0 -9
  183. package/dist/geometry/public-bounds.d.ts +26 -0
  184. package/dist/geometry/public-bounds.js +30 -0
  185. package/dist/geometry/public-combine.d.ts +78 -0
  186. package/dist/geometry/public-combine.js +82 -0
  187. package/dist/geometry/public-create.d.ts +54 -0
  188. package/dist/geometry/public-create.js +70 -0
  189. package/dist/geometry/public-measure.d.ts +170 -0
  190. package/dist/geometry/public-measure.js +283 -0
  191. package/dist/geometry/public-predicates.d.ts +54 -0
  192. package/dist/geometry/public-predicates.js +144 -0
  193. package/dist/geometry/public-repair.d.ts +75 -0
  194. package/dist/geometry/public-repair.js +77 -0
  195. package/dist/geometry/simplify.d.ts +0 -7
  196. package/dist/geometry/simplify.js +0 -7
  197. package/dist/geometry/split.d.ts +0 -20
  198. package/dist/geometry/split.js +0 -20
  199. package/dist/geometry/units.d.ts +20 -12
  200. package/dist/geometry/units.js +20 -12
  201. package/dist/index.d.ts +66 -66
  202. package/dist/index.js +6 -18
  203. package/dist/messages.d.ts +7 -7
  204. package/dist/modes/draw/circle.js +83 -207
  205. package/dist/modes/draw/commit-layer.d.ts +5 -16
  206. package/dist/modes/draw/commit-layer.js +2 -13
  207. package/dist/modes/draw/freehand.js +108 -246
  208. package/dist/modes/draw/image.js +4 -2
  209. package/dist/modes/draw/index.d.ts +5 -5
  210. package/dist/modes/draw/index.js +5 -5
  211. package/dist/modes/draw/line.js +18 -354
  212. package/dist/modes/draw/point.js +29 -78
  213. package/dist/modes/draw/polygon.js +23 -368
  214. package/dist/modes/draw/trace-support.d.ts +43 -36
  215. package/dist/modes/draw/trace-support.js +95 -98
  216. package/dist/modes/draw/vertex-drawing.d.ts +1 -0
  217. package/dist/modes/draw/vertex-drawing.js +176 -0
  218. package/dist/modes/handler.d.ts +1 -285
  219. package/dist/modes/manager.d.ts +2 -2
  220. package/dist/modes/manager.js +3 -0
  221. package/dist/modes/select/box-selection.d.ts +3 -3
  222. package/dist/modes/select/box-selection.js +2 -1
  223. package/dist/modes/select/click-handler.d.ts +5 -5
  224. package/dist/modes/select/click-handler.js +22 -20
  225. package/dist/modes/select/cursor-handler.d.ts +2 -2
  226. package/dist/modes/select/drag/intermediate-writes.js +1 -1
  227. package/dist/modes/select/drag/move.js +3 -3
  228. package/dist/modes/select/drag/operation.d.ts +3 -3
  229. package/dist/modes/select/drag/radius.js +5 -4
  230. package/dist/modes/select/drag/transform.d.ts +2 -2
  231. package/dist/modes/select/drag/transform.js +11 -5
  232. package/dist/modes/select/drag/vertex.d.ts +2 -2
  233. package/dist/modes/select/drag/vertex.js +13 -8
  234. package/dist/modes/select/drag-handler.js +44 -25
  235. package/dist/modes/select/mode.js +17 -17
  236. package/dist/modes/select/shortcut-handler.d.ts +8 -8
  237. package/dist/modes/select/shortcut-handler.js +9 -7
  238. package/dist/operations/layer-operations.js +29 -168
  239. package/dist/operations/resize.d.ts +2 -2
  240. package/dist/operations/resize.js +5 -5
  241. package/dist/operations/rotate.js +5 -5
  242. package/dist/operations/selection-operations.d.ts +16 -1
  243. package/dist/operations/selection-operations.js +28 -13
  244. package/dist/operations/shared-vertex.js +4 -2
  245. package/dist/operations/trace-graph.d.ts +0 -18
  246. package/dist/operations/trace-graph.js +0 -18
  247. package/dist/operations/vertex.js +36 -35
  248. package/dist/shared/color.d.ts +1 -0
  249. package/dist/shared/color.js +245 -0
  250. package/dist/shared/config/constants.d.ts +1 -54
  251. package/dist/shared/config/constants.js +1 -58
  252. package/dist/shared/config/feature-style.d.ts +16 -13
  253. package/dist/shared/config/feature-style.js +9 -0
  254. package/dist/shared/config/rendering.d.ts +3 -3
  255. package/dist/shared/config/selection-highlight.d.ts +0 -15
  256. package/dist/shared/config/selection-highlight.js +4 -7
  257. package/dist/shared/config/selection.d.ts +2 -32
  258. package/dist/shared/config/selection.js +0 -18
  259. package/dist/shared/config/topology.d.ts +2 -2
  260. package/dist/shared/config/trace.d.ts +4 -4
  261. package/dist/shared/math/angle.d.ts +1 -1
  262. package/dist/shared/math/angle.js +1 -1
  263. package/dist/shared/math/circle.d.ts +1 -1
  264. package/dist/shared/math/circle.js +1 -1
  265. package/dist/shared/math/distance.d.ts +2 -1
  266. package/dist/shared/math/distance.js +3 -2
  267. package/dist/shared/math/index.d.ts +1 -1
  268. package/dist/shared/math/index.js +1 -1
  269. package/dist/shared/math/transform.d.ts +23 -0
  270. package/dist/shared/math/transform.js +47 -28
  271. package/dist/shared/properties.d.ts +18 -0
  272. package/dist/shared/properties.js +129 -0
  273. package/dist/shared/types/events.d.ts +15 -7
  274. package/dist/shared/types/model.d.ts +109 -159
  275. package/dist/shared/types/style.d.ts +1 -1
  276. package/dist/shared/utils/color.d.ts +2 -36
  277. package/dist/shared/utils/color.js +13 -70
  278. package/dist/shared/utils/coordinates.d.ts +30 -4
  279. package/dist/shared/utils/coordinates.js +56 -10
  280. package/dist/shared/utils/event-emitter.d.ts +1 -262
  281. package/dist/shared/utils/event-emitter.js +4 -3
  282. package/dist/shared/utils/feature-bbox.js +8 -7
  283. package/dist/shared/utils/feature-segments.js +5 -4
  284. package/dist/shared/utils/image.d.ts +0 -4
  285. package/dist/shared/utils/image.js +0 -6
  286. package/dist/shared/utils/name-generator.d.ts +14 -20
  287. package/dist/shared/utils/name-generator.js +20 -3
  288. package/dist/shared/utils/pixel-ratio.d.ts +9 -3
  289. package/dist/shared/utils/pixel-ratio.js +13 -4
  290. package/dist/shared/utils/property.d.ts +7 -50
  291. package/dist/shared/utils/property.js +22 -87
  292. package/dist/snapping/custom-targets.js +21 -4
  293. package/dist/snapping/indicator.js +20 -11
  294. package/dist/snapping/providers/display.d.ts +6 -1
  295. package/dist/snapping/providers/display.js +2 -1
  296. package/dist/snapping/providers/guide.d.ts +3 -43
  297. package/dist/snapping/providers/guide.js +1 -44
  298. package/dist/snapping/providers/intersection.js +1 -1
  299. package/dist/snapping/providers/store.js +3 -3
  300. package/dist/snapping/service.js +18 -7
  301. package/dist/snapping/types.d.ts +9 -35
  302. package/dist/store/draw-store.js +233 -142
  303. package/dist/store/local-visibility.d.ts +11 -6
  304. package/dist/store/local-visibility.js +38 -7
  305. package/dist/store/lock.d.ts +0 -6
  306. package/dist/store/lock.js +0 -6
  307. package/dist/store/memory/change-bus.d.ts +3 -3
  308. package/dist/store/memory/change-bus.js +12 -0
  309. package/dist/store/memory/file-store.d.ts +2 -2
  310. package/dist/store/memory/ui-state.d.ts +1 -51
  311. package/dist/store/memory/ui-state.js +122 -134
  312. package/dist/store/memory.d.ts +1 -8
  313. package/dist/store/memory.js +249 -45
  314. package/dist/store/ordering.d.ts +9 -0
  315. package/dist/store/ordering.js +17 -0
  316. package/dist/store/spatial/spatial-index.d.ts +1 -1
  317. package/dist/store/spatial/spatial-index.js +1 -1
  318. package/dist/store/spatial/store-spatial-index.d.ts +3 -1
  319. package/dist/store/spatial/store-spatial-index.js +9 -3
  320. package/dist/store/store.d.ts +66 -107
  321. package/dist/store/types.d.ts +1 -1
  322. package/dist/store/writable-layer.d.ts +1 -1
  323. package/dist/store/writable-layer.js +2 -2
  324. package/dist/table/builder.d.ts +83 -0
  325. package/dist/table/builder.js +381 -0
  326. package/dist/table/index.d.ts +37 -0
  327. package/dist/table/index.js +5 -0
  328. package/dist/table/packed-rtree.d.ts +1 -0
  329. package/dist/{display → table}/packed-rtree.js +1 -1
  330. package/dist/table/partition.d.ts +1 -0
  331. package/dist/{display → table}/partition.js +3 -3
  332. package/dist/table/prepare.d.ts +44 -0
  333. package/dist/table/prepare.js +136 -0
  334. package/dist/table/table.d.ts +1 -0
  335. package/dist/{display/columnar → table}/table.js +29 -25
  336. package/dist/{display/columnar → table}/types.d.ts +68 -72
  337. package/dist/table/types.js +3 -0
  338. package/dist/view/coordinator.js +4 -3
  339. package/dist/view/feature-companion.d.ts +13 -7
  340. package/dist/view/feature-companion.js +11 -3
  341. package/dist/view/globe-subdivision.js +3 -3
  342. package/dist/view/layer/attach.d.ts +12 -6
  343. package/dist/view/layer/attach.js +86 -10
  344. package/dist/view/layer/custom-layer.js +40 -11
  345. package/dist/view/layer/display-list.js +2 -1
  346. package/dist/view/layer/drape-planner.d.ts +2 -2
  347. package/dist/view/layer/frame-render.d.ts +3 -3
  348. package/dist/view/layer/frame-render.js +7 -2
  349. package/dist/view/layer/frame-state.d.ts +6 -6
  350. package/dist/view/layer/index.d.ts +2 -2
  351. package/dist/view/layer/index.js +1 -1
  352. package/dist/view/layer/render.d.ts +4 -4
  353. package/dist/view/layer/render.js +3 -8
  354. package/dist/view/layer/renderers.d.ts +2 -2
  355. package/dist/view/layer/renderers.js +1 -1
  356. package/dist/view/layer/slot-manager.d.ts +2 -2
  357. package/dist/view/layer/slot-manager.js +4 -4
  358. package/dist/view/layer/slots.d.ts +2 -2
  359. package/dist/view/layer/slots.js +1 -1
  360. package/dist/view/layer/store-retained-bbox.js +3 -2
  361. package/dist/view/layer/store-retained-classify.d.ts +5 -7
  362. package/dist/view/layer/store-retained-classify.js +19 -7
  363. package/dist/view/layer/store-retained-collect.js +8 -7
  364. package/dist/view/layer/store-retained-coord-patch.d.ts +0 -8
  365. package/dist/view/layer/store-retained-coord-patch.js +13 -4
  366. package/dist/view/layer/store-retained-immediate.d.ts +3 -3
  367. package/dist/view/layer/store-retained-immediate.js +3 -8
  368. package/dist/view/layer/store-retained-invalidation.d.ts +2 -2
  369. package/dist/view/layer/store-retained.d.ts +2 -2
  370. package/dist/view/layer/store-retained.js +1 -1
  371. package/dist/view/renderers/batch-manager.js +8 -7
  372. package/dist/view/renderers/drawer.js +21 -15
  373. package/dist/view/renderers/image.js +13 -39
  374. package/dist/view/renderers/line/line-types.d.ts +3 -3
  375. package/dist/view/renderers/line/sdf-line.js +3 -1
  376. package/dist/view/renderers/point/billboard-depth.js +1 -1
  377. package/dist/view/renderers/point/point-instance.js +1 -1
  378. package/dist/view/renderers/point/point-shape.js +2 -2
  379. package/dist/view/renderers/point/point-style.js +48 -4
  380. package/dist/view/renderers/polygon/batch.js +1 -2
  381. package/dist/view/renderers/polygon/fill.js +3 -1
  382. package/dist/view/renderers/polygon/sdf-polygon.js +2 -3
  383. package/dist/view/renderers/polygon/triangulator.d.ts +1 -1
  384. package/dist/view/renderers/stroke.js +3 -1
  385. package/dist/view/shaders/helpers.d.ts +2 -42
  386. package/dist/view/shaders/helpers.js +0 -6
  387. package/dist/view/shaders/projection.d.ts +15 -7
  388. package/dist/view/shaders/projection.js +23 -6
  389. package/dist/view/shaders/quad.d.ts +16 -12
  390. package/dist/view/shaders/quad.js +34 -13
  391. package/dist/view/style-rule.d.ts +5 -42
  392. package/dist/view/style-rule.js +20 -44
  393. package/dist/view/terrain/binding.d.ts +1 -0
  394. package/dist/view/terrain/binding.js +29 -0
  395. package/dist/view/terrain/context.d.ts +3 -4
  396. package/dist/view/terrain/context.js +4 -5
  397. package/dist/view/terrain/drape/geometry.js +1 -1
  398. package/dist/view/terrain/drape/pass.d.ts +1 -1
  399. package/dist/view/terrain/drape/pass.js +8 -7
  400. package/dist/view/terrain/polygon.d.ts +9 -5
  401. package/dist/view/terrain/polygon.js +16 -2
  402. package/dist/view/terrain/state.d.ts +1 -1
  403. package/dist/view/terrain/state.js +1 -1
  404. package/dist/view/terrain/tessellation.d.ts +2 -2
  405. package/dist/view/terrain/tessellation.js +1 -1
  406. package/dist/view/ui/auxiliary-handles.d.ts +6 -29
  407. package/dist/view/ui/bounds.js +8 -12
  408. package/dist/view/ui/handle-test.js +2 -2
  409. package/dist/view/ui/handle-thinning.js +17 -17
  410. package/dist/view/ui/handles.js +21 -13
  411. package/dist/view/ui/helper.d.ts +1 -1
  412. package/dist/view/ui/helper.js +1 -1
  413. package/dist/view/ui/index.d.ts +1 -1
  414. package/dist/view/ui/selection-scope.js +4 -2
  415. package/dist/view/ui/selection-ui/bounding-box.js +6 -6
  416. package/dist/view/ui/selection-ui/extension-registry.d.ts +31 -20
  417. package/dist/view/ui/selection-ui/extension-registry.js +70 -7
  418. package/dist/view/ui/selection-ui/index.d.ts +2 -2
  419. package/dist/view/ui/selection-ui/index.js +1 -1
  420. package/dist/view/ui/selection-ui/renderer.js +15 -38
  421. package/dist/view/ui/selection-ui/types.d.ts +17 -2
  422. package/dist/view/ui/selection-ui-drawer.d.ts +8 -0
  423. package/dist/view/ui/selection-ui-drawer.js +63 -2
  424. package/dist/view/ui/tentative.js +1 -1
  425. package/dist/webgl/index.d.ts +65 -0
  426. package/dist/webgl/index.js +62 -0
  427. package/package.json +38 -15
  428. package/dist/api/api.d.ts +0 -1259
  429. package/dist/api/api.js +0 -86
  430. package/dist/api/context.d.ts +0 -173
  431. package/dist/api/display-api.js +0 -23
  432. package/dist/api/event-api.d.ts +0 -13
  433. package/dist/api/event-api.js +0 -17
  434. package/dist/api/extension-api.d.ts +0 -46
  435. package/dist/api/extension-api.js +0 -85
  436. package/dist/api/feature-api.d.ts +0 -11
  437. package/dist/api/feature-api.js +0 -101
  438. package/dist/api/geometry/apply.d.ts +0 -51
  439. package/dist/api/geometry/boolean.d.ts +0 -28
  440. package/dist/api/geometry/boolean.js +0 -65
  441. package/dist/api/geometry/types.d.ts +0 -31
  442. package/dist/api/geometry-operations.d.ts +0 -162
  443. package/dist/api/geometry-operations.js +0 -94
  444. package/dist/api/group-api.d.ts +0 -10
  445. package/dist/api/group-api.js +0 -78
  446. package/dist/api/import-export/constants.d.ts +0 -29
  447. package/dist/api/import-export/constants.js +0 -40
  448. package/dist/api/import-export/file-name.d.ts +0 -10
  449. package/dist/api/import-export/file-name.js +0 -20
  450. package/dist/api/import-export/geojson-export.d.ts +0 -19
  451. package/dist/api/import-export/image-import.d.ts +0 -12
  452. package/dist/api/import-export/image-import.js +0 -53
  453. package/dist/api/import-export/index.d.ts +0 -9
  454. package/dist/api/import-export/index.js +0 -107
  455. package/dist/api/import-export/native-format.d.ts +0 -15
  456. package/dist/api/import-export/style-validation.d.ts +0 -27
  457. package/dist/api/import-export/style-validation.js +0 -49
  458. package/dist/api/import-export/types.d.ts +0 -26
  459. package/dist/api/index.d.ts +0 -15
  460. package/dist/api/index.js +0 -3
  461. package/dist/api/input-api.d.ts +0 -158
  462. package/dist/api/input-api.js +0 -144
  463. package/dist/api/instance-api.d.ts +0 -38
  464. package/dist/api/instance-api.js +0 -136
  465. package/dist/api/layer-api.d.ts +0 -12
  466. package/dist/api/layer-api.js +0 -62
  467. package/dist/api/selection-api.d.ts +0 -7
  468. package/dist/api/selection-api.js +0 -78
  469. package/dist/api/snapping-api.d.ts +0 -125
  470. package/dist/api/snapping-api.js +0 -50
  471. package/dist/api/topology-api.d.ts +0 -30
  472. package/dist/api/topology-api.js +0 -16
  473. package/dist/api/tracing-api.d.ts +0 -31
  474. package/dist/api/tracing-api.js +0 -16
  475. package/dist/dispatcher/hit-test/index.d.ts +0 -14
  476. package/dist/dispatcher/hit-test/index.js +0 -9
  477. package/dist/dispatcher/index.d.ts +0 -15
  478. package/dist/dispatcher/index.js +0 -3
  479. package/dist/display/columnar/index.d.ts +0 -12
  480. package/dist/display/columnar/index.js +0 -13
  481. package/dist/display/columnar/prepare.d.ts +0 -42
  482. package/dist/display/columnar/prepare.js +0 -116
  483. package/dist/display/index.d.ts +0 -13
  484. package/dist/display/types.js +0 -43
  485. package/dist/maplibre-gl-draw.d.ts +0 -47
  486. package/dist/modes/cursor.js +0 -72
  487. package/dist/modes/index.d.ts +0 -7
  488. package/dist/operations/index.d.ts +0 -9
  489. package/dist/operations/index.js +0 -3
  490. package/dist/plugins/index.d.ts +0 -10
  491. package/dist/plugins/mutation-hooks.d.ts +0 -28
  492. package/dist/plugins/mutation-hooks.js +0 -84
  493. package/dist/plugins/plugin-context.js +0 -367
  494. package/dist/plugins/plugin-manager.d.ts +0 -103
  495. package/dist/plugins/plugin-manager.js +0 -316
  496. package/dist/plugins/plugin.d.ts +0 -529
  497. package/dist/shared/config/index.d.ts +0 -17
  498. package/dist/shared/config/index.js +0 -8
  499. package/dist/shared/utils/index.d.ts +0 -15
  500. package/dist/shared/utils/index.js +0 -24
  501. package/dist/snapping/index.d.ts +0 -14
  502. package/dist/snapping/index.js +0 -4
  503. package/dist/store/change-merger.js +0 -102
  504. package/dist/store/event-bridge.js +0 -161
  505. package/dist/store/index.d.ts +0 -12
  506. package/dist/store/index.js +0 -4
  507. package/dist/view/index.d.ts +0 -47
  508. package/dist/view/index.js +0 -20
  509. /package/dist/api/{geometry/types.js → datasets.js} +0 -0
  510. /package/dist/api/{import-export/types.js → document.js} +0 -0
  511. /package/dist/{display/columnar/types.js → api/drawing.js} +0 -0
  512. /package/dist/{display/index.js → api/events.js} +0 -0
  513. /package/dist/{modes/index.js → api/extension/context.js} +0 -0
  514. /package/dist/{plugins/index.js → api/extension/feature-type.js} +0 -0
  515. /package/dist/{plugins/plugin.js → api/extension/mode.js} +0 -0
  516. /package/dist/api/{display-api.d.ts → impl/adapters.d.ts} +0 -0
  517. /package/dist/{display/columnar/source.d.ts → api/impl/contexts.d.ts} +0 -0
  518. /package/dist/{display/columnar/table.d.ts → api/impl/create-draw.d.ts} +0 -0
  519. /package/dist/{display/interaction.d.ts → api/impl/datasets.d.ts} +0 -0
  520. /package/dist/{display/manager.d.ts → api/impl/destroy-guard.d.ts} +0 -0
  521. /package/dist/{display/packed-rtree.d.ts → api/impl/document.d.ts} +0 -0
  522. /package/dist/{display/partition.d.ts → api/impl/drawing.d.ts} +0 -0
  523. /package/dist/{display/provider.d.ts → api/impl/engine.d.ts} +0 -0
  524. /package/dist/{display/source.d.ts → api/impl/events.d.ts} +0 -0
  525. /package/dist/{display/spatial.d.ts → api/impl/extension-host.d.ts} +0 -0
  526. /package/dist/{display/style.d.ts → api/impl/extensions.d.ts} +0 -0
  527. /package/dist/{display/triangulation.d.ts → api/impl/features.d.ts} +0 -0
  528. /package/dist/{modes/cursor.d.ts → api/impl/geometry/types.d.ts} +0 -0
  529. /package/dist/{plugins/plugin-context.d.ts → api/impl/hidden.d.ts} +0 -0
  530. /package/dist/api/{import-export → impl/import-export}/embedded-file.d.ts +0 -0
  531. /package/dist/api/{import-export → impl/import-export}/own-property.d.ts +0 -0
  532. /package/dist/api/{import-export → impl/import-export}/own-property.js +0 -0
  533. /package/dist/{store/change-merger.d.ts → api/impl/input.d.ts} +0 -0
  534. /package/dist/{store/event-bridge.d.ts → api/impl/layers.d.ts} +0 -0
  535. /package/dist/{display → dataset}/chunk-set.d.ts +0 -0
  536. /package/dist/{display → dataset}/chunk-set.js +0 -0
  537. /package/dist/{display → dataset}/dataset.d.ts +0 -0
  538. /package/dist/{display → dataset}/manager.js +0 -0
  539. /package/dist/{display → dataset}/provider.js +0 -0
  540. /package/dist/{display → dataset}/selection.d.ts +0 -0
  541. /package/dist/{display → dataset}/selection.js +0 -0
@@ -7,6 +7,7 @@
7
7
  * They provide the intersection test logic for box selection of each feature type.
8
8
  */
9
9
  import { createOBB, DEFAULT_TILE_SIZE, getMetersPerPixel, getOBBCorners, metersToDegreesLat, metersToDegreesLng, pointInRectangle, rectangleIntersectsCircle, rectangleIntersectsLineString, rectangleIntersectsOBB, rectangleIntersectsPolygon, } from '../../shared/math/index.js';
10
+ import { coordinatesOf } from '../../shared/utils/coordinates.js';
10
11
  import { getCircleRadius, getImageProperties } from '../../shared/utils/property.js';
11
12
  /**
12
13
  * Box selection test for Point
@@ -27,7 +28,7 @@ export class PointBoxSelectionStrategy {
27
28
  intersects(feature, rect) {
28
29
  if (feature.type !== 'Point')
29
30
  return false;
30
- const coord = feature.coordinates;
31
+ const coord = coordinatesOf(feature);
31
32
  return pointInRectangle(coord, rect);
32
33
  }
33
34
  }
@@ -51,7 +52,7 @@ export class LineStringBoxSelectionStrategy {
51
52
  intersects(feature, rect) {
52
53
  if (feature.type !== 'LineString')
53
54
  return false;
54
- const coords = feature.coordinates;
55
+ const coords = coordinatesOf(feature);
55
56
  return rectangleIntersectsLineString(rect, coords);
56
57
  }
57
58
  }
@@ -77,7 +78,7 @@ export class PolygonBoxSelectionStrategy {
77
78
  intersects(feature, rect) {
78
79
  if (feature.type !== 'Polygon')
79
80
  return false;
80
- const rings = feature.coordinates;
81
+ const rings = coordinatesOf(feature);
81
82
  return rectangleIntersectsPolygon(rect, rings);
82
83
  }
83
84
  }
@@ -100,7 +101,7 @@ export class MultiPointBoxSelectionStrategy {
100
101
  intersects(feature, rect) {
101
102
  if (feature.type !== 'MultiPoint')
102
103
  return false;
103
- const parts = feature.coordinates;
104
+ const parts = coordinatesOf(feature);
104
105
  return parts.some((coord) => pointInRectangle(coord, rect));
105
106
  }
106
107
  }
@@ -123,7 +124,7 @@ export class MultiLineStringBoxSelectionStrategy {
123
124
  intersects(feature, rect) {
124
125
  if (feature.type !== 'MultiLineString')
125
126
  return false;
126
- const parts = feature.coordinates;
127
+ const parts = coordinatesOf(feature);
127
128
  return parts.some((coords) => rectangleIntersectsLineString(rect, coords));
128
129
  }
129
130
  }
@@ -146,7 +147,7 @@ export class MultiPolygonBoxSelectionStrategy {
146
147
  intersects(feature, rect) {
147
148
  if (feature.type !== 'MultiPolygon')
148
149
  return false;
149
- const parts = feature.coordinates;
150
+ const parts = coordinatesOf(feature);
150
151
  return parts.some((rings) => rings.length > 0 && rectangleIntersectsPolygon(rect, rings));
151
152
  }
152
153
  }
@@ -180,7 +181,7 @@ export class ImageBoxSelectionStrategy {
180
181
  const obb = this.createOBBForImage(feature);
181
182
  if (!obb) {
182
183
  // When the OBB cannot be created, test with the coordinate alone
183
- const coord = feature.coordinates;
184
+ const coord = coordinatesOf(feature);
184
185
  return pointInRectangle(coord, rect);
185
186
  }
186
187
  const corners = getOBBCorners(obb);
@@ -190,20 +191,19 @@ export class ImageBoxSelectionStrategy {
190
191
  const properties = getImageProperties(feature);
191
192
  if (!properties.imageFileId)
192
193
  return null;
193
- const coord = feature.coordinates;
194
+ const coord = coordinatesOf(feature);
194
195
  const latitude = coord[1];
195
- const style = feature.style;
196
196
  const imageWidth = properties.imageWidth || 100;
197
197
  const imageHeight = properties.imageHeight || 100;
198
- const displayWidth = style?.width || imageWidth;
199
- const displayHeight = style?.height || imageHeight;
198
+ const displayWidth = imageWidth;
199
+ const displayHeight = imageHeight;
200
200
  const createdZoom = properties.createdZoom || 14;
201
201
  const scale = properties.scale || 1;
202
202
  const widthMeters = getMetersPerPixel(latitude, createdZoom, this.tileSize) * displayWidth * scale;
203
203
  const heightMeters = getMetersPerPixel(latitude, createdZoom, this.tileSize) * displayHeight * scale;
204
204
  const widthDeg = metersToDegreesLng(widthMeters, latitude);
205
205
  const heightDeg = metersToDegreesLat(heightMeters);
206
- const rotation = (style?.rotation || 0) + (properties.rotation || 0);
206
+ const rotation = properties.rotation || 0;
207
207
  return createOBB(coord[0], coord[1], widthDeg, heightDeg, rotation);
208
208
  }
209
209
  }
@@ -232,7 +232,7 @@ export class CircleBoxSelectionStrategy {
232
232
  intersects(feature, rect) {
233
233
  if (feature.type !== 'Circle')
234
234
  return false;
235
- const center = feature.coordinates;
235
+ const center = coordinatesOf(feature);
236
236
  const radiusMeters = getCircleRadius(feature);
237
237
  if (!radiusMeters || radiusMeters <= 0) {
238
238
  return pointInRectangle(center, rect);
@@ -3,26 +3,13 @@
3
3
  *
4
4
  * Intersection test strategy interface for box selection.
5
5
  * Like HitTestStrategy, it defines the test logic per Feature type.
6
- * Tests for custom types can be added through CustomFeatureHandler.
6
+ * Tests for custom types can be added through FeatureTypeHandler.
7
7
  */
8
8
  import type { BoundingBox, Feature, FeatureType } from '../../store/types.js';
9
9
  /**
10
10
  * The test of one feature type against the rectangle of a box selection in select mode, as
11
- * registered through the `boxSelection` of a `CustomFeatureHandler`. A box that crosses the
11
+ * registered through the `boxSelection` of a `FeatureTypeHandler`. A box that crosses the
12
12
  * ±180 degree meridian is tested as two rectangles.
13
- *
14
- * @example
15
- * ```ts
16
- * import type { BoxSelectionStrategy } from '@sakuzu/maplibre-gl-draw';
17
- *
18
- * const markerBoxSelection: BoxSelectionStrategy = {
19
- * featureType: 'Marker',
20
- * intersects(feature, rect) {
21
- * const [lng, lat] = feature.coordinates as [number, number];
22
- * return lng >= rect.minX && lng <= rect.maxX && lat >= rect.minY && lat <= rect.maxY;
23
- * },
24
- * };
25
- * ```
26
13
  */
27
14
  export interface BoxSelectionStrategy {
28
15
  /** The target feature type */
@@ -1,6 +1,7 @@
1
1
  // SPDX-FileCopyrightText: 2026 SAKAIDA Atsushi
2
2
  // SPDX-License-Identifier: AGPL-3.0-only
3
3
  import { densifyOnMercatorPlane, globeSubdivisionGrid, } from '../../shared/math/globe-subdivision.js';
4
+ import { coordinatesOf, geometryFromCoordinates } from '../../shared/utils/coordinates.js';
4
5
  import { isGlobeProjection } from '../../shared/utils/map.js';
5
6
  /** The most points the cut adds to one path (a guard against a runaway) */
6
7
  const MAX_POINTS = 65536;
@@ -33,19 +34,19 @@ export function shapeOnGlobe(feature, grid) {
33
34
  switch (feature.type) {
34
35
  case 'LineString':
35
36
  case 'Freehand':
36
- coordinates = cut(feature.coordinates);
37
+ coordinates = cut(coordinatesOf(feature));
37
38
  break;
38
39
  case 'Polygon':
39
40
  case 'MultiLineString':
40
- coordinates = feature.coordinates.map(cut);
41
+ coordinates = coordinatesOf(feature).map(cut);
41
42
  break;
42
43
  case 'MultiPolygon':
43
- coordinates = feature.coordinates.map((rings) => rings.map(cut));
44
+ coordinates = coordinatesOf(feature).map((rings) => rings.map(cut));
44
45
  break;
45
46
  default:
46
47
  return feature;
47
48
  }
48
- return { ...feature, coordinates };
49
+ return { ...feature, geometry: geometryFromCoordinates(feature.type, coordinates) };
49
50
  }
50
51
  /**
51
52
  * A resolver that cuts on the globe of `map`, keeping the last cut of each feature
@@ -47,6 +47,12 @@ export interface AnchorScreenProjector {
47
47
  * draw instance (6 px by default).
48
48
  */
49
49
  export interface HitTestService {
50
+ /**
51
+ * Changes how far from a feature a click still hits it, in CSS pixels
52
+ *
53
+ * @param px The new tolerance
54
+ */
55
+ setClickTolerance(px: number): void;
50
56
  /**
51
57
  * Returns the frontmost feature that is hit, walking `orderedFeatures` from the end
52
58
  *
@@ -84,6 +90,10 @@ export interface HitTestService {
84
90
  * @returns a function that cancels the registration (see HitTestStrategyRegistry.register)
85
91
  */
86
92
  registerStrategy?(strategy: HitTestStrategy): () => void;
93
+ /**
94
+ * The strategy registered for a type, or `undefined`
95
+ */
96
+ getStrategy?(type: string): HitTestStrategy | undefined;
87
97
  /**
88
98
  * Registers, per feature type, the extra reach (in CSS pixels) for narrowing down the
89
99
  * candidates
@@ -117,4 +127,11 @@ export interface HitTestServiceImplOptions extends Partial<HitTestOptions> {
117
127
  * is tested
118
128
  */
119
129
  drawnShape?: DrawnShapeResolver;
130
+ /**
131
+ * How far the marker of a built-in point reaches from its position, in CSS pixels. When
132
+ * given, the built-in `Point` and `MultiPoint` strategies hit a point within that reach plus
133
+ * the tolerance, so a click anywhere on its marker hits it; when omitted a point is hit
134
+ * within the tolerance of its position alone
135
+ */
136
+ pointMarkerReachPx?: (feature: Feature) => number;
120
137
  }
@@ -1,5 +1,6 @@
1
1
  // SPDX-FileCopyrightText: 2026 SAKAIDA Atsushi
2
2
  // SPDX-License-Identifier: AGPL-3.0-only
3
+ import { coordinatesOf } from '../../shared/utils/coordinates.js';
3
4
  import { clickCopies, toleranceDegrees } from './local-frame.js';
4
5
  import { DEFAULT_HIT_TEST_OPTIONS, HitTestStrategyRegistry } from './strategies/base.js';
5
6
  import { CircleHitTestStrategy } from './strategies/circle.js';
@@ -22,6 +23,9 @@ const SCREEN_SPACE_TYPES = new Set(['Point', 'MultiPoint']);
22
23
  * @internal
23
24
  */
24
25
  export class HitTestServiceImpl {
26
+ setClickTolerance(px) {
27
+ this.options = { ...this.options, clickTolerance: px };
28
+ }
25
29
  constructor(store, spatialIndex, options = {}) {
26
30
  Object.defineProperty(this, "spatialIndex", {
27
31
  enumerable: true,
@@ -83,13 +87,14 @@ export class HitTestServiceImpl {
83
87
  const tileSize = options.tileSize ?? 512;
84
88
  // Register the default strategies
85
89
  this.registry = new HitTestStrategyRegistry();
86
- this.registry.register(new PointHitTestStrategy());
90
+ const markerReach = options.pointMarkerReachPx;
91
+ this.registry.register(withReach(new PointHitTestStrategy(), markerReach));
87
92
  this.registry.register(new LineHitTestStrategy());
88
93
  this.registry.register(new PolygonHitTestStrategy());
89
94
  this.registry.register(new CircleHitTestStrategy());
90
95
  // The Multi geometries (a hit if any part is hit / the distance is the minimum over the
91
96
  // parts)
92
- this.registry.register(new MultiPointHitTestStrategy());
97
+ this.registry.register(withReach(new MultiPointHitTestStrategy(), markerReach));
93
98
  this.registry.register(new MultiLineStringHitTestStrategy());
94
99
  this.registry.register(new MultiPolygonHitTestStrategy());
95
100
  // Freehand uses the same strategy as LineString
@@ -117,6 +122,12 @@ export class HitTestServiceImpl {
117
122
  registerStrategy(strategy) {
118
123
  return this.registry.register(strategy);
119
124
  }
125
+ /**
126
+ * The strategy registered for a type
127
+ */
128
+ getStrategy(type) {
129
+ return this.registry.get(type);
130
+ }
120
131
  /**
121
132
  * Registers the extra reach for narrowing down the candidates
122
133
  *
@@ -155,12 +166,36 @@ export class HitTestServiceImpl {
155
166
  * The radius is a square box in degrees. A latitude reach needs only radius * cos φ
156
167
  * (local-frame.ts), so the box never misses a candidate.
157
168
  */
158
- expandSearchRadius(toleranceLngLat, tolerancePx, screenPoint, unproject) {
159
- const reachPx = this.maxCandidateReachPx();
169
+ expandSearchRadius(toleranceLngLat, degPerPx, orderedFeatures) {
170
+ const reachPx = this.maxCandidateReachPx() + this.maxFeatureReachPx(orderedFeatures);
160
171
  if (reachPx <= 0)
161
172
  return toleranceLngLat;
162
- const degPerPx = tolerancePx > 0 ? toleranceLngLat / tolerancePx : toleranceDegrees(unproject, screenPoint, 1);
163
- return toleranceLngLat + reachPx * degPerPx;
173
+ return toleranceLngLat + reachPx * degPerPx();
174
+ }
175
+ /**
176
+ * The largest reach beyond its geometry among the features whose strategy has one
177
+ * (`HitTestStrategy.reachPx`, the markers of the points), in CSS pixels
178
+ *
179
+ * The reach depends on the style of each feature, so it is measured at query time like the
180
+ * rest of the appearance (the bounding boxes of the index stay those of the geometry).
181
+ */
182
+ maxFeatureReachPx(orderedFeatures) {
183
+ let max = 0;
184
+ let lastType;
185
+ let reachOf;
186
+ for (const feature of orderedFeatures) {
187
+ if (feature.type !== lastType) {
188
+ lastType = feature.type;
189
+ const strategy = this.registry.get(feature.type);
190
+ reachOf = strategy?.reachPx?.bind(strategy);
191
+ }
192
+ if (!reachOf)
193
+ continue;
194
+ const reach = reachOf(feature);
195
+ if (reach > max)
196
+ max = reach;
197
+ }
198
+ return max;
164
199
  }
165
200
  /**
166
201
  * Returns the frontmost hit in the visual stacking order
@@ -198,9 +233,16 @@ export class HitTestServiceImpl {
198
233
  // The search radius is widened by the registered extra reach
199
234
  // (registerCandidateReach). Unlike the approach of baking the appearance into the bbox
200
235
  // of the index, this does not depend on the state or the zoom. The tolerance passed to
201
- // the second stage is toleranceLngLat as before (only the candidate set widens; the
202
- // semantics of a hit are decided by the strategy).
203
- const searchRadius = this.expandSearchRadius(toleranceLngLat, tolerance, screenPoint, unproject);
236
+ // the second stage is toleranceLngLat as before, widened only by the reach of a strategy
237
+ // (the marker of a point); the registered reach widens only the candidate set.
238
+ // The degrees per CSS pixel at the click, measured once and only when needed (from the
239
+ // tolerance already converted, or from one pixel when the tolerance is 0)
240
+ let degPerPxMemo;
241
+ const degPerPx = () => {
242
+ degPerPxMemo ?? (degPerPxMemo = tolerance > 0 ? toleranceLngLat / tolerance : toleranceDegrees(unproject, screenPoint, 1));
243
+ return degPerPxMemo;
244
+ };
245
+ const searchRadius = this.expandSearchRadius(toleranceLngLat, degPerPx, orderedFeatures);
204
246
  // The click on each copy of the world it can reach (the stored copy alone away from the
205
247
  // antimeridian; local-frame.ts)
206
248
  const copies = clickCopies(coordinate, searchRadius);
@@ -222,8 +264,7 @@ export class HitTestServiceImpl {
222
264
  // Symbols (points) are tested in screen space. This works only while the terrain is
223
265
  // enabled; when it is disabled null is returned and the conventional path is used as
224
266
  // before.
225
- const degPerPx = tolerance > 0 ? toleranceLngLat / tolerance : 0;
226
- const screen = this.hitTestAnchorsInScreenSpace(screenPoint, orderedFeatures, tolerance, degPerPx, selectable);
267
+ const screen = this.hitTestAnchorsInScreenSpace(screenPoint, orderedFeatures, tolerance, tolerance > 0 ? toleranceLngLat / tolerance : 0, selectable);
227
268
  // Stage 2: the precise test
228
269
  const results = screen ? [...screen.results] : [];
229
270
  for (const feature of visibleCandidates) {
@@ -238,6 +279,9 @@ export class HitTestServiceImpl {
238
279
  // nearest hit counts. It is tested in the shape it is drawn with (the result keeps the
239
280
  // stored feature)
240
281
  const shape = this.drawnShape(feature);
282
+ // What is drawn beyond the geometry (the marker of a point) is hit as well
283
+ const reachPx = strategy.reachPx?.(feature) ?? 0;
284
+ const featureTolerance = reachPx > 0 ? toleranceLngLat + reachPx * degPerPx() : toleranceLngLat;
241
285
  let best = null;
242
286
  for (let i = 0; i < copies.length; i++) {
243
287
  if (!candidatesByCopy[i].has(feature.id))
@@ -247,9 +291,9 @@ export class HitTestServiceImpl {
247
291
  // A strategy that has testDistance finishes the test and the distance computation in
248
292
  // a single scan
249
293
  if (strategy.testDistance) {
250
- distance = strategy.testDistance(shape, copy, toleranceLngLat);
294
+ distance = strategy.testDistance(shape, copy, featureTolerance);
251
295
  }
252
- else if (strategy.test(shape, copy, toleranceLngLat)) {
296
+ else if (strategy.test(shape, copy, featureTolerance)) {
253
297
  distance = strategy.distance(shape, copy);
254
298
  }
255
299
  if (distance !== null && (best === null || distance < best))
@@ -289,10 +333,12 @@ export class HitTestServiceImpl {
289
333
  continue;
290
334
  if (!selectable(feature))
291
335
  continue;
292
- const reachPx = tolerancePx + this.candidateReachPx(feature.type);
336
+ const reachPx = tolerancePx +
337
+ this.candidateReachPx(feature.type) +
338
+ (this.registry.get(feature.type)?.reachPx?.(feature) ?? 0);
293
339
  const parts = feature.type === 'MultiPoint'
294
- ? feature.coordinates
295
- : [feature.coordinates];
340
+ ? coordinatesOf(feature)
341
+ : [coordinatesOf(feature)];
296
342
  let nearest = Number.POSITIVE_INFINITY;
297
343
  for (const coord of parts) {
298
344
  const point = projector.project(coord);
@@ -331,3 +377,21 @@ export class HitTestServiceImpl {
331
377
  return strategy.test(this.drawnShape(feature), coordinate, toleranceLngLat);
332
378
  }
333
379
  }
380
+ /**
381
+ * A built-in strategy that reaches as far as the feature is drawn (`HitTestStrategy.reachPx`)
382
+ *
383
+ * Without a reach the strategy is returned as it is.
384
+ */
385
+ function withReach(strategy, reachPx) {
386
+ if (!reachPx)
387
+ return strategy;
388
+ return {
389
+ geometryType: strategy.geometryType,
390
+ test: (feature, coordinate, tolerance) => strategy.test(feature, coordinate, tolerance),
391
+ distance: (feature, coordinate) => strategy.distance(feature, coordinate),
392
+ ...(strategy.testDistance && {
393
+ testDistance: (feature, coordinate, tolerance) => strategy.testDistance?.(feature, coordinate, tolerance) ?? null,
394
+ }),
395
+ reachPx,
396
+ };
397
+ }
@@ -51,7 +51,7 @@ export interface HitTestOptions {
51
51
  export declare const DEFAULT_HIT_TEST_OPTIONS: HitTestOptions;
52
52
  /**
53
53
  * The precise hit test of one feature type, as registered through the `hitTest` of a
54
- * `CustomFeatureHandler`.
54
+ * `FeatureTypeHandler`.
55
55
  *
56
56
  * The library first narrows the candidates with its spatial index and then calls `test` on
57
57
  * each candidate from the front; the first that returns `true` receives the click. The
@@ -60,24 +60,6 @@ export declare const DEFAULT_HIT_TEST_OPTIONS: HitTestOptions;
60
60
  * difference, divide it by cos φ of the click latitude (or multiply the tolerance by cos φ):
61
61
  * one degree of latitude is 1 / cos φ times longer on screen than one degree of longitude.
62
62
  * Distances are returned in the same unit.
63
- *
64
- * @example
65
- * ```ts
66
- * import type { HitTestStrategy } from '@sakuzu/maplibre-gl-draw';
67
- *
68
- * // A marker type hit within the tolerance around its single position
69
- * const markerHitTest: HitTestStrategy = {
70
- * geometryType: 'Marker',
71
- * test(feature, [lng, lat], tolerance) {
72
- * return this.distance(feature, [lng, lat]) <= tolerance;
73
- * },
74
- * distance(feature, [lng, lat]) {
75
- * const [x, y] = feature.coordinates as [number, number];
76
- * const cos = Math.cos((lat * Math.PI) / 180);
77
- * return Math.hypot(x - lng, (y - lat) / cos);
78
- * },
79
- * };
80
- * ```
81
63
  */
82
64
  export interface HitTestStrategy {
83
65
  /** The feature type this strategy tests; a registration replaces the one for the type */
@@ -112,6 +94,18 @@ export interface HitTestStrategy {
112
94
  * @returns The distance (same unit) if within the tolerance, null if outside
113
95
  */
114
96
  testDistance?(feature: Feature, coordinate: Coordinate, toleranceLngLat: number): number | null;
97
+ /**
98
+ * How far beyond its geometry the feature is drawn, in CSS pixels (the radius of the marker
99
+ * of a point)
100
+ *
101
+ * The service adds it to the tolerance it hands to `test` for this feature, and widens the
102
+ * candidate search by it, so that a click on what is drawn hits the feature. Without it,
103
+ * the feature is hit within the tolerance of its geometry alone.
104
+ *
105
+ * @param feature The feature to test
106
+ * @returns The reach in CSS pixels (0 or more)
107
+ */
108
+ reachPx?(feature: Feature): number;
115
109
  }
116
110
  /**
117
111
  * Hit testing strategy registry
@@ -7,6 +7,7 @@
7
7
  * It tests whether the distance from the center is within the radius.
8
8
  */
9
9
  import { haversineDistanceMeters, metersToLng } from '../../../shared/math/index.js';
10
+ import { coordinatesOf } from '../../../shared/utils/coordinates.js';
10
11
  import { getCircleRadius } from '../../../shared/utils/property.js';
11
12
  /**
12
13
  * Hit testing strategy for the Circle type
@@ -21,7 +22,7 @@ export class CircleHitTestStrategy {
21
22
  });
22
23
  }
23
24
  test(feature, coordinate, toleranceLngLat) {
24
- const center = feature.coordinates;
25
+ const center = coordinatesOf(feature);
25
26
  const radiusMeters = getCircleRadius(feature);
26
27
  if (radiusMeters === undefined || radiusMeters <= 0) {
27
28
  return false;
@@ -39,7 +40,7 @@ export class CircleHitTestStrategy {
39
40
  return distanceMeters <= radiusMeters + effectiveTolerance;
40
41
  }
41
42
  distance(feature, coordinate) {
42
- const center = feature.coordinates;
43
+ const center = coordinatesOf(feature);
43
44
  const radiusMeters = getCircleRadius(feature);
44
45
  if (radiusMeters === undefined || radiusMeters <= 0) {
45
46
  return Number.POSITIVE_INFINITY;
@@ -7,18 +7,8 @@
7
7
  * It tests using an OBB (Oriented Bounding Box).
8
8
  */
9
9
  import { createOBB, DEFAULT_TILE_SIZE, distanceToOBB, getMetersPerPixel, metersToDegreesLat, metersToDegreesLng, } from '../../../shared/math/index.js';
10
+ import { coordinatesOf } from '../../../shared/utils/coordinates.js';
10
11
  import { getImageProperties } from '../../../shared/utils/property.js';
11
- /**
12
- * Default image style
13
- * width/height: 0 = use the original size (the same as ImageRenderer)
14
- */
15
- const DEFAULT_IMAGE_STYLE = {
16
- width: 0,
17
- height: 0,
18
- rotation: 0,
19
- opacity: 1.0,
20
- imageOpacity: 1,
21
- };
22
12
  /**
23
13
  * ImageHitTestStrategy
24
14
  */
@@ -88,17 +78,15 @@ export class ImageHitTestStrategy {
88
78
  const properties = getImageProperties(feature);
89
79
  if (!properties.imageFileId)
90
80
  return null;
91
- const coord = feature.coordinates;
81
+ const coord = coordinatesOf(feature);
92
82
  const latitude = coord[1];
93
- const style = { ...DEFAULT_IMAGE_STYLE, ...feature.style };
94
83
  // Get the image size (the same logic as ImageRenderer)
95
84
  // The size stored in ImageProperties takes precedence
96
85
  // 100 is used as the fallback (for when the image has not been loaded)
97
86
  const imageWidth = properties.imageWidth || 100;
98
87
  const imageHeight = properties.imageHeight || 100;
99
- // Use the size specified by the style, or the original size
100
- const displayWidth = style.width || imageWidth;
101
- const displayHeight = style.height || imageHeight;
88
+ const displayWidth = imageWidth;
89
+ const displayHeight = imageHeight;
102
90
  // Compute the size based on the display at createdZoom
103
91
  const createdZoom = properties.createdZoom || 14;
104
92
  const scale = properties.scale || 1;
@@ -108,8 +96,8 @@ export class ImageHitTestStrategy {
108
96
  // Convert meters to degrees
109
97
  const widthDeg = metersToDegreesLng(widthMeters, latitude);
110
98
  const heightDeg = metersToDegreesLat(heightMeters);
111
- // Rotation angle (the sum of both the style and the properties)
112
- const rotation = (style.rotation || 0) + (properties.rotation || 0);
99
+ // Rotation angle
100
+ const rotation = properties.rotation || 0;
113
101
  return { center: coord, widthDeg, heightDeg, rotation };
114
102
  }
115
103
  }
@@ -1,9 +1,3 @@
1
- /**
2
- * LineHitTestStrategy
3
- *
4
- * Hit testing strategy for LineString features.
5
- * Distances are measured in the local frame of hit testing (local-frame.ts).
6
- */
7
1
  import type { Coordinate, Feature, FeatureType } from '../../../store/types.js';
8
2
  import type { HitTestStrategy } from './base.js';
9
3
  /**
@@ -1,5 +1,12 @@
1
1
  // SPDX-FileCopyrightText: 2026 SAKAIDA Atsushi
2
2
  // SPDX-License-Identifier: AGPL-3.0-only
3
+ /**
4
+ * LineHitTestStrategy
5
+ *
6
+ * Hit testing strategy for LineString features.
7
+ * Distances are measured in the local frame of hit testing (local-frame.ts).
8
+ */
9
+ import { coordinatesOf } from '../../../shared/utils/coordinates.js';
3
10
  import { latitudeScale, localPointToPolylineDistance } from '../local-frame.js';
4
11
  import { polylineDistanceWithin } from '../segment-grid.js';
5
12
  /**
@@ -18,13 +25,13 @@ export class LineHitTestStrategy {
18
25
  return this.testDistance(feature, coordinate, toleranceLngLat) !== null;
19
26
  }
20
27
  distance(feature, coordinate) {
21
- const lineCoords = feature.coordinates;
28
+ const lineCoords = coordinatesOf(feature);
22
29
  // The nearest distance without a tolerance cannot be answered by the index, so this
23
30
  // stays a full scan
24
31
  return localPointToPolylineDistance(coordinate, lineCoords, latitudeScale(coordinate[1]));
25
32
  }
26
33
  testDistance(feature, coordinate, toleranceLngLat) {
27
- const lineCoords = feature.coordinates;
34
+ const lineCoords = coordinatesOf(feature);
28
35
  return polylineDistanceWithin(lineCoords, coordinate, toleranceLngLat, latitudeScale(coordinate[1]));
29
36
  }
30
37
  }
@@ -1,11 +1,3 @@
1
- /**
2
- * Hit testing strategies for the Multi geometries
3
- *
4
- * MultiPoint / MultiLineString / MultiPolygon are tested as "a hit if any part is hit"
5
- * and "the distance is the minimum over the parts". The test logic itself reuses the
6
- * strategy of the single geometry for each part. Distances are measured in the local frame
7
- * of hit testing (local-frame.ts).
8
- */
9
1
  import type { Coordinate, Feature, FeatureType } from '../../../store/types.js';
10
2
  import type { HitTestStrategy } from './base.js';
11
3
  /**
@@ -1,5 +1,14 @@
1
1
  // SPDX-FileCopyrightText: 2026 SAKAIDA Atsushi
2
2
  // SPDX-License-Identifier: AGPL-3.0-only
3
+ /**
4
+ * Hit testing strategies for the Multi geometries
5
+ *
6
+ * MultiPoint / MultiLineString / MultiPolygon are tested as "a hit if any part is hit"
7
+ * and "the distance is the minimum over the parts". The test logic itself reuses the
8
+ * strategy of the single geometry for each part. Distances are measured in the local frame
9
+ * of hit testing (local-frame.ts).
10
+ */
11
+ import { coordinatesOf } from '../../../shared/utils/coordinates.js';
3
12
  import { latitudeScale, localDistance, localPointToPolylineDistance } from '../local-frame.js';
4
13
  import { polylineDistanceWithin } from '../segment-grid.js';
5
14
  import { PolygonHitTestStrategy } from './polygon.js';
@@ -18,7 +27,7 @@ export class MultiPointHitTestStrategy {
18
27
  });
19
28
  }
20
29
  test(feature, coordinate, toleranceLngLat) {
21
- const parts = feature.coordinates;
30
+ const parts = coordinatesOf(feature);
22
31
  const latScale = latitudeScale(coordinate[1]);
23
32
  for (const part of parts) {
24
33
  if (localDistance(coordinate, part, latScale) <= toleranceLngLat) {
@@ -28,7 +37,7 @@ export class MultiPointHitTestStrategy {
28
37
  return false;
29
38
  }
30
39
  distance(feature, coordinate) {
31
- const parts = feature.coordinates;
40
+ const parts = coordinatesOf(feature);
32
41
  const latScale = latitudeScale(coordinate[1]);
33
42
  let min = Number.POSITIVE_INFINITY;
34
43
  for (const part of parts) {
@@ -52,7 +61,7 @@ export class MultiLineStringHitTestStrategy {
52
61
  });
53
62
  }
54
63
  test(feature, coordinate, toleranceLngLat) {
55
- const parts = feature.coordinates;
64
+ const parts = coordinatesOf(feature);
56
65
  const latScale = latitudeScale(coordinate[1]);
57
66
  for (const part of parts) {
58
67
  if (part.length === 0)
@@ -64,7 +73,7 @@ export class MultiLineStringHitTestStrategy {
64
73
  return false;
65
74
  }
66
75
  distance(feature, coordinate) {
67
- const parts = feature.coordinates;
76
+ const parts = coordinatesOf(feature);
68
77
  const latScale = latitudeScale(coordinate[1]);
69
78
  let min = Number.POSITIVE_INFINITY;
70
79
  for (const part of parts) {
@@ -75,7 +84,7 @@ export class MultiLineStringHitTestStrategy {
75
84
  return min;
76
85
  }
77
86
  testDistance(feature, coordinate, toleranceLngLat) {
78
- const parts = feature.coordinates;
87
+ const parts = coordinatesOf(feature);
79
88
  const latScale = latitudeScale(coordinate[1]);
80
89
  let min = Number.POSITIVE_INFINITY;
81
90
  for (const part of parts) {
@@ -111,7 +120,7 @@ export class MultiPolygonHitTestStrategy {
111
120
  });
112
121
  }
113
122
  test(feature, coordinate, toleranceLngLat) {
114
- const parts = feature.coordinates;
123
+ const parts = coordinatesOf(feature);
115
124
  for (const rings of parts) {
116
125
  if (this.polygonStrategy.testRings(rings, coordinate, toleranceLngLat)) {
117
126
  return true;
@@ -120,7 +129,7 @@ export class MultiPolygonHitTestStrategy {
120
129
  return false;
121
130
  }
122
131
  distance(feature, coordinate) {
123
- const parts = feature.coordinates;
132
+ const parts = coordinatesOf(feature);
124
133
  let min = Number.POSITIVE_INFINITY;
125
134
  for (const rings of parts) {
126
135
  min = Math.min(min, this.polygonStrategy.distanceToRings(rings, coordinate));
@@ -128,7 +137,7 @@ export class MultiPolygonHitTestStrategy {
128
137
  return min;
129
138
  }
130
139
  testDistance(feature, coordinate, toleranceLngLat) {
131
- const parts = feature.coordinates;
140
+ const parts = coordinatesOf(feature);
132
141
  let min = Number.POSITIVE_INFINITY;
133
142
  for (const rings of parts) {
134
143
  const distance = this.polygonStrategy.testDistanceRings(rings, coordinate, toleranceLngLat);