vgrid-maplibre
Version:
DGGS Visualization for MapLibre GL JS
218 lines (193 loc) • 8.84 kB
HTML
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>H3 DGGS</title>
<!-- MapLibre GL CSS -->
<link rel="stylesheet" href="https://unpkg.com/maplibre-gl/dist/maplibre-gl.css" />
<style>
body {
margin: 0;
padding: 0;
}
#map {
width: 100%;
height: 100vh;
}
</style>
</head>
<body>
<!-- Map container -->
<div id="map"></div>
<!-- H3Grid.js -->
<!-- <script src="https://unpkg.com/h3-js/dist/h3-js.umd.js"></script> -->
<script src="https://unpkg.com/maplibre-gl/dist/maplibre-gl.js"></script>
<button onclick="exportToGeoJSON()" style="position: absolute; top: 10px; left: 10px;">
Export
</button>
<script type="module">
import H3Grid from "https://unpkg.com/vgrid-maplibre/H3/H3Grid.js";
// import H3Grid from "https://cdn.jsdelivr.net/gh/thangqd/vgrid-maplibre@main/H3/H3Grid.js";
// Initialize the map and grid
const config = {
lng: 106.706585,
lat: 10.775326,
zoom: 0
};
let currentPopup = null;
const map = new maplibregl.Map({
container: 'map', // The ID of the HTML element where MapLibre will render
center: [config.lng, config.lat],
zoom: config.zoom,
// style: 'https://raw.githubusercontent.com/opengeoshub/vstyles/main/vstyles/omt/fiord/fiord.json',
// style: 'https://demotiles.maplibre.org/style.json' // Default MapLibre style
// style: 'https://raw.githubusercontent.com/opengeoshub/vstyles/main/vstyles/omt/positron/positron.json',
style: 'https://raw.githubusercontent.com/opengeoshub/vstyles/main/vstyles/sbr/eclipse/eclipse.json',
});
function addIcosaLayer() {
map.addSource('icosahedron', {
type: 'geojson',
// Fetch GeoJSON from the URL
data: 'https://raw.githubusercontent.com/opengeoshub/vgrid-maplibre/main/H3/icosahedron.geojson'
// data: 'https://raw.githubusercontent.com/opengeoshub/vgrid-maplibre/main/H3/icosahedron_polygon.geojson'
});
// Add the layer to display the GeoJSON features
map.addLayer({
id: 'icosahedron-layer',
type: 'line',
// type: 'fill',
source: 'icosahedron', // The source we added above
paint: {
'line-color': 'white', // Line color (if using 'line' layer)
'line-width': 1.5, // Line width
'line-dasharray': [2, 4, 1, 4] // Dash-dot pattern: [dash length, gap length, dash length, gap length]
// 'fill-color': 'transparent', // Fill color
// 'fill-outline-color': '#008000' // Border/Outline color
}
});
}
map.on('load', () => {
map.addControl(new maplibregl.GlobeControl());
try {
const h3Grid = new H3Grid(map, {
color: 'rgba(255, 0, 0, 1)',
width: 1.5,
redraw: 'move',
});
addIcosaLayer();
console.log('H3 Grid added successfully.');
} catch (error) {
console.error('Failed to add H3 Grid:', error);
}
});
// Uncomment to display h3_id label
// map.on('sourcedata', (e) => {
// if (
// e.sourceId === 'h3-grid' &&
// map.getSource('h3-grid') &&
// !map.getLayer('h3-labels')
// ) {
// map.addLayer({
// id: 'h3-labels',
// type: 'symbol',
// source: 'h3-grid',
// layout: {
// 'text-field': ['get', 'h3_id'],
// 'text-size': 12,
// },
// paint: {
// 'text-color': ['get', 'color'],
// 'text-halo-color': 'white', // halo color
// 'text-halo-width': 1.5, // halo width
// 'text-halo-blur': 0.5 // optional: smooth halo edges
// },
// });
// }
// });
map.on('click', (e) => {
const features = map.queryRenderedFeatures(e.point, {
layers: ['h3-grid-layer'] // This should match the layer name where hexagons are rendered
});
if (features.length) {
const feature = features[0]; // Select the first feature if there are multiple
// Create a popup with the feature properties
const popupContent = `
<strong>H3 ID: </strong> ${feature.properties.h3_id} <br>
<strong>Resolution: </strong> ${feature.properties.resolution} <br>
<strong>Icosa Faces: </strong> ${feature.properties.icosa_faces} <br>
<strong>Area (${feature.properties.area_unit}): </strong> ${feature.properties.area} <br>
<strong>Total Cells: </strong> ${feature.properties.num_hex}<br>
`;
// Create a popup and set its coordinates
const popup = new maplibregl.Popup()
.setLngLat(e.lngLat) // Position the popup at the clicked location
.setHTML(popupContent) // Set the HTML content for the popup
.addTo(map); // Add the popup to the map
currentPopup = popup;
// Add a highlight layer if it doesn't exist yet
if (!map.getLayer('highlight-layer')) {
map.addLayer({
'id': 'highlight-layer',
'type': 'fill',
'source': {
'type': 'geojson',
'data': {
'type': 'FeatureCollection',
'features': []
}
},
'paint': {
'fill-color': 'rgba(255, 0, 0)', // Red color for highlight
'fill-opacity': 0.2, // Set transparency of the highlight
}
});
}
// Update the source of the highlight layer with the clicked feature's geometry
map.getSource('highlight-layer').setData({
'type': 'FeatureCollection',
'features': [feature]
});
}
});
map.on('contextmenu', () => {
// Remove the highlight layer by clearing its source data
if (map.getLayer('highlight-layer')) {
map.getSource('highlight-layer').setData({
'type': 'FeatureCollection',
'features': [] // Clear the data to remove the highlight
});
}
// Remove the current popup (if any) from the map
if (currentPopup) {
currentPopup.remove();
currentPopup = null; // Reset the currentPopup variable
}
});
function exportToGeoJSON() {
const features = map.queryRenderedFeatures({ layers: ['h3-grid-layer'] });
if (!features.length) {
alert("No H3 grid features found in the current viewport.");
return;
}
// Remove duplicates (some tiles can appear multiple times)
// const uniqueFeatures = Array.from(new Map(
// features.map(f => [f.properties.h3_id, f])
// ).values());
const geojson = {
type: 'FeatureCollection',
features: features
};
// Download as GeoJSON file
const blob = new Blob([JSON.stringify(geojson, null, 2)], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'h3-grid-export.geojson';
a.click();
URL.revokeObjectURL(url);
}
window.exportToGeoJSON = exportToGeoJSON;
</script>
</body>
</html>