PostgREST / postgrest

REST API for any Postgres database
https://postgrest.org
MIT License
22.68k stars 1.01k forks source link

PostGIS operators #223

Open jney opened 9 years ago

jney commented 9 years ago

just wondering if there is any postgis support planned ? or already working ?

thanks

begriffs commented 9 years ago

Nothing yet, but sounds like a good idea.

Tell me more about how you would like to do spatial queries. What kind of interface would be convenient for you?

jney commented 8 years ago

to be totally honest i haven't really thought how it should look like.

a postgis query looks like :

SELECT region, ST_Union(the_geom)
FROM usa_states
GROUP BY region

the point is to handle sql "functions", is there a postgrest way to call AVG, SUM or such functions ?

begriffs commented 8 years ago

You can make a view which uses these functions and the view will be accessible to the client just like a table would be.

parauchf commented 8 years ago

I don't have an immediate need, but I can see a use case for some additional filter operators. E.g., I want to return all records with location within 500m of a point.

begriffs commented 8 years ago

We could add some of the geospatial operators as valid filters in the URL, although there are a few problems. One is that many of the operators use an ampersand which would need to be urlencoded. Another is that operators like <-> are functions and we haven't devised a way yet to express things like foo <-> bar < 20 in the URL filter. Maybe this leads back to one of the first reported issues, #114. A stored procedure could take a point argument and return a view of some table filtered by postgis.

begriffs commented 8 years ago

For now try making and calling a stored procedure to do what you need. http://postgrest.com/api/reading/#stored-procedures

chrismayer commented 8 years ago

I also have the opinion that it would be great thing to have PostGIS support in PostgREST. I also go along with @begriffs that a lot of things can be achieved by calling stored procedures, whereas some spatial filter operators (as suggested by @parauchf) would also be nice to have.

But I think it would be a great benefit to have a built in support for a (Web)GIS compatible output format such as GeoJSON. GeoJSON is a standardized format in the GIS world which is often used as a return value of HTTP calls in Webmapping applications. So maybe by setting the HTTP-header Accept to an according value might let the PostgREST call to a table return a GeoJSON FeatureCollection.

What do you think about this?

Some more hints on this: PostGIS has a function ST_AsGeoJSON, which might be useful for this. To pack a table with features in a GeoJSON FeatureCollection the following SQL could be used

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(my_geo_table.geom)::json As geometry
    , row_to_json((gid, code)) As properties
   FROM my_geo_table  ) As f )  As fc;
calebmer commented 8 years ago

