Zapytanie SQL, aby mieć pełną funkcję geojson z PostGIS?

35

Chciałbym uzyskać funkcję geojson z właściwościami z PostGIS. Znalazłem przykład, że mam kolekcję funkcji, ale nie mogę sprawić, by działała tylko dla funkcji.

SELECT row_to_json(fc)
 FROM ( SELECT 'FeatureCollection' As type, array_to_json(array_agg(f)) As features
 FROM (SELECT 'Feature' As type
    , ST_AsGeoJSON(lg.geog)::json As geometry
    , row_to_json(lp) As properties
   FROM locations As lg 
         INNER JOIN (SELECT loc_id, loc_name FROM locations) As lp 
       ON lg.loc_id = lp.loc_id  ) As f )  As fc;

do tej pory próbowałem zmodyfikować zapytanie dotyczące kolekcji funkcji w przykładzie. ale dane wyjściowe są nieprawidłowe.

Poniżej radaru
źródło
Musiałem zrobić test koncepcyjny dla innej aplikacji, więc przygotowałem to repozytorium, które częściowo korzysta z odpowiedzi tutaj. Mam nadzieję, że pomaga w rozpoczęciu pracy z tym materiałem - znajdź go tutaj: pg-us-census-poc
zak

Odpowiedzi:

59

Można to zrobić nieco prościej json_build_objectw PostgreSQL 9.4+, który pozwala zbudować JSON poprzez dostarczanie przemiennych argumentów klucz / wartość. Na przykład:

SELECT json_build_object(
    'type',       'Feature',
    'id',         gid,
    'geometry',   ST_AsGeoJSON(geom)::json,
    'properties', json_build_object(
        'feat_type', feat_type,
        'feat_area', ST_Area(geom)::geography
     )
 )
 FROM input_table;

Sprawa staje się jeszcze lepsza w PostgreSQL 9.5+, gdzie dodano nowe operatory dla jsonbtypu danych ( docs ). Ułatwia to ustawienie obiektu „właściwości”, który zawiera wszystko oprócz identyfikatora i geometrii .

SELECT jsonb_build_object(
    'type',       'Feature',
    'id',         gid,
    'geometry',   ST_AsGeoJSON(geom)::jsonb,
    'properties', to_jsonb(row) - 'gid' - 'geom'
) FROM (SELECT * FROM input_table) row;

Chcesz zrobić FeatureCollection? Po prostu to wszystko jsonb_agg:

SELECT jsonb_build_object(
    'type',     'FeatureCollection',
    'features', jsonb_agg(features.feature)
)
FROM (
  SELECT jsonb_build_object(
    'type',       'Feature',
    'id',         gid,
    'geometry',   ST_AsGeoJSON(geom)::jsonb,
    'properties', to_jsonb(inputs) - 'gid' - 'geom'
  ) AS feature
  FROM (SELECT * FROM input_table) inputs) features;
dbaston
źródło
1
Już sama ta funkcjonalność zmusza mnie do aktualizacji rano z wersji 9.3.5 do 9.5.3. Gdyby to było tak proste jak regexp_replace(current_setting('server_version'),'(\d)\.(\d)\.(\d)','\1.\3.\2')...
GT.
1
OK - wszystkie zaktualizowane teraz (chociaż nie można uruchomić 9.5.3 do działania jako usługa Windoze). W każdym razie ... jedna mała rzecz w podanym przykładzie - druga json_build_objectma dwukropki zamiast przecinków.
GT.
nie działa dla mnie na pg v9.6
Pak
2
Dla kompletności prawdopodobne jest, że wierzchołki geometrii nie są w prawidłowej kolejności dla ścisłego geojsona (reguła praworęczna), aby to naprawić, możemy zmienić kolejność wierzchołków w geomie za pomocą ST_ForcePolygonCCW - postgis.net/docs/manual-dev/ ST_ForcePolygonCCW.html
chrismarx
1
@chrismarx jest to dobry punkt i podnosi kwestię, czy ST_AsGeoJSONfunkcja PostGIS powinna zostać zmodyfikowana, aby samodzielnie korygować orientację.
dbaston
21

Tej odpowiedzi można użyć w wersji PostgreSQL wcześniejszej niż 9.4. Użyj odpowiedzi dbaston dla PostgreSQL 9.4+

