geodjango query format coordinates- convert lat lon , open street map - django

I have a polygon in my model citys but in my map for example
bogota has the coordinate -8243997.66798 , 517864.86656 -> open street maps; but i need make query with coordinates like (4.697857, -74.144554) -> google maps.
pnt = 'POINT(%d %d)'%(lon, lat)
zonas = ciudad.zona_set.filter(polygon__contains=pnt)
zonas is empty :/ ,
how i can convert lat and lon to standar coordinates in open street map , or how know the srid code for one input (lat,lon)
pnt = GEOSGeometry('POINT(-96.876369 29.905320)', srid=srid_from_lat_lon)
thanks

When making spatial queries, its good practice to pass a geometry that has a specified spatial reference system (srid). Like this, GeoDjango will automatically convert the input query geometry to the coordinate system of your table (the coordinate systems of your city model in your case).
In the first code example you gave, you do not specify an srid on the geometry, so pnt = 'POINT(%d %d)'%(lon, lat) does not have an srid. In this case, GeoDjango will assume the srid is the same for the input and the model data table. Which is not the case in your example, and that is why you dont get any matches.
So you will need to create you point with the correct SRID. If you get the coordinates from OSM, most likely the coordinates are in the Web Mercator projection, which has the srid 3857. This projection is often used in web mapping.
For this, you can use the EWKT format (which is essentially SRID + WKT) like so:
pnt = 'SRID=4326;POINT(-96.876369 29.90532)'
Or if you have the coordinates in Web Mercator Projection, the following should work:
pnt = 'SRID=3857;POINT(-8243997.66798 517864.86656)'
zonas = ciudad.zona_set.filter(polygon__contains=pnt)
Just for reference, here are a few examples on how to go back an forth between EWKT and GEOSGeometries:
So this (normal WKT, with srid specified on creation of geometry)
GEOSGeometry('POINT(-8243997.66798 517864.86656)', srid=3857)
is equivalent to this (srid contained in EWKT string):
GEOSGeometry('SRID=3857;POINT(-8243997.66798 517864.86656)')

Related

GeoDjango: How to create a circle anywhere on earth based on point and radius?

I have a similar question to this one. Using geodjango, I want to draw a circle on a map with a certain radius in km. However, the suggested solution
a) does not use km but instead degrees, and
b) becomes an oval further north or south.
Here is what I do:
from django.contrib.gis import geos
lat = 49.17
lng = -123.96
center = geos.Point(lng, lat)
radius = 0.01
circle = center.buffer(radius)
# And I then use folium to show a map on-screen:
map = folium.Map(
location=[lat,lng],
zoom_start=14,
attr="Mapbox"
)
folium.GeoJson(
circle.geojson,
name="geojson",
).add_to(map)
The result is this:
How can I
a) draw a circle that is always 3 km in radius, independent from the position on the globe, and
b) ensure this is a circle and not an oval at all latitudes?
Here is the Code
from django.contrib.gis import geos
import folium
lat = 49.17
lng = -123.96
center = geos.Point(x=lng, y=lat, srid=4326)
center.transform(3857) # Transform Projection to Web Mercator
radius = 3000 # now you can use meters
circle = center.buffer(radius)
circle.transform(4326) # Transform back to WGS84 to create geojson
# And I then use folium to show a map on-screen:
map = folium.Map(
location=[lat,lng],
zoom_start=14,
attr="Mapbox"
)
geojson = folium.GeoJson(
circle.geojson,
name="geojson",
)
geojson.add_to(map)
Explanation
This problem occurs due to Map Projections.
Lat/Long Coordinates are represented by the Map Projection WGS84. The Values are in degrees.
The map you see in folium has another map projection (Web Mercator). It tries to represent the world as a plane, which produces distortions to the north and south. The coordinate values are in meters.
On a globe your created circle would look completely round, but because folium uses another projection it gets distorted.
It is also important to know that every projection is represented by a number (EPSG Code). With this epsg codes, you can transform your coordinates from one projection into another.
Web Mercator -> EPSG 3857
WGS84 -> EPSG 4326
With my Code you now get a round circle in folium for Web Mercator, but be aware that it would look oval and distorted, when looking at it on a globe.
This is just a very easy explanation. You might have a look at Map Projections to better understand the problem.
This guide gives a good overview:
Map Projections
try this
folium.Circle(
radius=3000,
location=[lat,lng],
popup="Whatever name",
color="#3186cc",
fill=True,
fill_color="#3186cc",
).add_to(m)

How to do location based query with simple Django filter?

I've saved user's coordinates in the User model. Post model has latitude, longitude and radius field. Only the users in that vicinity(of Post) will be able to see that post. I don't know how to use filter() here so I used the following approach:
post=Posts.objects.all()
for a in post:
distance= geopy.distance.geodesic((lat1,lng1), (a.latitude, a.longitude)).km
print(distance)
if distance < a.radius:
p.append(a)
else:
continue
Here, lat1 and lng1 are the coordinates of current User. Suggest if there is any better way as this seems very inefficient.
Depending on your requirements, you could use a square instead of a circle. Pre-calculate the x-max, x-min, y-max and y-min boundaries for your square and then do a simple User.filter(lat__gt=lat_min, user.lng__gt=lng_min, user.lat__lt=lat_max ... lookup in the database.
In a past project, I used this:
def get_latlng_bounderies(lat, lng, distance):
"""
Return min/max lat/lng values for a distance around a latlng.
:lat:, :lng: the center of the area.
:distance: in km, the "radius" around the center point.
:returns: Two corner points of a square that countains the circle,
lat_min, lng_min, lat_max, lng_max.
"""
gc = great_circle(kilometers=distance)
p0 = gc.destination((lat, lng), 0)
p90 = gc.destination((lat, lng), 90)
p180 = gc.destination((lat, lng), 180)
p270 = gc.destination((lat, lng), 270)
ret = p180[0], p270[1], p0[0], p90[1]
return ret
Its not a circle, so its not exact around the "corners" of the square, but its much faster, because its a simple float comparision in the database.

mask and extract cell values from a vrt file?

I have raster data for built up areas around the globe with 40m resolution as vrt file, download data from here , and I am trying to crop the data by a mask and then extract color index value for each cell.
Note: another 2 files exist with the data: vrt.clr and vrt.ovr
Here is a sample of data:
view of vrt data in arcmap.
My question: why I am getting empty cells values when I crop by mask ?
I have tried the following:
extract by mask using arcmap toolbox
using gdal in python 2.7
import gdal
ds = gdal.Open('input.vrt')
ds = gdal.Translate('output.vrt', ds, projWin =
[80.439,5.341,81.048,4.686])
ds = None
I have also try to save the data as tif
Also, is there any way to read the color index value at given coordinates (x,y) after masking the data?
The data appears to be in the Pseudo Mercator projection (EPSG 3857). So therefore you should either specify the extent for projWin in that coordinate system, or add projWinSRS if you want to provide them in a different coordinate system.
Also, if you want gdal.Translate to output to a VRT file, you should add format='VRT. Because in your code snippet outputs to the default file format, which is GeoTIFF.
When i assume your coordinates are WGS84 (EPSG 4326), it defines a small region over the ocean south of Sri Lanka. That doesn't make much sense given the nature of the data.
If you want to read the array given by your coordinates you could use:
invrt = 'GHS_BUILT_LDSMT_GLOBE_R2015B_3857_38_v1_0.vrt'
outfile = '/vsimem/tmpfile'
ds = gdal.Translate(outfile, invrt, projWin=[80.439, 5.341, 81.048, 4.686], projWinSRS='EPSG:4326')
data = ds.ReadAsArray()
ds = None
gdal.Unlink(outfile)
The plotted array looks like:

Clip a defined region in IRIS cube using a shapefile

I handle iris cubes containing meteorological data (lon, lat, precipitation, temperature,...) and I am interested in calculating statistics in defined areas (for example a country).
This post explains how to crop the cube with a box (min lon, min lat, max lon, max lat) but I would like to go a step further and select a precise area using a shapefile.
This post explains that it is possible to crop an image using a shapefile associated to a mask, but I don't know how I can make it work for my iris cubes.
If somebody could give me an example or explain me how to do that it would be very useful.
PS: I am quite noobie with python
Having read the shapefile using e.g. Fiona something like this should work:
from shapely.geometry import MultiPoint
# Create a mask for the data
mask = np.ones(cube.shape, dtype=bool)
# Create a set of x,y points from the cube
x, y = np.meshgrid(cube.coord(axis='X').points, cube.coord(axis='Y').points)
lat_lon_points = np.vstack([x.flat, y.flat])
points = MultiPoint(lat_lon_points.T)
# Find all points within the region of interest (a Shapely geometry)
indices = [i for i, p in enumerate(points) if region.contains(p)]
mask[np.unravel_index(indices)] = False
# Then apply the mask
if isinstance(cube.data, np.ma.MaskedArray):
cube.data.mask &= mask
else:
cube.data = np.ma.masked_array(cube.data, mask)
This only works for 2D cubes, but just needs tweaking for higher dimensions so that the mask is only over the lat/lon dimensions.
I actually implemented this behaviour in CIS recently so that you can do cube.subset(shape=region) which might be easier for you.

GeoDjango: How to create a circle based on point and radius

I have the following (simplified) Model:
class Zone(gismodels.Model):
name = gismodels.CharField()
poly = gismodels.PolygonField()
I want to create and save a polygon that represents a circle, based upon a given point and radius.
The only way I can figure out how to achieve this, is to call the postgis ST_Buffer function using raw SQL. I'm really hoping that there is another way.
Is it possible to access the GEOS buffer methods?
Yes, it is possible to use the geos buffer method:
>>> from django.contrib.gis import geos
>>> center = geos.Point(5, 5)
>>> radius = 2
>>> circle = center.buffer(radius)
>>> circle
<Polygon object at 0x1029d8370>
The radius here is in the same units as the coordinates of the points. This will work for some coordinate systems like UTM, but not as well for others.
Also, while this is appropriate for constructing a circular geometry, the PostGIS documentation notes that for doing radius searches ST_DWithin is more efficient.
I spent a ridiculous amount of time trying to get this working. Since this is the number one google search result, here's what worked for me:
radius_km = radius*1.609 # convert miles to km
point = target.geolocation # a django PointField using SRID 4326
# re-project point to a flat coordinate system
# so we can use meters instead of degrees below,
# AND get an actual circle instead of oval
point.transform(6347)
poly = point.buffer(radius_km*1000) # get a circular polygon from radius
poly.transform(4326)# re-project the resulting polygon back
Bonus: If you're doing this so you can get a circle on a google static map, grab polyline:
import polyline
import ast
geo = ast.literal_eval(poly.geojson) # turn the text into a dict
points = geo['coordinates'][0]
pl = polyline.encode(points, geojson=True, precision=5) # make a polyline out of the polygon for google
map_url += '&path=color:0x00000000%7Cfillcolor:0x0000AA33%7Cweight:1%7Cenc:' + pl