Skip to content

Commit 2914fd0

Browse files
authored
Add zooming when layer is added using <map-a> (#387)
* Add zooming when layer is added * Add inplace attribute to <map-a> * Inplace fix * Allow for async layer adding * Add comments to event handling * Add functionality for keypress * Focus new layer added on projection changes * Fix link autofocus behavior * Add test for projection negotiation and hash link * Fix zoomTo + _self behavior * Treat all sole fragments, of any target type as target=_self * Add longer time to test
1 parent 68da299 commit 2914fd0

File tree

13 files changed

+160
-105
lines changed

13 files changed

+160
-105
lines changed

src/layer.js

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,4 +302,41 @@ export class MapLayer extends HTMLElement {
302302
{target: this}}));
303303
},this);
304304
}
305+
focus(){
306+
if(!this.extent) return;
307+
let map = this._layer._map,
308+
tL = this.extent.topLeft.pcrs,
309+
bR = this.extent.bottomRight.pcrs,
310+
layerBounds = L.bounds(L.point(tL.horizontal, tL.vertical), L.point(bR.horizontal, bR.vertical)),
311+
center = map.options.crs.unproject(layerBounds.getCenter(true)),
312+
currentZoom = map.getZoom();
313+
314+
map.setView(center, currentZoom, {animate:false});
315+
let mapBounds = M.pixelToPCRSBounds(
316+
map.getPixelBounds(),
317+
map.getZoom(),
318+
map.options.projection);
319+
320+
//fits the bounds to the map view
321+
if(mapBounds.contains(layerBounds)){
322+
while(mapBounds.contains(layerBounds) && (currentZoom + 1) <= this.extent.zoom.maxZoom){
323+
currentZoom++;
324+
map.setView(center, currentZoom, {animate:false});
325+
mapBounds = M.pixelToPCRSBounds(
326+
map.getPixelBounds(),
327+
map.getZoom(),
328+
map.options.projection);
329+
}
330+
if(currentZoom - 1 >= 0) map.flyTo(center, (currentZoom - 1));
331+
} else {
332+
while(!(mapBounds.contains(layerBounds)) && (currentZoom - 1) >= this.extent.zoom.minZoom){
333+
currentZoom--;
334+
map.setView(center, currentZoom, {animate:false});
335+
mapBounds = M.pixelToPCRSBounds(
336+
map.getPixelBounds(),
337+
map.getZoom(),
338+
map.options.projection);
339+
}
340+
}
341+
}
305342
}

