Skip to content

Commit b48d26d

Browse files
AliyanHprushfor
and
prushfor
authored
Update default CS and Projection for layer- (#854)
* change default cs from pcrs to gcrs * update index to use default cs * default layer projection to map's projection when map-meta projection not present for layer * Add test to ensure a layer without any map-meta gets rendered * update _getnativeVariables to support query inputs * Provide map-meta in map-extent shadowRoots to give respect to mapml document' metadata + fix zoom to here link for query * Add bug fix for empty getFeatureInfo * Add map-feature._groupEl._feature property, which will allow code to navigate from focused on-screen element in shadow root to custom element (_groupEl already allows code navigation to SD-rendered content) Add test leveraging <g>._feature property to verify rendering of default coordinate system geometries. * Remove cs="gcrs" and <map-meta name="projection" content="OSMTILE"> because they are the defaults / fallback values when not explicitly marked up. * update test to use default projection and CS, remove cs="gcrs", map-meta name="cs" content="gcrs" and map-meta name="projection" content="OSMTILE" Revert "Remove cs="gcrs" and <map-meta name="projection" content="OSMTILE"> " This reverts commit 3be3684. update test to use default projection and CS * preserve map-metas in map-extent shadowroot when paginating * make statement fail-proof * Revert Add map-feature._groupEl._feature property. Will integrate (better) changes when we've resolved 'createmap' dilemma. * Add default projection (OSMTILE) + remove use of 'createmap' events * Revert "Add default projection (OSMTILE) + remove use of 'createmap' events" This reverts commit ca9399d. * fix small bug with remote layer with no map-metas + Add tests * bug fix after merging upstream --------- Co-authored-by: prushfor <[email protected]>
1 parent 0b85ad5 commit b48d26d

13 files changed

+218
-82
lines changed

index.html

+1-2
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,6 @@
7676
<map-caption>A pleasing map of Canada</map-caption>
7777
<layer- label="CBMT" src="https://geogratis.gc.ca/mapml/en/cbmtile/cbmt/" checked></layer->
7878
<layer- label="Hat Guy" checked>
79-
<map-meta name="projection" content="CBMTILE"></map-meta>
8079
<map-feature id="twohats" zoom="15" class="twohats">
8180
<map-properties>
8281
<table>
@@ -117,7 +116,7 @@
117116
</tr>
118117
</table>
119118
</map-properties>
120-
<map-geometry cs="gcrs">
119+
<map-geometry>
121120
<map-point>
122121
<map-coordinates>-75.705278 45.397778</map-coordinates>
123122
</map-point>

src/map-feature.js

+24-4
Original file line numberDiff line numberDiff line change
@@ -291,14 +291,32 @@ export class MapFeature extends HTMLElement {
291291
_getNativeZoomAndCS(content) {
292292
// content: referred to <layer- > if the <layer- > has inline <map-extent>, <map-feature> or <map-tile>
293293
// referred to remote mapml if the <layer- > has a src attribute, and the fetched mapml contains <map-feature>
294+
// referred to [map-meta, ...] if it is query
294295
// referred to null otherwise (i.e. <layer- > has fetched <map-extent> in shadow, the <map-feature> attaches to <map-extent>'s shadow)
295296
let nativeZoom, nativeCS;
296297
if (this._extentEl) {
297298
// feature attaches to extent's shadow
298299
if (this._extentEl.querySelector('map-link[rel=query]')) {
299300
// for query, fallback zoom is the current map zoom level that the query is returned
300-
nativeZoom = this._map.getZoom();
301-
nativeCS = 'pcrs';
301+
let metaZoom, metaCS;
302+
if (content) {
303+
metaZoom = M._metaContentToObject(
304+
Array.prototype.filter
305+
.call(content, function (elem) {
306+
return elem.matches('map-meta[name=zoom]');
307+
})[0]
308+
?.getAttribute('content')
309+
).content;
310+
metaCS = M._metaContentToObject(
311+
Array.prototype.filter
312+
.call(content, function (elem) {
313+
return elem.matches('map-meta[name=cs]');
314+
})[0]
315+
?.getAttribute('content')
316+
).content;
317+
}
318+
nativeZoom = metaZoom || this._map.getZoom();
319+
nativeCS = metaCS || 'gcrs';
302320
} else if (this._extentEl.querySelector('map-link[rel=features]')) {
303321
// for templated feature, read fallback from the fetched mapml's map-meta[name=zoom / cs]
304322
nativeZoom = this._extentEl._nativeZoom;
@@ -326,7 +344,7 @@ export class MapFeature extends HTMLElement {
326344
csLength = csMeta?.length;
327345
nativeCS = csLength
328346
? csMeta[csLength - 1].getAttribute('content')
329-
: 'pcrs';
347+
: 'gcrs';
330348
return { zoom: nativeZoom, cs: nativeCS };
331349
}
332350
}
@@ -347,7 +365,9 @@ export class MapFeature extends HTMLElement {
347365
// calculate feature extent
348366
let map = this._map,
349367
geometry = this.querySelector('map-geometry'),
350-
native = this._getNativeZoomAndCS(this._layer._content),
368+
native = this._getNativeZoomAndCS(
369+
this._layer._content || this._layer.metas
370+
),
351371
cs = geometry.getAttribute('cs') || native.cs,
352372
// zoom level that the feature rendered at
353373
zoom = this.zoom || native.zoom,

src/mapml/handlers/QueryHandler.js

+8-1
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,12 @@ export var QueryHandler = L.Handler.extend({
109109
features = Array.prototype.slice.call(
110110
mapmldoc.querySelectorAll('map-feature')
111111
);
112+
// <map-meta> elements
113+
layer.metas = Array.prototype.slice.call(
114+
mapmldoc.querySelectorAll(
115+
'map-meta[name=cs], map-meta[name=zoom], map-meta[name=projection]'
116+
)
117+
);
112118
if (features.length)
113119
layer._mapmlFeatures = layer._mapmlFeatures.concat(features);
114120
} else {
@@ -246,6 +252,7 @@ export var QueryHandler = L.Handler.extend({
246252
}
247253
}
248254
function displayFeaturesPopup(features, loc) {
255+
if (features.length === 0) return;
249256
let f = M.featureLayer(features, {
250257
// pass the vector layer a renderer of its own, otherwise leaflet
251258
// puts everything into the overlayPane
@@ -279,7 +286,7 @@ export var QueryHandler = L.Handler.extend({
279286
layer.on('popupclose', function () {
280287
map.removeLayer(f);
281288
});
282-
f.showPaginationFeature({ i: 0, popup: layer._popup });
289+
f.showPaginationFeature({ i: 0, popup: layer._popup, meta: layer.metas });
283290
}
284291
}
285292
});

src/mapml/layers/FeatureLayer.js

+58-16
Original file line numberDiff line numberDiff line change
@@ -88,14 +88,26 @@ export var FeatureLayer = L.FeatureGroup.extend({
8888
showPaginationFeature: function (e) {
8989
if (this.options.query && this._mapmlFeatures[e.i]) {
9090
let feature = this._mapmlFeatures[e.i];
91-
// remove the prev / next one <map-feature> from shadow if there is any
92-
feature._extentEl.shadowRoot.firstChild?.remove();
91+
if (e.type === 'featurepagination') {
92+
// remove map-feature only (keep meta's) when paginating
93+
feature._extentEl.shadowRoot.querySelector('map-feature')?.remove();
94+
} else {
95+
// empty the map-extent shadowRoot
96+
// remove the prev / next one <map-feature> and <map-meta>'s from shadow if there is any
97+
feature._extentEl.shadowRoot.replaceChildren();
98+
}
9399
this.clearLayers();
94100
feature._featureGroup = this.addData(
95101
feature,
96102
this.options.nativeCS,
97103
this.options.nativeZoom
98104
);
105+
// append all map-meta from mapml document
106+
if (e.meta) {
107+
for (let i = 0; i < e.meta.length; i++) {
108+
feature._extentEl.shadowRoot.appendChild(e.meta[i]);
109+
}
110+
}
99111
feature._extentEl.shadowRoot.appendChild(feature);
100112
e.popup._navigationBar.querySelector('p').innerText =
101113
e.i + 1 + '/' + this.options._leafletLayer._totalFeatureCount;
@@ -116,21 +128,51 @@ export var FeatureLayer = L.FeatureGroup.extend({
116128
}
117129
},
118130

131+
// _getNativeVariables: returns an object with the native zoom and CS,
132+
// based on the map-metas that are available within
133+
// the layer or the fallback default values.
134+
// _getNativeVariables: mapml-||layer-||null||[map-feature,...] -> {zoom: _, val: _}
135+
// mapml can be a mapml- element, layer- element, null, or an array of map-features
119136
_getNativeVariables: function (mapml) {
120-
let nativeZoom =
121-
(mapml.querySelector &&
122-
mapml.querySelector('map-meta[name=zoom]') &&
123-
+M._metaContentToObject(
124-
mapml.querySelector('map-meta[name=zoom]').getAttribute('content')
125-
).value) ||
126-
0;
127-
let nativeCS =
128-
(mapml.querySelector &&
129-
mapml.querySelector('map-meta[name=cs]') &&
130-
M._metaContentToObject(
131-
mapml.querySelector('map-meta[name=cs]').getAttribute('content')
132-
).content) ||
133-
'PCRS';
137+
let nativeZoom, nativeCS;
138+
// when mapml is an array of features provided by the query
139+
if (
140+
mapml.length &&
141+
mapml[0].parentElement.parentElement &&
142+
mapml[0].parentElement.parentElement.tagName === 'mapml-'
143+
) {
144+
let mapmlEl = mapml[0].parentElement.parentElement;
145+
nativeZoom =
146+
(mapmlEl.querySelector &&
147+
mapmlEl.querySelector('map-meta[name=zoom]') &&
148+
+M._metaContentToObject(
149+
mapmlEl.querySelector('map-meta[name=zoom]').getAttribute('content')
150+
).value) ||
151+
0;
152+
nativeCS =
153+
(mapmlEl.querySelector &&
154+
mapmlEl.querySelector('map-meta[name=cs]') &&
155+
M._metaContentToObject(
156+
mapmlEl.querySelector('map-meta[name=cs]').getAttribute('content')
157+
).content) ||
158+
'GCRS';
159+
} else {
160+
// when mapml is null or a layer-/mapml- element
161+
nativeZoom =
162+
(mapml.querySelector &&
163+
mapml.querySelector('map-meta[name=zoom]') &&
164+
+M._metaContentToObject(
165+
mapml.querySelector('map-meta[name=zoom]').getAttribute('content')
166+
).value) ||
167+
0;
168+
nativeCS =
169+
(mapml.querySelector &&
170+
mapml.querySelector('map-meta[name=cs]') &&
171+
M._metaContentToObject(
172+
mapml.querySelector('map-meta[name=cs]').getAttribute('content')
173+
).content) ||
174+
'GCRS';
175+
}
134176
return { zoom: nativeZoom, cs: nativeCS };
135177
},
136178

src/mapml/layers/MapMLLayer.js

+27-8
Original file line numberDiff line numberDiff line change
@@ -1401,11 +1401,21 @@ export var MapMLLayer = L.Layer.extend({
14011401
layer._content = mapml;
14021402
if (!this.responseXML && this.responseText)
14031403
mapml = new DOMParser().parseFromString(this.responseText, 'text/xml');
1404+
1405+
// if everything is ok, continue with the processing
14041406
if (
14051407
this.readyState === this.DONE &&
14061408
mapml.querySelector &&
14071409
!mapml.querySelector('parsererror')
14081410
) {
1411+
// Get layer's title/label
1412+
if (mapml.querySelector('map-title')) {
1413+
layer._title = mapml.querySelector('map-title').textContent.trim();
1414+
layer._titleIsReadOnly = true;
1415+
} else if (mapml instanceof Element && mapml.hasAttribute('label')) {
1416+
layer._title = mapml.getAttribute('label').trim();
1417+
}
1418+
14091419
var serverExtent = mapml.querySelectorAll('map-extent'),
14101420
projection,
14111421
projectionMatch,
@@ -1441,6 +1451,15 @@ export var MapMLLayer = L.Layer.extend({
14411451
projectionMatch =
14421452
projection && projection === layer.options.mapprojection;
14431453
}
1454+
} else {
1455+
// default projection set to parent projection when no map-meta projection element present
1456+
projection = layer.options.mapprojection;
1457+
projectionMatch = true;
1458+
serverMeta = projection;
1459+
console.log(
1460+
`A projection was not assigned to the '${layer._title}' Layer. Please specify a projection for that layer using a map-meta element. See more here - https://maps4html.org/web-map-doc/docs/elements/meta/`
1461+
);
1462+
// TODO: Add a more obvious warning.
14441463
}
14451464

14461465
var metaExtent = mapml.querySelector('map-meta[name=extent]'),
@@ -1509,7 +1528,13 @@ export var MapMLLayer = L.Layer.extend({
15091528
}
15101529
}
15111530
} else {
1512-
layer._extent = serverMeta;
1531+
if (typeof serverMeta === 'string') {
1532+
// when map-meta projection not present for layer
1533+
layer._extent = { serverMeta };
1534+
} else {
1535+
// when map-meta projection present for layer
1536+
layer._extent = serverMeta;
1537+
}
15131538
}
15141539
layer._parseLicenseAndLegend(mapml, layer, projection);
15151540

@@ -1626,12 +1651,6 @@ export var MapMLLayer = L.Layer.extend({
16261651
layer._styles = stylesControl;
16271652
}
16281653

1629-
if (mapml.querySelector('map-title')) {
1630-
layer._title = mapml.querySelector('map-title').textContent.trim();
1631-
layer._titleIsReadOnly = true;
1632-
} else if (mapml instanceof Element && mapml.hasAttribute('label')) {
1633-
layer._title = mapml.getAttribute('label').trim();
1634-
}
16351654
if (layer._map) {
16361655
layer._validateExtent();
16371656
// if the layer is checked in the layer control, force the addition
@@ -1742,7 +1761,7 @@ export var MapMLLayer = L.Layer.extend({
17421761
let extent = this._extent._mapExtents
17431762
? this._extent._mapExtents[0]
17441763
: this._extent; // the projections for each extent eould be the same (as) validated in _validProjection, so can use mapExtents[0]
1745-
if (!extent) return FALLBACK_PROJECTION;
1764+
if (extent.serverMeta) return extent.serverMeta;
17461765
switch (extent.tagName.toUpperCase()) {
17471766
case 'MAP-EXTENT':
17481767
if (extent.hasAttribute('units'))

src/mapml/layers/TemplatedFeaturesLayer.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ export var TemplatedFeaturesLayer = L.Layer.extend({
159159
.querySelector('map-meta[name=cs]')
160160
.getAttribute('content')
161161
).content) ||
162-
'PCRS');
162+
'GCRS');
163163
features.addData(mapml, nativeCS, nativeZoom);
164164
// "migrate" to extent's shadow
165165
// make a clone, prevent the elements from being removed from mapml file

test/e2e/core/metaDefault.html

+10
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,16 @@
3434
tref="http://maps.geogratis.gc.ca/wms/toporama_en?SERVICE=WMS&REQUEST=GetMap&FORMAT=image/jpeg&TRANSPARENT=FALSE&STYLES=&VERSION=1.3.0&LAYERS=WMS-Toporama&WIDTH={w}&HEIGHT={h}&CRS=EPSG:3978&BBOX={xmin},{ymin},{xmax},{ymax}&m4h=t" ></map-link>
3535
</map-extent>
3636
</layer->
37+
<layer- label="Default meta" checked>
38+
<map-feature>
39+
<map-geometry>
40+
<map-point>
41+
<map-coordinates>-79.477626 43.764814</map-coordinates>
42+
</map-point>
43+
</map-geometry>
44+
</map-feature>
45+
</layer->
46+
<layer- src="data/noMapMeta" checked></layer->
3747
</mapml-viewer>
3848

3949
</body>

test/e2e/core/metaDefault.test.js

+36
Original file line numberDiff line numberDiff line change
@@ -90,4 +90,40 @@ test.describe('Playwright Missing Min Max Attribute, Meta Default Tests', () =>
9090
expectedGCRSSecondLayer.bottomRight
9191
);
9292
});
93+
test("Layer with no map-meta's is rendered on map", async () => {
94+
const viewer = await page.evaluateHandle(() =>
95+
document.querySelector('mapml-viewer')
96+
);
97+
const layerSVG = await (
98+
await page.evaluateHandle(
99+
(map) =>
100+
map.shadowRoot
101+
.querySelectorAll('.mapml-layer')[2]
102+
.querySelector('path')
103+
.getAttribute('d'),
104+
viewer
105+
)
106+
).jsonValue();
107+
expect(layerSVG).toEqual(
108+
'M190 311 L177.5 281 C177.5 261, 202.5 261, 202.5 281 L190 311z'
109+
);
110+
});
111+
test("Fetched layer with no map-meta's is rendered on map", async () => {
112+
const viewer = await page.evaluateHandle(() =>
113+
document.querySelector('mapml-viewer')
114+
);
115+
const layerSVG = await (
116+
await page.evaluateHandle(
117+
(map) =>
118+
map.shadowRoot
119+
.querySelectorAll('.mapml-layer')[3]
120+
.querySelector('path')
121+
.getAttribute('d'),
122+
viewer
123+
)
124+
).jsonValue();
125+
expect(layerSVG).toEqual(
126+
'M243 255 L230.5 225 C230.5 205, 255.5 205, 255.5 225 L243 255z'
127+
);
128+
});
93129
});

test/e2e/data/noMapMeta.mapml

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<mapml- xmlns="http://www.w3.org/1999/xhtml">
2+
<map-head>
3+
<map-title>Capital of Canada</map-title>
4+
<map-meta charset="utf-8" ></map-meta>
5+
<map-meta content="text/mapml" http-equiv="Content-Type" ></map-meta>
6+
</map-head>
7+
<map-body>
8+
<map-feature zoom="12">
9+
<map-featurecaption>Ottawa</map-featurecaption>
10+
<map-properties><h2 style="text-align:center">Ottawa</h2></map-properties>
11+
<map-geometry>
12+
<map-point>
13+
<map-coordinates>-75.715027 45.424721</map-coordinates>
14+
</map-point>
15+
</map-geometry>
16+
</map-feature>
17+
</map-body>
18+
</mapml->

0 commit comments

Comments
 (0)