On the fly Darstellung von Grenzlängen und Richtungswinkeln

Aus kvwmap
Wechseln zu: Navigation, Suche

--Rainer Gareis 12:19, 26. Aug 2010 (CEST) Darstellung der Grenzlängen und Winkel in der Liegenschaftskarte Um in einem Tooltip-fenster die Grenzlänge(Gl) und den Richtungswinkel(Rw) zu sehen, muß man eine neue Tabelle mit oid und Geometrie erzeugen. In dieser Tabelle steht dann die Flurstücksgeometrie aufgeschlüsselt als Line und nicht mehr als Multipoygon.

Tabelle erzeugen:

 CREATE TABLE grenzabschnitte2( 
 the_geom geometry,
 CONSTRAINT enforce_dims_the_geom CHECK (ndims(the_geom) = 2),
 CONSTRAINT enforce_geotype_the_geom CHECK (geometrytype(the_geom) = 'LINESTRING'::text OR the_geom IS NULL),
 CONSTRAINT enforce_srid_the_geom CHECK (srid(the_geom) = 2398)
 )
 WITH (OIDS=TRUE);

um die Tabelle füllen zu können benötigt man eine neue Funktion "linefrompoly"

CREATE OR REPLACE FUNCTION linefrompoly(geometry)
RETURNS geometry AS
$BODY$SELECT 
	geomfromtext(
		replace(
			replace(
				replace(
					replace(
						replace(
							asText($1),'MULTIPOLYGON','MULTILINESTRING'
						),'POLYGON','MULTILINESTRING'
					), '(((', '(('
				), ')))', '))'
			), ')),((', '),('
		), srid($1)
	)$BODY$
LANGUAGE 'sql' IMMUTABLE STRICT;


mit folgendem Befehl wird dann die Tabelle gefüllt.

 insert into grenzabschnitte2 SELECT makeline(pointn(foo.linestring, foo.count1), pointn(foo.linestring, foo.count1 + 1)) AS the_geom
  FROM ( SELECT generate_series(1, npoints(foofoo.linestring)) AS count1, foofoo.linestring
          FROM ( SELECT linefrompoly(alkobj_e_fla.the_geom) AS linestring
                  FROM alkobj_e_fla where folie = '001') foofoo) foo
 WHERE (foo.count1 + 1) <= npoints(foo.linestring) 

Gist-Index erzeugen, damit die räumliche Abfrage auch schnell durchgeführt wird:

CREATE INDEX grenzabschnitte_gist
 ON grenzabschnitte2
 USING gist
 (the_geom);

(Dauer ca. 15 Minuten für einen Kompletten Landkreis)

Jetzt kann man sich noch eine Sicht erzeugen, in der die Länge und der Richtingswinkel berechnet wird.

CREATE OR REPLACE VIEW grenzabschnitte AS 
SELECT grenzabschnitte2.oid, grenzabschnitte2.the_geom, round(length(grenzabschnitte2.the_geom)::numeric, 2) AS laenge, 
       CASE
           WHEN ((y(endpoint(grenzabschnitte2.the_geom)) - y(startpoint(grenzabschnitte2.the_geom))) = 0 AND (x(endpoint(grenzabschnitte2.the_geom)) > x(startpoint(grenzabschnitte2.the_geom))))THEN '100.0000 gon'::text
           WHEN ((y(endpoint(grenzabschnitte2.the_geom)) - y(startpoint(grenzabschnitte2.the_geom))) = 0 AND (x(endpoint(grenzabschnitte2.the_geom)) < x(startpoint(grenzabschnitte2.the_geom))))THEN '300.0000 gon'::text
           WHEN round((degrees(atan((x(endpoint(grenzabschnitte2.the_geom)) - x(startpoint(grenzabschnitte2.the_geom))) / (y(endpoint(grenzabschnitte2.the_geom)) - y(startpoint(grenzabschnitte2.the_geom))))) * 1.1111111::double precision)::numeric, 4)::double precision < 0::double precision THEN (round((degrees(atan((x(endpoint(grenzabschnitte2.the_geom)) - x(startpoint(grenzabschnitte2.the_geom))) / (y(endpoint(grenzabschnitte2.the_geom)) - y(startpoint(grenzabschnitte2.the_geom))))) * 1.1111111::double precision)::numeric, 4)::double precision + 400::double precision) || ' gon'::text
           ELSE round((degrees(atan((x(endpoint(grenzabschnitte2.the_geom)) - x(startpoint(grenzabschnitte2.the_geom))) / (y(endpoint(grenzabschnitte2.the_geom)) - y(startpoint(grenzabschnitte2.the_geom))))) * 1.1111111::double precision)::numeric, 4)::double precision::text || ' gon'::text
       END AS winkel
  FROM grenzabschnitte2;

