Skip to content

Commit 56d67d1

Browse files
authored
Implement MapLibre GL Overture Maps plugin (#2)
* Implement MapLibre GL Overture Maps plugin Transform the plugin template into maplibre-gl-overture-maps, a MapLibre GL JS control for visualizing Overture Maps PMTiles themes: - OvertureMapsControl: collapsible control with release dropdown, per-theme visibility checkboxes, color swatches, and opacity sliders for all six Overture themes (addresses, base, buildings, divisions, places, transportation) - Fetch the latest release list dynamically from labs.overturemaps.org with a pinned-release option and offline fallback - Feature inspection popup showing Overture properties on click - Dark/light UI via CSS custom properties and prefers-color-scheme, with a theme option to force a scheme; icon uses currentColor for contrast in both modes - Small-screen support: panel height capped to available space with a vertical scrollbar - pmtiles protocol registered automatically (host-global maplibre preferred to support separately bundled hosts) - React wrapper (OvertureMapsControlReact) and useOvertureMapsState hook - Types exported from both entry points - Updated examples, tests (releases + theme layer builders), README, Dockerfile, and GeoLibre bundle * Address CodeRabbit review feedback - Add :focus-visible outline to the toggle and close buttons instead of removing the outline entirely, so keyboard focus stays visible - Lowercase the currentcolor keyword for stylelint value-keyword-case - Replace deprecated word-break: break-word with overflow-wrap: anywhere in the popup table - Sanitize feature-sourced strings in the inspect popup (strip control characters, truncate long values) so malformed tile properties cannot break the UI
1 parent d59f0ce commit 56d67d1

32 files changed

Lines changed: 2669 additions & 989 deletions

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ repos:
2222
- id: codespell
2323
args:
2424
[
25-
"--ignore-words-list=aci,acount,acounts,fallow,ges,hart,hist,nd,ned,ois,wqs,watermask,tre,mape",
25+
"--ignore-words-list=aci,acount,acounts,fallow,ges,hart,hist,nd,ned,ois,wqs,watermask,tre,mape,afterall",
2626
"--skip=*.csv,*.geojson,*.json,*.yml,*.min.js,*.bundle.js,*.html,*cff,*.pdf",
2727
]
2828

Dockerfile

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ RUN npm run build && npm run build:examples
2121
# Production stage
2222
FROM nginx:alpine
2323

24-
# Copy built examples to nginx (served under /geolibre-plugin-template/ to match Vite base path)
25-
COPY --from=builder /app/dist-examples /usr/share/nginx/html/geolibre-plugin-template
24+
# Copy built examples to nginx (served under /maplibre-gl-overture-maps/ to match Vite base path)
25+
COPY --from=builder /app/dist-examples /usr/share/nginx/html/maplibre-gl-overture-maps
2626

2727
# Copy custom nginx config
2828
RUN echo 'server { \
@@ -31,12 +31,12 @@ RUN echo 'server { \
3131
root /usr/share/nginx/html; \
3232
index index.html; \
3333
\
34-
location /geolibre-plugin-template/ { \
35-
try_files $uri $uri/ /geolibre-plugin-template/index.html; \
34+
location /maplibre-gl-overture-maps/ { \
35+
try_files $uri $uri/ /maplibre-gl-overture-maps/index.html; \
3636
} \
3737
\
3838
location = / { \
39-
return 302 /geolibre-plugin-template/; \
39+
return 302 /maplibre-gl-overture-maps/; \
4040
} \
4141
}' > /etc/nginx/conf.d/default.conf
4242

@@ -52,7 +52,7 @@ echo ""\n\
5252
echo " Server running on port 80"\n\
5353
echo ""\n\
5454
echo " If you ran: docker run -p 8080:80 ..."\n\
55-
echo " Open: http://localhost:8080/geolibre-plugin-template/"\n\
55+
echo " Open: http://localhost:8080/maplibre-gl-overture-maps/"\n\
5656
echo ""\n\
5757
echo "======================================================"\n\
5858
echo ""\n\

README.md

Lines changed: 151 additions & 136 deletions
Large diffs are not rendered by default.

examples/basic/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<head>
44
<meta charset="UTF-8">
55
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6-
<title>MapLibre Plugin Template - Basic Example</title>
6+
<title>MapLibre GL Overture Maps - Basic Example</title>
77
<link rel="stylesheet" href="https://unpkg.com/maplibre-gl@4.7.1/dist/maplibre-gl.css">
88
<style>
99
body {

examples/basic/main.ts

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
import maplibregl from 'maplibre-gl';
2-
import { PluginControl } from '../../src/index';
2+
import { OvertureMapsControl } from '../../src/index';
33
import '../../src/index.css';
44
import 'maplibre-gl/dist/maplibre-gl.css';
55

6-
// Create map
6+
// Create map centered on Lower Manhattan at zoom 14 so all Overture
7+
// themes (including addresses and places, which appear at z14+) render.
78
const map = new maplibregl.Map({
89
container: 'map',
910
style: 'https://tiles.openfreemap.org/styles/positron',
10-
center: [0, 0],
11-
zoom: 2,
11+
center: [-74.006, 40.7128],
12+
zoom: 14,
1213
});
1314

1415
// Add navigation controls to top-right
@@ -17,34 +18,35 @@ map.addControl(new maplibregl.NavigationControl(), 'top-right');
1718
// Add fullscreen control to top-right (after navigation)
1819
map.addControl(new maplibregl.FullscreenControl(), 'top-right');
1920

20-
// Add plugin control when map loads
21+
// Add the Overture Maps control when the map loads
2122
map.on('load', () => {
22-
// Create the plugin control with custom options
23-
// Set collapsed: true to start with just the 29x29 button (like navigation control)
24-
const pluginControl = new PluginControl({
25-
title: 'My Plugin',
23+
const overtureControl = new OvertureMapsControl({
2624
collapsed: false,
27-
panelWidth: 300,
25+
visibleThemes: ['buildings', 'transportation', 'places'],
2826
});
2927

3028
// Add control to the map
31-
map.addControl(pluginControl, 'top-right');
29+
map.addControl(overtureControl, 'top-right');
3230

3331
// Add Globe control to the map
3432
map.addControl(new maplibregl.GlobeControl(), 'top-right');
3533

3634
// Listen for state changes
37-
pluginControl.on('statechange', (event) => {
38-
console.log('Plugin state changed:', event.state);
35+
overtureControl.on('statechange', (event) => {
36+
console.log('Overture control state changed:', event.state);
3937
});
4038

41-
pluginControl.on('collapse', () => {
42-
console.log('Plugin panel collapsed');
39+
overtureControl.on('releasechange', (event) => {
40+
console.log('Overture release changed:', event.state.release);
4341
});
4442

45-
pluginControl.on('expand', () => {
46-
console.log('Plugin panel expanded');
43+
overtureControl.on('themechange', (event) => {
44+
console.log('Overture themes changed:', event.state.themes);
4745
});
4846

49-
console.log('Plugin control added to map');
47+
overtureControl.on('error', (event) => {
48+
console.warn('Overture control error:', event.state.error);
49+
});
50+
51+
console.log('Overture Maps control added to map');
5052
});

examples/react/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<head>
44
<meta charset="UTF-8">
55
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6-
<title>MapLibre Plugin Template - React Example</title>
6+
<title>MapLibre GL Overture Maps - React Example</title>
77
<link rel="stylesheet" href="https://unpkg.com/maplibre-gl@4.7.1/dist/maplibre-gl.css">
88
<style>
99
body {

examples/react/main.tsx

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { useEffect, useRef, useState } from 'react';
22
import { createRoot } from 'react-dom/client';
33
import maplibregl, { Map } from 'maplibre-gl';
4-
import { PluginControlReact, usePluginState } from '../../src/react';
4+
import { OvertureMapsControlReact, useOvertureMapsState } from '../../src/react';
55
import '../../src/index.css';
66
import 'maplibre-gl/dist/maplibre-gl.css';
77

@@ -11,7 +11,7 @@ import 'maplibre-gl/dist/maplibre-gl.css';
1111
function App() {
1212
const mapContainer = useRef<HTMLDivElement>(null);
1313
const [map, setMap] = useState<Map | null>(null);
14-
const { state, toggle } = usePluginState({ collapsed: false });
14+
const { state, setState, toggle } = useOvertureMapsState({ collapsed: false });
1515

1616
// Initialize the map
1717
useEffect(() => {
@@ -20,8 +20,8 @@ function App() {
2020
const mapInstance = new maplibregl.Map({
2121
container: mapContainer.current,
2222
style: 'https://tiles.openfreemap.org/styles/positron',
23-
center: [0, 0],
24-
zoom: 2,
23+
center: [-74.006, 40.7128],
24+
zoom: 14,
2525
});
2626

2727
// Add navigation controls to top-right
@@ -40,7 +40,10 @@ function App() {
4040
}, []);
4141

4242
const handleStateChange = (newState: typeof state) => {
43-
console.log('Plugin state changed:', newState);
43+
console.log('Overture control state changed:', newState);
44+
// Keep the hook state in sync so the external button label matches
45+
// the actual panel state (e.g. after click-outside collapses it).
46+
setState(newState);
4447
};
4548

4649
return (
@@ -67,13 +70,13 @@ function App() {
6770
{state.collapsed ? 'Expand' : 'Collapse'} Panel
6871
</button>
6972

70-
{/* Plugin control */}
73+
{/* Overture Maps control */}
7174
{map && (
72-
<PluginControlReact
75+
<OvertureMapsControlReact
7376
map={map}
74-
title="React Plugin"
7577
collapsed={state.collapsed}
7678
panelWidth={320}
79+
visibleThemes={['buildings', 'transportation', 'places']}
7780
onStateChange={handleStateChange}
7881
/>
7982
)}

geolibre-plugin/plugin.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
{
2-
"id": "geolibre-plugin-template",
3-
"name": "GeoLibre Plugin Template",
2+
"id": "maplibre-gl-overture-maps",
3+
"name": "MapLibre GL Overture Maps",
44
"version": "0.1.0",
55
"entry": "dist/index.js",
6-
"description": "A starter GeoLibre plugin bundle with a MapLibre control.",
6+
"description": "Visualize Overture Maps PMTiles themes with a MapLibre control.",
77
"style": "dist/style.css"
88
}

index.html

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<head>
44
<meta charset="UTF-8">
55
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6-
<title>MapLibre GL Plugin Template - Examples</title>
6+
<title>MapLibre GL Overture Maps - Examples</title>
77
<style>
88
* {
99
margin: 0;
@@ -110,13 +110,13 @@
110110
</head>
111111
<body>
112112
<div class="container">
113-
<h1>MapLibre GL Plugin Template</h1>
114-
<p class="subtitle">A template for creating MapLibre GL JS plugins with TypeScript and React support</p>
113+
<h1>MapLibre GL Overture Maps</h1>
114+
<p class="subtitle">A MapLibre GL JS plugin for visualizing Overture Maps PMTiles themes</p>
115115

116116
<div class="examples-grid">
117117
<a href="./examples/basic/index.html" class="example-card">
118118
<h2>Basic Example</h2>
119-
<p>Simple vanilla JavaScript/TypeScript example showing how to add the plugin control to a map.</p>
119+
<p>Vanilla TypeScript example showing how to add the Overture Maps control to a map and toggle themes.</p>
120120
<div class="tags">
121121
<span class="tag vanilla">Vanilla JS</span>
122122
<span class="tag typescript">TypeScript</span>
@@ -125,7 +125,7 @@ <h2>Basic Example</h2>
125125

126126
<a href="./examples/react/index.html" class="example-card">
127127
<h2>React Example</h2>
128-
<p>React example demonstrating the React wrapper component and custom hooks integration.</p>
128+
<p>React example demonstrating the React wrapper component and custom hooks for Overture themes.</p>
129129
<div class="tags">
130130
<span class="tag react">React</span>
131131
<span class="tag typescript">TypeScript</span>
@@ -135,8 +135,9 @@ <h2>React Example</h2>
135135

136136
<div class="footer">
137137
<p>
138-
<a href="https://github.com/your-username/maplibre-gl-plugin-template">GitHub</a> ·
139-
<a href="https://www.npmjs.com/package/maplibre-gl-plugin-template">npm</a>
138+
<a href="https://github.com/opengeos/maplibre-gl-overture-maps">GitHub</a> ·
139+
<a href="https://www.npmjs.com/package/maplibre-gl-overture-maps">npm</a> ·
140+
<a href="https://docs.overturemaps.org/">Overture Maps Docs</a>
140141
</p>
141142
</div>
142143
</div>

package-lock.json

Lines changed: 14 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)