BAG 3D richting en helling dakvlakken bepalen (slope en azimuth)

Ik heb de BAG3D van TU Delft ingelezen in een postgreSQL PostGIS database. Nu probeer ik de helling en de richting (slope en azimuth) van de (dak) vlakken te determineren. Ik kan helaas geen postgis functie vinden die dit snel en gemakkelijk teruggeeft per 3Dvlak.

Ik heb nu een eigen st_slope3D en st_azimuth3D functie gemaakt die de punten van een vlak dumpt en op basis van het laagste en hoogste punt (st_minz en st_maxz) wat uitrekent, Maar dat gaat nog niet helemaal goed, en is ook wat sloom.

Daarom toch hier ook nog even gevraagd: Zijn er snellere en gemakkelijkere manieren om de slope en de azimuth uit te rekenen van een 3D polygoon?

Ik weet zo gauw niet een PostGIS-functie hiervoor, maar volgens mij is je uitgangspunt met minz en maxz (dz) niet juist. Je moet denk ik de steilste lijn vinden, dus die waarvan de verhouding afstand-xy tot dz het grootst is. Maar goed, dat bedenk ik bij mijn eerste kopje koffie op maandagochtend. :slight_smile:

Voor de snelheid kun je misschien 1 functie maken die zowel slope als azimuth berekent, omdat de functies nu 2x bijna hetzelfde doen. Dat scheelt weer een beetje.

Nog een bakkie verder bedenk ik dat het toch nog iets lastiger is. De steilste lijn bij een driehoek (bijvoorbeeld zijvlak piramide) loopt niet naar 1 van de onderste punten maar naar het midden van het de onderste zijde…

Volgens mij staat de uitwerking hier: mathematics - Calculate the slope of a 3D triangle? - Game Development Stack Exchange

En misschien dat CGAL dit kan, die library wordt ook gebruikt in postgresql: https://www.cgal.org/

In mijn beleving bereken je eerst de normaalvector van een vlak: Meetkunde in 3D

Maar eerlijk gezegd denk ik dat je niet zo low-level hoeft te gaan rekenen. Dit is vast eerder gedaan :slight_smile: .

Je ziet dat ook in de 3d BAG viewer gebeuren als je op een dakvlak klikt.

Deze link is nog interessant: 3DDistancecalc – PostGIS

Die normaal vector is het denk ik wel. Zit even te zoeken tussen de postgis 3D functies. Ik denk dat de simpelste manier om de normaal vector te maken de functie ST_3DShortestLine() is.

  1. Maak 3d-punt boven het 3d-vlak = st_makepoint() met st_centroid( 2d vlak) [x,y] en ST_ZMax() [z] (+1?)
  2. normaalvector = ST_3DShortestLine( 3d-punt, 3d-vlak)

En met die lijn kan ik dan de slope en de azimuth berekenen. Ik weet niet of ik er vandaag aan toekom, maar ik zal melden wat de uitkomst is.

Enorm bedankt in ieder geval voor het meedenken!

Ik denk niet dat dat altijd goed gaat, omdat je soms de afstand naar een vertex of edge zult berekenen en niet naar het vlak. Ook moet je rekening houden met de richting van de normal, omdat een vlak 2 zijden heeft.

Volgens mij staat het scriptje gewoon in de uitwerking die ik eerder stuurde en kun je dat namaken in postgres (of als python functie?)

Ter info: Het resultaat: Moet nog even goed testen of ergens nog gekke dingen gebeuren.
Nogmaals dank voor de input!

4 likes

Zou je in de beste open source gedacht ook willen delen hoe je dit hebt gedaan? Dan heeft een volgende met dezelfde vraag er ook wat aan!

Dit is de code:

drop table if exists daken_eindhoven;
with dump as 
(
	select gid, ST_ZMin( geometrie ) floorz, 
	(st_dump(geometrie)).geom::geometry(polygonz,7415) as geom3d 
	from bag3d_eindhoven 
),
data as
(
select gid, 
		floorz,
		st_3dSlope(geom3d)::integer as slope,
		st_3dAzimuth(geom3d)::integer as azimuth,
		st_3dHeight(geom3d) as height,
		ST_ZMin( geom3d ) minz,
		ST_ZMax( geom3d ) maxz,	
		--geom3d, 
		st_transform( st_force2d( geom3d ), 28992)::geometry(polygon,28992) geom2d
	from dump
)
select *
into daken_eindhoven
from data 
where data.minz > data.floorz+2 -- anders krijg je ook het vloeropp

;

En die query maakt gebruikt de volgende functies:

