From 6a104a4f775a2e10d02573a8f82645f76fce42c8 Mon Sep 17 00:00:00 2001 From: ronitjadhav Date: Fri, 2 Aug 2024 13:07:16 +0200 Subject: [PATCH] create a layer based on a Mapbox-hosted style --- package-lock.json | 125 +++++++++++++++++- .../core/fixtures/map-context.fixtures.ts | 7 + packages/core/lib/model/map-context.ts | 10 +- .../openlayers/lib/map/create-map.test.ts | 23 ++++ packages/openlayers/lib/map/create-map.ts | 10 +- packages/openlayers/package.json | 3 +- 6 files changed, 168 insertions(+), 10 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0a62914..50f6441 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,11 +10,6 @@ "docs", "packages/*" ], - "dependencies": { - "@camptocamp/ogc-client": "1.1.1-dev.9671ef4", - "@geospatial-sdk/core": "*", - "proj4": "^2.9.2" - }, "devDependencies": { "@babel/cli": "^7.23.9", "@babel/core": "^7.23.9", @@ -3014,6 +3009,49 @@ "node": ">=18.0.0" } }, + "node_modules/@mapbox/jsonlint-lines-primitives": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@mapbox/jsonlint-lines-primitives/-/jsonlint-lines-primitives-2.0.2.tgz", + "integrity": "sha512-rY0o9A5ECsTQRVhv7tL/OyDpGAoUB4tTvLiW1DSzQGq4bvTPhNw1VpSNjDJc5GFZ2XuyOtSWSVN05qOtcD71qQ==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@mapbox/mapbox-gl-style-spec": { + "version": "13.28.0", + "resolved": "https://registry.npmjs.org/@mapbox/mapbox-gl-style-spec/-/mapbox-gl-style-spec-13.28.0.tgz", + "integrity": "sha512-B8xM7Fp1nh5kejfIl4SWeY0gtIeewbuRencqO3cJDrCHZpaPg7uY+V8abuR+esMeuOjRl5cLhVTP40v+1ywxbg==", + "dev": true, + "dependencies": { + "@mapbox/jsonlint-lines-primitives": "~2.0.2", + "@mapbox/point-geometry": "^0.1.0", + "@mapbox/unitbezier": "^0.0.0", + "csscolorparser": "~1.0.2", + "json-stringify-pretty-compact": "^2.0.0", + "minimist": "^1.2.6", + "rw": "^1.3.3", + "sort-object": "^0.3.2" + }, + "bin": { + "gl-style-composite": "bin/gl-style-composite.js", + "gl-style-format": "bin/gl-style-format.js", + "gl-style-migrate": "bin/gl-style-migrate.js", + "gl-style-validate": "bin/gl-style-validate.js" + } + }, + "node_modules/@mapbox/point-geometry": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@mapbox/point-geometry/-/point-geometry-0.1.0.tgz", + "integrity": "sha512-6j56HdLTwWGO0fJPlrZtdU/B13q8Uwmo18Ck2GnGgN9PCFyKTZ3UbXeEdRFh18i9XQ92eH2VdtpJHpBD3aripQ==", + "dev": true + }, + "node_modules/@mapbox/unitbezier": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/@mapbox/unitbezier/-/unitbezier-0.0.0.tgz", + "integrity": "sha512-HPnRdYO0WjFjRTSwO3frz1wKaU649OBFPX3Zo/2WZvuRi6zMiRGui8SnPQiQABgqCf8YikDe5t3HViTVw1WUzA==", + "dev": true + }, "node_modules/@nicolo-ribaudo/chokidar-2": { "version": "2.1.8-no-fsevents.3", "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz", @@ -6005,6 +6043,12 @@ "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" } }, + "node_modules/csscolorparser": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/csscolorparser/-/csscolorparser-1.0.3.tgz", + "integrity": "sha512-umPSgYwZkdFoUrH5hIq5kf0wPSXiro51nPw0j2K/c83KflkPSTBGMz6NJvMB+07VlL0y7VPo6QJcDjcgKTTm3w==", + "dev": true + }, "node_modules/cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", @@ -8439,6 +8483,12 @@ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, + "node_modules/json-stringify-pretty-compact": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/json-stringify-pretty-compact/-/json-stringify-pretty-compact-2.0.0.tgz", + "integrity": "sha512-WRitRfs6BGq4q8gTgOy4ek7iPFXjbra0H3PmDLKm2xnZ+Gh1HUhiKGgCZkSPNULlP7mvfu6FV/mOLhCarspADQ==", + "dev": true + }, "node_modules/json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", @@ -8957,6 +9007,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/mapbox-to-css-font": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/mapbox-to-css-font/-/mapbox-to-css-font-2.4.5.tgz", + "integrity": "sha512-VJ6nB8emkO9VODI0Fk+TQ/0zKBTqmf/Pkt8Xv0kHstoc0iXRajA00DAid4Kc3K5xeFIOoiZrVxijEzj0GLVO2w==", + "dev": true + }, "node_modules/mark.js": { "version": "8.11.1", "resolved": "https://registry.npmjs.org/mark.js/-/mark.js-8.11.1.tgz", @@ -10166,6 +10222,19 @@ "url": "https://opencollective.com/openlayers" } }, + "node_modules/ol-mapbox-style": { + "version": "12.3.5", + "resolved": "https://registry.npmjs.org/ol-mapbox-style/-/ol-mapbox-style-12.3.5.tgz", + "integrity": "sha512-1tdq+jpzJ7BuqCeRpNV5u90X369MXDbHKpPPt0BNpbzi+4UEJ2dJIrd3eFQV9VbqvZeEIioEjyK7qOqXsUZs8w==", + "dev": true, + "dependencies": { + "@mapbox/mapbox-gl-style-spec": "^13.23.1", + "mapbox-to-css-font": "^2.4.1" + }, + "peerDependencies": { + "ol": "*" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -11810,6 +11879,12 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/rw": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", + "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==", + "dev": true + }, "node_modules/rxjs": { "version": "7.8.1", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", @@ -12188,6 +12263,24 @@ "node": ">= 14" } }, + "node_modules/sort-asc": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/sort-asc/-/sort-asc-0.1.0.tgz", + "integrity": "sha512-jBgdDd+rQ+HkZF2/OHCmace5dvpos/aWQpcxuyRs9QUbPRnkEJmYVo81PIGpjIdpOcsnJ4rGjStfDHsbn+UVyw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sort-desc": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/sort-desc/-/sort-desc-0.1.1.tgz", + "integrity": "sha512-jfZacW5SKOP97BF5rX5kQfJmRVZP5/adDUTY8fCSPvNcXDVpUEe2pr/iKGlcyZzchRJZrswnp68fgk3qBXgkJw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/sort-keys": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-2.0.0.tgz", @@ -12200,6 +12293,19 @@ "node": ">=4" } }, + "node_modules/sort-object": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/sort-object/-/sort-object-0.3.2.tgz", + "integrity": "sha512-aAQiEdqFTTdsvUFxXm3umdo04J7MRljoVGbBlkH7BgNsMvVNAJyGj7C/wV1A8wHWAJj/YikeZbfuCKqhggNWGA==", + "dev": true, + "dependencies": { + "sort-asc": "^0.1.0", + "sort-desc": "^0.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -14050,7 +14156,11 @@ "packages/core": { "name": "@geospatial-sdk/core", "version": "0.0.5-alpha.2", - "license": "BSD-3-Clause" + "license": "BSD-3-Clause", + "dependencies": { + "@camptocamp/ogc-client": "1.1.1-dev.9671ef4", + "proj4": "^2.9.2" + } }, "packages/geocoding": { "name": "@geospatial-sdk/geocoding", @@ -14067,7 +14177,8 @@ }, "devDependencies": { "@types/chroma-js": "^2.4.3", - "ol": "^8.2.0" + "ol": "^8.2.0", + "ol-mapbox-style": "^12.3.5" }, "peerDependencies": { "ol": ">6.x" diff --git a/packages/core/fixtures/map-context.fixtures.ts b/packages/core/fixtures/map-context.fixtures.ts index 6e45d77..d57283d 100644 --- a/packages/core/fixtures/map-context.fixtures.ts +++ b/packages/core/fixtures/map-context.fixtures.ts @@ -3,6 +3,7 @@ import { Extent, MapContext, MapContextLayerGeojson, + MapContextLayerMapLibreStyle, MapContextLayerOgcApi, MapContextLayerWfs, MapContextLayerWms, @@ -56,6 +57,12 @@ export const MAP_CTX_LAYER_GEOJSON_REMOTE_FIXTURE: MapContextLayerGeojson = type: "geojson", url: "https://my.host.com/data/regions.json", }); +export const MAP_CTX_LAYER_MAPBLIBRE_STYLE_FIXTURE: MapContextLayerMapLibreStyle = + deepFreeze({ + type: "maplibre-style", + styleUrl: "http://my.host.com/maplibre/style.json", + accessToken: "abcdefgh", + }); export const MAP_CTX_VIEW_FIXTURE: MapContextView = deepFreeze({ center: [7.75, 48.6], diff --git a/packages/core/lib/model/map-context.ts b/packages/core/lib/model/map-context.ts index 15f3234..427beba 100644 --- a/packages/core/lib/model/map-context.ts +++ b/packages/core/lib/model/map-context.ts @@ -51,6 +51,13 @@ export interface MapContextLayerOgcApi extends MapContextBaseLayer { options?: Record; } +// Layer pointing to a MapLibre Style spec, see https://maplibre.org/maplibre-style-spec/ +export interface MapContextLayerMapLibreStyle extends MapContextBaseLayer { + type: "maplibre-style"; + styleUrl: string; + accessToken?: string; +} + export interface MapContextLayerXyz extends MapContextBaseLayer { type: "xyz"; url: string; @@ -82,7 +89,8 @@ export type MapContextLayer = | MapContextLayerWfs | MapContextLayerXyz | MapContextLayerGeojson - | MapContextLayerOgcApi; + | MapContextLayerOgcApi + | MapContextLayerMapLibreStyle; export type Coordinate = [number, number]; diff --git a/packages/openlayers/lib/map/create-map.test.ts b/packages/openlayers/lib/map/create-map.test.ts index 7f40703..0eabb1b 100644 --- a/packages/openlayers/lib/map/create-map.test.ts +++ b/packages/openlayers/lib/map/create-map.test.ts @@ -12,6 +12,7 @@ import { MAP_CTX_FIXTURE, MAP_CTX_LAYER_GEOJSON_FIXTURE, MAP_CTX_LAYER_GEOJSON_REMOTE_FIXTURE, + MAP_CTX_LAYER_MAPBLIBRE_STYLE_FIXTURE, MAP_CTX_LAYER_OGCAPI_FIXTURE, MAP_CTX_LAYER_WFS_FIXTURE, MAP_CTX_LAYER_WMS_FIXTURE, @@ -32,6 +33,8 @@ import { resetMapFromContext, } from "./create-map"; import WMTS from "ol/source/WMTS"; +import { VectorTile } from "ol/source"; +import { MapboxVectorLayer } from "ol-mapbox-style"; describe("MapContextService", () => { describe("#createLayer", () => { @@ -291,6 +294,26 @@ describe("MapContextService", () => { ]); }); }); + + describe("Maplibre Style", () => { + beforeEach(async () => { + (layerModel = MAP_CTX_LAYER_MAPBLIBRE_STYLE_FIXTURE), + (layer = await createLayer(layerModel)); + }); + it("create a tile layer", () => { + expect(layer).toBeTruthy(); + expect(layer).toBeInstanceOf(MapboxVectorLayer); + }); + it("set correct layer properties", () => { + expect(layer.getVisible()).toBe(true); + expect(layer.getOpacity()).toBe(1); + expect(layer.get("label")).toBeUndefined(); + }); + it("create a Vector Tile source", () => { + const source = layer.getSource(); + expect(source).toBeInstanceOf(VectorTile); + }); + }); }); describe("#createView", () => { diff --git a/packages/openlayers/lib/map/create-map.ts b/packages/openlayers/lib/map/create-map.ts index e3b4dae..7bc257d 100644 --- a/packages/openlayers/lib/map/create-map.ts +++ b/packages/openlayers/lib/map/create-map.ts @@ -27,6 +27,7 @@ import { WfsEndpoint, WmtsEndpoint, } from "@camptocamp/ogc-client"; +import { MapboxVectorLayer } from "ol-mapbox-style"; const GEOJSON = new GeoJSON(); const WFS_MAX_FEATURES = 10000; @@ -112,6 +113,13 @@ export async function createLayer(layerModel: MapContextLayer): Promise { layer = olLayer; break; } + case "maplibre-style": { + layer = new MapboxVectorLayer({ + styleUrl: layerModel.styleUrl, + accessToken: layerModel.accessToken, + }) as unknown as Layer; + break; + } case "geojson": { if (layerModel.url !== undefined) { layer = new VectorLayer({ @@ -147,7 +155,7 @@ export async function createLayer(layerModel: MapContextLayer): Promise { break; } case "ogcapi": { - const ogcEndpoint = await new OgcApiEndpoint(layerModel.url); + const ogcEndpoint = new OgcApiEndpoint(layerModel.url); let layerUrl: string; if (layerModel.useTiles) { if (layerModel.useTiles === "vector") { diff --git a/packages/openlayers/package.json b/packages/openlayers/package.json index 816900c..d1fbc51 100644 --- a/packages/openlayers/package.json +++ b/packages/openlayers/package.json @@ -29,7 +29,8 @@ }, "devDependencies": { "@types/chroma-js": "^2.4.3", - "ol": "^8.2.0" + "ol": "^8.2.0", + "ol-mapbox-style": "^12.3.5" }, "peerDependencies": { "ol": ">6.x"