Nun benötigt man nur noch einen abfragbaren Layer ohne Klassen.

Mit freundlicher Unterstützung durch STEFAN RAHN ist dieses Kunststück gelungen.


--Markus Hentschel 08:29, 5. Okt 2010 (CEST) Da zwei benachbarte Flurstücke auch zwei gemeinsame Grenzabschnitte mitbringen, muss man die doppelten noch löschen. Dafür bietet sich folgender Befehl an:

delete from grenzabschnitte2 where oid in (
select a.oid from grenzabschnitte2 a left join grenzabschnitte2 b 
on length(a.the_geom)=length(b.the_geom) 
and a.the_geom=b.the_geom 
and x(pointn(a.the_geom,1)) > x(pointn(b.the_geom,1))
)

Problem: Es gibt zu viele Linien, die Abfrage dauert unendlich. Lösung: ein Script. In diesem Script kann dann auch das Füllen der Tabelle passieren. Das Script geht kilometerquadratweise vor und löscht die doppelten Linien immer nur in diesem Bereich, wodurch die Abfragezeit auf der Datenbank deutlich verringert wird.


Dazu muss eine weitere Tabelle angelegt werden, die ebenfalls nur ein Geometriefeld enthält. Angenommen, sie heißt grenzabschnitte1. Dann sieht das Script so aus:

#!/bin/ksh

# Füllt die Tabelle grenzabschnitte2 mit den Grenzlängen aus den Flurstücksgeometrien

cd /usr/local/pgsql/bin

LOGFILE=/pfad/zu/load_grenzabschnitte.log; export LOGFILE
DBNAME=kvwmapsp; export DBNAME
DBUSER=kvwmap; export DBUSER

# Definition des zu kachelnden Gebiets im Ziel-Koordinatenreferenzsystem S42/83
min_rechts=4517000   # Rechtswert der Ecke links unten
min_hoch=5982000     # Hochwert der Ecke links unten
max_rechts=70000     # maximale Ausdehnung des Bereichs in Metern (min_rechts + max_rechts = Rechtswert rechts oben)
max_hoch=59000	      # maximale Ausdehnung des Bereichs in Metern (min_hoch + max_hoch = Hochwert rechts oben)
kachel=1000          # Größe der Kachel in Metern

echo "############## Beginn `date +%c` ################" >> $LOGFILE 2>&1

# Leeren und neues Füllen der temporären Tabelle
./psql -d $DBNAME -U $DBUSER -c "

   -- Vorhandene Indices löschen
   DROP INDEX g1ixoid;
   DROP INDEX g1ixgeom;

   TRUNCATE grenzabschnitte1;

   -- temporäre Tabelle füllen
   INSERT INTO grenzabschnitte1 
     SELECT makeline(pointn(foo.linestring, foo.count1), pointn(foo.linestring, foo.count1 + 1)) AS the_geom
     FROM ( SELECT generate_series(1, npoints(foofoo.linestring)) AS count1, foofoo.linestring
            FROM ( SELECT linefrompoly(alkobj_e_fla.the_geom) AS linestring
                   FROM alkobj_e_fla where folie = '001') foofoo) foo
     WHERE (foo.count1 + 1) <= npoints(foo.linestring);
     
"  # Ende SQL

