Skip to content

geoalchemy2 support #140

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wahello opened this issue Jul 4, 2018 · 6 comments
Open

geoalchemy2 support #140

wahello opened this issue Jul 4, 2018 · 6 comments

Comments

@wahello
Copy link

wahello commented Jul 4, 2018

Help! geoalchemy2 support!


class CoordinateMixin():
    location = db.Column('location', Geography(geometry_type='POINT' ,srid=4326, spatial_index=True, dimension=2), doc='gps coordinate')


Exception: Don't know how to convert the SQLAlchemy field user.location (<class 'sqlalchemy.sql.schema.Column'>)

I'v already add the following code in schema file.


from geoalchemy2 import Geography
from graphene_sqlalchemy.converter import get_column_doc, convert_sqlalchemy_type
from app.graphql.extesions import CoordinateJSON

@convert_sqlalchemy_type.register(Geography)
def convert_column_to_coordinatejson(type, column, registry=None):
    return graphene.Field(CoordinateJSON, description=get_column_doc(column))

schema = graphene.Schema(query=RootQuery, types=types, mutation=MyMutation)


@neebj
Copy link

neebj commented Jan 11, 2019

Please let me know how to import CoordinateJSON, also can anyone provide solution for how it works.

@flewellyn
Copy link

@keyeMyria What you have won't work, because you haven't told GraphQL how the underlying type should be represented.

Here's a way to do it, with geometries instead of geographies, but it should be applicable to geography type with minor changes. This implementation assumes that you want to represent the geometry or geography as its WKT; you can sub the CoordinateJSON output instead, as you prefer.

I imported graphene_sqlalchemy like so, for easier typing:

import graphene_sqlalchemy as gsqa

First, you need to define a scalar type that will represent the geometry:

class Geometry_WKT(graphene.Scalar):
     '''Geometry WKT custom type.'''

     @staticmethod
     def serialize(geom):
         return engine.scalar(geom.ST_AsText())

     @staticmethod
     def parse_literal(node):
         if isinstance(node, graphql.language.ast.StringValue):
             return engine.scalar(geoalchemy2.func.ST_GeomFromText(node.value))

     @staticmethod
     def parse_value(value):
         return engine.scalar(geoalchemy2.func.ST_GeomFromText(value))

Then, you define the conversion using the convert_sqlalchemy_type decorator:

@gsqa.converter.convert_sqlalchemy_type.register(geoalchemy2.Geometry)
def _convert_geometry(thetype, column, registry=None):
    return Geometry_WKT(description=gsqa.converter.get_column_doc(column),
            required=not(gsqa.converter.is_column_nullable(column))

That's it! Your Geometry outputs will now be represented as WKT. You can do the same with the Geography type, instead. And, if you wish to use CoordinateJSON, just set up the CoordinateJSON conversion in the scalar type's methods, instead of using ST_AsText() or ST_GeomFromText().

@cglacet
Copy link

cglacet commented Apr 7, 2021

Strange, I have a piece of code that used to work but doesn't anymore, I wonder what changed here (I also used Geography). I never wrote any code to do conversion.

@jose-lpa
Copy link

Strange, I have a piece of code that used to work but doesn't anymore, I wonder what changed here (I also used Geography). I never wrote any code to do conversion.

I just landed here today with the same issue: something that was working before doesn't work any more. In my case, all this mess raises when I install a 3rd party package sqlalchemy-utils. And I mean when I install, even when not using it at all in the codebase, it blows up with this graphene-sqlalchemy errors.

@dpitch40
Copy link

dpitch40 commented Jun 23, 2021

I am trying the code suggested by @flewellyn and am getting a long exception traceback ending with

File ".../.tox/py37/lib/python3.7/site-packages/graphql/type/typemap.py", line 87, in reducer
    if type_.name in map_:
AttributeError: 'Geometry_WKT' object has no attribute 'name'

If I try adding a name attribute to the Geometry_WKT class, I get a similarly long error traceback ending with

  File ".../.tox/py37/lib/python3.7/site-packages/graphql/type/typemap.py", line 127, in reducer
    type_, field_name, field.type
AssertionError: LocationObject.location field type must be Output Type but got: <Geometry_WKT object at 0x7ff6829a7390>.

I am not familiar enough with the inner workings of Graphene/GraphQL to know what to do about either of these tracebacks. Can anyone confirm if this approach still works for them? Also, what is engine supposed to be?

@fvallee-bnx
Copy link

If anyone still interested I figured out a way to get the geometry content using shapely-geojson (0.0.1) and graphene-sqlalchemy (2.3.0)

import graphene_sqlalchemy as gsqa
from shapely_geojson import dumps
from geoalchemy2.shape import to_shape


class Geometry(graphene.ObjectType):
    geojson = graphene.Field(graphene.String)
    srid = graphene.Int(required=True)

    def resolve_srid(self, info):
        return self.srid

    def resolve_geojson(self, info):
        return dumps(to_shape(self))


@gsqa.converter.convert_sqlalchemy_type.register(geoalchemy2.Geometry)
def _convert_geometry(thetype, column, registry=None):
    return Geometry


class Address(SQLAlchemyObjectType):
    class Meta:
        interfaces = (relay.Node, )
        model = schemas.Address # a schema with geometry = Column(Geometry(srid=4326), nullable=True)

Result is like:


            "geometry": {
              "geojson": "{\"type\": \"Point\", \"coordinates\": [5.605069, 47.440551]}",
              "srid": 4326
            }

(as far as I can tell the geojson string is matching posgraphile output).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants