Consider the following SQLAalchemy / GeoAlchemy2 ORM with a geometry field:
from geoalchemy2 import Geometry, WKTElement
class Item(Base):
__tablename__ = 'item'
id = Column(Integer, primary_key=True)
...
geom = Column(Geometry(geometry_type='POINTZ', srid=4326))
When I update an item in the PostgreSQL shell:
UPDATE item SET geom = st_geomFromText('POINT(2 3 0)', 4326) WHERE id = 5;
Fetching the field:
items = session.query(Item).\
filter(Item.id == 3)
for item in items:
print item.geom
Gives:
01e9030000000000000000004000000000000008400000000000000000
This isn't a proper WKB - at least, it does not parse with Shapely's loads.
How do I get the lat/lon of the geom field?
Fetching the lat, lon via ST_X and ST_Y might not be the most elegant approach, but it works:
from sqlalchemy import func
items = session.query(
Item,
func.st_y(Item.geom),
func.st_x(Item.geom)
).filter(Item.id == 3)
for item in items:
print(item.geom)
Gives:
(<Item 3>, 3.0, 2.0)
geoalchemy2 to_shape function to convert a :class:geoalchemy2.types.SpatialElement
to a Shapely geometry.
in Item class :
from geoalchemy2.shape import to_shape
point = to_shape(self.geo)
return {
'latitude': point.y,
'longitude': point.x
}
Point type syntax is
Point ( Lat, Long)
So basic from mosi_kha's answer, return should be
from geoalchemy2.shape import to_shape
point = to_shape(self.geo)
return {
'latitude': point.x,
'longitude': point.y
}
Related
Since few days I am wondering how to make my Flask app return valid GeoJSON, here is what I got so far:
models.py
class Building(base):
__tablename__ = 'buildings'
id = Column(Integer, primary_key=True)
district = Column(Unicode)
address = Column(Unicode)
name = Column(Unicode)
building_type = Column(Unicode)
mpoly = Column(Geometry('MULTIPOLYGON'))
building = relationship("User")
# this part is done as answered here: https://stackoverflow.com/questions/41684512/cant-transform-geometry-to-geojson
def building_to_dict(self):
return mapping(shapely.wkb.loads(str(self.mpoly), True))
def __str__(self):
d = dict()
d["id"] = self.id
d["district"] = self.district
d["address"] = self.address
d["name"] = self.name
d["building_type"] = self.building_type
d["mpoly"] = self.building_to_dict()
return shapely.dumps(d)
Now at main file I have following routing:
app.py
#app.route('/geojson')
def get_json():
features = session.query(Building.mpoly.ST_AsGeoJSON()).all()
return jsonify(features)
And here are my two problems:
1)
Returned JSON-like response that looks like following:
"{\"type\":\"MultiPolygon\",\"coordinates\":[[[[16.8933137,52.471446],...,]]]}"
Is not proper GeoJSON.
Features variable before jsonify looks this way:
[('{"type":"MultiPolygon","coordinates":[[[[16.914159616,52.473822807],...,]]]}',)]
2)
How my GeoAlchemy query should look like, to return not just geometry field, but others as well?
Any kind of hints or helps highly appreciated, thanks in advance!
You may use marshmallow-sqlalchemy for creating json like responses.
create
schemas.py
#!schemas.py
from marshmallow import fields
from marshmallow_sqlalchemy import ModelSchema
from app.models import Building
from geoalchemy.shape import to_shape
class BuildingSchema(ModelSchema):
id = fields.Integer()
district = fileds.Unicode()
address = fileds.Unicode()
name = fields.Unicode()
building_type = fields.Unicode()
mpoly = fileds.Method("geom_to_json")
#staticmethod
def geom_to_json(obj):
mpoly = to_shape(obj.mpoly)
return {
lat: mpoly.y,
lon: mpoly.x,
#and so on ...
}
class Meta:
model = Building
exclude = ("mpoly")
bulding_schema = BuildingSchema(many=True)
after this you can use it in your views(routes)
from app.schemas import building_schema
#app.route('/geojson')
def json():
buildings = Buildings.query.all()
response = building_schema.dumps(buildings)
return response
I am try to create a way to narrow down posts on a django app by location & radius. The way I am trying to do this is:
Use 3rd party (OpenCage) API to convert location to lat / lng
Gather the JSON response, filter for the most correct one (first response in US)
Use the response lat & lng to filter listings via GIS
What I have done is successfully connect to the API, and get a lat & lng back, however I am having to filter the response in my templates rather than in my models. I have also found the following which appears to be the solution needed for narrowing results in a radius:
from django.contrib.gis.geos import Point
from django.contrib.gis.measure import Distance
lat = 52.5
lng = 1.0
radius = 10
point = Point(lng, lat)
Place.objects.filter(location__distance_lt=(point, Distance(km=radius)))
What I need to solve, and am hoping for your help with is how do I
filter the api responses and only get what I need from them (components country, geometry lat, geometry lng)
gather the lat & lng for use in the above code?
views.py
from posts import services
def search(request):
queryset_list = posts.objects.order_by('id')
if 'location' in request.GET:
q = request.GET['location']
locations_list = services.get_location(q)
context = {
'posts': queryset_list,
'locations_list': locations_list
}
return render(request, 'posts/search.html', context)
services.py
import requests
def get_location(location):
url = 'https://api.opencagedata.com/geocode/v1/json'
params = {'q': location, 'key': '###', 'language': 'en', 'pretty': 1}
r = requests.get(url, params=params)
locations = r.json()
return locations
I think you can try like this(Based on demo JSON response provided by OpenCage):
import requests
from django.db.models import Q
def get_location(location):
url = 'https://api.opencagedata.com/geocode/v1/json'
params = {'q': location, 'key': '###', 'language': 'en', 'pretty': 1}
r = requests.get(url, params=params)
locations = r.json()
filter_query = Q()
radius = 10
for item in locations.get('results', []):
geo = item.get('geometry')
lat = geo.get('lat')
lng = geo.get('lng')
point = Point(lng, lat)
filter_query |= Q(location__distance_lt=(point, Distance(km=radius)))
return Place.objects.filter(filter_query)
Currently, this shows 2 variables on the screen. Whenever that variable changes, it is also shown on the GUI.
I would like to bind array indexes in a similar way. For example: x[1],
and whenever x[1] changes, so does the value shows on GUI
EDIT: array x doesn't update, I also don't want the whole array but each index on a new line.
import groovy.swing.SwingBuilder
import groovy.beans.Bindable
#Bindable
class controller{
boolean stop = false;
String status = "RUNNING";
String name = "";
String[] x= new String[5];
}
def ctrl = new controller();
def UI = new SwingBuilder().edt {
frame(title: 'Menu', size: [220, 150], locationRelativeTo: null, show: true, alwaysOnTop: true){
gridLayout(cols:1, rows: 5)
label(text: bind(source: ctrl, sourceProperty: 'status', converter: { v -> v? "Status: $v": ''}))
label(text: bind(source: ctrl, sourceProperty: 'name', converter: { v -> v? "$v": ''}))
label(text: bind(source: ctrl, sourceProperty: 'x', converter: { v -> v? "$v": ''}))
}
}
for(i = 0; i < 5 ; i++){
sleep(500);
ctrl.name+= "Hi there ";
ctrl.x[i] = "T"+i;
}
ctrl.status = "DONE";
sleep(1000);
UI.dispose();
You can't observe changes on an array using #Bindable in this way. The AST transformation is a convenience for registering methods that rely on PropertyChange and PropertyChangeListener.
Changes made to name and status properties work because you're replacing their value, thus a PropertyChangeEvent is triggered for every change. However for the x property you're not changing the array reference, but the elements within the array.
You have two options to make this work:
either observe changes on the index itself (add an Integer property to the controller class).
use an ObservableList instead of an array.
Personally I prefer option #1. You will have to use a converter such as
label(text: bind(source: ctrl, sourceProperty: 'index', converter: { v -> v != -1? ctrl.x[v]: ''}))
Notice the use of -1 to check for a selected index as a value of 0 would make the previous check (v?) fail due to Groovy Truth.
The controller class can be updated as follows
#Bindable
class controller{
boolean stop = false;
String status = "RUNNING";
String name = "";
String[] x= new String[5];
int index = -1;
}
I have a problem! need to get a json object that already with an id already set that is not yet saved. but when trying to create the object id is null. And it is only set when call the save method generating different id's.
ex:
objetoJson = {id: "123413215123", name: "Leonan Teixeira", address: {id: "12345", city: "Victory", state: "Bahia"}}
def person = new Person (objetoJson)
end state of the object:
Person.id = null
Person.Address = null
but I need the id are stored.
the same happens if I do
person.properties = objetoJson
My classes are mapped with id 'uuid'
String id;
static mapping = {
id generator: 'uuid'
}
Solution
add a bindable:true to the constraints
class MyDomain {
static constraints = {
// allow binding of "id" attribute (e.g. in constructor or url parameters)
id bindable: true
}
}
http://www.redcube.de/assigning-id-domain-objects-grails-via-constructor/
Try this:
def jsonSlurper = new JsonSlurper()
def dataPerson = jsonSlurper.parseText('{ "id":"123456789", "nome": "Leonardo Meurer", "age":"29" }')
def person = new Person (dataPerson)
I have been using slick 2 as the dbms for my play app, though using play-slick plugin rather then slick independently, and added the tototoshi plugins as well, dependencies are:
"joda-time" % "joda-time" % "2.4"
"org.joda" % "joda-convert" % "1.6"
"com.github.tototoshi" %% "slick-joda-mapper" % "1.2.0"
"com.typesafe.play" %% "play-slick" % "0.6.1"
"com.typesafe.slick" %% "slick" % "2.0.3"
And the case class and projection are :
//Slick imports used
import play.api.db.slick.Config.driver.simple._
import play.api.db.slick.DB
import scala.slick.lifted.ProvenShape
import com.github.tototoshi.slick.MySQLJodaSupport._
case class Design(
var id: Int,
var imageName: String,
var title: String,
var creatorID: Int,
var flagged: Boolean,
var modifiedTimestamp: Instant,
var createdTimestamp: Instant) {
def this() = this(0, "", "", 0, false, DateTime.now.toInstant, DateTime.now.toInstant)
}
class DesignProjection(tag: Tag) extends Table[Design](tag, "designs_47") {
def id: Column[Int] = column[Int]("id", O.PrimaryKey, O.AutoInc)
def imageName: Column[String] = column[String]("des_image_name")
def title: Column[String] = column[String]("des_title")
def creatorID: Column[Int] = column[Int]("des_creator_id")
def flagged: Column[Boolean] = column[Boolean]("des_flagged_link")
def modifiedTimestamp: Column[Instant] = column[Instant]("tt_modified_timestamp")
def createdTimestamp: Column[Instant] = column[Instant]("tt_tweeted_timestamp")
def * : ProvenShape[Design] = (id, imageName, title, creatorID, flagged, modifiedTimestamp, createdTimestamp) <> (
((Design.apply _): (Int, String, String, Int, Boolean, Instant, Instant) => Design).tupled,
Design.unapply)
}
And when I try to list all rows using method :
def list: List[Design] = {
println("Start Listing")
val result = DB.withSession { implicit session =>
val res = designProjection.list <-error here
res
}
convertListResultSet(result)
}
I get [RuntimeException: java.lang.AbstractMethodError], well I am more then sure its cause of the DataTime class, but I really don't know whats going wrong. I have used the tototoshi plugins as well. All other cases where DateTime are considered, they are working fine.
Any help or pointers are really welcome. Thank you
It is a known issue.
https://github.com/tototoshi/slick-joda-mapper/issues/19
Could you try slick-joda-mapper 1.1.0 ?