if test $? -eq 0
  then
    echo "`date +%c`   grenzabschnitte1 aktualisiert" >> $LOGFILE 2>&1
    
    # Tabelle analysieren für einen schnellen Zugriff
    ./psql -d $DBNAME -U $DBUSER -c "

       VACUUM ANALYZE grenzabschnitte1;

    "  # Ende SQL
    ./psql -d $DBNAME -U $DBUSER -c "
    
       -- Indices wieder aufbauen für einen noch schnelleren Zugriff
       CREATE INDEX g1ixoid ON grenzabschnitte1 (oid);
       CREATE INDEX g1ixgeom ON grenzabschnitte1 USING gist (the_geom);

    "  # Ende SQL
    
    # Lösche die doppelt vorhandenen wieder raus
    # portionsweiseweise, weil sonst die Prüfung zu lange dauert
    
    # äußere Schleife - erst die Zeilen von unten nach oben ...
    i=0
    while [ $i -lt $max_hoch + $kachel ]
    do
    
          z2=$(($min_hoch + $i))
          
    
    # innere Schleife - ... dann die Kacheln von links nach rechts
      j=0
      while [ $j -lt $max_rechts + $kachel ]
      do
    	z1=$(($min_rechts + $j))
    
    	z3=`expr $z1 + $kachel`
    	z4=`expr $z2 + $kachel`
    
        echo $z1 " " $z2 ", " $z3 " " $z4
       
            sql="delete from grenzabschnitte1 where oid in (select a.oid from grenzabschnitte1 a left join grenzabschnitte1 b on length(a.the_geom)=length(b.the_geom) and a.the_geom=b.the_geom and x(pointn(a.the_geom,1)) > x(pointn(b.the_geom,1)) where a.the_geom && GeomFromText('POLYGON((${z1} ${z2},${z3} ${z2},${z3} ${z4},${z1} ${z4},${z1} ${z2}))', 2398) and b.the_geom && GeomFromText('POLYGON((${z1} ${z2},${z3} ${z2},${z3} ${z4},${z1} ${z4},${z1} ${z2}))', 2398));"
            #echo $sql
            ./psql -d $DBNAME -U $DBUSER -c "$sql"
      
            j=`expr $j + $kachel`
    
      done # Ende innere Schleife
    
          i=`expr $i + $kachel`
    
    done # Ende äußere Schleife
    
    echo "`date +%c`   grenzabschnitte1 auf doppelte Grenzen geprüft" >> $LOGFILE 2>&1
   
    
    # Übertragung der Grenzlängen in die endgültige Tabelle
    ./psql -d $DBNAME -U $DBUSER -c "
    
       -- Starten der Transaktion
       BEGIN;
       
       -- Index entfernen
       DROP INDEX g2ixgeom;
       
       -- Leeren der Tabelle
       TRUNCATE grenzabschnitte2;  
       
       -- Füllen der Tabelle
       INSERT INTO grenzabschnitte2 SELECT * FROM grenzabschnitte1;
       
       -- Beenden der Transaktion
       END;

    "  # Ende SQL
    
    if test $? -eq 0
      then
        echo "   grenzabschnitte2 erfolgreich aktualisiert" >> $LOGFILE 2>&1
      else
        echo ">> grenzabschnitte2 konnte nicht aktualisiert werden" >> $LOGFILE 2>&1
    fi
    
    # Tabelle analysieren
    ./psql -d $DBNAME -U $DBUSER -c "
    
       VACUUM ANALYZE grenzabschnitte2;
       
    "  # Ende SQL
    
    if test $? -eq 0
      then
        echo "   VACUUM erfolgreich durchgeführt" >> $LOGFILE 2>&1
      else
        echo ">> VACUUM konnte nicht durchgeführt werden" >> $LOGFILE 2>&1
    fi
    
    # Tabelle indizieren
    ./psql -d $DBNAME -U $DBUSER -c "
    
       CREATE INDEX g2ixgeom
       ON grenzabschnitte2
       USING gist
       (the_geom);
     
    "  # Ende SQL
    
    if test $? -eq 0
      then
        echo "   INDEX erfolgreich angelegt" >> $LOGFILE 2>&1
      else
        echo ">> INDEX konnte nicht angelegt werden" >> $LOGFILE 2>&1
    fi

  else
    echo "`date +%c`>> grenzabschnitte1 konnte nicht aktualisiert werden" >> $LOGFILE 2>&1
    echo "`date +%c`>> grenzabschnitte2 behält den alten Stand" >> $LOGFILE 2>&1
fi

echo " " >> $LOGFILE 2>&1
echo "############## Ende `date +%c` ################" >> $LOGFILE 2>&1
echo " " >> $LOGFILE 2>&1

exit 0

Das Script benötigt auf meiner Maschine etwas unter einer halben Stunde bei 2,5 Millionen Ausgangslinien.