Skip to content
This repository has been archived by the owner on Apr 26, 2024. It is now read-only.

Feature image overlay #44

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
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
36 changes: 36 additions & 0 deletions docs/docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,42 @@ Valid format: array of coordinates for Point and LineString, array of arrays of
remove: remove data-geometry attributes <br>
json: remove them and add to end of the document <br>

##### ImageOverlay Attributes

Support for `L.imageOverlay(url, bounds, options)` can be accessed by using the `data-l`,
`data-image-url`,
`data-image-bounds` and
`data-options` attributes.

``` html
<div data-hyperleaflet-source>
<span
data-id="1"
data-l="imageOverlay"
data-image-url="/static/image.png"
data-image-bounds="[[38.5, 37.0], [40.5, 39.0]]"
></span>
</div>
```

`#!css data-l`
: Specifies the constructor used to generate a leaflet object, e.g. `L.imageOverlay` can be accessed using `data-l="imageOverlay"`.

`#!css data-image-url`
: Specifies the URL used to generate an imageOverlay.
```html
<span ... data-image-url="/static/image.png"></span>
```
Valid format: Text.

`#!css data-image-bounds`
: Specifies the `L.latLngBounds` needed to place an image overlay on the map.
```html
<span ... data-image-bounds="[[38.5, 37.0], [40.5, 39.0]]"></span>
```
Valid format: Text.


