flatgeobuf/examples/leaflet/filtered.html
2025-12-01 09:43:14 +01:00

145 lines
6.0 KiB
HTML

<html>
<head>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
<link rel="stylesheet" href="/examples/site.css" />
<script src="https://unpkg.com/underscore@1.13.1/underscore-min.js"></script>
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<script src="https://unpkg.com/flatgeobuf@4.3.3/dist/flatgeobuf-geojson.min.js"></script>
<script src="https://unpkg.com/json-formatter-js@2.5.23/dist/json-formatter.umd.js"></script>
<style>
#map { height: 480px; }
</style>
</head>
<body>
<ul class="primary-navigation">
<li class="active">
Leaflet Example
</li>
<li>
<a href="/examples/openlayers/filtered.html">OpenLayers Example</a>
</li>
<li>
<a href="/examples/maplibre/">MapLibre Example</a>
</li>
</ul>
<ul class="secondary-navigation">
<li><a href="/examples/leaflet/">Basic Example</a></li>
<li class="active">Filter By Rect</li>
<li><a href="/examples/leaflet/large.html">Filtering a Large Dataset</a></li>
</ul>
<div id="map"></div>
<script>
document.addEventListener("DOMContentLoaded", async () => {
// basic OSM Leaflet map
const map = L.map('map').setView([39, -104], 6);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 19,
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}).addTo(map);
// optionally show some meta-data about the FGB file
function handleHeaderMeta(headerMeta) {
const header = document.getElementById('header')
const formatter = new JSONFormatter(headerMeta, 10)
header.appendChild(formatter.render())
}
// For the example, we fix a visible Rect representing the query
// bounding box in the middle of the map
function getBoundForRect() {
const widthMeters = 500000;
return map.getCenter().toBounds(widthMeters);
}
// convert the rect into the format flatgeobuf expects
function fgBoundingBox() {
const bounds = getBoundForRect();
return {
minX: bounds.getWest(),
minY: bounds.getSouth(),
maxX: bounds.getEast(),
maxY: bounds.getNorth(),
};
}
// track the previous results so we can remove them when adding new results
let previousResults = L.layerGroup().addTo(map);
async function updateResults() {
// remove the old results
previousResults.remove();
const nextResults = L.layerGroup().addTo(map);
previousResults = nextResults;
// Use flatgeobuf JavaScript API to iterate features as geojson.
// Because we specify a bounding box, flatgeobuf will only fetch the relevant subset of data,
// rather than the entire file.
const iter = flatgeobuf.deserialize('/test/data/UScounties.fgb', fgBoundingBox(), handleHeaderMeta);
for await (const feature of iter) {
const defaultStyle = {
color: 'blue',
weight: 2,
fillOpacity: 0.1,
};
L.geoJSON(feature, {
style: defaultStyle,
}).on({
'mouseover': (e) => {
const layer = e.target;
layer.setStyle({
color: 'blue',
weight: 4,
fillOpacity: 0.7,
});
layer.bringToFront();
},
'mouseout': (e) => {
const layer = e.target;
layer.setStyle(defaultStyle);
}
}).bindPopup(`<h1>${feature.properties.NAME} ${feature.properties.LSAD}, ${feature.properties.STATE}</h1>`)
.addTo(nextResults);
}
}
// if the user is panning around alot, only update once per second max
const updateResultsThrottled = _.throttle(updateResults, 1000);
// show a leaflet rect corresponding to our bounding box
const rectangle = L.rectangle(getBoundForRect(), { color: "yellow", fillOpacity: 0.7, opacity: 1.0 }).addTo(map);
// show results based on the initial map
updateResultsThrottled();
// ...and update the results whenever the map moves
map.on("moveend", ()=> {
rectangle.setBounds(getBoundForRect());
updateResultsThrottled();
});
});
</script>
<p>
FlatGeobuf's spatial index allows you to fetch the features that
intersect a given bounding box, without downloading the entire file.
This can be helpful when you have a very large file but are only
interested in a small portion of it, and want to keep your page loads
small and fast.
</p>
<p>
For the purposes of the example we've highlighted our bounding box in
yellow. Pan the map to move the query's bounding box.
</p>
<p>
Open your developer console's network pane, and inspect the network
traffic. Compared to the <a href="/examples/leaflet/">example which loads the entire file</a>,
you'll see that this example makes more requests for the .fgb file, but
they are much smaller because we fetch only the relevant sections of
the file.
</p>
<div id="header">
<h3>Parsed header content</h3>
</div>
</body>
</html>