@sakuzu/maplibre-gl-draw 1.0.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.
- package/CHANGELOG.md +43 -0
- package/LICENSE +661 -0
- package/README.ja.md +248 -0
- package/README.md +255 -0
- package/THIRD_PARTY_NOTICES.md +275 -0
- package/dist/api/api.d.ts +1259 -0
- package/dist/api/api.js +86 -0
- package/dist/api/context.d.ts +173 -0
- package/dist/api/context.js +193 -0
- package/dist/api/display-api.d.ts +1 -0
- package/dist/api/display-api.js +23 -0
- package/dist/api/event-api.d.ts +13 -0
- package/dist/api/event-api.js +17 -0
- package/dist/api/extension-api.d.ts +46 -0
- package/dist/api/extension-api.js +85 -0
- package/dist/api/feature-api.d.ts +11 -0
- package/dist/api/feature-api.js +101 -0
- package/dist/api/geometry/apply.d.ts +51 -0
- package/dist/api/geometry/apply.js +114 -0
- package/dist/api/geometry/boolean.d.ts +28 -0
- package/dist/api/geometry/boolean.js +65 -0
- package/dist/api/geometry/buffer.d.ts +11 -0
- package/dist/api/geometry/buffer.js +105 -0
- package/dist/api/geometry/split.d.ts +16 -0
- package/dist/api/geometry/split.js +75 -0
- package/dist/api/geometry/targets.d.ts +76 -0
- package/dist/api/geometry/targets.js +158 -0
- package/dist/api/geometry/types.d.ts +31 -0
- package/dist/api/geometry/types.js +3 -0
- package/dist/api/geometry-operations.d.ts +162 -0
- package/dist/api/geometry-operations.js +94 -0
- package/dist/api/group-api.d.ts +10 -0
- package/dist/api/group-api.js +78 -0
- package/dist/api/import-export/constants.d.ts +29 -0
- package/dist/api/import-export/constants.js +40 -0
- package/dist/api/import-export/embedded-file.d.ts +13 -0
- package/dist/api/import-export/embedded-file.js +31 -0
- package/dist/api/import-export/file-name.d.ts +10 -0
- package/dist/api/import-export/file-name.js +20 -0
- package/dist/api/import-export/format-detection.d.ts +12 -0
- package/dist/api/import-export/format-detection.js +29 -0
- package/dist/api/import-export/geojson-export.d.ts +19 -0
- package/dist/api/import-export/geojson-export.js +262 -0
- package/dist/api/import-export/geojson-import.d.ts +34 -0
- package/dist/api/import-export/geojson-import.js +538 -0
- package/dist/api/import-export/geometry-validation.d.ts +20 -0
- package/dist/api/import-export/geometry-validation.js +107 -0
- package/dist/api/import-export/image-import.d.ts +12 -0
- package/dist/api/import-export/image-import.js +53 -0
- package/dist/api/import-export/index.d.ts +9 -0
- package/dist/api/import-export/index.js +107 -0
- package/dist/api/import-export/native-format.d.ts +15 -0
- package/dist/api/import-export/native-format.js +126 -0
- package/dist/api/import-export/native-validation.d.ts +28 -0
- package/dist/api/import-export/native-validation.js +212 -0
- package/dist/api/import-export/own-property.d.ts +10 -0
- package/dist/api/import-export/own-property.js +19 -0
- package/dist/api/import-export/style-validation.d.ts +27 -0
- package/dist/api/import-export/style-validation.js +49 -0
- package/dist/api/import-export/types.d.ts +26 -0
- package/dist/api/import-export/types.js +3 -0
- package/dist/api/index.d.ts +15 -0
- package/dist/api/index.js +3 -0
- package/dist/api/input-api.d.ts +158 -0
- package/dist/api/input-api.js +144 -0
- package/dist/api/instance-api.d.ts +38 -0
- package/dist/api/instance-api.js +136 -0
- package/dist/api/layer-api.d.ts +12 -0
- package/dist/api/layer-api.js +62 -0
- package/dist/api/selection-api.d.ts +7 -0
- package/dist/api/selection-api.js +78 -0
- package/dist/api/snapping-api.d.ts +125 -0
- package/dist/api/snapping-api.js +50 -0
- package/dist/api/topology-api.d.ts +30 -0
- package/dist/api/topology-api.js +16 -0
- package/dist/api/tracing-api.d.ts +31 -0
- package/dist/api/tracing-api.js +16 -0
- package/dist/dispatcher/hit-test/box-strategies.d.ts +19 -0
- package/dist/dispatcher/hit-test/box-strategies.js +275 -0
- package/dist/dispatcher/hit-test/box-strategy.d.ts +59 -0
- package/dist/dispatcher/hit-test/box-strategy.js +37 -0
- package/dist/dispatcher/hit-test/globe-shape.d.ts +46 -0
- package/dist/dispatcher/hit-test/globe-shape.js +70 -0
- package/dist/dispatcher/hit-test/index.d.ts +14 -0
- package/dist/dispatcher/hit-test/index.js +9 -0
- package/dist/dispatcher/hit-test/local-frame.d.ts +77 -0
- package/dist/dispatcher/hit-test/local-frame.js +112 -0
- package/dist/dispatcher/hit-test/segment-grid.d.ts +32 -0
- package/dist/dispatcher/hit-test/segment-grid.js +79 -0
- package/dist/dispatcher/hit-test/service.d.ts +120 -0
- package/dist/dispatcher/hit-test/service.js +333 -0
- package/dist/dispatcher/hit-test/strategies/base.d.ts +136 -0
- package/dist/dispatcher/hit-test/strategies/base.js +66 -0
- package/dist/dispatcher/hit-test/strategies/circle.d.ts +10 -0
- package/dist/dispatcher/hit-test/strategies/circle.js +56 -0
- package/dist/dispatcher/hit-test/strategies/image.d.ts +22 -0
- package/dist/dispatcher/hit-test/strategies/image.js +115 -0
- package/dist/dispatcher/hit-test/strategies/index.d.ts +11 -0
- package/dist/dispatcher/hit-test/strategies/index.js +10 -0
- package/dist/dispatcher/hit-test/strategies/line.d.ts +17 -0
- package/dist/dispatcher/hit-test/strategies/line.js +30 -0
- package/dist/dispatcher/hit-test/strategies/multi.d.ts +44 -0
- package/dist/dispatcher/hit-test/strategies/multi.js +141 -0
- package/dist/dispatcher/hit-test/strategies/point.d.ts +20 -0
- package/dist/dispatcher/hit-test/strategies/point.js +29 -0
- package/dist/dispatcher/hit-test/strategies/polygon.d.ts +52 -0
- package/dist/dispatcher/hit-test/strategies/polygon.js +138 -0
- package/dist/dispatcher/hit-test/topmost.d.ts +48 -0
- package/dist/dispatcher/hit-test/topmost.js +157 -0
- package/dist/dispatcher/hit-test/visibility-lookup.d.ts +23 -0
- package/dist/dispatcher/hit-test/visibility-lookup.js +40 -0
- package/dist/dispatcher/index.d.ts +15 -0
- package/dist/dispatcher/index.js +3 -0
- package/dist/dispatcher/input-router.d.ts +1 -0
- package/dist/dispatcher/input-router.js +288 -0
- package/dist/dispatcher/normalizer.d.ts +1 -0
- package/dist/dispatcher/normalizer.js +513 -0
- package/dist/dispatcher/types.d.ts +144 -0
- package/dist/dispatcher/types.js +15 -0
- package/dist/display/chunk-set.d.ts +24 -0
- package/dist/display/chunk-set.js +533 -0
- package/dist/display/chunk.d.ts +1 -0
- package/dist/display/chunk.js +104 -0
- package/dist/display/columnar/index.d.ts +12 -0
- package/dist/display/columnar/index.js +13 -0
- package/dist/display/columnar/prepare.d.ts +42 -0
- package/dist/display/columnar/prepare.js +116 -0
- package/dist/display/columnar/source.d.ts +1 -0
- package/dist/display/columnar/source.js +531 -0
- package/dist/display/columnar/table.d.ts +1 -0
- package/dist/display/columnar/table.js +471 -0
- package/dist/display/columnar/types.d.ts +223 -0
- package/dist/display/columnar/types.js +3 -0
- package/dist/display/dataset.d.ts +2 -0
- package/dist/display/dataset.js +891 -0
- package/dist/display/index.d.ts +13 -0
- package/dist/display/index.js +3 -0
- package/dist/display/interaction.d.ts +1 -0
- package/dist/display/interaction.js +90 -0
- package/dist/display/manager.d.ts +1 -0
- package/dist/display/manager.js +440 -0
- package/dist/display/packed-rtree.d.ts +1 -0
- package/dist/display/packed-rtree.js +244 -0
- package/dist/display/partition.d.ts +1 -0
- package/dist/display/partition.js +226 -0
- package/dist/display/provider.d.ts +1 -0
- package/dist/display/provider.js +342 -0
- package/dist/display/retained.d.ts +40 -0
- package/dist/display/retained.js +898 -0
- package/dist/display/selection.d.ts +13 -0
- package/dist/display/selection.js +226 -0
- package/dist/display/source.d.ts +1 -0
- package/dist/display/source.js +119 -0
- package/dist/display/spatial.d.ts +1 -0
- package/dist/display/spatial.js +71 -0
- package/dist/display/style.d.ts +1 -0
- package/dist/display/style.js +126 -0
- package/dist/display/thinning.d.ts +65 -0
- package/dist/display/thinning.js +910 -0
- package/dist/display/triangulation.d.ts +1 -0
- package/dist/display/triangulation.js +0 -0
- package/dist/display/types.d.ts +561 -0
- package/dist/display/types.js +43 -0
- package/dist/extension/feature-handler.d.ts +182 -0
- package/dist/extension/feature-handler.js +3 -0
- package/dist/extension/index.d.ts +9 -0
- package/dist/extension/index.js +3 -0
- package/dist/extension/renderers.d.ts +208 -0
- package/dist/extension/renderers.js +3 -0
- package/dist/geometry/angle.d.ts +47 -0
- package/dist/geometry/angle.js +44 -0
- package/dist/geometry/bbox.d.ts +48 -0
- package/dist/geometry/bbox.js +83 -0
- package/dist/geometry/boolean.d.ts +162 -0
- package/dist/geometry/boolean.js +331 -0
- package/dist/geometry/buffer.d.ts +72 -0
- package/dist/geometry/buffer.js +218 -0
- package/dist/geometry/circle.d.ts +55 -0
- package/dist/geometry/circle.js +107 -0
- package/dist/geometry/coords.d.ts +97 -0
- package/dist/geometry/coords.js +149 -0
- package/dist/geometry/distance.d.ts +79 -0
- package/dist/geometry/distance.js +111 -0
- package/dist/geometry/errors.d.ts +74 -0
- package/dist/geometry/errors.js +104 -0
- package/dist/geometry/index.d.ts +28 -0
- package/dist/geometry/index.js +38 -0
- package/dist/geometry/measure.d.ts +81 -0
- package/dist/geometry/measure.js +236 -0
- package/dist/geometry/predicates.d.ts +62 -0
- package/dist/geometry/predicates.js +132 -0
- package/dist/geometry/simplify.d.ts +74 -0
- package/dist/geometry/simplify.js +159 -0
- package/dist/geometry/split.d.ts +68 -0
- package/dist/geometry/split.js +430 -0
- package/dist/geometry/types.d.ts +95 -0
- package/dist/geometry/types.js +3 -0
- package/dist/geometry/units.d.ts +35 -0
- package/dist/geometry/units.js +41 -0
- package/dist/index.d.ts +72 -0
- package/dist/index.js +20 -0
- package/dist/maplibre-gl-draw.d.ts +47 -0
- package/dist/maplibre-gl-draw.js +407 -0
- package/dist/messages.d.ts +64 -0
- package/dist/messages.js +36 -0
- package/dist/modes/cursor.d.ts +1 -0
- package/dist/modes/cursor.js +72 -0
- package/dist/modes/draw/circle.d.ts +1 -0
- package/dist/modes/draw/circle.js +222 -0
- package/dist/modes/draw/commit-layer.d.ts +26 -0
- package/dist/modes/draw/commit-layer.js +31 -0
- package/dist/modes/draw/freehand.d.ts +1 -0
- package/dist/modes/draw/freehand.js +264 -0
- package/dist/modes/draw/image.d.ts +1 -0
- package/dist/modes/draw/image.js +60 -0
- package/dist/modes/draw/index.d.ts +9 -0
- package/dist/modes/draw/index.js +11 -0
- package/dist/modes/draw/line.d.ts +1 -0
- package/dist/modes/draw/line.js +363 -0
- package/dist/modes/draw/point.d.ts +1 -0
- package/dist/modes/draw/point.js +87 -0
- package/dist/modes/draw/polygon.d.ts +1 -0
- package/dist/modes/draw/polygon.js +378 -0
- package/dist/modes/draw/trace-support.d.ts +79 -0
- package/dist/modes/draw/trace-support.js +161 -0
- package/dist/modes/handler.d.ts +285 -0
- package/dist/modes/handler.js +3 -0
- package/dist/modes/index.d.ts +7 -0
- package/dist/modes/index.js +3 -0
- package/dist/modes/manager.d.ts +15 -0
- package/dist/modes/manager.js +196 -0
- package/dist/modes/select/box-selection.d.ts +52 -0
- package/dist/modes/select/box-selection.js +140 -0
- package/dist/modes/select/click-handler.d.ts +32 -0
- package/dist/modes/select/click-handler.js +166 -0
- package/dist/modes/select/cursor-handler.d.ts +15 -0
- package/dist/modes/select/cursor-handler.js +59 -0
- package/dist/modes/select/drag/auxiliary.d.ts +26 -0
- package/dist/modes/select/drag/auxiliary.js +56 -0
- package/dist/modes/select/drag/intermediate-writes.d.ts +1 -0
- package/dist/modes/select/drag/intermediate-writes.js +171 -0
- package/dist/modes/select/drag/move.d.ts +24 -0
- package/dist/modes/select/drag/move.js +59 -0
- package/dist/modes/select/drag/operation.d.ts +30 -0
- package/dist/modes/select/drag/operation.js +18 -0
- package/dist/modes/select/drag/radius.d.ts +15 -0
- package/dist/modes/select/drag/radius.js +49 -0
- package/dist/modes/select/drag/transform.d.ts +17 -0
- package/dist/modes/select/drag/transform.js +155 -0
- package/dist/modes/select/drag/vertex.d.ts +17 -0
- package/dist/modes/select/drag/vertex.js +163 -0
- package/dist/modes/select/drag-handler.d.ts +1 -0
- package/dist/modes/select/drag-handler.js +254 -0
- package/dist/modes/select/hit-helpers.d.ts +40 -0
- package/dist/modes/select/hit-helpers.js +51 -0
- package/dist/modes/select/mode.d.ts +1 -0
- package/dist/modes/select/mode.js +397 -0
- package/dist/modes/select/shortcut-handler.d.ts +40 -0
- package/dist/modes/select/shortcut-handler.js +128 -0
- package/dist/operations/index.d.ts +9 -0
- package/dist/operations/index.js +3 -0
- package/dist/operations/layer-operations.d.ts +1 -0
- package/dist/operations/layer-operations.js +350 -0
- package/dist/operations/resize.d.ts +71 -0
- package/dist/operations/resize.js +252 -0
- package/dist/operations/rotate.d.ts +17 -0
- package/dist/operations/rotate.js +166 -0
- package/dist/operations/selection-operations.d.ts +20 -0
- package/dist/operations/selection-operations.js +159 -0
- package/dist/operations/shared-vertex.d.ts +1 -0
- package/dist/operations/shared-vertex.js +210 -0
- package/dist/operations/trace-graph.d.ts +96 -0
- package/dist/operations/trace-graph.js +283 -0
- package/dist/operations/vertex.d.ts +1 -0
- package/dist/operations/vertex.js +538 -0
- package/dist/plugins/index.d.ts +10 -0
- package/dist/plugins/index.js +3 -0
- package/dist/plugins/mutation-hooks.d.ts +28 -0
- package/dist/plugins/mutation-hooks.js +84 -0
- package/dist/plugins/plugin-context.d.ts +1 -0
- package/dist/plugins/plugin-context.js +367 -0
- package/dist/plugins/plugin-manager.d.ts +103 -0
- package/dist/plugins/plugin-manager.js +316 -0
- package/dist/plugins/plugin.d.ts +529 -0
- package/dist/plugins/plugin.js +3 -0
- package/dist/shared/config/constants.d.ts +66 -0
- package/dist/shared/config/constants.js +60 -0
- package/dist/shared/config/feature-style.d.ts +155 -0
- package/dist/shared/config/feature-style.js +154 -0
- package/dist/shared/config/index.d.ts +17 -0
- package/dist/shared/config/index.js +8 -0
- package/dist/shared/config/rendering.d.ts +107 -0
- package/dist/shared/config/rendering.js +33 -0
- package/dist/shared/config/selection-highlight.d.ts +29 -0
- package/dist/shared/config/selection-highlight.js +35 -0
- package/dist/shared/config/selection.d.ts +273 -0
- package/dist/shared/config/selection.js +266 -0
- package/dist/shared/config/topology.d.ts +33 -0
- package/dist/shared/config/topology.js +17 -0
- package/dist/shared/config/trace.d.ts +40 -0
- package/dist/shared/config/trace.js +17 -0
- package/dist/shared/math/angle.d.ts +21 -0
- package/dist/shared/math/angle.js +34 -0
- package/dist/shared/math/circle.d.ts +6 -0
- package/dist/shared/math/circle.js +10 -0
- package/dist/shared/math/constants.d.ts +48 -0
- package/dist/shared/math/constants.js +59 -0
- package/dist/shared/math/distance.d.ts +78 -0
- package/dist/shared/math/distance.js +147 -0
- package/dist/shared/math/globe-subdivision.d.ts +62 -0
- package/dist/shared/math/globe-subdivision.js +99 -0
- package/dist/shared/math/index.d.ts +17 -0
- package/dist/shared/math/index.js +21 -0
- package/dist/shared/math/intersection.d.ts +63 -0
- package/dist/shared/math/intersection.js +236 -0
- package/dist/shared/math/longitude.d.ts +20 -0
- package/dist/shared/math/longitude.js +31 -0
- package/dist/shared/math/mercator-plane.d.ts +27 -0
- package/dist/shared/math/mercator-plane.js +26 -0
- package/dist/shared/math/obb.d.ts +65 -0
- package/dist/shared/math/obb.js +107 -0
- package/dist/shared/math/rotation.d.ts +93 -0
- package/dist/shared/math/rotation.js +186 -0
- package/dist/shared/math/segment-grid.d.ts +69 -0
- package/dist/shared/math/segment-grid.js +159 -0
- package/dist/shared/math/transform.d.ts +140 -0
- package/dist/shared/math/transform.js +189 -0
- package/dist/shared/types/events.d.ts +128 -0
- package/dist/shared/types/events.js +3 -0
- package/dist/shared/types/model.d.ts +950 -0
- package/dist/shared/types/model.js +3 -0
- package/dist/shared/types/selection-box.d.ts +23 -0
- package/dist/shared/types/selection-box.js +3 -0
- package/dist/shared/types/style.d.ts +68 -0
- package/dist/shared/types/style.js +3 -0
- package/dist/shared/utils/color.d.ts +49 -0
- package/dist/shared/utils/color.js +180 -0
- package/dist/shared/utils/coordinates.d.ts +52 -0
- package/dist/shared/utils/coordinates.js +75 -0
- package/dist/shared/utils/embedded-image.d.ts +34 -0
- package/dist/shared/utils/embedded-image.js +161 -0
- package/dist/shared/utils/event-emitter.d.ts +262 -0
- package/dist/shared/utils/event-emitter.js +44 -0
- package/dist/shared/utils/feature-bbox.d.ts +8 -0
- package/dist/shared/utils/feature-bbox.js +207 -0
- package/dist/shared/utils/feature-segments.d.ts +23 -0
- package/dist/shared/utils/feature-segments.js +179 -0
- package/dist/shared/utils/id.d.ts +1 -0
- package/dist/shared/utils/id.js +35 -0
- package/dist/shared/utils/image.d.ts +34 -0
- package/dist/shared/utils/image.js +106 -0
- package/dist/shared/utils/index.d.ts +15 -0
- package/dist/shared/utils/index.js +24 -0
- package/dist/shared/utils/map.d.ts +26 -0
- package/dist/shared/utils/map.js +35 -0
- package/dist/shared/utils/name-generator.d.ts +197 -0
- package/dist/shared/utils/name-generator.js +337 -0
- package/dist/shared/utils/pixel-ratio.d.ts +111 -0
- package/dist/shared/utils/pixel-ratio.js +85 -0
- package/dist/shared/utils/property.d.ts +88 -0
- package/dist/shared/utils/property.js +140 -0
- package/dist/shared/utils/vertex-ref.d.ts +23 -0
- package/dist/shared/utils/vertex-ref.js +20 -0
- package/dist/snapping/custom-targets.d.ts +1 -0
- package/dist/snapping/custom-targets.js +21 -0
- package/dist/snapping/geometry.d.ts +1 -0
- package/dist/snapping/geometry.js +61 -0
- package/dist/snapping/index.d.ts +14 -0
- package/dist/snapping/index.js +4 -0
- package/dist/snapping/indicator.d.ts +19 -0
- package/dist/snapping/indicator.js +145 -0
- package/dist/snapping/providers/display.d.ts +72 -0
- package/dist/snapping/providers/display.js +268 -0
- package/dist/snapping/providers/guide.d.ts +69 -0
- package/dist/snapping/providers/guide.js +187 -0
- package/dist/snapping/providers/intersection.d.ts +1 -0
- package/dist/snapping/providers/intersection.js +92 -0
- package/dist/snapping/providers/shared.d.ts +39 -0
- package/dist/snapping/providers/shared.js +146 -0
- package/dist/snapping/providers/store.d.ts +1 -0
- package/dist/snapping/providers/store.js +132 -0
- package/dist/snapping/service.d.ts +1 -0
- package/dist/snapping/service.js +272 -0
- package/dist/snapping/types.d.ts +267 -0
- package/dist/snapping/types.js +66 -0
- package/dist/store/change-merger.d.ts +1 -0
- package/dist/store/change-merger.js +102 -0
- package/dist/store/draw-store.d.ts +1 -0
- package/dist/store/draw-store.js +396 -0
- package/dist/store/event-bridge.d.ts +1 -0
- package/dist/store/event-bridge.js +161 -0
- package/dist/store/index.d.ts +12 -0
- package/dist/store/index.js +4 -0
- package/dist/store/local-visibility.d.ts +21 -0
- package/dist/store/local-visibility.js +27 -0
- package/dist/store/lock.d.ts +67 -0
- package/dist/store/lock.js +54 -0
- package/dist/store/memory/change-bus.d.ts +60 -0
- package/dist/store/memory/change-bus.js +232 -0
- package/dist/store/memory/file-store.d.ts +15 -0
- package/dist/store/memory/file-store.js +33 -0
- package/dist/store/memory/frozen.d.ts +21 -0
- package/dist/store/memory/frozen.js +51 -0
- package/dist/store/memory/ui-state.d.ts +51 -0
- package/dist/store/memory/ui-state.js +223 -0
- package/dist/store/memory.d.ts +26 -0
- package/dist/store/memory.js +708 -0
- package/dist/store/spatial/index.d.ts +6 -0
- package/dist/store/spatial/index.js +4 -0
- package/dist/store/spatial/spatial-index.d.ts +90 -0
- package/dist/store/spatial/spatial-index.js +148 -0
- package/dist/store/spatial/store-spatial-index.d.ts +60 -0
- package/dist/store/spatial/store-spatial-index.js +125 -0
- package/dist/store/store.d.ts +417 -0
- package/dist/store/store.js +3 -0
- package/dist/store/types.d.ts +8 -0
- package/dist/store/types.js +3 -0
- package/dist/store/writable-layer.d.ts +35 -0
- package/dist/store/writable-layer.js +20 -0
- package/dist/view/cache/buffer.d.ts +28 -0
- package/dist/view/cache/buffer.js +176 -0
- package/dist/view/cache/earcut.d.ts +1 -0
- package/dist/view/cache/earcut.js +122 -0
- package/dist/view/cache/style-rule.d.ts +1 -0
- package/dist/view/cache/style-rule.js +97 -0
- package/dist/view/cache/terrain-fill.d.ts +30 -0
- package/dist/view/cache/terrain-fill.js +60 -0
- package/dist/view/cache/texture.d.ts +14 -0
- package/dist/view/cache/texture.js +292 -0
- package/dist/view/coordinator.d.ts +27 -0
- package/dist/view/coordinator.js +72 -0
- package/dist/view/feature-companion.d.ts +173 -0
- package/dist/view/feature-companion.js +108 -0
- package/dist/view/globe-subdivision.d.ts +78 -0
- package/dist/view/globe-subdivision.js +128 -0
- package/dist/view/index.d.ts +47 -0
- package/dist/view/index.js +20 -0
- package/dist/view/layer/attach.d.ts +20 -0
- package/dist/view/layer/attach.js +47 -0
- package/dist/view/layer/blend.d.ts +57 -0
- package/dist/view/layer/blend.js +14 -0
- package/dist/view/layer/custom-layer.d.ts +1 -0
- package/dist/view/layer/custom-layer.js +509 -0
- package/dist/view/layer/depth-state.d.ts +14 -0
- package/dist/view/layer/depth-state.js +44 -0
- package/dist/view/layer/display-list.d.ts +1 -0
- package/dist/view/layer/display-list.js +111 -0
- package/dist/view/layer/drape-planner.d.ts +139 -0
- package/dist/view/layer/drape-planner.js +726 -0
- package/dist/view/layer/frame-render.d.ts +57 -0
- package/dist/view/layer/frame-render.js +209 -0
- package/dist/view/layer/frame-state.d.ts +147 -0
- package/dist/view/layer/frame-state.js +484 -0
- package/dist/view/layer/gl-state.d.ts +77 -0
- package/dist/view/layer/gl-state.js +132 -0
- package/dist/view/layer/index.d.ts +6 -0
- package/dist/view/layer/index.js +5 -0
- package/dist/view/layer/render-scope.d.ts +30 -0
- package/dist/view/layer/render-scope.js +28 -0
- package/dist/view/layer/render.d.ts +87 -0
- package/dist/view/layer/render.js +187 -0
- package/dist/view/layer/renderers.d.ts +81 -0
- package/dist/view/layer/renderers.js +142 -0
- package/dist/view/layer/slot-manager.d.ts +26 -0
- package/dist/view/layer/slot-manager.js +133 -0
- package/dist/view/layer/slots.d.ts +33 -0
- package/dist/view/layer/slots.js +50 -0
- package/dist/view/layer/store-retained-bbox.d.ts +36 -0
- package/dist/view/layer/store-retained-bbox.js +188 -0
- package/dist/view/layer/store-retained-chunk.d.ts +97 -0
- package/dist/view/layer/store-retained-chunk.js +94 -0
- package/dist/view/layer/store-retained-classify.d.ts +34 -0
- package/dist/view/layer/store-retained-classify.js +88 -0
- package/dist/view/layer/store-retained-collect.d.ts +24 -0
- package/dist/view/layer/store-retained-collect.js +121 -0
- package/dist/view/layer/store-retained-coord-patch.d.ts +119 -0
- package/dist/view/layer/store-retained-coord-patch.js +168 -0
- package/dist/view/layer/store-retained-immediate.d.ts +63 -0
- package/dist/view/layer/store-retained-immediate.js +49 -0
- package/dist/view/layer/store-retained-invalidation.d.ts +96 -0
- package/dist/view/layer/store-retained-invalidation.js +192 -0
- package/dist/view/layer/store-retained.d.ts +198 -0
- package/dist/view/layer/store-retained.js +430 -0
- package/dist/view/layer/terrain-resolver.d.ts +1 -0
- package/dist/view/layer/terrain-resolver.js +174 -0
- package/dist/view/renderers/batch-manager.d.ts +61 -0
- package/dist/view/renderers/batch-manager.js +718 -0
- package/dist/view/renderers/draw-factors.d.ts +60 -0
- package/dist/view/renderers/draw-factors.js +35 -0
- package/dist/view/renderers/drawer.d.ts +1 -0
- package/dist/view/renderers/drawer.js +346 -0
- package/dist/view/renderers/image.d.ts +1 -0
- package/dist/view/renderers/image.js +255 -0
- package/dist/view/renderers/line/dash.d.ts +31 -0
- package/dist/view/renderers/line/dash.js +152 -0
- package/dist/view/renderers/line/line-geometry.d.ts +39 -0
- package/dist/view/renderers/line/line-geometry.js +460 -0
- package/dist/view/renderers/line/line-gl.d.ts +1 -0
- package/dist/view/renderers/line/line-gl.js +236 -0
- package/dist/view/renderers/line/line-shader.d.ts +1 -0
- package/dist/view/renderers/line/line-shader.js +364 -0
- package/dist/view/renderers/line/line-types.d.ts +43 -0
- package/dist/view/renderers/line/line-types.js +3 -0
- package/dist/view/renderers/line/line-uniforms.d.ts +1 -0
- package/dist/view/renderers/line/line-uniforms.js +390 -0
- package/dist/view/renderers/line/sdf-line.d.ts +92 -0
- package/dist/view/renderers/line/sdf-line.js +567 -0
- package/dist/view/renderers/point/billboard-depth.d.ts +10 -0
- package/dist/view/renderers/point/billboard-depth.js +56 -0
- package/dist/view/renderers/point/point-instance.d.ts +124 -0
- package/dist/view/renderers/point/point-instance.js +738 -0
- package/dist/view/renderers/point/point-sdf.d.ts +58 -0
- package/dist/view/renderers/point/point-sdf.js +170 -0
- package/dist/view/renderers/point/point-shape.d.ts +99 -0
- package/dist/view/renderers/point/point-shape.js +504 -0
- package/dist/view/renderers/point/point-style.d.ts +1 -0
- package/dist/view/renderers/point/point-style.js +46 -0
- package/dist/view/renderers/polygon/batch.d.ts +26 -0
- package/dist/view/renderers/polygon/batch.js +336 -0
- package/dist/view/renderers/polygon/earcut-input.d.ts +37 -0
- package/dist/view/renderers/polygon/earcut-input.js +55 -0
- package/dist/view/renderers/polygon/earcut-sliced.d.ts +69 -0
- package/dist/view/renderers/polygon/earcut-sliced.js +960 -0
- package/dist/view/renderers/polygon/fill.d.ts +74 -0
- package/dist/view/renderers/polygon/fill.js +251 -0
- package/dist/view/renderers/polygon/sdf-polygon.d.ts +62 -0
- package/dist/view/renderers/polygon/sdf-polygon.js +919 -0
- package/dist/view/renderers/polygon/terrain-cull.d.ts +3 -0
- package/dist/view/renderers/polygon/terrain-cull.js +14 -0
- package/dist/view/renderers/polygon/triangulator.d.ts +41 -0
- package/dist/view/renderers/polygon/triangulator.js +3 -0
- package/dist/view/renderers/retained.d.ts +43 -0
- package/dist/view/renderers/retained.js +3 -0
- package/dist/view/renderers/stroke.d.ts +121 -0
- package/dist/view/renderers/stroke.js +621 -0
- package/dist/view/shaders/frame.d.ts +8 -0
- package/dist/view/shaders/frame.js +37 -0
- package/dist/view/shaders/helpers.d.ts +238 -0
- package/dist/view/shaders/helpers.js +611 -0
- package/dist/view/shaders/initializer.d.ts +27 -0
- package/dist/view/shaders/initializer.js +125 -0
- package/dist/view/shaders/projection.d.ts +123 -0
- package/dist/view/shaders/projection.js +299 -0
- package/dist/view/shaders/quad-grid.d.ts +77 -0
- package/dist/view/shaders/quad-grid.js +128 -0
- package/dist/view/shaders/quad.d.ts +189 -0
- package/dist/view/shaders/quad.js +582 -0
- package/dist/view/shaders/retained-origin.d.ts +50 -0
- package/dist/view/shaders/retained-origin.js +67 -0
- package/dist/view/shaders/terrain-shade.d.ts +23 -0
- package/dist/view/shaders/terrain-shade.js +93 -0
- package/dist/view/style-rule.d.ts +166 -0
- package/dist/view/style-rule.js +300 -0
- package/dist/view/terrain/anchor.d.ts +135 -0
- package/dist/view/terrain/anchor.js +226 -0
- package/dist/view/terrain/context.d.ts +132 -0
- package/dist/view/terrain/context.js +437 -0
- package/dist/view/terrain/dem-atlas.d.ts +97 -0
- package/dist/view/terrain/dem-atlas.js +534 -0
- package/dist/view/terrain/detect.d.ts +126 -0
- package/dist/view/terrain/detect.js +198 -0
- package/dist/view/terrain/drape/bin-store.d.ts +179 -0
- package/dist/view/terrain/drape/bin-store.js +529 -0
- package/dist/view/terrain/drape/binning.d.ts +259 -0
- package/dist/view/terrain/drape/binning.js +604 -0
- package/dist/view/terrain/drape/edge-constrain.d.ts +37 -0
- package/dist/view/terrain/drape/edge-constrain.js +175 -0
- package/dist/view/terrain/drape/geometry.d.ts +95 -0
- package/dist/view/terrain/drape/geometry.js +260 -0
- package/dist/view/terrain/drape/mesh.d.ts +50 -0
- package/dist/view/terrain/drape/mesh.js +86 -0
- package/dist/view/terrain/drape/pass.d.ts +122 -0
- package/dist/view/terrain/drape/pass.js +379 -0
- package/dist/view/terrain/drape/quad-glyphs.d.ts +64 -0
- package/dist/view/terrain/drape/quad-glyphs.js +117 -0
- package/dist/view/terrain/drape/quad.d.ts +249 -0
- package/dist/view/terrain/drape/quad.js +835 -0
- package/dist/view/terrain/drape/renderer.d.ts +184 -0
- package/dist/view/terrain/drape/renderer.js +706 -0
- package/dist/view/terrain/drape/shared.d.ts +80 -0
- package/dist/view/terrain/drape/shared.js +173 -0
- package/dist/view/terrain/drape/stitch.d.ts +155 -0
- package/dist/view/terrain/drape/stitch.js +228 -0
- package/dist/view/terrain/ground.d.ts +72 -0
- package/dist/view/terrain/ground.js +151 -0
- package/dist/view/terrain/metrics.d.ts +180 -0
- package/dist/view/terrain/metrics.js +231 -0
- package/dist/view/terrain/occlusion.d.ts +11 -0
- package/dist/view/terrain/occlusion.js +129 -0
- package/dist/view/terrain/polygon.d.ts +86 -0
- package/dist/view/terrain/polygon.js +301 -0
- package/dist/view/terrain/shade.d.ts +48 -0
- package/dist/view/terrain/shade.js +45 -0
- package/dist/view/terrain/state.d.ts +99 -0
- package/dist/view/terrain/state.js +186 -0
- package/dist/view/terrain/tessellation.d.ts +265 -0
- package/dist/view/terrain/tessellation.js +924 -0
- package/dist/view/terrain/tiling.d.ts +56 -0
- package/dist/view/terrain/tiling.js +155 -0
- package/dist/view/terrain/upstream-terrain.d.ts +82 -0
- package/dist/view/terrain/upstream-terrain.js +116 -0
- package/dist/view/ui/auxiliary-handles.d.ts +151 -0
- package/dist/view/ui/auxiliary-handles.js +22 -0
- package/dist/view/ui/bounds.d.ts +53 -0
- package/dist/view/ui/bounds.js +149 -0
- package/dist/view/ui/box-selection.d.ts +1 -0
- package/dist/view/ui/box-selection.js +83 -0
- package/dist/view/ui/handle-test.d.ts +1 -0
- package/dist/view/ui/handle-test.js +437 -0
- package/dist/view/ui/handle-thinning.d.ts +122 -0
- package/dist/view/ui/handle-thinning.js +430 -0
- package/dist/view/ui/handles.d.ts +25 -0
- package/dist/view/ui/handles.js +673 -0
- package/dist/view/ui/helper.d.ts +19 -0
- package/dist/view/ui/helper.js +76 -0
- package/dist/view/ui/index.d.ts +15 -0
- package/dist/view/ui/index.js +15 -0
- package/dist/view/ui/selection-scope.d.ts +25 -0
- package/dist/view/ui/selection-scope.js +34 -0
- package/dist/view/ui/selection-ui/bounding-box.d.ts +44 -0
- package/dist/view/ui/selection-ui/bounding-box.js +240 -0
- package/dist/view/ui/selection-ui/extension-registry.d.ts +76 -0
- package/dist/view/ui/selection-ui/extension-registry.js +78 -0
- package/dist/view/ui/selection-ui/index.d.ts +12 -0
- package/dist/view/ui/selection-ui/index.js +12 -0
- package/dist/view/ui/selection-ui/renderer.d.ts +29 -0
- package/dist/view/ui/selection-ui/renderer.js +227 -0
- package/dist/view/ui/selection-ui/types.d.ts +55 -0
- package/dist/view/ui/selection-ui/types.js +3 -0
- package/dist/view/ui/selection-ui-drawer.d.ts +52 -0
- package/dist/view/ui/selection-ui-drawer.js +329 -0
- package/dist/view/ui/tentative.d.ts +1 -0
- package/dist/view/ui/tentative.js +320 -0
- package/dist/view/viewport.d.ts +119 -0
- package/dist/view/viewport.js +254 -0
- package/package.json +113 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2026 SAKAIDA Atsushi
|
|
2
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
3
|
+
/**
|
|
4
|
+
* Import of image files (creates an Image feature)
|
|
5
|
+
*/
|
|
6
|
+
import { processImageFile } from '../../shared/utils/image.js';
|
|
7
|
+
/**
|
|
8
|
+
* Imports an image file and creates an Image feature
|
|
9
|
+
*/
|
|
10
|
+
export async function loadImage(file, options, deps) {
|
|
11
|
+
if (!options.coordinate) {
|
|
12
|
+
throw new Error('Image files require coordinate option');
|
|
13
|
+
}
|
|
14
|
+
const { store, autoNameGenerator, generateFeatureId, getCurrentLayerId } = deps;
|
|
15
|
+
const processed = await processImageFile(file);
|
|
16
|
+
// Create the FileData
|
|
17
|
+
const fileId = generateFeatureId();
|
|
18
|
+
const fileData = {
|
|
19
|
+
id: fileId,
|
|
20
|
+
mimeType: processed.mimeType,
|
|
21
|
+
dataURL: processed.dataUrl,
|
|
22
|
+
};
|
|
23
|
+
// Create the Image feature
|
|
24
|
+
const featureId = generateFeatureId();
|
|
25
|
+
const layerId = options.layerId || getCurrentLayerId();
|
|
26
|
+
const autoName = autoNameGenerator.generateName('Image');
|
|
27
|
+
const feature = {
|
|
28
|
+
id: featureId,
|
|
29
|
+
type: 'Image',
|
|
30
|
+
coordinates: options.coordinate,
|
|
31
|
+
layerId,
|
|
32
|
+
properties: {
|
|
33
|
+
imageFileId: fileId,
|
|
34
|
+
imageWidth: processed.width,
|
|
35
|
+
imageHeight: processed.height,
|
|
36
|
+
createdZoom: options.zoom ?? 1,
|
|
37
|
+
...(autoName !== undefined && { name: autoName }),
|
|
38
|
+
},
|
|
39
|
+
locked: false,
|
|
40
|
+
visible: true,
|
|
41
|
+
};
|
|
42
|
+
// Create the file and the feature in a transaction
|
|
43
|
+
store.transact(() => {
|
|
44
|
+
store.createFile(fileData);
|
|
45
|
+
store.createFeature(feature);
|
|
46
|
+
store.setSelection('feature', [featureId]);
|
|
47
|
+
});
|
|
48
|
+
return {
|
|
49
|
+
format: 'image',
|
|
50
|
+
featureIds: [featureId],
|
|
51
|
+
replaced: false,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ExportFormat, ExportOptions, ExportResult, LoadOptions, LoadResult } from '../../store/types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Import/Export API interface
|
|
4
|
+
*/
|
|
5
|
+
export interface ImportExportAPI {
|
|
6
|
+
load(source: File | unknown, options?: LoadOptions): Promise<LoadResult>;
|
|
7
|
+
export(format: ExportFormat, options?: ExportOptions): ExportResult;
|
|
8
|
+
getSuggestedFileName(): string;
|
|
9
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2026 SAKAIDA Atsushi
|
|
2
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
3
|
+
/**
|
|
4
|
+
* Orchestrator of the Import/Export API
|
|
5
|
+
*
|
|
6
|
+
* createImportExportAPI is a thin orchestrator that detects the format and then delegates
|
|
7
|
+
* to each import/export function. The implementation is spread across format-detection /
|
|
8
|
+
* geojson-import / geojson-export / native-format / image-import / file-name.
|
|
9
|
+
*/
|
|
10
|
+
import { isImageFile } from '../../shared/utils/image.js';
|
|
11
|
+
import { buildExportFileName } from './file-name.js';
|
|
12
|
+
import { isGeoJSONFeatureCollection, isNativeFormat } from './format-detection.js';
|
|
13
|
+
import { exportGeoJSON } from './geojson-export.js';
|
|
14
|
+
import { loadGeoJSON } from './geojson-import.js';
|
|
15
|
+
import { loadImage } from './image-import.js';
|
|
16
|
+
import { exportNative, loadNative } from './native-format.js';
|
|
17
|
+
/**
|
|
18
|
+
* Creates the Import/Export API
|
|
19
|
+
*
|
|
20
|
+
* @internal
|
|
21
|
+
*/
|
|
22
|
+
export function createImportExportAPI(context) {
|
|
23
|
+
const { store, generateFeatureId, getCurrentLayerId, autoNameGenerator } = context;
|
|
24
|
+
const importDeps = {
|
|
25
|
+
store,
|
|
26
|
+
autoNameGenerator,
|
|
27
|
+
generateFeatureId,
|
|
28
|
+
getCurrentLayerId,
|
|
29
|
+
};
|
|
30
|
+
const api = {
|
|
31
|
+
/**
|
|
32
|
+
* Loads data or a file.
|
|
33
|
+
*
|
|
34
|
+
* Detects the kind of source automatically and handles it accordingly:
|
|
35
|
+
* - File: detects the file kind automatically (image / .json / .geojson)
|
|
36
|
+
* - object: detects the data format automatically (native format /
|
|
37
|
+
* GeoJSON FeatureCollection)
|
|
38
|
+
*/
|
|
39
|
+
async load(source, options) {
|
|
40
|
+
if (source instanceof File) {
|
|
41
|
+
if (isImageFile(source)) {
|
|
42
|
+
return loadImage(source, options || {}, importDeps);
|
|
43
|
+
}
|
|
44
|
+
const fileName = source.name.toLowerCase();
|
|
45
|
+
const mimeType = source.type;
|
|
46
|
+
const isJsonFile = fileName.endsWith('.json') ||
|
|
47
|
+
fileName.endsWith('.geojson') ||
|
|
48
|
+
mimeType === 'application/json' ||
|
|
49
|
+
mimeType === 'application/geo+json';
|
|
50
|
+
if (isJsonFile) {
|
|
51
|
+
const text = await source.text();
|
|
52
|
+
let data;
|
|
53
|
+
try {
|
|
54
|
+
data = JSON.parse(text);
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
58
|
+
throw new Error(`Invalid JSON file "${source.name}": ${detail}`);
|
|
59
|
+
}
|
|
60
|
+
return api.load(data, options);
|
|
61
|
+
}
|
|
62
|
+
throw new Error(`Unsupported file type: ${source.type || source.name}`);
|
|
63
|
+
}
|
|
64
|
+
if (isNativeFormat(source)) {
|
|
65
|
+
return loadNative(source, { store });
|
|
66
|
+
}
|
|
67
|
+
if (isGeoJSONFeatureCollection(source)) {
|
|
68
|
+
return loadGeoJSON(source, importDeps, { flattenMulti: options?.flattenMulti });
|
|
69
|
+
}
|
|
70
|
+
throw new Error('Unsupported data format. Expected native format or GeoJSON FeatureCollection.');
|
|
71
|
+
},
|
|
72
|
+
/**
|
|
73
|
+
* Exports the data
|
|
74
|
+
*/
|
|
75
|
+
export(format, options) {
|
|
76
|
+
const metadata = store.getMetadata();
|
|
77
|
+
if (format === 'native') {
|
|
78
|
+
const data = exportNative(store, options);
|
|
79
|
+
const fileName = options?.fileName || buildExportFileName(metadata, '.maplibre-gl-draw.json');
|
|
80
|
+
return {
|
|
81
|
+
format: 'native',
|
|
82
|
+
data: JSON.stringify(data, null, 2),
|
|
83
|
+
mimeType: 'application/json',
|
|
84
|
+
fileName,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
if (format === 'geojson') {
|
|
88
|
+
const data = exportGeoJSON(store, options);
|
|
89
|
+
const fileName = options?.fileName || buildExportFileName(metadata, '.geojson');
|
|
90
|
+
return {
|
|
91
|
+
format: 'geojson',
|
|
92
|
+
data: JSON.stringify(data, null, 2),
|
|
93
|
+
mimeType: 'application/geo+json',
|
|
94
|
+
fileName,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
throw new Error(`Unsupported export format: ${format}`);
|
|
98
|
+
},
|
|
99
|
+
/**
|
|
100
|
+
* Generates a suggested export file name
|
|
101
|
+
*/
|
|
102
|
+
getSuggestedFileName() {
|
|
103
|
+
return buildExportFileName(store.getMetadata(), '.maplibre-gl-draw.json');
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
return api;
|
|
107
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Import / export of the native format
|
|
3
|
+
*/
|
|
4
|
+
import type { Store } from '../../store/store.js';
|
|
5
|
+
import type { Data, ExportOptions, LoadResult } from '../../store/types.js';
|
|
6
|
+
/**
|
|
7
|
+
* Exports the current data in the native format
|
|
8
|
+
*/
|
|
9
|
+
export declare function exportNative(store: Store, options?: ExportOptions): Data;
|
|
10
|
+
/**
|
|
11
|
+
* Imports the native format (replaces the existing data)
|
|
12
|
+
*/
|
|
13
|
+
export declare function loadNative(data: Data, deps: {
|
|
14
|
+
store: Store;
|
|
15
|
+
}): Promise<LoadResult>;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2026 SAKAIDA Atsushi
|
|
2
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
3
|
+
import { NATIVE_VERSION } from './constants.js';
|
|
4
|
+
import { validateNativeData } from './native-validation.js';
|
|
5
|
+
/** The layer that survives the replacement (it is updated in place when the data has it) */
|
|
6
|
+
const DEFAULT_LAYER_ID = 'default-layer';
|
|
7
|
+
/**
|
|
8
|
+
* Exports the current data in the native format
|
|
9
|
+
*/
|
|
10
|
+
export function exportNative(store, options) {
|
|
11
|
+
let features = store.getAllFeatures();
|
|
12
|
+
const layers = store.getAllLayers();
|
|
13
|
+
const groups = store.getAllGroups();
|
|
14
|
+
if (options?.featureIds && options.featureIds.length > 0) {
|
|
15
|
+
const featureIdSet = new Set(options.featureIds);
|
|
16
|
+
features = features.filter((f) => featureIdSet.has(f.id));
|
|
17
|
+
}
|
|
18
|
+
if (options?.layerIds && options.layerIds.length > 0) {
|
|
19
|
+
const layerIdSet = new Set(options.layerIds);
|
|
20
|
+
features = features.filter((f) => layerIdSet.has(f.layerId));
|
|
21
|
+
}
|
|
22
|
+
// Get only the files that are in use
|
|
23
|
+
const usedFileIds = new Set();
|
|
24
|
+
for (const feature of features) {
|
|
25
|
+
if (feature.type === 'Image' && feature.properties.imageFileId) {
|
|
26
|
+
usedFileIds.add(feature.properties.imageFileId);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
const files = store.getAllFiles();
|
|
30
|
+
const filesRecord = {};
|
|
31
|
+
for (const file of files) {
|
|
32
|
+
if (usedFileIds.has(file.id)) {
|
|
33
|
+
filesRecord[file.id] = file;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
const metadata = store.getMetadata();
|
|
37
|
+
const now = new Date().toISOString();
|
|
38
|
+
return {
|
|
39
|
+
version: NATIVE_VERSION,
|
|
40
|
+
created: now,
|
|
41
|
+
modified: now,
|
|
42
|
+
metadata: Object.keys(metadata).length > 0 ? metadata : undefined,
|
|
43
|
+
layers,
|
|
44
|
+
// The whole stacking order, entries of the application that are not layers included
|
|
45
|
+
layerOrder: [...store.getLayerOrder()],
|
|
46
|
+
groups,
|
|
47
|
+
features,
|
|
48
|
+
files: Object.keys(filesRecord).length > 0 ? filesRecord : undefined,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Imports the native format (replaces the existing data)
|
|
53
|
+
*/
|
|
54
|
+
export async function loadNative(data, deps) {
|
|
55
|
+
const { store } = deps;
|
|
56
|
+
// Validate before applying. Since store.transact does not roll back, if an exception
|
|
57
|
+
// is thrown on invalid data after the existing data has been cleared, the existing data
|
|
58
|
+
// is lost. Everything the steps below rely on (the shape of the layers, groups, features
|
|
59
|
+
// and files, the references between them, and the uniqueness of the ids) is checked
|
|
60
|
+
// before the destructive operation, so a problem throws while the existing data is
|
|
61
|
+
// still intact.
|
|
62
|
+
const retainedLayerIds = new Set();
|
|
63
|
+
if (store.getLayer(DEFAULT_LAYER_ID))
|
|
64
|
+
retainedLayerIds.add(DEFAULT_LAYER_ID);
|
|
65
|
+
const { layers, layerOrder, groups, features, files } = await validateNativeData(data, retainedLayerIds);
|
|
66
|
+
const featureIds = features.map((f) => f.id);
|
|
67
|
+
// A kept layer the data does not list goes to the back, where it stays when the data does
|
|
68
|
+
// not mention it (every layer must be on the stacking order to be drawn)
|
|
69
|
+
const retainedUnlisted = [...retainedLayerIds].filter((id) => !layerOrder.includes(id));
|
|
70
|
+
// Import atomically (silent, so a subscriber that records changes leaves it out)
|
|
71
|
+
store.transact(() => {
|
|
72
|
+
// 1. Clear the existing data
|
|
73
|
+
const existingFeatures = store.getAllFeatures();
|
|
74
|
+
for (const feature of existingFeatures) {
|
|
75
|
+
store.deleteFeature(feature.id);
|
|
76
|
+
}
|
|
77
|
+
const existingGroups = store.getAllGroups();
|
|
78
|
+
for (const group of existingGroups) {
|
|
79
|
+
store.deleteGroup(group.id);
|
|
80
|
+
}
|
|
81
|
+
const existingLayers = store.getAllLayers();
|
|
82
|
+
for (const layer of existingLayers) {
|
|
83
|
+
if (layer.id !== DEFAULT_LAYER_ID) {
|
|
84
|
+
store.deleteLayer(layer.id);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// The files belong to the features that were just removed, and the data brings its own
|
|
88
|
+
// (leaving them would also make a file id of the data collide)
|
|
89
|
+
for (const file of store.getAllFiles()) {
|
|
90
|
+
store.deleteFile(file.id);
|
|
91
|
+
}
|
|
92
|
+
// 2. Import the layers
|
|
93
|
+
for (const layer of layers) {
|
|
94
|
+
if (store.getLayer(layer.id)) {
|
|
95
|
+
store.updateLayer(layer.id, layer);
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
store.createLayer(layer);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
// 3. Import the groups
|
|
102
|
+
for (const group of groups) {
|
|
103
|
+
store.createGroup(group);
|
|
104
|
+
}
|
|
105
|
+
// 4. Import the features
|
|
106
|
+
for (const feature of features) {
|
|
107
|
+
store.createFeature(feature);
|
|
108
|
+
}
|
|
109
|
+
// 5. Import the files
|
|
110
|
+
for (const file of files) {
|
|
111
|
+
store.createFile(file);
|
|
112
|
+
}
|
|
113
|
+
// 6. Replace the whole stacking order. Entries that are not layers are replaced too, so
|
|
114
|
+
// nothing of the previous document is left on it
|
|
115
|
+
store.setLayerOrder([...retainedUnlisted, ...layerOrder]);
|
|
116
|
+
// 7. Import the metadata
|
|
117
|
+
if (data.metadata) {
|
|
118
|
+
store.setMetadata(data.metadata);
|
|
119
|
+
}
|
|
120
|
+
}, 'silent');
|
|
121
|
+
return {
|
|
122
|
+
format: 'native',
|
|
123
|
+
featureIds,
|
|
124
|
+
replaced: true,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validation of native-format data before it replaces the store
|
|
3
|
+
*
|
|
4
|
+
* Loading the native format clears the existing data first, and store.transact does not roll
|
|
5
|
+
* back. Every property the import relies on is therefore checked here, before anything is
|
|
6
|
+
* deleted: a problem throws while the existing data is still intact. The checks follow the
|
|
7
|
+
* native format described in docs/reference/data-format.md.
|
|
8
|
+
*/
|
|
9
|
+
import type { Data, Feature, FileData, Group, Layer } from '../../store/types.js';
|
|
10
|
+
/** Native data that passed validation, with the files normalized */
|
|
11
|
+
export interface ValidatedNativeData {
|
|
12
|
+
layers: Layer[];
|
|
13
|
+
/** The stacking order, from the back */
|
|
14
|
+
layerOrder: string[];
|
|
15
|
+
groups: Group[];
|
|
16
|
+
/** The features in data order, each style reduced to its usable keys */
|
|
17
|
+
features: Feature[];
|
|
18
|
+
files: FileData[];
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Validates native data before the destructive part of the import
|
|
22
|
+
*
|
|
23
|
+
* @param data - the data to load
|
|
24
|
+
* @param retainedLayerIds - IDs of the layers that remain in the store after it is cleared
|
|
25
|
+
* @throws when anything the import relies on is missing or malformed, or when the data has
|
|
26
|
+
* another major version
|
|
27
|
+
*/
|
|
28
|
+
export declare function validateNativeData(data: Data, retainedLayerIds: ReadonlySet<string>): Promise<ValidatedNativeData>;
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2026 SAKAIDA Atsushi
|
|
2
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
3
|
+
import { NATIVE_VERSION } from './constants.js';
|
|
4
|
+
import { normalizeEmbeddedFile } from './embedded-file.js';
|
|
5
|
+
import { describeCoordinateProblem } from './geometry-validation.js';
|
|
6
|
+
import { sanitizeFeatureStyle } from './style-validation.js';
|
|
7
|
+
function fail(message) {
|
|
8
|
+
throw new Error(`Invalid native data: ${message}`);
|
|
9
|
+
}
|
|
10
|
+
function isRecord(value) {
|
|
11
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
12
|
+
}
|
|
13
|
+
function isNonEmptyString(value) {
|
|
14
|
+
return typeof value === 'string' && value !== '';
|
|
15
|
+
}
|
|
16
|
+
function validateLayer(layer, index) {
|
|
17
|
+
if (!isRecord(layer))
|
|
18
|
+
fail(`layers[${index}] is not an object`);
|
|
19
|
+
const where = `layer "${String(layer.id)}"`;
|
|
20
|
+
if (!isNonEmptyString(layer.id))
|
|
21
|
+
fail(`layers[${index}] has no valid string id`);
|
|
22
|
+
if (typeof layer.name !== 'string')
|
|
23
|
+
fail(`${where} has no string name`);
|
|
24
|
+
if (typeof layer.visible !== 'boolean')
|
|
25
|
+
fail(`${where} has no boolean visible`);
|
|
26
|
+
if (typeof layer.locked !== 'boolean')
|
|
27
|
+
fail(`${where} has no boolean locked`);
|
|
28
|
+
if (typeof layer.opacity !== 'number' || !Number.isFinite(layer.opacity)) {
|
|
29
|
+
fail(`${where} has no finite opacity`);
|
|
30
|
+
}
|
|
31
|
+
if (!Array.isArray(layer.order) || !layer.order.every((id) => typeof id === 'string')) {
|
|
32
|
+
fail(`${where} has no order array of strings`);
|
|
33
|
+
}
|
|
34
|
+
if (layer.metadata !== undefined && !isRecord(layer.metadata)) {
|
|
35
|
+
fail(`${where} has a metadata that is not an object`);
|
|
36
|
+
}
|
|
37
|
+
if (layer.styleRule !== undefined && !isRecord(layer.styleRule)) {
|
|
38
|
+
fail(`${where} has a styleRule that is not an object`);
|
|
39
|
+
}
|
|
40
|
+
return layer;
|
|
41
|
+
}
|
|
42
|
+
function validateGroup(group, index) {
|
|
43
|
+
if (!isRecord(group))
|
|
44
|
+
fail(`groups[${index}] is not an object`);
|
|
45
|
+
const where = `group "${String(group.id)}"`;
|
|
46
|
+
if (!isNonEmptyString(group.id))
|
|
47
|
+
fail(`groups[${index}] has no valid string id`);
|
|
48
|
+
if (typeof group.name !== 'string')
|
|
49
|
+
fail(`${where} has no string name`);
|
|
50
|
+
if (typeof group.visible !== 'boolean')
|
|
51
|
+
fail(`${where} has no boolean visible`);
|
|
52
|
+
if (typeof group.locked !== 'boolean')
|
|
53
|
+
fail(`${where} has no boolean locked`);
|
|
54
|
+
if (!Array.isArray(group.featureIds) || !group.featureIds.every((id) => typeof id === 'string')) {
|
|
55
|
+
fail(`${where} has no featureIds array of strings`);
|
|
56
|
+
}
|
|
57
|
+
return group;
|
|
58
|
+
}
|
|
59
|
+
/** Parses a semantic version (major.minor.patch, with an optional pre-release or build) */
|
|
60
|
+
function parseVersion(value) {
|
|
61
|
+
const match = /^(\d+)\.(\d+)\.(\d+)(?:[-+].*)?$/.exec(value);
|
|
62
|
+
return match ? [Number(match[1]), Number(match[2]), Number(match[3])] : null;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Checks the version of the data against the version this library writes
|
|
66
|
+
*
|
|
67
|
+
* Another major version is rejected, because it can change the meaning of any field. A newer
|
|
68
|
+
* minor version only adds fields, so it is read with a warning: the fields it adds are kept
|
|
69
|
+
* as they are but not interpreted.
|
|
70
|
+
*/
|
|
71
|
+
function validateVersion(version) {
|
|
72
|
+
if (typeof version !== 'string')
|
|
73
|
+
fail('version is not a string');
|
|
74
|
+
const parsed = parseVersion(version);
|
|
75
|
+
if (!parsed)
|
|
76
|
+
fail(`version "${version}" is not a semantic version`);
|
|
77
|
+
const [major, minor] = parsed;
|
|
78
|
+
const [supportedMajor, supportedMinor] = parseVersion(NATIVE_VERSION);
|
|
79
|
+
if (major !== supportedMajor) {
|
|
80
|
+
fail(`version ${version} is not supported (this library reads ${supportedMajor}.x)`);
|
|
81
|
+
}
|
|
82
|
+
if (minor > supportedMinor) {
|
|
83
|
+
console.warn(`Native data version ${version} is newer than ${NATIVE_VERSION}; ` +
|
|
84
|
+
'the fields it adds are kept but not interpreted');
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Validates a feature and returns it with its style reduced to the usable keys
|
|
89
|
+
*/
|
|
90
|
+
function validateFeature(feature, index, layerIds, groupIds) {
|
|
91
|
+
if (!isRecord(feature))
|
|
92
|
+
fail(`features[${index}] is not an object`);
|
|
93
|
+
if (!isNonEmptyString(feature.id))
|
|
94
|
+
fail('feature without a valid string id');
|
|
95
|
+
const where = `feature "${feature.id}"`;
|
|
96
|
+
if (!isNonEmptyString(feature.type))
|
|
97
|
+
fail(`${where} has no string type`);
|
|
98
|
+
const coordinateProblem = describeCoordinateProblem(feature.type, feature.coordinates);
|
|
99
|
+
if (coordinateProblem)
|
|
100
|
+
fail(`${where} has ${coordinateProblem}`);
|
|
101
|
+
if (typeof feature.layerId !== 'string' || !layerIds.has(feature.layerId)) {
|
|
102
|
+
fail(`${where} refers to a layer that does not exist`);
|
|
103
|
+
}
|
|
104
|
+
if (feature.groupId !== undefined &&
|
|
105
|
+
(typeof feature.groupId !== 'string' || !groupIds.has(feature.groupId))) {
|
|
106
|
+
fail(`${where} refers to a group that does not exist`);
|
|
107
|
+
}
|
|
108
|
+
if (!isRecord(feature.properties))
|
|
109
|
+
fail(`${where} has no properties object`);
|
|
110
|
+
if (feature.style !== undefined && !isRecord(feature.style)) {
|
|
111
|
+
fail(`${where} has a style that is not an object`);
|
|
112
|
+
}
|
|
113
|
+
if (typeof feature.visible !== 'boolean')
|
|
114
|
+
fail(`${where} has no boolean visible`);
|
|
115
|
+
if (typeof feature.locked !== 'boolean')
|
|
116
|
+
fail(`${where} has no boolean locked`);
|
|
117
|
+
// A style key of the wrong type or form is dropped rather than rejecting the file: the
|
|
118
|
+
// store accepts any value through its API, so the library's own export can carry one
|
|
119
|
+
if (feature.style === undefined)
|
|
120
|
+
return feature;
|
|
121
|
+
const { style: _style, ...rest } = feature;
|
|
122
|
+
const style = sanitizeFeatureStyle(feature.style);
|
|
123
|
+
return (style === undefined ? rest : { ...rest, style });
|
|
124
|
+
}
|
|
125
|
+
async function validateFiles(files) {
|
|
126
|
+
if (files === undefined)
|
|
127
|
+
return [];
|
|
128
|
+
if (!isRecord(files))
|
|
129
|
+
fail('files is not an object');
|
|
130
|
+
const result = [];
|
|
131
|
+
for (const [key, file] of Object.entries(files)) {
|
|
132
|
+
if (!isRecord(file) || !isNonEmptyString(file.id) || file.id !== key) {
|
|
133
|
+
fail(`file "${key}" is not an object whose id matches its key`);
|
|
134
|
+
}
|
|
135
|
+
const content = await normalizeEmbeddedFile(file.dataURL, file.mimeType);
|
|
136
|
+
if (!content) {
|
|
137
|
+
fail(`file "${key}" is not an embedded PNG / JPEG / WebP / GIF data URL of its declared type`);
|
|
138
|
+
}
|
|
139
|
+
result.push({ ...file, ...content });
|
|
140
|
+
}
|
|
141
|
+
return result;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Validates the stacking order: distinct non-empty strings that list every layer of the data
|
|
145
|
+
*
|
|
146
|
+
* Entries that are not layers belong to the application and are kept as they are.
|
|
147
|
+
*/
|
|
148
|
+
function validateLayerOrder(value, layers) {
|
|
149
|
+
if (!Array.isArray(value))
|
|
150
|
+
fail('layerOrder is not an array');
|
|
151
|
+
const seen = new Set();
|
|
152
|
+
for (const [index, entry] of value.entries()) {
|
|
153
|
+
if (!isNonEmptyString(entry))
|
|
154
|
+
fail(`layerOrder[${index}] is not a non-empty string`);
|
|
155
|
+
if (seen.has(entry))
|
|
156
|
+
fail(`layerOrder lists "${entry}" twice`);
|
|
157
|
+
seen.add(entry);
|
|
158
|
+
}
|
|
159
|
+
for (const layer of layers) {
|
|
160
|
+
if (!seen.has(layer.id))
|
|
161
|
+
fail(`layerOrder does not list layer "${layer.id}"`);
|
|
162
|
+
}
|
|
163
|
+
return value;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Validates native data before the destructive part of the import
|
|
167
|
+
*
|
|
168
|
+
* @param data - the data to load
|
|
169
|
+
* @param retainedLayerIds - IDs of the layers that remain in the store after it is cleared
|
|
170
|
+
* @throws when anything the import relies on is missing or malformed, or when the data has
|
|
171
|
+
* another major version
|
|
172
|
+
*/
|
|
173
|
+
export async function validateNativeData(data, retainedLayerIds) {
|
|
174
|
+
validateVersion(data.version);
|
|
175
|
+
if (!Array.isArray(data.features))
|
|
176
|
+
fail('features is not an array');
|
|
177
|
+
if (data.layers !== undefined && !Array.isArray(data.layers))
|
|
178
|
+
fail('layers is not an array');
|
|
179
|
+
if (data.groups !== undefined && !Array.isArray(data.groups))
|
|
180
|
+
fail('groups is not an array');
|
|
181
|
+
if (data.metadata !== undefined && !isRecord(data.metadata))
|
|
182
|
+
fail('metadata is not an object');
|
|
183
|
+
const layers = (data.layers ?? []).map(validateLayer);
|
|
184
|
+
const layerIds = new Set(retainedLayerIds);
|
|
185
|
+
const seenLayerIds = new Set();
|
|
186
|
+
for (const layer of layers) {
|
|
187
|
+
if (seenLayerIds.has(layer.id))
|
|
188
|
+
fail(`duplicate layer id "${layer.id}"`);
|
|
189
|
+
seenLayerIds.add(layer.id);
|
|
190
|
+
layerIds.add(layer.id);
|
|
191
|
+
}
|
|
192
|
+
const layerOrder = validateLayerOrder(data.layerOrder, layers);
|
|
193
|
+
// Feature and group IDs share the entries of layer.order, so they must not collide either
|
|
194
|
+
const itemIds = new Set();
|
|
195
|
+
const groups = (data.groups ?? []).map(validateGroup);
|
|
196
|
+
const groupIds = new Set();
|
|
197
|
+
for (const group of groups) {
|
|
198
|
+
if (itemIds.has(group.id))
|
|
199
|
+
fail(`duplicate group id "${group.id}"`);
|
|
200
|
+
itemIds.add(group.id);
|
|
201
|
+
groupIds.add(group.id);
|
|
202
|
+
}
|
|
203
|
+
const features = data.features.map((entry, index) => {
|
|
204
|
+
const feature = validateFeature(entry, index, layerIds, groupIds);
|
|
205
|
+
if (itemIds.has(feature.id))
|
|
206
|
+
fail(`duplicate feature id "${feature.id}"`);
|
|
207
|
+
itemIds.add(feature.id);
|
|
208
|
+
return feature;
|
|
209
|
+
});
|
|
210
|
+
const files = await validateFiles(data.files);
|
|
211
|
+
return { layers, layerOrder, groups, features, files };
|
|
212
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copying of user-defined properties
|
|
3
|
+
*
|
|
4
|
+
* Property names come from the data being imported, so `__proto__` is an ordinary key there
|
|
5
|
+
* (JSON.parse creates it as an own property). Assigning it with `target[key] = value` would
|
|
6
|
+
* replace the prototype of the target instead, which hides the key, makes the other keys of
|
|
7
|
+
* the value appear as inherited properties and loses the key on the next export. Properties
|
|
8
|
+
* are therefore defined as own data properties, the same as JSON.parse does.
|
|
9
|
+
*/
|
|
10
|
+
export declare function setOwnProperty(target: Record<string, unknown>, key: string, value: unknown): void;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2026 SAKAIDA Atsushi
|
|
2
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
3
|
+
/**
|
|
4
|
+
* Copying of user-defined properties
|
|
5
|
+
*
|
|
6
|
+
* Property names come from the data being imported, so `__proto__` is an ordinary key there
|
|
7
|
+
* (JSON.parse creates it as an own property). Assigning it with `target[key] = value` would
|
|
8
|
+
* replace the prototype of the target instead, which hides the key, makes the other keys of
|
|
9
|
+
* the value appear as inherited properties and loses the key on the next export. Properties
|
|
10
|
+
* are therefore defined as own data properties, the same as JSON.parse does.
|
|
11
|
+
*/
|
|
12
|
+
export function setOwnProperty(target, key, value) {
|
|
13
|
+
Object.defineProperty(target, key, {
|
|
14
|
+
value,
|
|
15
|
+
writable: true,
|
|
16
|
+
enumerable: true,
|
|
17
|
+
configurable: true,
|
|
18
|
+
});
|
|
19
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validation of an imported feature style
|
|
3
|
+
*
|
|
4
|
+
* A style arrives from a file (the maplibre-gl-draw:style property of GeoJSON, the style of a
|
|
5
|
+
* native feature, the simplestyle-spec keys) and reaches the renderer, which parses the
|
|
6
|
+
* colors as #rgb / #rrggbb and uses the numbers as they are. A value of the wrong type or
|
|
7
|
+
* form (a number or null for a color, `red`, an opacity of 5) would make the parsing throw or
|
|
8
|
+
* produce NaN, so each known key is checked here and a key that fails is dropped. Keys this
|
|
9
|
+
* library does not define are kept unchanged, whatever their value: they belong to the host or
|
|
10
|
+
* to an extension, which gives features keys of its own and checks their values where it reads
|
|
11
|
+
* them (values reach the Store through updateFeature and a replaced store as well, without
|
|
12
|
+
* passing here).
|
|
13
|
+
*/
|
|
14
|
+
import type { FeatureStyle } from '../../store/types.js';
|
|
15
|
+
type Check = (value: unknown) => boolean;
|
|
16
|
+
/** A #rgb or #rrggbb color */
|
|
17
|
+
export declare const isHexColor: Check;
|
|
18
|
+
/**
|
|
19
|
+
* Returns the usable part of an imported style
|
|
20
|
+
*
|
|
21
|
+
* A key of FeatureStyle whose value fails its check is dropped; every other key is kept.
|
|
22
|
+
*
|
|
23
|
+
* @param value The style as read from the file
|
|
24
|
+
* @returns The style, or undefined when the value is not an object or nothing is left
|
|
25
|
+
*/
|
|
26
|
+
export declare function sanitizeFeatureStyle(value: unknown): FeatureStyle | undefined;
|
|
27
|
+
export {};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2026 SAKAIDA Atsushi
|
|
2
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
3
|
+
import { setOwnProperty } from './own-property.js';
|
|
4
|
+
const HEX_COLOR = /^#(?:[0-9a-f]{3}|[0-9a-f]{6})$/i;
|
|
5
|
+
/** A #rgb or #rrggbb color */
|
|
6
|
+
export const isHexColor = (value) => typeof value === 'string' && HEX_COLOR.test(value);
|
|
7
|
+
const isFiniteNumber = (value) => typeof value === 'number' && Number.isFinite(value);
|
|
8
|
+
const isOpacity = (value) => isFiniteNumber(value) && value >= 0 && value <= 1;
|
|
9
|
+
const isNonNegative = (value) => isFiniteNumber(value) && value >= 0;
|
|
10
|
+
const oneOf = (...values) => (value) => typeof value === 'string' && values.includes(value);
|
|
11
|
+
/** The check of every key of FeatureStyle */
|
|
12
|
+
const STYLE_CHECK_TABLE = {
|
|
13
|
+
fillColor: isHexColor,
|
|
14
|
+
fillOpacity: isOpacity,
|
|
15
|
+
strokeColor: isHexColor,
|
|
16
|
+
strokeWidth: isNonNegative,
|
|
17
|
+
strokeOpacity: isOpacity,
|
|
18
|
+
lineStyle: oneOf('solid', 'dashed', 'dotted'),
|
|
19
|
+
pointRadius: isNonNegative,
|
|
20
|
+
pointColor: isHexColor,
|
|
21
|
+
pointShape: oneOf('circle', 'square', 'triangle', 'star'),
|
|
22
|
+
imageOpacity: isOpacity,
|
|
23
|
+
};
|
|
24
|
+
const STYLE_CHECKS = new Map(Object.entries(STYLE_CHECK_TABLE));
|
|
25
|
+
function isRecord(value) {
|
|
26
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Returns the usable part of an imported style
|
|
30
|
+
*
|
|
31
|
+
* A key of FeatureStyle whose value fails its check is dropped; every other key is kept.
|
|
32
|
+
*
|
|
33
|
+
* @param value The style as read from the file
|
|
34
|
+
* @returns The style, or undefined when the value is not an object or nothing is left
|
|
35
|
+
*/
|
|
36
|
+
export function sanitizeFeatureStyle(value) {
|
|
37
|
+
if (!isRecord(value))
|
|
38
|
+
return undefined;
|
|
39
|
+
const style = {};
|
|
40
|
+
let kept = 0;
|
|
41
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
42
|
+
const check = STYLE_CHECKS.get(key);
|
|
43
|
+
if (check && !check(entry))
|
|
44
|
+
continue;
|
|
45
|
+
setOwnProperty(style, key, entry);
|
|
46
|
+
kept++;
|
|
47
|
+
}
|
|
48
|
+
return kept > 0 ? style : undefined;
|
|
49
|
+
}
|