mirror of
https://github.com/flatgeobuf/flatgeobuf.git
synced 2025-12-08 19:06:03 +00:00
171 lines
7.4 KiB
HTML
171 lines
7.4 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/large.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><a href="/examples/leaflet/filtered.html">Filter By Rect</a></li>
|
|
<li class="active">Filtering a Large Dataset</li>
|
|
</ul>
|
|
|
|
<div id="map"></div>
|
|
<script>
|
|
document.addEventListener("DOMContentLoaded", async () => {
|
|
// basic OSM Leaflet map
|
|
const map = L.map('map').setView([40.766, -73.98], 14);
|
|
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
|
maxZoom: 18,
|
|
minZoom: 12,
|
|
attribution: '© <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)
|
|
while (header.firstChild)
|
|
header.removeChild(header.firstChild)
|
|
header.appendChild(formatter.render())
|
|
}
|
|
|
|
// For the example, we fix a visible Rect in the middle of the map
|
|
function getBoundForRect() {
|
|
const bounds = map.getBounds();
|
|
|
|
const width = map.distance(bounds.getNorthWest(), bounds.getNorthEast());
|
|
const height = map.distance(bounds.getNorthWest(), bounds.getSouthWest());
|
|
return map.getCenter().toBounds(Math.min(width, height) * 0.8);
|
|
}
|
|
|
|
// convert the rect into the format flatgeobuf expects
|
|
function fgBoundingBox() {
|
|
const bounds = getBoundForRect();
|
|
return {
|
|
minX: bounds.getWest(),
|
|
maxX: bounds.getEast(),
|
|
minY: bounds.getSouth(),
|
|
maxY: bounds.getNorth(),
|
|
};
|
|
}
|
|
|
|
// show a leaflet rect corresponding to our bounding box
|
|
const rectangle = L.rectangle(getBoundForRect(), { interactive: false, color: "blue", fillOpacity: 0.0, opacity: 1.0 }).addTo(map);
|
|
|
|
// 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('https://flatgeobuf.septima.dk/population_areas.fgb', fgBoundingBox(), handleHeaderMeta);
|
|
|
|
const colorScale = ((d) => {
|
|
return d > 750 ? '#800026' :
|
|
d > 500 ? '#BD0026' :
|
|
d > 250 ? '#E31A1C' :
|
|
d > 100 ? '#FC4E2A' :
|
|
d > 50 ? '#FD8D3C' :
|
|
d > 25 ? '#FEB24C' :
|
|
d > 10 ? '#FED976' :
|
|
'#FFEDA0'
|
|
});
|
|
|
|
for await (const feature of iter) {
|
|
// Leaflet styling
|
|
const defaultStyle = {
|
|
color: colorScale(feature.properties.population),
|
|
weight: 1,
|
|
fillOpacity: 0.4,
|
|
};
|
|
L.geoJSON(feature, {
|
|
style: defaultStyle,
|
|
}).on({
|
|
'mouseover': (e) => {
|
|
const layer = e.target;
|
|
layer.setStyle({
|
|
weight: 4,
|
|
fillOpacity: 0.8,
|
|
});
|
|
},
|
|
'mouseout': (e) => {
|
|
const layer = e.target;
|
|
layer.setStyle(defaultStyle);
|
|
}
|
|
}).bindPopup(`${feature.properties.population} people live in this census block.</h1>`)
|
|
.addTo(nextResults);
|
|
}
|
|
rectangle.bringToFront();
|
|
}
|
|
// if the user is panning around alot, only update once per second max
|
|
const updateResultsThrottled = _.throttle(updateResults, 1000);
|
|
|
|
// 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>
|
|
This is a map of every census block in the USA, including its
|
|
population. The entire file is over 12GB, but FlatGeobuf fetches only
|
|
the tiny subset of data that intersects with the bounding box (drawn
|
|
in blue). Pan the map to move the query's bounding box.
|
|
</p>
|
|
<p>
|
|
When you have feature data that cover a large area in fine-grained
|
|
detail like this, the typical options are to either manually slice up
|
|
your file into manageable regions or to rely on running an application
|
|
server which does this slicing dynamically.
|
|
</p>
|
|
<p>
|
|
Hosting an application has initial complexity and ongoing maintenance
|
|
costs. Slicing files can be tedious and inevitiably you might be
|
|
interested in an area on the boundary of slices.
|
|
</p>
|
|
<p>
|
|
For these cases, consider instead using a single indexed FlatGeobuf.
|
|
Because FlatGeobuf's spatial index allows you to fetch only the data
|
|
you're interested in, you can keep your page size down while avoiding
|
|
the tedium of slicing up files manually, or building and maintaining an
|
|
application server.
|
|
</p>
|
|
<p>
|
|
Though getting a small subset of features from a large dataset transfers
|
|
relatively little data, it does so using multiple requests. There are some
|
|
<a href="https://github.com/flatgeobuf/flatgeobuf?tab=readme-ov-file#optimizing-remotely-hosted-flatgeobufs">tips for optimizing remote hosted flatgeobufs</a>, mostly oriented around minimizing round trip latency.
|
|
</p>
|
|
<div id="header">
|
|
<h3>Parsed header content</h3>
|
|
</div>
|
|
</body>
|
|
</html>
|