@chrismayer I would like to see PostgREST support for this, but I don't think any potential contributors are interested in tackling this problem. So three questions:

  1. How big of a footprint does it leave on PostgREST core?
  2. Could this be implemented in a layer outside of PostgREST?
  3. Would you be willing to implement this? (Haskell isn't that bad to learn 😊)

One of the 6 REST principles is Layered System. If this support could be implemented in a layer that would be the best case scenario.

chrismayer commented 8 years ago

Hi @calebmer,

thanks for your sharing your thoughts.

How big of a footprint does it leave on PostgREST core?

This i cannot answer since I did not do any work in the PostgREST core. Maybe a core dev can tell us more here?

Could this be implemented in a layer outside of PostgREST?

I guess it would be possible to implement this outside of PostgREST core. So this would be a kind of extension / plugin / or similar, right? Maybe some hints by the pros how to achieve this would be very good.

Would you be willing to implement this?

I am not sure if I am the right guy for this. I am more a user than a dev of PostgREST and in general I haven't wrote a line of Haskel yet. I can support with my GIS / Geo knowledge.

gorbypark commented 7 years ago

I would also be very, very interested in GeoJSON/PostGIS support. I'd love to contribute but my Haskel skills are not up to snuff...is there any way I could offer a small bounty to any of the core devs for this feature? I was thinking of something pretty simple, such as a "selector" option to output as geojson http://url.com/items?geomcol=geom (where geom is the column with geometry). Even these core features would make it usable for me. Some of the more exotic PostGIS functions could be added later.

begriffs commented 7 years ago

One option to add a geojson column in the output is by using a computed column as described in https://postgrest.com/en/v0.4/api.html#computed-columns

I think it would output just what you want, but it would require writing similar computed column code for each table for which you want to include geojson.

begriffs commented 7 years ago

@gorbypark want to give the computed column approach a try and let me know how it works for you?

gorbypark commented 7 years ago

@begriffs I'll give it a go sometime this week, hopefully. My only concern is that I have a large, ever changing dataset (user created tables and columns). I will have to see if I can automate creating computed columns somehow...I haven't had a chance to even read the blurb about it yet, so that may be easier or harder than I'm imagining!

gorbypark commented 7 years ago

@begriffs So, I had a few minutes to put together a SQL query that outputs valid GeoJSON. I'm not quite sure how to route filters into the SQL query. Any hints? I just slapped $1, $2, $3 ($1= columns to select, $2 = column, $3 = query) as a guess...

CREATE FUNCTION geohydrants(select, filterColumn, filter) RETURNS text AS $$

SELECT jsonb_build_object(
    'type',     'FeatureCollection',
    'features', jsonb_agg(features)
)
FROM (
  SELECT jsonb_agg(jsonb_build_object(
    'type',       'Feature',
    'geometry',   ST_AsGeoJSON(ST_Transform(geom, 4326))::jsonb,
    'properties', to_json(row)
  )) FROM (SELECT $1 FROM schema.hydrants WHERE $2 = '$3' ) row) features;

$$ LANGUAGE SQL;
palmerj commented 7 years ago

Why has this issue been closed, it's still unresolved

palmerj commented 7 years ago

I think for PostgREST to be useful for PostGIS developers it needs to detect if any of the object output column datatypes are geometry and convert them to an appropriate text representation. e.g

Yes a stored DB functions could be used, but that means all database tables/views objects with geometries outputs will not work as expected. Also the user of the API can ask for CSV or JSON output it can't be nicely dealt with at the database level.

begriffs commented 7 years ago

@palmerj fair point, I'll re-open the issue.

Maybe the best way to focus the issue is to post SQL for creating a table with columns of the desired postgis types, include example data, and then the desired output when issuing a GET request on that table. I can then use that information to create a feature test and see how far off we are and how to fix it.

palmerj commented 7 years ago

Ok thanks, will do

palmerj commented 7 years ago

Also I also think (as commented above) it would be good to support some common geometry operators. At least intersects, if not all of the standard geometry binary predicates. e.g:

ST_Equals ST_Disjoint ST_Touches ST_Contains ST_Intersects ST_Within ST_Crosses ST_Overlaps

To support these functions I think at least ST_MakeEnvelope(xmin,ymin,xmax,ymax[,srs]) and ST_GeomFromText(wkt,[srs]) geometry constructor functions are required as well.

ruslantalpa commented 7 years ago

@palmerj @begriffs i don't think postgrest should be detecting any column type (for now) in order to format it in some way. The users have the tools to format them as needed. As i've said in other places, postgrest should not be used to expose tables directly, but views that draw data from those tables. Considering that, it means the users can have views with columns formatted as needed, independent of the column type in the table. You can even have one column in the view that can output different formats based on the csv/json thing using https://postgrest.com/en/v0.4/api.html#computed-columns because now in that function you can read the request headers and make a decision on how to format the output.

As for operators, i agree it would be a good idea to have some support but we need to think carefully about this since they are not really operators but functions and they can't be easily supported by our current code just by adding one line.

begriffs commented 7 years ago

Ruslan is right in that creating a computed column for a table is really easy -- you just make a function. Then the table gets a new column that you can select as part of the table. No extra views even needed for that. Now if you have a lot of geometry columns you would have to make a bunch of these functions to get the columns you need. Might get repetitive.

I am curious what the default output format is for geometry columns. Depending on what that is, we should ask if it is ever desirable. If the default serialization is garbage that nobody would consume then auto-converting it kind of makes sense. Same as if the default output format of an int column was a binary string, I would consider that a deficiency in postgrest.

palmerj commented 7 years ago

@palmerj @begriffs i don't think postgrest should be detecting any column type (for now) in order to format it in some way. The users have the tools to format them as needed.

This is true, but IHMO to approach of creating specific pl/PgSQL function per view/table that has to read a PostgreSQL GUC setting is cumbersome, as @begriffs said it's repetitive. One of looking at this issue is we have a database application that we might potential expose 100's of table and PgSQL functions APIs that return geometry types. Under this solution I would need to write and maintain 100's of functions like this:

CREATE FUNCTION geom_geojson(foo.bar) RETURNS text AS $$
  SELECT ST_AsGeoJSON($1.geometry);
$$ LANGUAGE SQL;

As i've said in other places, postgrest should not be used to expose tables directly, but views that draw data from those tables. Considering that, it means the users can have views with columns formatted as needed, independent of the column type in the table.

Ok I agree with this, especially when dealing with versioning an API. BTW I don't see any documentation about best approaches for handling versioning of the API. Can you point me in the right place? This seems to be out of date https://github.com/begriffs/postgrest/wiki/API-Versioning and I can't find anything in the official docs.

You can even have one column in the view that can output different formats based on the csv/json thing using https://postgrest.com/en/v0.4/api.html#computed-columns because now in that function you can read the request headers and make a decision on how to format the output.

Yes this would at mean that only one function would be required per table if CSV and GeoJSON was required. For this functionality I assume you are talking about this recent merged PR which is not part of the current 0.4 release? https://github.com/begriffs/postgrest/pull/849. It would be nice if there were some docs for how to use this via current_setting() because I'm not sure what GUC parameter I need to use.

treuherz commented 7 years ago

The canonical form returned by PostGIS is a hex representation of EWKB, which is a superset of the OGC-standard Well-Known Binary. They recommend not relying on that feature, though, as it may change if the OGC changes the standard to support 3D geometries (more info here https://postgis.net/docs/using_postgis_dbmanagement.html#EWKB_EWKT). WKB is widely parseable, but I don't know how an interface expecting WKB would deal with an EWKB input, so outputting that might not be desirable.

CMCDragonkai commented 6 years ago

I'd suggest considering raster support as well.

amaury1093 commented 6 years ago

@begriffs Ok for adding computed columns, pretty easy. However how would I do inserts?

This is an example query:

INSERT INTO countries ("id","the_geom") VALUES (DEFAULT,ST_GeomFromGeoJSON('{some_geo_json_object}'))

Would be nice to be able to POST /countries with a GeoJSON object.

ruslantalpa commented 6 years ago

before insert trigger NEW.the_geom = _GeomFromGeoJSON(NEW.the_geom)

rastermanden commented 5 years ago

Paul Ramsey made this post about returning af geojson Feature collection:

CREATE OR REPLACE FUNCTION rowjsonb_to_geojson(
  rowjsonb JSONB, 
  geom_column TEXT DEFAULT 'geom')
RETURNS TEXT AS 
$$
DECLARE 
 json_props jsonb;
 json_geom jsonb;
 json_type jsonb;
BEGIN
 IF NOT rowjsonb ? geom_column THEN
   RAISE EXCEPTION 'geometry column ''%'' is missing', geom_column;
 END IF;
 json_geom := ST_AsGeoJSON((rowjsonb ->> geom_column)::geometry)::jsonb;
 json_geom := jsonb_build_object('geometry', json_geom);
 json_props := jsonb_build_object('properties', rowjsonb - geom_column);
 json_type := jsonb_build_object('type', 'Feature');
 return (json_type || json_geom || json_props)::text;
END; 
$$ 
LANGUAGE 'plpgsql' IMMUTABLE STRICT;

--Voila! Now we can turn any relation into a proper GeoJSON “Feature” with just one(ish) function call.

SELECT rowjsonb_to_geojson(to_jsonb(mytable.*)) FROM mytable;  

Could we have a url param like &format=geojson

steve-chavez commented 5 years ago

@rastermanden I think we could be more in line with HTTP semantics and use Accept: application/vnd.geo+json to request geojson.

Supposing we manage to accommodate that function you shared as inline sql, how could we handle the name of the geometry column to choose? What if the table has more than one geometry column?

rastermanden commented 5 years ago

Accept: application/vnd.geo+json would most elegant but desktop GIS users will probably not have the option to set those headers in their environment.

Regarding geometry columns i'm not sure. Carto (Carotodb) use to rely on a fixed column name: "the_geom". Maybe not the best solution but has been working for years

leblancfg commented 4 years ago

Related project: https://github.com/tobinbradley/dirt-simple-postgis-http-api

steve-chavez commented 4 years ago

@leblancfg Hmm. I don't see how that's related to this project.

Perhaps the name of this issue leaves the impression that PostGIS doesn't work with PostgREST but in fact there are projects that have already been done with PostGIS/PostgREST.

This issue is more about facilities for PostGIS, namely operators and geosjon. Those needs can already be solved with some work using views/stored procedures/computed columns. Docs/tutorials are definitely missing but it's doable.

Not discarding the addition of those facilities but there has been some other pressing issues.

leblancfg commented 4 years ago

@steve-chavez please excuse my terse comment back there, that wasn't very actionable! :smile:

The project I linked to has got a nice out-of-the-box set of operations for spatial tables. Possible inspiration or starting point when ever this feature gets considered. Here's a still from a 2015 video:

Dirt-Simple PostGIS REST API

Stock operations are here: https://github.com/tobinbradley/dirt-simple-postgis-http-api/tree/master/routes

steve-chavez commented 4 years ago

There are many good ideas here. After we're done with this release, I'll focus on first class facilities for PostGIS.

For starters, we could detect if a table has a column of the geometry type and then allow application/vnd.geo+json on these tables to generate a query with ST_AsGeoJSON. Then I'll have a look at operators.

After that since we can output binary(bytea) we could see about raster or mvt support(postgrest could also be a tileserver? It would be really interesting to explore the possibilities).

DanMossa commented 2 years ago

I'm curious if there has been any updates or progress on this?

exotfboy commented 1 year ago

7 years passed. Any solutions for spatial operation? View/function is not always the best idea.