Skip to content

Commit

Permalink
WIP initial Polygonlayer examples
Browse files Browse the repository at this point in the history
  • Loading branch information
thomasm0 committed May 23, 2024
1 parent 2ad6e22 commit c9ee022
Show file tree
Hide file tree
Showing 9 changed files with 3,118 additions and 0 deletions.
14 changes: 14 additions & 0 deletions src/pages/PolygonLayer/PolygonLayer.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { describe, expect, it } from 'vitest';
import { render } from '@testing-library/react';
import PolygonLayer from './PolygonLayer';

describe('PolygonLayer', () => {
it('renders the component', () => {
const { container } = render(<PolygonLayer />);
expect(container.firstChild).toBeDefined();
});

// it('', () => {

// });
});
89 changes: 89 additions & 0 deletions src/pages/PolygonLayer/PolygonLayer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { useEffect, useRef, useState } from 'react';
import type { FunctionComponent } from 'react';
import L, { LatLngTuple } from 'leaflet';
import 'leaflet/dist/leaflet.css';
import getCrsRd from '@/utils/getCrsRd';
import styles from './styles.module.css';
import data from './single.json';

const polygonStyles = {
fillOpacity: 0.2,
color: '#0000ff',
};

const polygonHoverStyles = {
...polygonStyles,
fillOpacity: 0.5,
color: '#ff0000',
};

const PolygonLayer: FunctionComponent = () => {
const containerRef = useRef<HTMLDivElement>(null);
const [mapInstance, setMapInstance] = useState<L.Map | null>(null);
const createdMapInstance = useRef(false);

const polygonRef = useRef<L.Polygon | null>(null);

// Set the Leaflet map and Amsterdam base layer
useEffect(() => {
if (containerRef.current === null || createdMapInstance.current !== false) {
return;
}

const map = new L.Map(containerRef.current, {
center: L.latLng([52.370216, 4.895168]),
zoom: 12,
layers: [
L.tileLayer('https://{s}.data.amsterdam.nl/topo_rd/{z}/{x}/{y}.png', {
attribution: '',
subdomains: ['t1', 't2', 't3', 't4'],
tms: true,
}),
],
zoomControl: false,
maxZoom: 16,
minZoom: 3,
crs: getCrsRd(),
maxBounds: [
[52.25168, 4.64034],
[52.50536, 5.10737],
],
});

map.attributionControl.setPrefix(false);

createdMapInstance.current = true;
setMapInstance(map);

return () => {
if (mapInstance) mapInstance.remove();
};
}, []);

// Create the polygon layer and add it to the map
useEffect(() => {
if (mapInstance) {
// TypeScript will often throw errors with Leaflet coordinate sets if you don't explicitly cast the type
polygonRef.current = L.polygon(
data.geometry.coordinates as LatLngTuple[][][]
)
.addTo(mapInstance)

Check failure on line 70 in src/pages/PolygonLayer/PolygonLayer.tsx

View workflow job for this annotation

GitHub Actions / Setup and install

src/pages/PolygonLayer/PolygonLayer.test.tsx > PolygonLayer > renders the component

TypeError: Cannot use 'in' operator to search for '_leaflet_id' in null ❯ Util.stamp node_modules/leaflet/src/core/Util.js:55:21 ❯ NewClass.hasLayer node_modules/leaflet/src/layer/Layer.js:203:10 ❯ NewClass.getRenderer node_modules/leaflet/src/layer/vector/Renderer.getRenderer.js:20:13 ❯ NewClass.beforeAdd node_modules/leaflet/src/layer/vector/Path.js:80:24 ❯ NewClass.addLayer node_modules/leaflet/src/layer/Layer.js:169:10 ❯ NewClass.addTo node_modules/leaflet/src/layer/Layer.js:52:7 ❯ src/pages/PolygonLayer/PolygonLayer.tsx:70:10 ❯ commitHookEffectListMount node_modules/react-dom/cjs/react-dom.development.js:23189:26 ❯ commitPassiveMountOnFiber node_modules/react-dom/cjs/react-dom.development.js:24970:11 ❯ commitPassiveMountEffects_complete node_modules/react-dom/cjs/react-dom.development.js:24930:9
.on('mouseover', () => {
polygonRef.current?.setStyle(polygonHoverStyles);
})
.on('mouseout', () => {
polygonRef.current?.setStyle(polygonStyles);
});
}

return () => {
if (polygonRef.current && mapInstance) {
mapInstance.removeLayer(polygonRef.current);
}
};
}, [data, mapInstance]);

return <div className={styles.container} ref={containerRef} />;
};

