View a district with buildings and terrain
View district buildings and model terrain on a map, then turn on map terrain to compare the wider landscape.
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1">
<title>View a district with buildings and terrain</title>
<meta name="description" content="View district buildings and model terrain on a map, then turn on map terrain to compare the wider landscape.">
<style>
html, body { height: 100%; margin: 0; }
#example-viewer-mount { position: fixed; inset: 0; }
bim-grid#ifc-viewer { display: block; position: absolute; inset: 0; width: 100%; height: 100%; min-width: 0; min-height: 0; }
.example-ui { position: fixed; z-index: 10; box-sizing: border-box; color: #253041; font: 12px/1.4 system-ui, sans-serif; }
.example-ui.example-task { right: 10px; bottom: 64px; width: 224px; max-width: calc(100vw - 64px); display: grid; gap: 6px; padding: 10px; background: #fff; border: 1px solid #dfe3e8; border-radius: 4px; box-shadow: 0 1px 5px #14203310; }
.example-ui :is(h2, p) { margin: 0; }
.example-ui h2 { font-size: 13px; line-height: 1.3; font-weight: 650; }
.example-ui p { color: #5a6575; }
.example-ui button { box-sizing: border-box; min-width: 0; max-width: 100%; font: inherit; padding: 5px 7px; border: 1px solid #cbd2dc; border-radius: 3px; background: #fff; color: #253041; cursor: pointer; }
.example-ui button:hover { background: #f3f6fa; }
.example-ui button:focus-visible { outline: 2px solid #3568ba; outline-offset: 2px; }
.example-ui [hidden] { display: none; }
.example-ui.example-credit { right: 8px; bottom: 32px; width: max-content; max-width: calc(100vw - 64px); padding: 2px 5px; background: #ffffffdf; border-radius: 2px; font-size: 9px; text-align: right; overflow-wrap: anywhere; }
.example-ui.example-credit a { color: #566171; }
@media (max-width: 600px) {
.example-ui.example-task { right: 8px; padding: 8px; }
.example-ui button { min-height: 30px; }
}
</style>
<!-- generated:example-seo:start -->
<link rel="canonical" href="https://viewer.flinker.app/examples/zurich-district/index.html">
<meta name="robots" content="index,follow">
<meta property="og:title" content="View a district with buildings and terrain">
<meta property="og:description" content="View district buildings and model terrain on a map, then turn on map terrain to compare the wider landscape.">
<meta property="og:url" content="https://viewer.flinker.app/examples/zurich-district/index.html"><meta property="og:type" content="article">
<meta property="og:site_name" content="Flinker IFC Viewer"><meta property="og:image" content="https://viewer.flinker.app/examples/zurich-district/thumbnail.webp">
<meta name="twitter:card" content="summary_large_image"><meta name="twitter:title" content="View a district with buildings and terrain">
<meta name="twitter:description" content="View district buildings and model terrain on a map, then turn on map terrain to compare the wider landscape."><meta name="twitter:image" content="https://viewer.flinker.app/examples/zurich-district/thumbnail.webp">
<script type="application/ld+json">{"@context":"https://schema.org","@type":"TechArticle","headline":"View a district with buildings and terrain","description":"View district buildings and model terrain on a map, then turn on map terrain to compare the wider landscape.","url":"https://viewer.flinker.app/examples/zurich-district/index.html","mainEntityOfPage":"https://viewer.flinker.app/examples/zurich-district/index.html","image":"https://viewer.flinker.app/examples/zurich-district/thumbnail.webp","inLanguage":"en","author":{"@type":"Organization","name":"Flinker","url":"https://www.flinker.app/"}}</script>
<!-- generated:example-seo:end -->
<script type="application/json" id="example-data">
{
"category": "Infrastructure",
"formats": [
"IFC"
],
"capabilities": [
"Georeferencing",
"Urban context",
"Terrain",
"Large models"
],
"audience": "BIM workflow",
"order": 19,
"viewer": {
"assetIds": [
"zurich-district"
],
"presentation": "map"
},
"assets": [
{
"id": "zurich-district",
"name": "showcase_zurich_paradeplatz.ifc",
"format": "IFC",
"bytes": 90774981,
"url": "https://media.githubusercontent.com/media/louistrue/ifc-site/675ff0afe440f5390a66c2bb170bd381e7d9053e/results/showcase_zurich_paradeplatz.ifc",
"sourceUrl": "https://github.com/louistrue/ifc-site/blob/675ff0afe440f5390a66c2bb170bd381e7d9053e/results/showcase_zurich_paradeplatz.ifc",
"license": "AGPL-3.0 / swisstopo OGD / ODbL-1.0",
"licenseUrl": "https://github.com/louistrue/ifc-site/blob/675ff0afe440f5390a66c2bb170bd381e7d9053e/LICENSE",
"noticeUrl": "https://viewer.flinker.app/examples/zurich-district/index.html#example-notices",
"attribution": "Original IFC exports by louistrue / Swiss Site Boundaries Tool (AGPL-3.0). Source: Federal Office of Topography swisstopo (free geodata, attribution required). © OpenStreetMap contributors (ODbL-1.0).",
"notes": "Complete original published IFC4 urban geodata export; contents unchanged. Buildings, terrain and mapped infrastructure use Swiss LV95 (EPSG:2056), metre units and the declared LN02 height datum. The model includes its own IFC terrain; the optional 30 m Copernicus surface includes roofs and vegetation. This is geographic context data, not a detailed construction-design model."
}
]
}
</script>
</head>
<body>
<main id="example-viewer-mount" aria-label="Model viewer"><bim-grid id="ifc-viewer"></bim-grid></main>
<aside class="example-ui example-task" aria-label="District in context">
<h2>District in context</h2>
<p>Zoom between the district and map. Select a building, then click <strong>1 element selected</strong> at the top left to open Properties.</p>
<p>Open <strong>View</strong> at the top right → <strong>Layers</strong> and enable <strong>Terrain heights (30 m)</strong> to compare the map surface with the model terrain.</p>
<button id="sample-retry" type="button" hidden>Retry sample</button>
</aside>
<div class="example-ui example-credit" aria-label="Sample source and license"><a href="https://github.com/louistrue/ifc-site/blob/675ff0afe440f5390a66c2bb170bd381e7d9053e/results/showcase_zurich_paradeplatz.ifc" target="_blank" rel="noopener noreferrer">louistrue · swisstopo · © OpenStreetMap contributors</a> · <a href="https://github.com/louistrue/ifc-site/blob/675ff0afe440f5390a66c2bb170bd381e7d9053e/LICENSE" target="_blank" rel="noopener noreferrer">AGPL-3.0 / swisstopo OGD / ODbL-1.0</a></div>
<template id="example-guide">
<h2 data-example-title>View a district with buildings and terrain</h2>
<p data-example-description>View district buildings and model terrain on a map, then turn on map terrain to compare the wider landscape.</p>
<h3>Try it</h3><ol><li>Compare the district buildings and streets with the map.</li><li>Select terrain or a building to identify its IFC properties.</li><li>Open View at the top right → Layers and enable Terrain heights (30 m) to compare the wider surface with the model terrain.</li></ol><h2>Developer integration</h2><p>Import the geographic IFC with <code>viewer.add()</code> and show its map with <code>viewer.toggleMap(true)</code>. The model includes terrain; optional map terrain is a 30 m surface. Use this example for urban context, not detailed construction levels.</p><p>Copy this folder and serve it over HTTP, for example <code>python -m http.server 8080</code>. Open <code>http://localhost:8080/</code>; the SDK and sample downloads require internet access.</p>
<h3>Model notes</h3><p>Urban context with model terrain. Optional terrain is a 30 m surface.</p>
</template>
<template id="example-notices">
<h2>Sources and license notices</h2><p><a href="https://github.com/louistrue/ifc-site/blob/675ff0afe440f5390a66c2bb170bd381e7d9053e/results/showcase_zurich_paradeplatz.ifc" target="_blank" rel="noopener noreferrer">louistrue</a> · <a href="https://www.swisstopo.admin.ch/en/faq-free-geodata" target="_blank" rel="noopener noreferrer">swisstopo</a> · © <a href="https://www.openstreetmap.org/copyright" target="_blank" rel="noopener noreferrer">OpenStreetMap contributors</a> · <a href="https://github.com/louistrue/ifc-site/blob/675ff0afe440f5390a66c2bb170bd381e7d9053e/LICENSE" target="_blank" rel="noopener noreferrer">AGPL-3.0 / OGD / ODbL-1.0</a></p><p>CHGeo2004 / HTRANS ETRS89-LN02 · <a href="https://www.swisstopo.admin.ch/en/geoid-en" target="_blank" rel="noopener noreferrer">swisstopo (CC0)</a> · <a href="https://cms.geo.admin.ch/ogd/geodesy/Geoid_OGD.zip" target="_blank" rel="noopener noreferrer">Original grid</a>. The grid is unchanged.</p>
<p>Complete original published IFC4 urban geodata export; contents unchanged. Buildings, terrain and mapped infrastructure use Swiss LV95 (EPSG:2056), metre units and the declared LN02 height datum. The model includes its own IFC terrain; the optional 30 m Copernicus surface includes roofs and vegetation. This is geographic context data, not a detailed construction-design model.</p>
</template>
<script type="module">
import { Viewer } from "https://cdn.flinker.app/ifc-viewer/v4.0.3/ifc-viewer.es.js";
// Example-only download feedback. All file parsing remains in the published SDK.
async function loadExampleFile(name, url, load = blob => viewer.add(name, blob), size = 0) {
const show = stage => viewer.setMessage(name + ': ' + stage, { type: 'loading' });
const chunks = [], chunkSize = 4 * 1024 * 1024;
let offset = 0, attempts = 0, action = 'download', type = '';
try {
await show('Downloading');
while (!size || offset < size) {
try {
const end = Math.min(offset + chunkSize, size || Infinity) - 1;
const response = await fetch(url, {
headers: { Range: 'bytes=' + offset + '-' + end },
cache: 'no-store', signal: AbortSignal.timeout(60000),
});
if (!response.ok) {
await response.body?.cancel();
// GitHub raw hides Content-Range from browser JavaScript: probe EOF.
if (response.status === 416 && offset && !size) break;
throw Object.assign(new Error('HTTP ' + response.status), { retry: [408, 429].includes(response.status) || response.status >= 500 });
}
const range = response.headers.get('content-range')?.match(/^bytes (\d+)-(\d+)\/(\d+)$/);
if (range && Number(range[1]) !== offset) throw new TypeError('Unexpected byte range');
size ||= Number(range?.[3]) || 0;
const bytes = await response.arrayBuffer();
const expected = response.status === 206 ? (size ? Math.min(chunkSize, size - offset) : 0) : size;
if (!bytes.byteLength || (expected && bytes.byteLength !== expected) || (response.status === 206 && bytes.byteLength > chunkSize))
throw new TypeError('Incomplete download at byte ' + offset);
if (response.status === 200) chunks.length = 0; // Server ignored Range.
chunks.push(bytes);
offset = response.status === 200 ? bytes.byteLength : offset + bytes.byteLength;
type = response.headers.get('content-type') || '';
attempts = 0;
if (response.status === 200) break;
await show('Downloading' + (size ? ' ' + Math.round(offset / size * 100) + '%' : ''));
}
catch (error) {
if (error.retry === false || ++attempts === 3) throw error;
await show('Retrying download');
await new Promise(resolve => setTimeout(resolve, 500 * 2 ** (attempts - 1)));
}
}
action = 'open';
const result = await load(new Blob(chunks, { type }), show);
if (result?.isError) throw new Error('The viewer could not open the file');
await viewer.setMessage(null);
return result;
}
catch (error) {
throw new Error('Could not ' + action + ' ' + name + (action === 'download' ? ' from ' + url : '') + ': ' + error.message, { cause: error });
}
}
const { assets } = JSON.parse(document.getElementById("example-data").textContent);
const viewer = new Viewer('#ifc-viewer');
window.exampleViewer = viewer;
document.body.dataset.exampleState = 'loading';
const imported = new Set();
const retry = document.querySelector('#sample-retry');
let running = false;
async function load() {
if (running)
return;
running = true;
retry.hidden = true;
document.body.dataset.exampleState = 'loading';
try {
await viewer.ready;
for (const asset of assets) {
if (imported.has(asset.id))
continue;
await loadExampleFile(asset.name, asset.url, blob => {
if (blob.size !== asset.bytes)
throw new Error(`The download of ${asset.name} is incomplete (expected ${asset.bytes} bytes, received ${blob.size}).`);
return viewer.add(asset.name, blob);
}, asset.bytes);
imported.add(asset.id);
}
await viewer.toggleMap(true);
document.body.dataset.exampleState = 'loaded';
}
catch (error) {
document.body.dataset.exampleState = 'error';
await viewer.setMessage(error instanceof Error ? error.message : String(error), { type: 'error' });
retry.hidden = false;
}
finally {
running = false;
}
}
retry.addEventListener('click', () => { void load(); });
void load();
</script>
</body>
</html>
How it works
Try it
- Compare the district buildings and streets with the map.
- Select terrain or a building to identify its IFC properties.
- Open View at the top right → Layers and enable Terrain heights (30 m) to compare the wider surface with the model terrain.
Developer integration
Import the geographic IFC with viewer.add() and show its map with viewer.toggleMap(true). The model includes terrain; optional map terrain is a 30 m surface. Use this example for urban context, not detailed construction levels.
Copy this folder and serve it over HTTP, for example python -m http.server 8080. Open http://localhost:8080/; the SDK and sample downloads require internet access.
Model notes
Urban context with model terrain. Optional terrain is a 30 m surface.
Sources and license notices
louistrue · swisstopo · © OpenStreetMap contributors · AGPL-3.0 / OGD / ODbL-1.0
CHGeo2004 / HTRANS ETRS89-LN02 · swisstopo (CC0) · Original grid. The grid is unchanged.
Complete original published IFC4 urban geodata export; contents unchanged. Buildings, terrain and mapped infrastructure use Swiss LV95 (EPSG:2056), metre units and the declared LN02 height datum. The model includes its own IFC terrain; the optional 30 m Copernicus surface includes roofs and vegetation. This is geographic context data, not a detailed construction-design model.