Daha çox

PostGIS istifadə edərək xətləri üst-üstə düşməyən alt qruplara bölmək


Xətt həndəsəsi olan bir cədvəl və ayrı bir cədvəldə bu xəttə yapışdırılan bir və ya daha çox nöqtəni nəzərə alaraq, xəttin bir nöqtəni kəsdiyi yerlərin hər birində bir və ya daha çox kəsişən nöqtələri olan hər bir xətti bölmək istərdim.

Məsələn, xəttin həndəsəsi boyunca A, B və C üç kəsişən nöqtəsi olan L xətti var. L -ni dörd fərqli həndəsə olaraq qaytarmaq istərdim: L -nin əvvəlindən A -ya, L boyunca A -dan B -yə, L boyunca B -dən C -yə və C -dən L -nin sonuna qədər.

Keçmişdə xətti istinad problemi olan bu vəzifə üçün düzgün istifadə etdim (http://sgillies.net/blog/1040/shapely-recipes/). Ancaq milyonlarla xətti və nöqtəsi olan bu vəziyyətdə bu mümkün olmayacaq. Bunun əvəzinə PostgreSQL/PostGIS istifadə edərək bir həll axtarıram.

Diqqət yetirin ki, nöqtələr xəttin üzərində olmalıdır. Bundan əlavə, bir nöqtə xəttin əvvəlində və ya sonunda ola bilər, bu halda xəttin bölünməsinə ehtiyac yoxdur (eyni xəttin başlanğıc və ya bitmə nöqtələri ilə üst -üstə düşməyən digər nöqtələr olmadıqda). Alt dəst xətləri öz istiqamətlərini və xüsusiyyətlərini saxlamalıdır, ancaq nöqtə xüsusiyyətlərinin atributları heç bir əhəmiyyət kəsb etmir.


ST_Split PostGIS funksiyası, ehtimal ki, istədiyiniz şeydir.

PostGIS 2.2+ artıq ST_Splitdə Multi* həndəsələrini dəstəkləyir.

PostGIS -in köhnə versiyaları üçün oxuyun:


Tək bir xəttin birdən çox nöqtəyə bölünməsi üçün bu çox nöqtəli sarıcı plpgsql funksiyasından istifadə edə bilərsiniz. Aşağıdakı "çox nöqtəli (çoxlu) xətlərə bölün" xətti ilə sadələşdirdim:

DARAQ FUNKSİYASI VARSA split_line_multipoint (input_geom həndəsəsi, bıçaq həndəsəsi); CREATE FUNCTION split_line_multipoint (input_geom həndəsəsi, bıçaq həndəsi) $ BODY $ HƏDİYYƏSİNƏ QAYDAR - bu funksiya çox nöqtələrin çox nöqtələrə bölünməsinə icazə vermək üçün ST_Split funksiyası ətrafında bir sarmalayıcıdır - nəticə həndəsəsini elan edin; sadə bıçaq həndəsəsi; blade_geometry_type mətni: = GeometryType (bıçaq); geom_geometry_type mətni: = GeometryType (input_geom); BAŞLAYIN blade_geometry_type 'MULTI%' İSTƏMƏYƏCƏK ST_Split (input_geom, blade); ELSIF blade_geometry_type "%POINT" QEYD EDİLMƏSİNDƏ XƏBƏRDARLIĞI yüksəldin 'Bir Nöqtəyə/Çox Noktalı Bıçağa ehtiyac var'; RETURN NULL; BİTMƏK SONDA; IF geom_geometry_type "%LINESTRING" İSTƏMƏYƏNDƏ SONRA XƏBƏRDARLIĞI QALDIRIN 'LineString/MultiLineString input_geom lazımdır'; RETURN NULL; BİTMƏK SONDA; nəticə: = input_geom; - Bıçaqdakı bütün nöqtələrdə döngə SEÇİMDƏ Simple_blade (ST_Dump (ST_CollectionExtract (bıçaq, 1)))). Geom LOOP- əvvəlki nəticə nəticəsini bölməyə davam edin: = ST_CollectionExtract (ST_Split (nəticə, sadə_blade), 2); SON DÖNGÜ; RETURN nəticəsi; SON; $ BODY $ LANGUAGE plpgsql IMMUTABLE; -SELECT ST_AsText (split_line_multipoint (geom, blade)) testi -0.5 0), (0.5 0)) ') AS bıçağı --ST_GeomFromText (' POINT (-0.5 0) ') AS bıçağı) AS T;

Sonra kəsmək üçün çox nöqtəli bir həndəsə yaratmaq üçün ST_Collect istifadə edin və ya girişlərdən əl ilə yaradın:

SEÇ ST_AsText (ST_Collect (ST_GeomFromText ('POINT (1 2)'), ST_GeomFromText ('POINT (-2 3)')))); st_astext ---------- MULTIPOINT (1 2, -2 3)

Və ya bir alt sorğudan toplayın:

