Skip to content

Update default CS and Projection for layer #854

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 18 commits into from
Jul 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
defbef0
change default cs from pcrs to gcrs
AliyanH May 18, 2023
2cf9a24
update index to use default cs
AliyanH May 18, 2023
e1bfd0c
default layer projection to map's projection when map-meta projection…
AliyanH May 31, 2023
1d6dd65
Add test to ensure a layer without any map-meta gets rendered
AliyanH Jun 2, 2023
64f2404
update _getnativeVariables to support query inputs
AliyanH Jun 14, 2023
330e7d8
Provide map-meta in map-extent shadowRoots to give respect to mapml d…
AliyanH Jun 16, 2023
0f408b7
Add bug fix for empty getFeatureInfo
AliyanH Jun 19, 2023
41f373e
Add map-feature._groupEl._feature property, which will allow code to
Jun 19, 2023
3be3684
Remove cs="gcrs" and <map-meta name="projection" content="OSMTILE">
Jun 19, 2023
5fb03f9
update test to use default projection and CS, remove cs="gcrs", map-m…
AliyanH Jun 20, 2023
376e43a
preserve map-metas in map-extent shadowroot when paginating
AliyanH Jun 25, 2023
f11128f
make statement fail-proof
AliyanH Jun 25, 2023
9585291
Revert Add map-feature._groupEl._feature property. Will integrate (b…
Jun 26, 2023
ca9399d
Add default projection (OSMTILE) + remove use of 'createmap' events
AliyanH Jul 7, 2023
a3b4a0b
Revert "Add default projection (OSMTILE) + remove use of 'createmap' …
AliyanH Jul 18, 2023
b8b529e
fix small bug with remote layer with no map-metas + Add tests
AliyanH Jul 19, 2023
05b6d68
Merge branch 'main' into default-CS-Projection
AliyanH Jul 19, 2023
fafca0e
bug fix after merging upstream
AliyanH Jul 19, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,6 @@
<map-caption>A pleasing map of Canada</map-caption>
<layer- label="CBMT" src="https://geogratis.gc.ca/mapml/en/cbmtile/cbmt/" checked></layer->
<layer- label="Hat Guy" checked>
<map-meta name="projection" content="CBMTILE"></map-meta>
<map-feature id="twohats" zoom="15" class="twohats">
<map-properties>
<table>
Expand Down Expand Up @@ -117,7 +116,7 @@
</tr>
</table>
</map-properties>
<map-geometry cs="gcrs">
<map-geometry>
<map-point>
<map-coordinates>-75.705278 45.397778</map-coordinates>
</map-point>
Expand Down
28 changes: 24 additions & 4 deletions src/map-feature.js
Original file line number Diff line number Diff line change
Expand Up @@ -291,14 +291,32 @@ export class MapFeature extends HTMLElement {
_getNativeZoomAndCS(content) {
// content: referred to <layer- > if the <layer- > has inline <map-extent>, <map-feature> or <map-tile>
// referred to remote mapml if the <layer- > has a src attribute, and the fetched mapml contains <map-feature>
// referred to [map-meta, ...] if it is query
// referred to null otherwise (i.e. <layer- > has fetched <map-extent> in shadow, the <map-feature> attaches to <map-extent>'s shadow)
let nativeZoom, nativeCS;
if (this._extentEl) {
// feature attaches to extent's shadow
if (this._extentEl.querySelector('map-link[rel=query]')) {
// for query, fallback zoom is the current map zoom level that the query is returned
nativeZoom = this._map.getZoom();
nativeCS = 'pcrs';
let metaZoom, metaCS;
if (content) {
metaZoom = M._metaContentToObject(
Array.prototype.filter
.call(content, function (elem) {
return elem.matches('map-meta[name=zoom]');
})[0]
?.getAttribute('content')
).content;
metaCS = M._metaContentToObject(
Array.prototype.filter
.call(content, function (elem) {
return elem.matches('map-meta[name=cs]');
})[0]
?.getAttribute('content')
).content;
}
nativeZoom = metaZoom || this._map.getZoom();
nativeCS = metaCS || 'gcrs';
} else if (this._extentEl.querySelector('map-link[rel=features]')) {
// for templated feature, read fallback from the fetched mapml's map-meta[name=zoom / cs]
nativeZoom = this._extentEl._nativeZoom;
Expand Down Expand Up @@ -326,7 +344,7 @@ export class MapFeature extends HTMLElement {
csLength = csMeta?.length;
nativeCS = csLength
? csMeta[csLength - 1].getAttribute('content')
: 'pcrs';
: 'gcrs';
return { zoom: nativeZoom, cs: nativeCS };
}
}
Expand All @@ -347,7 +365,9 @@ export class MapFeature extends HTMLElement {
// calculate feature extent
let map = this._map,
geometry = this.querySelector('map-geometry'),
native = this._getNativeZoomAndCS(this._layer._content),
native = this._getNativeZoomAndCS(
this._layer._content || this._layer.metas
),
cs = geometry.getAttribute('cs') || native.cs,
// zoom level that the feature rendered at
zoom = this.zoom || native.zoom,
Expand Down
9 changes: 8 additions & 1 deletion src/mapml/handlers/QueryHandler.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,12 @@ export var QueryHandler = L.Handler.extend({
features = Array.prototype.slice.call(
mapmldoc.querySelectorAll('map-feature')
);
// <map-meta> elements
layer.metas = Array.prototype.slice.call(
mapmldoc.querySelectorAll(
'map-meta[name=cs], map-meta[name=zoom], map-meta[name=projection]'
)
);
if (features.length)
layer._mapmlFeatures = layer._mapmlFeatures.concat(features);
} else {
Expand Down Expand Up @@ -246,6 +252,7 @@ export var QueryHandler = L.Handler.extend({
}
}
function displayFeaturesPopup(features, loc) {
if (features.length === 0) return;
let f = M.featureLayer(features, {
// pass the vector layer a renderer of its own, otherwise leaflet
// puts everything into the overlayPane
Expand Down Expand Up @@ -279,7 +286,7 @@ export var QueryHandler = L.Handler.extend({
layer.on('popupclose', function () {
map.removeLayer(f);
});
f.showPaginationFeature({ i: 0, popup: layer._popup });
f.showPaginationFeature({ i: 0, popup: layer._popup, meta: layer.metas });
}
}
});
74 changes: 58 additions & 16 deletions src/mapml/layers/FeatureLayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,14 +88,26 @@ export var FeatureLayer = L.FeatureGroup.extend({
showPaginationFeature: function (e) {
if (this.options.query && this._mapmlFeatures[e.i]) {
let feature = this._mapmlFeatures[e.i];
// remove the prev / next one <map-feature> from shadow if there is any
feature._extentEl.shadowRoot.firstChild?.remove();
if (e.type === 'featurepagination') {
// remove map-feature only (keep meta's) when paginating
feature._extentEl.shadowRoot.querySelector('map-feature')?.remove();
} else {
// empty the map-extent shadowRoot
// remove the prev / next one <map-feature> and <map-meta>'s from shadow if there is any
feature._extentEl.shadowRoot.replaceChildren();
}
this.clearLayers();
feature._featureGroup = this.addData(
feature,
this.options.nativeCS,
this.options.nativeZoom
);
// append all map-meta from mapml document
if (e.meta) {
for (let i = 0; i < e.meta.length; i++) {
feature._extentEl.shadowRoot.appendChild(e.meta[i]);
}
}
feature._extentEl.shadowRoot.appendChild(feature);
e.popup._navigationBar.querySelector('p').innerText =
e.i + 1 + '/' + this.options._leafletLayer._totalFeatureCount;
Expand All @@ -116,21 +128,51 @@ export var FeatureLayer = L.FeatureGroup.extend({
}
},

// _getNativeVariables: returns an object with the native zoom and CS,
// based on the map-metas that are available within
// the layer or the fallback default values.
// _getNativeVariables: mapml-||layer-||null||[map-feature,...] -> {zoom: _, val: _}
// mapml can be a mapml- element, layer- element, null, or an array of map-features
_getNativeVariables: function (mapml) {
let nativeZoom =
(mapml.querySelector &&
mapml.querySelector('map-meta[name=zoom]') &&
+M._metaContentToObject(
mapml.querySelector('map-meta[name=zoom]').getAttribute('content')
).value) ||
0;
let nativeCS =
(mapml.querySelector &&
mapml.querySelector('map-meta[name=cs]') &&
M._metaContentToObject(
mapml.querySelector('map-meta[name=cs]').getAttribute('content')
).content) ||
'PCRS';
let nativeZoom, nativeCS;
// when mapml is an array of features provided by the query
if (
mapml.length &&
mapml[0].parentElement.parentElement &&
mapml[0].parentElement.parentElement.tagName === 'mapml-'
) {
let mapmlEl = mapml[0].parentElement.parentElement;
nativeZoom =
(mapmlEl.querySelector &&
mapmlEl.querySelector('map-meta[name=zoom]') &&
+M._metaContentToObject(
mapmlEl.querySelector('map-meta[name=zoom]').getAttribute('content')
).value) ||
0;
nativeCS =
(mapmlEl.querySelector &&
mapmlEl.querySelector('map-meta[name=cs]') &&
M._metaContentToObject(
mapmlEl.querySelector('map-meta[name=cs]').getAttribute('content')
).content) ||
'GCRS';
} else {
// when mapml is null or a layer-/mapml- element
nativeZoom =
(mapml.querySelector &&
mapml.querySelector('map-meta[name=zoom]') &&
+M._metaContentToObject(
mapml.querySelector('map-meta[name=zoom]').getAttribute('content')
).value) ||
0;
nativeCS =
(mapml.querySelector &&
mapml.querySelector('map-meta[name=cs]') &&
M._metaContentToObject(
mapml.querySelector('map-meta[name=cs]').getAttribute('content')
).content) ||
'GCRS';
}
return { zoom: nativeZoom, cs: nativeCS };
},

Expand Down
35 changes: 27 additions & 8 deletions src/mapml/layers/MapMLLayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -1401,11 +1401,21 @@ export var MapMLLayer = L.Layer.extend({
layer._content = mapml;
if (!this.responseXML && this.responseText)
mapml = new DOMParser().parseFromString(this.responseText, 'text/xml');

// if everything is ok, continue with the processing
if (
this.readyState === this.DONE &&
mapml.querySelector &&
!mapml.querySelector('parsererror')
) {
// Get layer's title/label
if (mapml.querySelector('map-title')) {
layer._title = mapml.querySelector('map-title').textContent.trim();
layer._titleIsReadOnly = true;
} else if (mapml instanceof Element && mapml.hasAttribute('label')) {
layer._title = mapml.getAttribute('label').trim();
}

var serverExtent = mapml.querySelectorAll('map-extent'),
projection,
projectionMatch,
Expand Down Expand Up @@ -1441,6 +1451,15 @@ export var MapMLLayer = L.Layer.extend({
projectionMatch =
projection && projection === layer.options.mapprojection;
}
} else {
// default projection set to parent projection when no map-meta projection element present
projection = layer.options.mapprojection;
projectionMatch = true;
serverMeta = projection;
console.log(
`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/`
);
// TODO: Add a more obvious warning.
}

var metaExtent = mapml.querySelector('map-meta[name=extent]'),
Expand Down Expand Up @@ -1509,7 +1528,13 @@ export var MapMLLayer = L.Layer.extend({
}
}
} else {
layer._extent = serverMeta;
if (typeof serverMeta === 'string') {
// when map-meta projection not present for layer
layer._extent = { serverMeta };
} else {
// when map-meta projection present for layer
layer._extent = serverMeta;
}
}
layer._parseLicenseAndLegend(mapml, layer, projection);

Expand Down Expand Up @@ -1626,12 +1651,6 @@ export var MapMLLayer = L.Layer.extend({
layer._styles = stylesControl;
}

if (mapml.querySelector('map-title')) {
layer._title = mapml.querySelector('map-title').textContent.trim();
layer._titleIsReadOnly = true;
} else if (mapml instanceof Element && mapml.hasAttribute('label')) {
layer._title = mapml.getAttribute('label').trim();
}
if (layer._map) {
layer._validateExtent();
// if the layer is checked in the layer control, force the addition
Expand Down Expand Up @@ -1742,7 +1761,7 @@ export var MapMLLayer = L.Layer.extend({
let extent = this._extent._mapExtents
? this._extent._mapExtents[0]
: this._extent; // the projections for each extent eould be the same (as) validated in _validProjection, so can use mapExtents[0]
if (!extent) return FALLBACK_PROJECTION;
if (extent.serverMeta) return extent.serverMeta;
switch (extent.tagName.toUpperCase()) {
case 'MAP-EXTENT':
if (extent.hasAttribute('units'))
Expand Down
2 changes: 1 addition & 1 deletion src/mapml/layers/TemplatedFeaturesLayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ export var TemplatedFeaturesLayer = L.Layer.extend({
.querySelector('map-meta[name=cs]')
.getAttribute('content')
).content) ||
'PCRS');
'GCRS');
features.addData(mapml, nativeCS, nativeZoom);
// "migrate" to extent's shadow
// make a clone, prevent the elements from being removed from mapml file
Expand Down
10 changes: 10 additions & 0 deletions test/e2e/core/metaDefault.html
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,16 @@
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>
</map-extent>
</layer->
<layer- label="Default meta" checked>
<map-feature>
<map-geometry>
<map-point>
<map-coordinates>-79.477626 43.764814</map-coordinates>
</map-point>
</map-geometry>
</map-feature>
</layer->
<layer- src="data/noMapMeta" checked></layer->
</mapml-viewer>

</body>
Expand Down
36 changes: 36 additions & 0 deletions test/e2e/core/metaDefault.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,40 @@ test.describe('Playwright Missing Min Max Attribute, Meta Default Tests', () =>
expectedGCRSSecondLayer.bottomRight
);
});
test("Layer with no map-meta's is rendered on map", async () => {
const viewer = await page.evaluateHandle(() =>
document.querySelector('mapml-viewer')
);
const layerSVG = await (
await page.evaluateHandle(
(map) =>
map.shadowRoot
.querySelectorAll('.mapml-layer')[2]
.querySelector('path')
.getAttribute('d'),
viewer
)
).jsonValue();
expect(layerSVG).toEqual(
'M190 311 L177.5 281 C177.5 261, 202.5 261, 202.5 281 L190 311z'
);
});
test("Fetched layer with no map-meta's is rendered on map", async () => {
const viewer = await page.evaluateHandle(() =>
document.querySelector('mapml-viewer')
);
const layerSVG = await (
await page.evaluateHandle(
(map) =>
map.shadowRoot
.querySelectorAll('.mapml-layer')[3]
.querySelector('path')
.getAttribute('d'),
viewer
)
).jsonValue();
expect(layerSVG).toEqual(
'M243 255 L230.5 225 C230.5 205, 255.5 205, 255.5 225 L243 255z'
);
});
});
18 changes: 18 additions & 0 deletions test/e2e/data/noMapMeta.mapml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<mapml- xmlns="http://www.w3.org/1999/xhtml">
<map-head>
<map-title>Capital of Canada</map-title>
<map-meta charset="utf-8" ></map-meta>
<map-meta content="text/mapml" http-equiv="Content-Type" ></map-meta>
</map-head>
<map-body>
<map-feature zoom="12">
<map-featurecaption>Ottawa</map-featurecaption>
<map-properties><h2 style="text-align:center">Ottawa</h2></map-properties>
<map-geometry>
<map-point>
<map-coordinates>-75.715027 45.424721</map-coordinates>
</map-point>
</map-geometry>
</map-feature>
</map-body>
</mapml->
Loading