export default PolygonLayer;
121 changes: 121 additions & 0 deletions src/pages/PolygonLayer/PolygonLayerArray.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import { useEffect, useRef, useState } from 'react';
import type { FunctionComponent } from 'react';
import type { FeatureCollection, LineString } from 'geojson';
import L, { LatLngTuple } from 'leaflet';
import 'leaflet/dist/leaflet.css';
import getCrsRd from '@/utils/getCrsRd';
import styles from './styles.module.css';
import data from './data.json';
import reverseLatLng from '@/utils/reverseLatLng';

const polygonInactiveStyles = {
fillOpacity: 0.2,
color: '#0000ff',
};

const polygonActiveStyles = {
...polygonInactiveStyles,
fillOpacity: 0.5,
color: '#ff0000',
};

// TODO align code comments with Marker
const PolygonLayer: FunctionComponent = () => {
const containerRef = useRef<HTMLDivElement>(null);
const [mapInstance, setMapInstance] = useState<L.Map | null>(null);
const createdMapInstance = useRef(false);

const polygonRefs = useRef<L.Polygon[]>([]);

// Set the Leaflet map and Amsterdam base layer
useEffect(() => {
if (containerRef.current === null || createdMapInstance.current !== false) {
return;
}

const map = new L.Map(containerRef.current, {
center: L.latLng([52.370216, 4.895168]),
zoom: 12,
layers: [
L.tileLayer('https://{s}.data.amsterdam.nl/topo_rd/{z}/{x}/{y}.png', {
attribution: '',
subdomains: ['t1', 't2', 't3', 't4'],
tms: true,
}),
],
zoomControl: false,
maxZoom: 16,
minZoom: 3,
crs: getCrsRd(),
maxBounds: [
[52.25168, 4.64034],
[52.50536, 5.10737],
],
});

map.attributionControl.setPrefix(false);

createdMapInstance.current = true;
setMapInstance(map);

return () => {
if (mapInstance) mapInstance.remove();
};
}, []);

// Create the polygon layer and add it to the map
useEffect(() => {
let selectedPolygon: number | undefined = undefined;

if (mapInstance) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
data.map((record: any, index: number) => {
// Reverse the lat lng arrays otherwise Leaflet will plot incorrectly
const geoJson = reverseLatLng(record.geometry);

// Create the polygon layer
polygonRefs.current[index] = L.polygon(
(geoJson.toGeoJSON() as FeatureCollection<LineString>).features[0]
.geometry.coordinates as LatLngTuple[]
)
.addTo(mapInstance)
.on('mouseover', () => {
polygonRefs.current[index].setStyle(polygonActiveStyles);
})
.on('mouseout', () => {
if (selectedPolygon !== index) {
polygonRefs.current[index].setStyle(polygonInactiveStyles);
}
})
.on('click', () => {
L.popup()
.setLatLng([
geoJson.getBounds().getCenter().lng,
geoJson.getBounds().getCenter().lat,
])
.setContent(`${index}: ${record.name}<br />`)
.openOn(mapInstance as L.Map)
.on('remove', () => {
selectedPolygon = undefined;
polygonRefs.current[index].setStyle(polygonInactiveStyles);
});

selectedPolygon = index;
polygonRefs.current[index].setStyle(polygonActiveStyles);
});
});
}

return () => {
if (polygonRefs.current && mapInstance) {
polygonRefs.current.forEach(polygon =>
mapInstance.removeLayer(polygon)
);
}
};
}, [data, mapInstance]);

return <div className={styles.container} ref={containerRef} />;
};

export default PolygonLayer;
Loading

0 comments on commit c9ee022

Please sign in to comment.