SELECT stusps, ST_Multi (ST_Collect (f.the_geom)) subgeom FROM (SELECT stusps, (ST_Dump (the_geom)). Geom the_geom FROM somestatetable) As f GROUP BY stusps

PostGIS 2.2 -ə yüksəltburada ST_Split çox xətti, çox nöqtəli və ya (çox) çoxbucaqlı bir sərhəd ilə bölünməni dəstəkləmək üçün genişləndirilmişdir.

postgis =# SEÇ postgis_version (), ST_AsText (ST_Split ('LINESTRING (0 0, 2 0)', 'MULTIPOINT (0 0, 1 0)')); -[QEYDİYYAT 1] ---+----------------------------------------- ------------------- postgis_version | 2.2 USE_GEOS = 1 USE_PROJ = 1 USE_STATS = 1 st_astext | GEOMETRİK KOLLEKSİYASI (XƏBƏRDARLIQ (1 0,2 0), XƏBƏRDARLIQ (0 0,1 0))

Sənin üçün tam cavabım yoxdur, amma ST_Line_Locate_Point arqument olaraq bir xətt və nöqtə götürür və xətt boyunca olan məsafəni nöqtəyə ən yaxın olan mövqeyə təmsil edən 0 ilə 1 arasındakı bir rəqəmi qaytarır.

ST_Line_Substring arqument olaraq hər biri 0 ilə 1 arasında olan bir xətt və iki ədəd alır. Nömrələr kəsirli məsafələr olaraq xəttin mövqelərini təmsil edir. Funksiya bu iki mövqe arasında işləyən xətt seqmentini qaytarır.

Bu iki funksiya ilə işləyərək nə etmək istədiyinizə nail olmalısınız.


İndi məndən iki dəfə soruşdular, gecikdiyim üçün üzr istəyirəm. Bunun çətin bir həll hesab edilməsi ehtimalı azdır; Öyrənmə əyrisini indiki halımdan bir qədər aşağıya endirəndə yazdım. Hər hansı bir məsləhət, hətta stilistik olanlar da xoş gəlir.