Zapytanie jest następujące: (gdzie 'GEOM'jest pole geometrii, idpole do uwzględnienia we właściwościach json, shapefile_featurenazwa tabeli i 489445identyfikator żądanej funkcji)

SELECT row_to_json(f) As feature \
     FROM (SELECT 'Feature' As type \
     , ST_AsGeoJSON('GEOM')::json As geometry \
     , row_to_json((SELECT l FROM (SELECT id AS feat_id) As l)) As properties \
     FROM shapefile_feature As l WHERE l.id = 489445) As f;

wydajność:

{
   "geometry":{
      "type":"MultiPolygon",
      "coordinates":[
         [
            [
               [
                  -309443.24253826,
                  388111.579584133
               ],
               [
                  -134666.391073443,
                  239616.414560895
               ],
               [
                  -308616.222736376,
                  238788.813082666
               ],
               [
                  -309443.24253826,
                  388111.579584133
               ]
            ]
         ]
      ]
   },
   "type":"Feature",
   "properties":{
      "feat_id":489445
   }
}
Poniżej radaru
źródło
skoro przeniosłeś to z treści pytania na odpowiedź, czy to oznacza, że ​​zapytanie i wynik działają teraz poprawnie? Po uruchomieniu przez GeoJSONLint nadal nie wydaje się, aby dawał prawidłowe dane wyjściowe.
RyanDalton,
1
Świetnie, to ma sens. Chyba po prostu nie spojrzałem wystarczająco uważnie. Oznacz to jako „Zaakceptowane”, gdy GIS.SE pozwoli mu zamknąć pytanie. Dzięki!
RyanDalton
1
Nie tylko GeoJSONLint nie akceptuje pojedynczych cytatów. JSON także formalnie nie rozpoznaje pojedynczych cudzysłowów. Jeśli jakiś parser je rozpozna, jest to niestandardowe rozszerzenie i prawdopodobnie najlepiej go unikać.
jpmc26
@BelowtheRadar That, a dictnie JSON. To są bardzo różne rzeczy. JSON jest ciągiem. Zawsze. Jest to format tekstowy, podobnie jak XML jest tylko formatem tekstowym. A dictjest obiektem w pamięci.
jpmc26,
5

Wystarczy drobna korekta odpowiedzi dbaston (chciałbym skomentować, ale nie mam punktów) Musisz rzutować dane wyjściowe ST_AsGeoJSON jako json (rzecz ::json):

SELECT json_build_object(
  'type',       'Feature',
  'id',         gid,
  'geometry',   ST_AsGeoJSON(geom)::json,
  'properties', json_build_object(
    'feat_type', feat_type,
    'feat_area', ST_Area(geom)::geography
  )
)
FROM input_table;

W przeciwnym razie element geometrii będzie ciągiem. To nie jest poprawny GeoJSON

JavPL
źródło
4

@ dbaston za odpowiedź została zmodyfikowana ostatnio przez @John Powell aka Barçy, a to powoduje nieprawidłowe geojsons na mój koniec. Po zmodyfikowaniu agregacja funkcji zwraca każdą funkcję zagnieżdżoną w obiekcie json, co jest nieprawidłowe.

Nie mam reputacji, aby komentować bezpośrednio odpowiedź, ale końcowy plik jsonb_agg powinien znajdować się w kolumnie „funkcja”, a nie w podzapytaniu „funkcje”. Agregowanie nazwy kolumny (lub „features.feature”, jeśli uważasz, że jest to bardziej odpowiednie) umieszcza każdy element prosto w tablicy „features” po agregacji, co jest właściwą drogą.

Tak więc działa następująca, podobna do odpowiedzi @ dbaston, jak to było jeszcze kilka tygodni temu (plus korekta @Jonh Powell do nazewnictwa podzapytań):

SELECT jsonb_build_object(
  'type',     'FeatureCollection',
  'features', jsonb_agg(feature)
)
FROM (
  SELECT jsonb_build_object(
    'type',       'Feature',
    'id',         gid,
    'geometry',   ST_AsGeoJSON(geom)::jsonb,
    'properties', to_jsonb(inputs) - 'gid' - 'geom'
  ) AS feature
  FROM (
    SELECT * FROM input_table
  ) inputs
) features;
jufaua
źródło