Consulta SQL para ter um recurso geojson completo do PostGIS?

35

Gostaria de obter um recurso geojson com propriedades do PostGIS. Encontrei um exemplo para ter uma coleção de recursos, mas não posso fazê-lo funcionar apenas para um recurso.

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;

até agora, tentei modificar a consulta de coleção de recursos do exemplo. mas a saída não é válida.

Abaixo do radar
fonte
Eu tive que fazer uma prova de conceito para outro aplicativo, então monte este repositório que, em parte, usa as respostas daqui. Espero que ajude a começar com esse material - encontre-o aqui: pg-us-census-poc
zak 10/10

Respostas:

59

Isso pode ser feito de maneira um pouco mais simples json_build_objectno PostgreSQL 9.4+, que permite criar um JSON fornecendo argumentos alternativos de chave / valor. Por exemplo:

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;

As coisas ficam ainda melhores no PostgreSQL 9.5+, onde alguns novos operadores são adicionados ao jsonbtipo de dados ( docs ). Isso facilita a configuração de um objeto "propriedades" que contém tudo, exceto o ID e a geometria .

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;

Deseja fazer uma FeatureCollection? Basta encerrar tudo com 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
fonte
1
Somente essa funcionalidade me fez tentar atualizar da 9.3.5 para a 9.5.3 hoje de manhã. Se ao menos fosse tão simples quanto regexp_replace(current_setting('server_version'),'(\d)\.(\d)\.(\d)','\1.\3.\2')...
GT.
1
OK - tudo atualizado agora (embora não seja possível executar o 9.5.3 como um serviço Windoze). De qualquer forma ... uma coisinha sobre o exemplo dado - o segundo json_build_objecttem dois pontos em vez de vírgulas.
GT.
não funciona para mim no v9.6 pg
Pak
2
Para completar, é provável que os vértices da geometria não estejam na ordem correta para o geojson estrito (a regra do destro). Para corrigir isso, podemos reordenar os vértices no geom com ST_ForcePolygonCCW - postgis.net/docs/manual-dev/ ST_ForcePolygonCCW.html
chrismarx
1
@chrismarx este é um bom argumento e levanta a questão de saber se a ST_AsGeoJSONfunção do PostGIS deve ser modificada para corrigir a orientação por conta própria.
dbaston
21

Esta resposta pode ser usada na versão PostgreSQL anterior à 9.4. Use a resposta do dbaston para o PostgreSQL 9.4+

A consulta é a seguinte: (onde 'GEOM'está o campo de geometria, ido campo a ser incluído nas propriedades json, shapefile_featureo nome da tabela e 489445o ID do recurso desejado)

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;

saída:

{
   "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
   }
}
Abaixo do radar
fonte
desde que você moveu isso do corpo da sua pergunta para a resposta, isso significa que essa consulta e o resultado agora estão funcionando corretamente? Executando isso através do GeoJSONLint , ele ainda não parece estar fornecendo saída válida.
RyanDalton
1
Ótimo, isso faz sentido. Acho que simplesmente não olhei de perto o suficiente. Sinta-se à vontade para marcar isso como "Aceito" quando o GIS.SE permitir que ela encerre a pergunta. Obrigado!
precisa saber é o seguinte
1
Não é apenas o GeoJSONLint que não aceita aspas simples. O JSON também não reconhece formalmente aspas simples. Se algum analisador os reconhecer, é uma extensão fora do padrão e provavelmente é melhor evitar.
precisa saber é o seguinte
@BelowtheRadar Isso é um dict, não JSON. São coisas muito diferentes. JSON é uma sequência. Sempre. É um formato de texto, da mesma forma que XML é apenas um formato de texto. A dicté um objeto na memória.
precisa saber é
5

Apenas uma ligeira correção na resposta do dbaston (gostaria de comentar, mas não tenho pontos). Você precisa converter a saída de ST_AsGeoJSON como json (the ::jsonthingie):

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;

Caso contrário, o membro da geometria será uma sequência. GeoJSON inválido

JavPL
fonte
4

A resposta do @ dbaston foi modificada ultimamente por @ John Powell, também conhecido como Barça, e produz geojsons inválidos para mim. Conforme modificada, a agregação nos recursos retorna cada recurso aninhado dentro de um objeto json, que é inválido.

Não tenho reputação de comentar diretamente a resposta, mas o jsonb_agg final deve estar na coluna "feature" e não na subconsulta "features". A agregação no nome da coluna (ou "features.feature", se você achar melhor) coloca todos os elementos diretamente na matriz "features" após a agregação, que é o caminho certo a seguir.

Portanto, o seguinte, que é bastante semelhante à resposta do @ dbaston, como era até algumas semanas atrás (mais a correção de @ Jonh Powell para nomear subconsultas) funciona:

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
fonte