-Girişlər: --walkingNetwork = Piyadaların gedə biləcəyi kənarları əks etdirən xətt xüsusiyyətləri-dayanacaqlar = Avtobus dayanacaqları-XƏBƏR: stops.geom xətti xüsusiyyətlərlə üst-üstə düşmək üçün artıq məhdudlaşdırılıb. Bir təpədə və ya iki təpə arasında ola bilərlər. --Bu sorğu seriyası, dayanacaqların kəsişdiyi ayrı xüsusiyyətlərə görə kənarları bölünmüş, walkNetwork versiyasını qaytarır. TABLO tmp_lineswithstops AS (İLƏ subq AS (SE_ST_Line_Locate_Point (roads.geom, ST_ClosestPoint (roads.geom, stops.geom)) AS LR, rütbə () OVER (PARTITION BY roads.gid STYLP_Point_Clos__Locate_Lose roads.geom, stops.geom))) LRRank, ST_ClosestPoint (roads.geom, stops.geom), yollar. (ST_StartPoint (roads.geom), stops.geom) YALAN VƏ ST_Equals (ST_EndPoint (roads.geom), stops.geom) yanlışdır SİFARİŞ EDİR, LRRank) SEÇİN * FROM subq); -Daxili kənarları bir birləşmə ilə hesablayın-Matç sıfırdırsa, xətti sonuna qədər hesablayın CREATE TABLE tmp_testsplit AS (SELECT l1.gid, l1.geom, l1.lr AS LR1, l1.st_closestest AS AS LR1geom, l1 .lrrank AS lr1rank, l2.lr AS LR2, l2.st_closestpoint AS LR2geom, l2.lrrank AS lr2rank, l2.lrrank NULL OLDUĞU VƏZİYYƏT - Nöqtənin sonu ST_Line_Substring (l1.geom, l1) lr, 1) -alt sətir xəttini sona qədər əldə edin ELSE ST_Line_Substring (l1.geom, l1.lr, l2.lr) -iki nöqtə arasındakı alt sətri sona çatdırın tmp_lineswithstops AS l1 SOL OUTER JOIN tmp_lineswithstops AS2 ON l1.gid = l2.gid AND l2.lrrank = (l1.lrrank + 1)); -tmp_testsplit (gid, geom, lr1, lr1geom, lr1rank, lr2, lr2geom, lr2rank, sublinegeom) SEÇ gid, geom, lr1, ST_StartPoint (geom) kimi lr1ge1 , lr AS lr2, st_closestpoint AS lr2geom, lrrank AS lr2rank, ST_Line_Substring (l1.geom, 0, lr) AS sublinegeom -Tmp_lineswithstops AS l1 HARADA l1.lrrank = 1 -dən FROM nöqtəsinə başlayın; -İndi həm dəyişdirilmiş, həm də dəyişdirilməmiş orijinal yol xüsusiyyətlərinə uyğun gəlin TABLO YARADIŞNETwork_split AS (roadsplit.sublinegeom, roadsplit.gid AS sgid,-split-gid yolları seçin. gid = roadsplit.gid SAĞ ÇIXIŞ Şəbəkəyə qoşulma AS yolları -Dəyişməsə sıfır olan orijinal kənarları, ayrılmış geomlu orijinal kənarları başqa yollarda ONs.s.gid = roadsplit.gid); --İndi lazımi sütunları yeniləyin və müvəqqəti sütunları buraxın-Yəqin ki, öz uzunluğunuz və xərc funksiyalarınız üzərində işləməlisiniz-Burada köhnə dəyəri-uzunluğun hissəsinə vurmağın etibarlı olduğunu düşünürəm indi bölünmüş xətt, bölünməyən xətti əks etdirir UPDATE walkingNetwork_split SET geom = sublinegeom, lengthz = lengthz*(ST_Length (sublinegeom)/ST_Length (geom)), walk_seconds_ft = walk_seconds_ft*(ST_Length (sublinegeom)/ST_Length (ge) , walk_seconds_tf = walk_seconds_tf*(ST_Length (sublinegeom)/ST_Length (geom)) Sublinegeom NULL VƏ ST_Length (sublinegeom)> 0 olmadığı yerlərdə; ALTER TABLE walkingNetwork_split DROP COLUMN sublinegeom, DROP COLUMN sgid; -Aralıq cədvəlləri buraxın-Yəqin ki, faktiki müvəqqəti cədvəllərdən istifadə edə bilərsiniz; --Hər mərhələdə ağıl sağlamlığı yoxlamasına üstünlük verirəm TAMP TABLE Mövcuddursa tmp_testsplit; TAMP CƏDVƏLİ MÜVƏQQƏT OLSA tmp_lineswithstops; -Kenarlara yeni bir unikal id təyin edin, buna görə də bunu pgRouting ALTER TABLE-də mənbə/hədəf sütunları kimi istifadə edə bilirik ALTER TABLE walkingNetwork_split SÜTUN ƏLAVƏ EDİN FİT BÜTÜN; SEQUENCE CREATE road_seq; YENİLƏYİCİ WalkNetwork_split SET fid = nextval ('road_seq'); ALTER TABLE walkingNetwork_split ƏLAVƏ İLK AÇMA ("fid");

Başlanğıc baxımından yuxarıdakı cavabları genişləndirmək istəyirəm. Bu ssenaridə bir sıra nöqtələriniz var və bunları xətləri seqmentlərə ayırmaq üçün "bıçaq" kimi istifadə etməyi izləyirsiniz. Bu bütün nümunə, nöqtələrinizi əvvəlcə xəttə bağladığınızı və nöqtələrin kəsilmiş xəttindən unikal ID atributuna malik olduğunuzu ehtimal edir. Xəttin unikal identifikatorunu təmsil etmək üçün 'column_id' istifadə edirəm.

Birincisi, birdən çox bıçaq bir xəttə düşdükdə xallarınızı çox nöqtələrə qruplaşdırmaq istəyirsiniz. Əks halda, split_line_multipoint funksiyası istədiyiniz nəticə olmayan ST_Split funksiyası kimi fəaliyyət göstərir.

TABLO multple_terminal_lines SELECT ST_Multi (ST_Union (the_geom)) olaraq the_geom, a.matched_alid FROM point_table ilə INNER JOIN (SEÇİM sütun_id FROM point_table GROUP BY sütun_VAR SAYINI ÜZRƏ (*)cütləli_Ol_b_b_b) a.kolun_id;

Sonra, bu çox nöqtələrə əsaslanaraq şəbəkənizi bölmək istəyirsiniz.

TABLE split_multi seçin SEÇİM OLARAK (ST_Dump (split_line_multipoint (ST_Snap (a.the_geometry, b.the_geom, 0.00001), b.the_geom)))). Geom olaraq_geom FROM line_table a JOIN multple_terminal_umn_umn b a = bc


1 və 2 -ci addımları yalnız bir kəsişmə nöqtəsi olan xətlərinizlə təkrarlayın. Bunu etmək üçün kodu 1 -ci addımdan 'HAVING COUNT (*) = 1' olaraq yeniləməlisiniz. Buna uyğun olaraq masaların adını dəyişdirin.


Sonrakı, bir dublikat sətir cədvəli yaradın və üzərində nöqtələri olan girişləri silin.

Line_dup SEÇMƏ OLARAK CREATE * FROM line_table; - Paylaşılan girişləri silin (satır bölüşdürmək üçün sütun_iddən ayırın) və ya bölünmüş sütun_idindən sütun_idini silin;


Son olaraqistifadə edərək üç masanıza qoşulunUNION ALL:

TABLO yaradın line_filtered SE_Te_geom FROM split_single UNION ALL SELECT the_geom FROM split_multi UNION ALL SELECT the_geom FROM line_dup;

BAM!


Videoya baxın: Using QGIS with PostGIS (Oktyabr 2021).