UNPKG

terriajs

Version:

Geospatial data visualization platform.

387 lines (346 loc) 13.1 kB
import { action } from "mobx"; import { http, passthrough } from "msw"; import { Filter, GeomType, LineSymbolizer, PolygonSymbolizer } from "protomaps-leaflet"; import ProtomapsImageryProvider from "../../../../lib/Map/ImageryProvider/ProtomapsImageryProvider"; import { filterFn, getFont, numberFn, numberOrFn } from "../../../../lib/Map/Vector/Protomaps/mapboxStyleJsonToProtomaps"; import { ImageryParts } from "../../../../lib/ModelMixins/MappableMixin"; import MapboxVectorTileCatalogItem from "../../../../lib/Models/Catalog/CatalogItems/MapboxVectorTileCatalogItem"; import CommonStrata from "../../../../lib/Models/Definition/CommonStrata"; import updateModelFromJson from "../../../../lib/Models/Definition/updateModelFromJson"; import Terria from "../../../../lib/Models/Terria"; import { worker } from "../../../mocks/browser"; describe("MapboxVectorTileCatalogItem", function () { let mvt: MapboxVectorTileCatalogItem; beforeEach(function () { mvt = new MapboxVectorTileCatalogItem("test", new Terria()); }); it("has a type", function () { expect(MapboxVectorTileCatalogItem.type).toBe("mvt"); expect(mvt.type).toBe("mvt"); }); describe("imageryProvider", function () { let imageryProvider: ProtomapsImageryProvider; beforeEach(function () { mvt.setTrait(CommonStrata.user, "url", "http://test"); mvt.setTrait(CommonStrata.user, "layer", "test-layer"); }); it("is an instance of ProtomapsImageryProvider", async function () { await mvt.loadMapItems(); if (!ImageryParts.is(mvt.mapItems[0])) throw new Error("Expected MapItem to be an ImageryParts"); imageryProvider = mvt.mapItems[0] .imageryProvider as ProtomapsImageryProvider; expect(imageryProvider instanceof ProtomapsImageryProvider).toBeTruthy(); }); it("sets min/max/native zoom properties", async function () { mvt.setTrait(CommonStrata.user, "minimumZoom", 1); mvt.setTrait(CommonStrata.user, "maximumZoom", 20); mvt.setTrait(CommonStrata.user, "maximumNativeZoom", 10); await mvt.loadMapItems(); if (!ImageryParts.is(mvt.mapItems[0])) throw new Error("Expected MapItem to be an ImageryParts"); imageryProvider = mvt.mapItems[0] .imageryProvider as ProtomapsImageryProvider; // Note: ProtomapsImageryProvider uses softMinimumLevel instead of minimumLevel expect(imageryProvider.minimumLevel).toBe(0); expect(imageryProvider.softMinimumLevel).toBe(1); expect(imageryProvider.maximumLevel).toBe(20); expect(imageryProvider.maximumNativeZoom).toBe(10); }); it("sets idProperty", async function () { mvt.setTrait(CommonStrata.user, "idProperty", "id"); await mvt.loadMapItems(); if (!ImageryParts.is(mvt.mapItems[0])) throw new Error("Expected MapItem to be an ImageryParts"); imageryProvider = mvt.mapItems[0] .imageryProvider as ProtomapsImageryProvider; expect(imageryProvider.idProperty).toBe("id"); }); }); describe("legends", function () { it( "constructs a default legend from the definition", action(async function () { mvt.setTrait(CommonStrata.user, "fillColor", "red"); mvt.setTrait(CommonStrata.user, "lineColor", "yellow"); mvt.setTrait(CommonStrata.user, "name", "Test"); await mvt.loadMapItems(); const legendItem = mvt.legends[0].items[0]; expect(legendItem.color).toBe("red"); expect(legendItem.outlineColor).toBe("yellow"); expect(legendItem.outlineWidth).toBe(1); expect(legendItem.title).toBe("Test"); }) ); }); describe("paint rules", function () { it( "creates paint rules from simple styles", action(function () { mvt.setTrait(CommonStrata.user, "fillColor", "red"); mvt.setTrait(CommonStrata.user, "lineColor", "yellow"); mvt.setTrait(CommonStrata.user, "layer", "Test"); expect(mvt.paintRules.length).toBe(2); expect(mvt.paintRules[0].dataLayer).toBe("Test"); expect(mvt.paintRules[1].dataLayer).toBe("Test"); expect( mvt.paintRules[0].symbolizer instanceof PolygonSymbolizer ).toBeTruthy(); expect( mvt.paintRules[1].symbolizer instanceof LineSymbolizer ).toBeTruthy(); }) ); }); describe("highlight features", function () { let imageryProvider: ProtomapsImageryProvider; beforeEach(async () => { updateModelFromJson(mvt, CommonStrata.definition, { name: "Mapbox vector tiles Test", type: "mvt", url: "/test/mvt/nsw-lga-mvt/{z}/{x}/{y}.pbf", fillColor: "#ff0000", lineColor: "#ffff00", minimumZoom: 0, maximumNativeZoom: 11, maximumZoom: 28, idProperty: "LGA_CODE13", layer: "FID_LGA_2013_AUST" }); await mvt.loadMapItems(); if (!ImageryParts.is(mvt.mapItems[0])) throw new Error("Expected MapItem to be an ImageryParts"); imageryProvider = mvt.mapItems[0] .imageryProvider as ProtomapsImageryProvider; }); it("correctly picks features", async function () { worker.use( http.get("*.pbf", () => { return passthrough(); }) ); // Get polygon const polygon = await imageryProvider.pickFeatures( 1881, 1229, 11, 2.630470869072516, -0.5932730847619763 ); expect(polygon.length).toBe(1); expect(polygon[0]?.properties?.LGA_CODE13).toBe("11450"); // Select nothing const nothingToSelect = await imageryProvider.pickFeatures( 1884, 1230, 11, 2.6387404164527286, -0.5945282912087255 ); expect(nothingToSelect.length).toBe(0); }); }); /** Mapbox style json tests are adapted from from https://github.com/protomaps/protomaps-leaflet/blob/a08304417ef36fef03679976cd3e5a971fec19a2/test/json_style.test.ts * License: BSD-3-Clause * Copyright 2021-2024 Protomaps LLC * Full license https://github.com/protomaps/protomaps-leaflet/blob/main/LICENSE */ describe("mapbox style json", function () { const emptyFeature = { props: {}, geomType: GeomType.Point, numVertices: 0, geom: [], bbox: { minX: 0, minY: 0, maxX: 0, maxY: 0 } }; let f: Filter | undefined; it("==", async () => { f = filterFn(["==", "building", "yes"]); expect(f(0, { ...emptyFeature, props: { building: "yes" } })).toBe(true); }); it("!=", async () => { f = filterFn(["!=", "building", "yes"]); expect(!f(0, { ...emptyFeature, props: { building: "yes" } })).toBe(true); expect(f(0, { ...emptyFeature, props: { building: "no" } })).toBe(true); }); it("<", async () => { f = filterFn(["<", "level", 3]); expect(f(0, { ...emptyFeature, props: { level: 2 } })).toBe(true); expect(!f(0, { ...emptyFeature, props: { level: 3 } })).toBe(true); }); it("<=", async () => { f = filterFn(["<=", "level", 3]); expect(f(0, { ...emptyFeature, props: { level: 2 } })).toBe(true); expect(f(0, { ...emptyFeature, props: { level: 3 } })).toBe(true); }); it(">", async () => { f = filterFn([">", "level", 3]); expect(f(0, { ...emptyFeature, props: { level: 4 } })).toBe(true); expect(!f(0, { ...emptyFeature, props: { level: 3 } })).toBe(true); }); it(">=", async () => { f = filterFn([">=", "level", 3]); expect(f(0, { ...emptyFeature, props: { level: 4 } })).toBe(true); expect(f(0, { ...emptyFeature, props: { level: 3 } })).toBe(true); }); it("in", async () => { f = filterFn(["in", "type", "foo", "bar"]); expect(f(0, { ...emptyFeature, props: { type: "foo" } })).toBe(true); expect(f(0, { ...emptyFeature, props: { type: "bar" } })).toBe(true); expect(!f(0, { ...emptyFeature, props: { type: "baz" } })).toBe(true); }); it("!in", async () => { f = filterFn(["!in", "type", "foo", "bar"]); expect(!f(0, { ...emptyFeature, props: { type: "bar" } })).toBe(true); expect(f(0, { ...emptyFeature, props: { type: "baz" } })).toBe(true); }); it("has", async () => { f = filterFn(["has", "type"]); expect(f(0, { ...emptyFeature, props: { type: "foo" } })).toBe(true); expect(!f(0, { ...emptyFeature, props: {} })).toBe(true); }); it("!has", async () => { f = filterFn(["!has", "type"]); expect(!f(0, { ...emptyFeature, props: { type: "foo" } })).toBe(true); expect(f(0, { ...emptyFeature, props: {} })).toBe(true); }); it("!", async () => { f = filterFn(["!", ["has", "type"]]); expect(!f(0, { ...emptyFeature, props: { type: "foo" } })).toBe(true); expect(f(0, { ...emptyFeature, props: {} })).toBe(true); }); it("all", async () => { f = filterFn(["all", ["==", "building", "yes"], ["==", "type", "foo"]]); expect(!f(0, { ...emptyFeature, props: { building: "yes" } })).toBe(true); expect(!f(0, { ...emptyFeature, props: { type: "foo" } })).toBe(true); expect( f(0, { ...emptyFeature, props: { building: "yes", type: "foo" } }) ).toBe(true); }); it("any", async () => { f = filterFn(["any", ["==", "building", "yes"], ["==", "type", "foo"]]); expect(!f(0, { ...emptyFeature, props: {} })).toBe(true); expect(f(0, { ...emptyFeature, props: { building: "yes" } })).toBe(true); expect(f(0, { ...emptyFeature, props: { type: "foo" } })).toBe(true); expect( f(0, { ...emptyFeature, props: { building: "yes", type: "foo" } }) ).toBe(true); }); it("numberFn constant", async () => { let n = numberOrFn(5); expect(n).toEqual(5); n = numberOrFn(undefined); expect(n).toEqual(0); }); it("numberFn function", async () => { const n = numberFn({ base: 1, stops: [ [14, 0], [16, 2] ] }); expect(n.length).toEqual(1); expect(n(15)).toEqual(0); expect(n(16)).toEqual(1); expect(n(17)).toEqual(2); }); it("numberFn interpolate", async () => { const n = numberFn([ "interpolate", ["exponential", 1], ["zoom"], 14, 0, 16, 2 ]); expect(n.length).toEqual(1); expect(n(15)).toEqual(0); expect(n(16)).toEqual(1); expect(n(17)).toEqual(2); }); it("numberFn properties", async () => { const n = numberFn(["step", ["get", "scalerank"], 0, 1, 2, 3, 4]); expect(n.length).toEqual(2); expect(n(14, { ...emptyFeature, props: { scalerank: 0 } })).toEqual(0); expect(n(14, { ...emptyFeature, props: { scalerank: 1 } })).toEqual(2); expect(n(14, { ...emptyFeature, props: { scalerank: 3 } })).toEqual(4); expect(n(14, { ...emptyFeature, props: { scalerank: 4 } })).toEqual(4); }); it("font", async () => { let n = getFont({ "text-font": ["Noto"], "text-size": 14 }, {}); expect(n(1)).toEqual("14px sans-serif"); n = getFont({ "text-font": ["Noto"], "text-size": 15 }, {}); expect(n(1)).toEqual("15px sans-serif"); n = getFont( { "text-font": ["Noto"], "text-size": 15 }, { Noto: { face: "serif" } } ); expect(n(1)).toEqual("15px serif"); n = getFont( { "text-font": ["Boto", "Noto"], "text-size": 15 }, { Noto: { face: "serif" }, Boto: { face: "Comic Sans" } } ); expect(n(1)).toEqual("15px Comic Sans, serif"); }); it("font weight and style", async () => { let n = getFont( { "text-font": ["Noto"], "text-size": 15 }, { Noto: { face: "serif", weight: 100 } } ); expect(n(1)).toEqual("100 15px serif"); n = getFont( { "text-font": ["Noto"], "text-size": 15 }, { Noto: { face: "serif", style: "italic" } } ); expect(n(1)).toEqual("italic 15px serif"); }); it("font size fn zoom", async () => { const n = getFont( { "text-font": ["Noto"], "text-size": { base: 1, stops: [ [14, 1], [16, 3] ] } }, {} ); expect(n(15)).toEqual("1px sans-serif"); expect(n(16)).toEqual("2px sans-serif"); expect(n(17)).toEqual("3px sans-serif"); }); it("font size fn zoom props", async () => { const n = getFont( { "text-font": ["Noto"], "text-size": ["step", ["get", "scalerank"], 0, 1, 12, 2, 10] }, {} ); expect(n(14, { ...emptyFeature, props: { scalerank: 0 } })).toEqual( "0px sans-serif" ); expect(n(14, { ...emptyFeature, props: { scalerank: 1 } })).toEqual( "12px sans-serif" ); expect(n(14, { ...emptyFeature, props: { scalerank: 2 } })).toEqual( "10px sans-serif" ); }); }); });