andrewgryan marked this conversation as resolved.
Show resolved Hide resolved
andrewgryan marked this conversation as resolved.
Show resolved Hide resolved
### Event Handling
Hyperleaflet provides an event system for interacting with the map and geometries. It sends custom events to the **window** object, which can be listened to and handled by JavaScript. We recommend using either [_hyperscript](https://hyperscript.org/){:target="_blank"} or [alpine.js](https://alpinejs.dev/){:target="_blank"} etc. to handle the events. The custom events contain a **detail** object with useful attributes, such as the clicked point on the map.

Expand Down
13 changes: 11 additions & 2 deletions src/Geometry/hyperleaflet-geometry-handler.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ function deleteNodeFromHyperleaflet(node, map) {
}

function changeNodeInHyperleaflet(change, map) {
const rowId = change.changes['data-id'];
const rowId = change['data-id'];
// eslint-disable-next-line no-underscore-dangle
const leafletLayers = Object.values(map._layers);
const leafletObject = leafletLayers.find((layer) => layer.hlID === rowId);
Expand All @@ -50,8 +50,17 @@ export default function hyperleafletGeometryHandler(map, { addCallback = () => {
}

function changeNodesInHyperleaflet(changes) {
// changes is an array of changes and a dataset
changes.forEach((change) => {
changeNodeInHyperleaflet(change, map);
// NOTE: Some changes have shape { node, changes }, but these seem to be related to add/remove
// lifecycle events.
const { dataset, ...partialChanges } = change;
if (typeof dataset !== 'undefined') {
// Handle { dataset, { i: change } } format
Object.values(partialChanges).forEach((partialChange) => {
changeNodeInHyperleaflet({ ...partialChange, dataset }, map);
});
}
andrewgryan marked this conversation as resolved.
Show resolved Hide resolved
});
}

Expand Down
2 changes: 1 addition & 1 deletion src/Geometry/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ function hyperleafletDataToMap(map) {
hyperChangeDetection.observe({
targetSelector: HYPERLEAFLET_DATA_SOURCE,
uniqueAttribute: 'data-id',
attributeFilter: ['data-geometry'],
attributeFilter: ['data-geometry', 'data-l', 'data-image-url', 'data-image-bounds'],
});

hyperChangeDetection.subscribe(HYPERLEAFLET_DATA_SOURCE, 'node_adds', (data) => {
Expand Down
68 changes: 63 additions & 5 deletions src/Geometry/leaflet-geometry.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,11 +93,20 @@ function changeGeometry(leafletObject, change) {

switch (geometryType) {
case 'Point':
return changePointGeometry(leafletObject, parsedGeometry, { ...change.dataset, reverseOrderAll });
return changePointGeometry(leafletObject, parsedGeometry, {
...change.dataset,
reverseOrderAll,
});
case 'LineString':
return changeLineGeometry(leafletObject, parsedGeometry, { ...change.dataset, reverseOrderAll });
return changeLineGeometry(leafletObject, parsedGeometry, {
...change.dataset,
reverseOrderAll,
});
case 'Polygon':
return changePolygonGeometry(leafletObject, parsedGeometry, { ...change.dataset, reverseOrderAll });
return changePolygonGeometry(leafletObject, parsedGeometry, {
...change.dataset,
reverseOrderAll,
});
default:
// eslint-disable-next-line no-console
console.warn(`${geometryType} is not supported`);
Expand All @@ -106,18 +115,67 @@ function changeGeometry(leafletObject, change) {
}

export function createLeafletObject(dataset) {
// Image overlay
if ('l' in dataset) {
return createL(dataset);
}

// Geometry based L objects
const { geometry, popup, tooltip, geometryType, id, reverseOrder } = dataset;
const parsedGeometry = JSON.parse(geometry);
const { reverseOrderAll } = hyperleafletConfig;
const createGeometryFn = createGeometry(geometryType);
return createGeometryFn(parsedGeometry, { popup, tooltip, id, reverseOrderAll, reverseOrder });
return createGeometryFn(parsedGeometry, {
popup,
tooltip,
id,
reverseOrderAll,
reverseOrder,
});
}

/**
* Create a L.* leaflet object from HTML data-* attributes
*/
function createL(dataset) {
if (dataset.l.toLowerCase() === 'imageoverlay') {
[
['imageUrl', 'data-image-url'],
['imageBounds', 'data-image-bounds'],
].forEach(([attr, htmlAttr]) => {
if (typeof dataset[attr] === 'undefined') {
throw new Error(`Required attribute ${htmlAttr} for image overlay not specified in dataset.`);
}
});
return L.imageOverlay(dataset.imageUrl, JSON.parse(dataset.imageBounds));
} else {
throw new Error(`data-l ${dataset.l} not supported`);
}
}

/**
* Create a L.* leaflet object attributes
*/
function changeL(leafletObject, change) {
switch (change.attribute.toLowerCase()) {
case 'data-image-bounds':
return leafletObject.setBounds(JSON.parse(change.to));
case 'data-image-url':
return leafletObject.setUrl(change.to);
default:
throw new Error(`change to ${change.attribute} not supported`);
}
}

export function changeLeafletObject(leafletObject, change) {
switch (change.attribute) {
switch (change.attribute.toLowerCase()) {
case 'data-geometry': {
return changeGeometry(leafletObject, change);
}
case 'data-l':
case 'data-image-url':
case 'data-image-bounds':
return changeL(leafletObject, change);
default: {
throw new Error('Parameter is not a number!');
}
Expand Down
64 changes: 63 additions & 1 deletion src/Geometry/test/hyperleaflet-geometry-handler.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,11 @@ describe('createLeafletObject', () => {
to: '[-110.414,30.776]',
attribute: 'data-geometry',
'data-id': 123,
dataset: { geometry: '[-122.414,37.776]', geometryType: 'Point', id: '123' },
dataset: {
geometry: '[-122.414,37.776]',
geometryType: 'Point',
id: '123',
},
});
expect(marker).toBeInstanceOf(L.Marker);
expect(marker.getLatLng()).toEqual(L.latLng(-110.414, 30.776));
Expand Down Expand Up @@ -121,4 +125,62 @@ describe('createLeafletObject', () => {
expect(polygon.getPopup().getContent()).toEqual('Hello, world!');
expect(polygon.getTooltip().getContent()).toEqual('I am a polygon');
});

it('should create a Leaflet imageOverlay given imageUrl', () => {
const dataset = {
l: 'imageOverlay',
imageUrl: '/static/image.png',
imageBounds: '[[0, 1], [2, 3]]',
};
const overlay = createLeafletObject(dataset);
expect(overlay).toBeInstanceOf(L.ImageOverlay);
expect(overlay.getBounds()).toEqual(
L.latLngBounds([
[0, 1],
[2, 3],
]),
);
});

it('should change imageOverlay latLngBounds', () => {
const dataset = {
l: 'imageOverlay',
imageUrl: 'url.png',
imageBounds: '[[0, 1], [2, 3]]',
};
let overlay = createLeafletObject(dataset);
const change = {
'data-id': 123,
attribute: 'data-image-bounds',
dataset,
from: '[[0, 1], [2, 3]]',
to: '[[1, 1], [2, 3]]',
};
overlay = changeLeafletObject(overlay, change);
expect(overlay).toBeInstanceOf(L.ImageOverlay);
expect(overlay.getBounds()).toEqual(
L.latLngBounds([
[1, 1],
[2, 3],
]),
);
});

it('should change imageOverlay imageurl', () => {
const dataset = {
l: 'imageOverlay',
imageUrl: 'foo.png',
imageBounds: '[[0, 1], [2, 3]]',
};
let overlay = createLeafletObject(dataset);
const change = {
'data-id': 123,
attribute: 'data-image-url',
dataset,
from: 'foo.png',
to: 'bar.png',
};
overlay = changeLeafletObject(overlay, change);
expect(overlay).toBeInstanceOf(L.ImageOverlay);
});
});