src/mapml-viewer.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -493,8 +493,8 @@ export class MapViewer extends HTMLElement {
493493
}
494494
}
495495
zoomTo(lat, lon, zoom) {
496-
zoom = Number.isInteger(zoom)? zoom:this.zoom;
497-
var location = new L.LatLng(lat,lon);
496+
zoom = Number.isInteger(+zoom) ? +zoom : this.zoom;
497+
let location = new L.LatLng(+lat, +lon);
498498
this._map.setView(location, zoom);
499499
this.zoom = zoom;
500500
this.lat = location.lat;

src/mapml/features/feature.js

Lines changed: 9 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -49,42 +49,24 @@ export var Feature = L.Path.extend({
4949
this.isClosed = this._isClosed();
5050
},
5151

52-
/**
53-
* Removes the focus handler, and calls the leaflet L.Path.onRemove
54-
*/
55-
onRemove: function () {
56-
if(this.options.link) {
57-
this.off({
58-
click: this._handleLinkClick,
59-
keypress: this._handleLinkKeypress,
60-
});
61-
}
62-
63-
if(this.options.interactive) this.off('keypress', this._handleSpaceDown);
64-
65-
L.Path.prototype.onRemove.call(this);
66-
},
67-
6852
/**
6953
* Attaches link handler to the sub parts' paths
7054
* @param path
7155
* @param link
72-
* @param linkTarget
73-
* @param linkType
7456
* @param leafletLayer
7557
*/
76-
attachLinkHandler: function (path, link, linkTarget, linkType, leafletLayer) {
58+
attachLinkHandler: function (path, link, leafletLayer) {
7759
let drag = false; //prevents click from happening on drags
7860
L.DomEvent.on(path, 'mousedown', () =>{ drag = false;}, this);
7961
L.DomEvent.on(path, 'mousemove', () =>{ drag = true;}, this);
8062
L.DomEvent.on(path, "mouseup", (e) => {
8163
L.DomEvent.stop(e);
82-
if(!drag) M.handleLink(link, linkTarget, linkType, leafletLayer);
64+
if(!drag) M.handleLink(link, leafletLayer);
8365
}, this);
8466
L.DomEvent.on(path, "keypress", (e) => {
8567
L.DomEvent.stop(e);
8668
if(e.keyCode === 13 || e.keyCode === 32)
87-
M.handleLink(link, linkTarget, linkType, leafletLayer);
69+
M.handleLink(link, leafletLayer);
8870
}, this);
8971
},
9072

@@ -159,9 +141,12 @@ export var Feature = L.Path.extend({
159141
}*/
160142
classList +=`${elem.className} `;
161143
} else if(!output.link && elem.getAttribute("href")) {
162-
output.link = elem.getAttribute("href");
163-
if(elem.hasAttribute("target")) output.linkTarget = elem.getAttribute("target");
164-
if(elem.hasAttribute("type")) output.linkType = elem.getAttribute("type");
144+
let link = {};
145+
link.url = elem.getAttribute("href");
146+
if(elem.hasAttribute("target")) link.target = elem.getAttribute("target");
147+
if(elem.hasAttribute("type")) link.type = elem.getAttribute("type");
148+
if(elem.hasAttribute("inplace")) link.inPlace = true;
149+
output.link = link;
165150
}
166151
}
167152
output.className = `${classList} ${this.options.className}`.trim();

src/mapml/features/featureGroup.js

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,9 @@ export var FeatureGroup = L.FeatureGroup.extend({
1616
L.DomUtil.addClass(this.options.group, "leaflet-interactive");
1717
L.DomEvent.on(this.options.group, "keyup keydown mousedown", this._handleFocus, this);
1818
let firstLayer = layers[Object.keys(layers)[0]];
19-
if(layers.length === 1 && firstLayer.options.link){ //if it's the only layer and it has a link, take it's link
20-
this.options.link = firstLayer.options.link;
21-
this.options.linkTarget = firstLayer.options.linkTarget;
22-
this.options.linkType = firstLayer.options.linkType;
23-
}
19+
if(layers.length === 1 && firstLayer.options.link) this.options.link = firstLayer.options.link;
2420
if(this.options.link){
25-
M.Feature.prototype.attachLinkHandler.call(this, this.options.group, this.options.link, this.options.linkTarget, this.options.linkType, this.options._leafletLayer);
21+
M.Feature.prototype.attachLinkHandler.call(this, this.options.group, this.options.link, this.options._leafletLayer);
2622
} else {
2723
this.options.group.setAttribute("aria-expanded", "false");
2824
this.options.onEachFeature(this.options.properties, this);

src/mapml/features/featureRenderer.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ export var FeatureRenderer = L.SVG.extend({
8888
if (p.path)
8989
layer.group.appendChild(p.path);
9090
if (interactive){
91-
if(layer.options.link) layer.attachLinkHandler(p.path, layer.options.link, layer.options.linkTarget, layer.options.linkType, layer.options._leafletLayer);
91+
if(layer.options.link) layer.attachLinkHandler(p.path, layer.options.link, layer.options._leafletLayer);
9292
layer.addInteractiveTarget(p.path);
9393
}
9494

@@ -100,7 +100,7 @@ export var FeatureRenderer = L.SVG.extend({
100100
for (let subP of p.subrings) {
101101
if (subP.path) {
102102
if (subP.link){
103-
layer.attachLinkHandler(subP.path, subP.link, subP.linkTarget, subP.linkType, layer.options._leafletLayer);
103+
layer.attachLinkHandler(subP.path, subP.link, layer.options._leafletLayer);
104104
layer.addInteractiveTarget(subP.path);
105105
}
106106
layer.group.appendChild(subP.path);

src/mapml/handlers/ContextMenu.js

Lines changed: 2 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -195,41 +195,8 @@ export var ContextMenu = L.Handler.extend({
195195
},
196196

197197
_zoomToLayer: function (e) {
198-
let map = e instanceof KeyboardEvent ? this._map : this,
199-
layerElem = map.contextMenu._layerClicked.layer._layerEl,
200-
tL = layerElem.extent.topLeft.pcrs,
201-
bR = layerElem.extent.bottomRight.pcrs,
202-
layerBounds = L.bounds(L.point(tL.horizontal, tL.vertical), L.point(bR.horizontal, bR.vertical)),
203-
center = map.options.crs.unproject(layerBounds.getCenter(true)),
204-
currentZoom = map.getZoom();
205-
206-
map.setView(center, currentZoom, {animate:false});
207-
let mapBounds = M.pixelToPCRSBounds(
208-
map.getPixelBounds(),
209-
map.getZoom(),
210-
map.options.projection);
211-
212-
//fits the bounds to the map view
213-
if(mapBounds.contains(layerBounds)){
214-
while(mapBounds.contains(layerBounds) && (currentZoom + 1) <= layerElem.extent.zoom.maxZoom){
215-
currentZoom++;
216-
map.setView(center, currentZoom, {animate:false});
217-
mapBounds = M.pixelToPCRSBounds(
218-
map.getPixelBounds(),
219-
map.getZoom(),
220-
map.options.projection);
221-
}
222-
if(currentZoom - 1 >= 0) map.flyTo(center, (currentZoom - 1));
223-
} else {
224-
while(!(mapBounds.contains(layerBounds)) && (currentZoom - 1) >= layerElem.extent.zoom.minZoom){
225-
currentZoom--;
226-
map.setView(center, currentZoom, {animate:false});
227-
mapBounds = M.pixelToPCRSBounds(
228-
map.getPixelBounds(),
229-
map.getZoom(),
230-
map.options.projection);
231-
}
232-
}
198+
let context = e instanceof KeyboardEvent ? this._map.contextMenu : this.contextMenu;
199+
context._layerClicked.layer._layerEl.focus();
233200
},
234201

235202
_goForward: function(e){

src/mapml/layers/MapLayer.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -936,6 +936,7 @@ export var MapMLLayer = L.Layer.extend({
936936
layer.error = true;
937937
}
938938
layer.fire('extentload', layer, false);
939+
layer._layerEl.dispatchEvent(new CustomEvent('extentload', {detail: layer,}));
939940
}
940941
},
941942
_createExtent: function () {

src/mapml/utils/Util.js

Lines changed: 45 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -322,31 +322,52 @@ export var Util = {
322322
this.push(parseFloat(element));
323323
},
324324

325-
handleLink: function (link, linkTarget, linkType, leafletLayer) {
326-
let layer = document.createElement('layer-');
327-
if(linkType === "text/html" && linkTarget !== "_blank") linkTarget = "_top";
328-
layer.setAttribute('src', link);
329-
layer.setAttribute('checked', '');
330-
switch (linkTarget) {
331-
case "_blank":
332-
if(linkType === "text/html"){
333-
window.open(link);
334-
} else {
325+
handleLink: function (link, leafletLayer) {
326+
let zoomTo, justPan = false, layer;
327+
if(link.type === "text/html" && link.target !== "_blank"){ // all other target values other than blank behave as _top
328+
link.target = "_top";
329+
} else if (link.type !== "text/html" && link.url.includes("#")){
330+
let hash = link.url.split("#"), loc = hash[1].split(",");
331+
zoomTo = {z: loc[0] || 0, lng: loc[1] || 0, lat: loc[2] || 0};
332+
justPan = !hash[0]; // if the first half of the array is an empty string then the link is just for panning
333+
if(["/", ".","#"].includes(link.url[0])) link.target = "_self";
334+
}
335+
if(!justPan) {
336+
let newLayer = false;
337+
layer = document.createElement('layer-');
338+
layer.setAttribute('src', link.url);
339+
layer.setAttribute('checked', '');
340+
switch (link.target) {
341+
case "_blank":
342+
if (link.type === "text/html") {
343+
window.open(link.url);
344+
} else {
345+
leafletLayer._map.options.mapEl.appendChild(layer);
346+
newLayer = true;
347+
}
348+
break;
349+
case "_parent":
350+
for (let l of leafletLayer._map.options.mapEl.querySelectorAll("layer-"))
351+
if (l._layer !== leafletLayer) leafletLayer._map.options.mapEl.removeChild(l);
335352
leafletLayer._map.options.mapEl.appendChild(layer);
353+
leafletLayer._map.options.mapEl.removeChild(leafletLayer._layerEl);
354+
newLayer = true;
355+
break;
356+
case "_top":
357+
window.location.href = link.url;
358+
break;
359+
default:
360+
leafletLayer._layerEl.insertAdjacentElement('beforebegin', layer);
361+
leafletLayer._map.options.mapEl.removeChild(leafletLayer._layerEl);
362+
newLayer = true;
363+
}
364+
if(!link.inPlace && newLayer) L.DomEvent.on(layer,'extentload', function focusOnLoad(e) {
365+
if(layer.extent){
366+
if(zoomTo) layer.parentElement.zoomTo(+zoomTo.lat, +zoomTo.lng, +zoomTo.z);
367+
else layer.focus();
368+
L.DomEvent.off(layer, 'extentload', focusOnLoad);
336369
}
337-
break;
338-
case "_parent":
339-
for(let l of leafletLayer._map.options.mapEl.querySelectorAll("layer-"))
340-
if(l._layer !== leafletLayer) leafletLayer._map.options.mapEl.removeChild(l);
341-
leafletLayer._map.options.mapEl.appendChild(layer);
342-
leafletLayer._map.options.mapEl.removeChild(leafletLayer._layerEl);
343-
break;
344-
case "_top":
345-
window.location.href = link;
346-
break;
347-
default:
348-
leafletLayer._layerEl.insertAdjacentElement('beforebegin', layer);
349-
leafletLayer._map.options.mapEl.removeChild(leafletLayer._layerEl);
350-
}
370+
});
371+
} else if (zoomTo && !link.inPlace && justPan) leafletLayer._map.options.mapEl.zoomTo(+zoomTo.lat, +zoomTo.lng, +zoomTo.z);
351372
},
352373
};

src/web-map.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -527,8 +527,8 @@ export class WebMap extends HTMLMapElement {
527527
}
528528
}
529529
zoomTo(lat, lon, zoom) {
530-
zoom = Number.isInteger(zoom)? zoom:this.zoom;
531-
var location = new L.LatLng(lat,lon);
530+
zoom = Number.isInteger(+zoom) ? +zoom : this.zoom;
531+
let location = new L.LatLng(+lat, +lon);
532532
this._map.setView(location, zoom);
533533
this.zoom = zoom;
534534
this.lat = location.lat;

test/e2e/core/featureLinks.html

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ <h1>Test</h1>
6868
</feature>
6969

7070
<feature zoom="2" class="refDiff">
71+
<featurecaption>Inplace</featurecaption>
7172
<properties>
7273
<h1>Test</h1>
7374
</properties>
@@ -77,7 +78,7 @@ <h1>Test</h1>
7778
<polygon>
7879
<coordinates>2771 3106 2946 3113 2954 3210 2815 3192</coordinates>
7980
</polygon>
80-
<map-a href="http://geogratis.gc.ca/mapml/en/cbmtile/fdi/" target="_blank">
81+
<map-a href="http://geogratis.gc.ca/mapml/en/cbmtile/fdi/" target="_blank" inplace>
8182
<point>
8283
<coordinates>2745 3218</coordinates>
8384
</point>
@@ -124,6 +125,18 @@ <h1>Test</h1>
124125
</map-a>
125126
</geometry>
126127
</feature>
128+
<feature zoom="2" class="refDiff">
129+
<properties>
130+
<h1>Test</h1>
131+
</properties>
132+
<geometry cs="tilematrix">
133+
<map-a href="data/vector-tile-test.mapml#2,-98,37" target="_blank">
134+
<polygon>
135+
<coordinates>10.5 11 10.75 11 10.75 11.25 10.5 11.25 10.5 11</coordinates>
136+
</polygon>
137+
</map-a>
138+
</geometry>
139+
</feature>
127140
</layer->
128141
</map>
129142

0 commit comments

Comments
 (0)