swisstopo: Gebaeude als Volumen + Terrain-Mesh in die 3D-Ansicht
Gebaeude-Footprints (swisstopo vec25) werden jetzt zu Volumen extrudiert (z=0 bis Hoehe; Hoehe aus OSM height/building:levels, sonst 9 m; Deckel/ Boden via Delaunay, konkave Grundrisse) und als importedMesh gerendert — der flache Footprint-contourSet bleibt fuer den 2D-Plan erhalten. Neuer Terrain-Abruf (swissALTI3D ueber den CORS-faehigen profile.json-Dienst, zeilenweise parallel) liefert ein N-Raster, das terrainMeshFromGrid trianguliert; Hoehen aufs Zentrum bezogen, lagerichtig zu den Gebaeuden. Neue Quelle 'Swisstopo-Gelaende' im Import-Dialog. Viewport3D unveraendert (bestehender importedMesh/terrainMesh-Pfad). 8 neue Tests.
This commit is contained in:
@@ -11,12 +11,13 @@
|
||||
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { t } from "../i18n";
|
||||
import { geocode, fetchBuildings } from "../io/swissTopo";
|
||||
import { geocode, fetchBuildings, fetchTerrain } from "../io/swissTopo";
|
||||
import type { GeocodeCandidate } from "../io/swissTopo";
|
||||
import { fetchOsm } from "../io/osm";
|
||||
import type { OsmSelection } from "../io/osm";
|
||||
import { featuresToContextObjects } from "../io/geoContext";
|
||||
import type { GeoFeature } from "../io/geoContext";
|
||||
import { terrainMeshFromGrid } from "../model/terrain";
|
||||
import type { GeoOrigin } from "../io/lv95";
|
||||
import type { ContextObject } from "../model/types";
|
||||
|
||||
@@ -28,6 +29,7 @@ export interface ContextImportDialogProps {
|
||||
|
||||
type Sources = {
|
||||
swissBuildings: boolean;
|
||||
swissTerrain: boolean;
|
||||
osmBuildings: boolean;
|
||||
osmRoads: boolean;
|
||||
osmWater: boolean;
|
||||
@@ -36,6 +38,7 @@ type Sources = {
|
||||
|
||||
const DEFAULT_SOURCES: Sources = {
|
||||
swissBuildings: true,
|
||||
swissTerrain: true,
|
||||
osmBuildings: false,
|
||||
osmRoads: true,
|
||||
osmWater: true,
|
||||
@@ -91,6 +94,7 @@ export function ContextImportDialog({
|
||||
|
||||
const anySource =
|
||||
sources.swissBuildings ||
|
||||
sources.swissTerrain ||
|
||||
sources.osmBuildings ||
|
||||
sources.osmRoads ||
|
||||
sources.osmWater ||
|
||||
@@ -129,13 +133,25 @@ export function ContextImportDialog({
|
||||
all.push(...r.features);
|
||||
}
|
||||
|
||||
if (all.length === 0) {
|
||||
// Gelände-Höhenraster (swissALTI3D via geo.admin-Profil) → Terrain-Mesh.
|
||||
let terrain: ContextObject | null = null;
|
||||
if (sources.swissTerrain) {
|
||||
const r = await fetchTerrain(center, radius, origin);
|
||||
origin = r.origin;
|
||||
if (r.grid) {
|
||||
const mesh = terrainMeshFromGrid(r.grid);
|
||||
if (mesh.positions.length >= 9) terrain = mesh;
|
||||
}
|
||||
}
|
||||
|
||||
if (all.length === 0 && !terrain) {
|
||||
setError(t("ctxImport.empty"));
|
||||
setStatus(null);
|
||||
return;
|
||||
}
|
||||
|
||||
const objs = featuresToContextObjects(all, selected.label);
|
||||
if (terrain) objs.push(terrain);
|
||||
onImport(objs);
|
||||
setStatus(
|
||||
t("ctxImport.imported", { count: all.length, sets: objs.length }),
|
||||
@@ -249,6 +265,14 @@ export function ContextImportDialog({
|
||||
/>
|
||||
{t("ctxImport.src.swissBuildings")}
|
||||
</label>
|
||||
<label className="cim-check">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={sources.swissTerrain}
|
||||
onChange={() => toggle("swissTerrain")}
|
||||
/>
|
||||
{t("ctxImport.src.swissTerrain")}
|
||||
</label>
|
||||
<label className="cim-check">
|
||||
<input
|
||||
type="checkbox"
|
||||
|
||||
Reference in New Issue
Block a user