DROP FUNCTION st_3DSlope(geometry);
DROP FUNCTION st_3DAzimuth(geometry);
DROP FUNCTION st_3DHeight(geometry);
DROP FUNCTION ST_3DPointOnSurface(geometry);
DROP FUNCTION st_3Dinfo(geometry) ;

--******************************
CREATE OR REPLACE FUNCTION public.st_3dheight( geom3d geometry ) RETURNS  float LANGUAGE plpgsql
AS $function$
begin
	return abs( st_zmax(geom3d)-st_zmin(geom3d) );
END; $function$;
--******************************
CREATE OR REPLACE FUNCTION public.st_3dslope( geom3d geometry ) RETURNS  float LANGUAGE plpgsql
AS $function$
declare 
	info float[2];
begin
	info := st_3dinfo( geom3d );
	return info[1];
END; $function$;
--******************************
CREATE OR REPLACE FUNCTION public.st_3dazimuth( geom3d geometry ) RETURNS  float LANGUAGE plpgsql
AS $function$
declare 
	info float[2];
begin
	info := st_3dinfo( geom3d );
	return info[2];
END; $function$;
--******************************
CREATE OR REPLACE FUNCTION public.ST_3DPointOnSurface( geom3d geometry ) 
	RETURNS  geometry LANGUAGE plpgsql
AS $function$
declare 
	z_max float;
	z_min float;
	geom2d geometry;
begin
	z_max = ST_ZMax( geom3d );
	z_min = ST_ZMin( geom3d );
	geom2d := st_force2d( geom3d );
	return ST_3DClosestPoint(
				geom3d,
				st_force3D(
					ST_PointOnSurface( geom2d ),
					(z_max+z_min)/2)
				);	
END; $function$;
--******************************
CREATE OR REPLACE FUNCTION public.st_3dinfo( geom3d geometry )
 RETURNS  float[][]
 LANGUAGE plpgsql
AS $function$
declare
	midpoint geometry;
	normalvector geometry;
	geomstart geometry;
	geomend geometry;
	h float;
	d float; 
	slope float;
	azimuth float;
	p1 geometry;
	p2 geometry;
	p3 geometry;
	try integer;
	isok boolean;
begin
	isok := true;
	slope := 0;
	azimuth :=  null;
	--check for 3d
	if not ST_NDims( geom3d ) = 3 then
		isok := false;
	end if; 
	--check for minimal slope (make it a default parameter?)
	if ST_3DHeight( geom3d) < 0.1 then
		isok := false;
	end if;
	--do the complicated stuff
	if isok then
		--**************************************
		--create normal vector
		midpoint := ST_3DPointOnSurface( geom3d );
		midpoint := st_force3D(st_force2d(midpoint), st_z(midpoint)+0.1); 		--raise point with 10cm
		normalvector := ST_3DShortestLine( midpoint, geom3d);
		--**************************************
		--get azimuth = direction of line
		geomstart := ST_StartPoint(normalvector);
		geomend := ST_EndPoint(normalvector);
		azimuth := 	degrees(ST_Azimuth( geomend,geomstart )) ;
		--**************************************
		--get slope = angle between two 3D points
		h := abs( st_z( geomstart ) - st_z( geomend ) );
		d := st_distance( st_force2d(geomstart), st_force2d(geomend) );
		p1 := st_makePoint( 0,0 );	
		p2 := st_makePoint( 0,h );
		p3 := st_makePoint( d,0 );				
		slope := 90-degrees(st_angle(p1,p3,p2));
	end if;
	return array[ slope, azimuth ];
--EXCEPTION WHEN others THEN
--	raise notice  'ST_slope3d error';
--	return false;
END; $function$
;

--**************************************************
3 likes

@ArendjanvdNeut Bedankt voor het delen! Ik probeer het dakoppervlak en de hellingshoek te bepalen voor een adres. In de BAG 3d viewer is de hoek te zien:

Echter, hoe krijg ik deze uit de resultaten van je functies? Zou dat de Azimuth moeten zijn?

Daarnaast, welke LOD gebruik je? Want ik zie dat je bag3d_eindhoven gebruikt, is dat een custom set op basis van LOD22_3d?

image

Azimuth is de richting en de Slope is de hellingshoek. Dus deze functies:
st_3dSlope(geom3d) en st_3dAzimuth(geom3d)

Mijn bag3d_eindhoven was een testtukje (zelf gemaakte selectie) uit een grotere set die ik gedownload heb. Wat ik indertijd exact gedownload heb weet ik niet meer, maar je hebt 3D (dak) geometrien nodig.