Building Canadian Maps with MapLibre GL JS & Photon
Create high-performance WebGL vector maps with instant address search, Canadian boundary filters, and zero request logging without Mapbox license fees.
Why Choose MapLibre GL JS?
MapLibre GL JS is the fully open-source (BSD-licensed) fork of Mapbox GL JS v1 created by the community. Unlike proprietary mapping SDKs that require tracking tokens, charge steep per-load fees, and log end-user IP addresses, MapLibre provides 60 FPS GPU-accelerated vector rendering with 100% telemetry-free client code.
Coupled with Geo Service's sovereign Canadian Photon and Nominatim endpoints, you can deliver an enterprise-grade mapping experience completely hosted within Canadian borders.
Step 1: HTML Setup & Map Initialization
Include the MapLibre GL JS script and stylesheet. Center the initial viewport over Canada (e.g., Downtown Toronto, Montreal, or Calgary):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Canadian MapLibre Search Demo</title>
<link rel="stylesheet" href="https://unpkg.com/maplibre-gl@4.7.1/dist/maplibre-gl.css" />
<script src="https://unpkg.com/maplibre-gl@4.7.1/dist/maplibre-gl.js"></script>
<style>
#map { width: 100%; height: 500px; border-radius: 12px; }
.search-box { position: absolute; top: 16px; left: 16px; z-index: 10; width: 340px; }
</style>
</head>
<body>
<div id="map"></div>
<script>
const map = new maplibregl.Map({
container: 'map',
style: 'https://demotiles.maplibre.org/style.json', // Or your custom vector tiles
center: [-79.3832, 43.6532], // Toronto, ON
zoom: 13
});
// Add standard navigation controls
map.addControl(new maplibregl.NavigationControl(), 'top-right');
</script>
</body>
</html>
Step 2: Add Real-Time Canadian Autocomplete
Attach an input listener to an address search bar. When the user types 3 or more characters, query the /api Photon endpoint with your frontend Browser Key:
const BROWSER_KEY = "your_domain_restricted_browser_key";
const SEARCH_URL = "https://geo.jerielbenavides.com/api";
let debounceTimer;
const marker = new maplibregl.Marker({ color: "#4f46e5" });
async function searchCanadianAddress(query) {
if (!query || query.trim().length < 3) return;
// Center bias and bounding box for Canadian searches
const params = new URLSearchParams({
q: query.trim(),
limit: "5",
lon: "-79.3832",
lat: "43.6532"
});
try {
const res = await fetch(`${SEARCH_URL}?${params.toString()}`, {
headers: { "Authorization": `Bearer ${BROWSER_KEY}` }
});
const geojson = await res.json();
if (geojson.features && geojson.features.length > 0) {
const firstMatch = geojson.features[0];
const [lon, lat] = firstMatch.geometry.coordinates;
// Fly to location and place marker
map.flyTo({ center: [lon, lat], zoom: 16, essential: true });
marker.setLngLat([lon, lat]).addTo(map);
console.log("Selected:", firstMatch.properties.name, firstMatch.properties.city);
}
} catch (err) {
console.error("Geocoding failed", err);
}
}
// Debounce input to protect user typing experience
document.getElementById('address-input').addEventListener('input', (e) => {
clearTimeout(debounceTimer);
debounceTimer = setTimeout(() => searchCanadianAddress(e.target.value), 250);
});
🔒 Security Best Practice: Use Domain-Restricted Browser Keys
Never put your secret Server Key into client-side MapLibre JavaScript. Instead, generate a Browser Key from your dashboard and restrict it strictly to your approved web origins (e.g., https://myapp.ca). Browser keys can only access lookup and autocomplete endpoints and enforce strict per-minute rate limits.
100% In-Memory Processing in Beauharnois, Quebec
By combining open-source MapLibre GL JS with Geo Service, your user search queries and map viewport coordinates never cross international boundaries or get retained in US advertising cloud databases.