I want my users to query two slugs fields (latitude, longitude) and then the 2 slug fields get compared to find nearest distance within 1.5km radius and display the api according to the nearest safehouses.
For example: when the users add latitude, longitude in their query,
www.example.com/safeplace/?find=-37.8770,145.0442
This will show the nearest safeplaces within 1.5km
Here is my function
def distance(lat1, long1, lat2, long2):
R = 6371 # Earth Radius in Km
dLat = math.radians(lat2 - lat1) # Convert Degrees 2 Radians
dLong = math.radians(long2 - long1)
lat1 = math.radians(lat1)
lat2 = math.radians(lat2)
a = math.sin(dLat/2) * math.sin(dLat/2) + math.sin(dLong/2) *
math.sin(dLong/2) * math.cos(lat1) * math.cos(lat2)
c = 2 * math.atan2(math.sqrt(a), math.sqrt(1-a))
d = R * c
return d
Here is my model
class Safeplace(models.Model):
establishment = models.CharField(max_length=250)
address = models.CharField(max_length=250)
suburb = models.CharField(max_length=250)
postcode = models.IntegerField()
state = models.CharField(max_length=250)
type = models.CharField(max_length=250)
latitude = models.DecimalField(decimal_places=6,max_digits=10)
longtitude = models.DecimalField(decimal_places=6,max_digits=10)
Is there a way to run a for loop in my database? I am currently working on Django SQLite. On Views.py, how can i implement the distance function with the user input in my rest api url to find the nearest safeplace and display as REST Api?
What you need is to run a comparison for loop in your views.py. It is pretty difficult to execute but I will try to explain step by step.
assuming you are using that distance(lat, lng, lat2, lng2) function and trying to find the distance within 2km for example.
In views.py
import pandas as pd
class someapiview(ListAPIView):
serializer_class = SafeplaceSerializer
### Now we are creating definition which sorts parameters lng and lat ###
def get_queryset(self):
queryset = Safeplace.Objects.all()
lat = float(self.query_params.get('lag', None)
lng = float(self.query_params.get('lng', None)
### Now, we are reading your api using pandas ###
df = pd.read_json('yourapi') ## yourapi is a url to ur api
obj = []
for x in range(0, len(df)):
latx = float(df['latitude'][x])
lngx = float(df['longitude'][x])
### Calculating distance ###
km = distance(lat, lng, latx, lngx)
if km <= 2:
obj.append(df['id'][x])
### Django auto generate primary key which usually calls id ###
### Now we are going to call those pk as a queryset ###
return Safeplace.objects.filter(pk__in=obj)
I used pandas to work around, the load time might be slow if you have lots of data. However, I think this does the job. Usually Geo Django provides an efficient system to deal with long and lat, however I am not very competent in Geo Django so I cannot really tell. But I Believe this is a good work around.
UPDATE :
you can query with "www.yourapi.com/safeplace?lat=x&lng=y"
I believe you know how to set urls
Related
Hi there, i want to insert a value between 0 and the output of function total polygons twice to the column of 'mer_value'(from the bottom left) of a shapefile so that the print output would look some thing like this.
mer_value
0.0
0.0
1.0
1.0
2.0
2.0
....
right now it only inserts the value from 0.0 all the way to the end repeating only once, like the below example.
mer_value
0.0
1.0
2.0
.....
any help is appreciated
{
import geopandas as gpd
number_of_geocells = 2
#create a funtion to read the total polygons in a given shapefile and divide it by the number of geocells and store the value in a new varialbe called "total_polygons"
def total_polygons(shapefile):
total_polygons = gpd.read_file(shapefile).shape[0] / number_of_geocells
total_geocells_per_zone_rounded = round(total_polygons)
return total_geocells_per_zone_rounded
#create a function to duplicate the shapefile and save it as a new shapefile called "zones"
def duplicate_shapefile(shapefile):
zones = gpd.read_file(shapefile)
zones.to_file("C:/tmp/shapefiletotestautomation/zones.shp")
return zones
#create a function to create a colum called 'mer_value' in the shapefile called "zones"
def create_merge_value(shapefile):
zones = gpd.read_file(shapefile)
zones['mer_value'] = zones.index
zones.to_file("C:/tmp/shapefiletotestautomation/zones.shp")
return zones
#create a loop to insert the same number twice to column "mer_value" in the shapefile called "zones"
def insert_merge_value(shapefile):
zones = gpd.read_file(shapefile)
for i in range(total_polygons(shapefile)):
zones.loc[i, 'mer_value'] = i
zones.loc[i + number_of_geocells, 'mer_value'] = i
zones.to_file("C:/tmp/shapefiletotestautomation/zones.shp")
return zones
print(insert_merge_value("C:/tmp/shapefiletotestautomation/m/GEOCELLS.shp"))
}
let's see if I can ask the questions correctly?
model.py
class Point(models.Model):
name = models.CharField(
"nome del sistema",
max_length=128,
)
x = models.FloatField(
"cordinata spaziale x",
)
y = models.FloatField(
"cordinata spaziale y",
)
z = models.FloatField(
"cordinata spaziale z",
)
distance = float(0)
def __str__(self) -> str:
return f"{self.name}"
#property
def get_distance(self):
return self.distance
#get_distance.setter
def get_distance(self, point):
"""
ritorna la distanza che ce tra due sistemi
"""
ad = float((point.x-self.x) if point.x > self.x else (self.x-point.x))
bc = float((point.y-self.y) if point.y > self.y else (self.y- point.y))
af = float((point.z-self.z) if point.z > self.z else (self.z-point.z))
return (pow(ad,2)+pow(bc,2)+pow(af,2))**(1/2)
class Meta:
verbose_name = "point"
verbose_name_plural = "points"
in the particular model there are two defs which calculate, save and return the distance with respect to the point we pass them
wenws.py
class PointViewset(viewsets.ModelViewSet):
"""
modelo generico per un systema
"""
queryset = Point.objects.all()
serializer_class = PointSerializers
filterset_class = PointFilter
in the wenws not that much particular to explain and base base the only thing we have to say and that as filters I use 'django_filters'
filters.py
import django_filters as filters
import Point
class CustomOrderFilter(filters.CharFilter):
def filter(self, qs:QuerySet, value):
if value in ([], (), {}, '', None):
return qs
try:
base = qs.get(name=value)
for point in qs:
point.get_distance = base
qs = sorted(qs, key= lambda x: x.get_distance)
except Point.DoesNotExist:
qs = qs.none()
return qs
class PointFilter(filters.rest_framework.FilterSet):
security = filters.ChoiceFilter(choices=security_choices
point= CustomCharFilter(
label = "point"
)
class Meta:
model = Point
fields = {
'name':['exact'],
}
now the complicated thing with 'CustomCharFilter' I pass in the http request the name of the system which then returns to me in the filter as value after I check that it is not empty and I start with returning the point that I have passed with base = qs.get ( name = value)
to then calculate and save the distance for each point with point.get_distance = base '' on the inside of the for, at the end I reorder the QuerySet with qs = sorted (qs, key = lambda x: x.get_distance) '' the problem that both with this way and with another that I have tried the QuerySet it 'transforms' into a list and this does not suit me since I have to return a QuerySet in the order of here I want. I don't know how to do otherwise, since order_by I can't use it since the distance is not inside the database
can someone help me?
So the problem is that you want to filter from a python function which cant be done in a query, as they only speak SQL.
The easy slow solution is to do the filtering in python, this might work if there are just a few Points.
points = list(Point.objects.all())
points.sort(cmp=comparison_function)
The actual real solution is to port that math to Djangos ORM and annotate your queryset with the distance to a given point, this is quite an advanced query, If you tell us your database server you use we can probably help you with that too.
ps. There is an abs() function in python to get absolute value instead of the if/else in get_distance
I have a list of new postcodes and I'm trying to find the nearest postcode from an existing postcode file to attach to the new postcodes. I am using the below code but it seems to have duplicated some rows, please could I have some help resolving this...
My 2 dataframes are:
new_postcode_df which contains 92,590 rows, and columns:
Postcode e.g. "AB101BJ"
Latitude e.g. 57.146051
Longitude e.g. -2.107375
current_postcode_df which contains 1,738,339 rows, and columns:
Postcode e.g. "AB101AB"
Latitude e.g. 57.149606
Longitude e.g. -2.096916
my desired output is output_df
new_postcode e.g. "AB101BJ"
current_postcode e.g. "AB101AB"
My code is below:
new_postcode_df_gps = new_postcode_df[["lat", "long"]].values
current_postcode_df_gps = current_postcode_df[["Latitude", "Longitude"]].values
new_postcode_df_radians = np.radians(new_postcode_df_gps)
current_postcode_df_radians = np.radians(current_postcode_df_gps)
tree = BallTree(current_postcode_df_radians , leaf_size=15, metric='haversine')
distance, index = tree.query(new_postcode_df_radians, k=1)
earth_radius = 6371000
distance_in_meters = distance * earth_radius
current_postcode_df.Postcode_NS[index[:,0]]
my output is shown in the attached where you can see postcodes beginning with "GY" have been added near the top which should not be the case. Postcodes starting with "AB" should all be at the top.
The new dataframe has increase from 92,590 rows to 92,848 rows
Image of final output dataframe
Libraries I'm using are:
import pandas as pd
import numpy as np
from sklearn.neighbors import BallTree
new_postcode_df = pd.DataFrame({"Postcode":["AB101BJ", "AB101BL", "AB107FU"],
"Latitude":[57.146051, 57.148655, 57.119636],
"Longitude":[-2.107375, -2.097433, -2.147906]})
current_postcode_df = pd.DataFrame({"Postcode":["AB101AB", "AB101AF", "AB101AG"],
"Latitude":[57.149606, 57.148707, 57.149051],
"Longitude":[-2.096916, -2.097806, -2.097004]})
output_df = pd.DataFrame({"Postcode":["AB101RS", "AB129TS", "GY35HG"]})
I'm stumped. I have a Django query that should return results, but does not seem to.
I have a database with a model Postcodes with latitude and longitude data.
class Postcode(models.Model):
name = models.CharField(max_length=50)
postcode = models.CharField(max_length=7)
latitude = models.DecimalField(max_digits=8, decimal_places=5, blank=True, null=True)
longitude = models.DecimalField(max_digits=8, decimal_places=5, blank=True, null=True)
I want to find the postcodes with 100km of a given postcode, at:
{'latitude': 1.445671659052796, 'longitude': 1.6673342919117797}
and I find the latitude and longitude ranges to be:
longitude_max = 1.9703812919117922
longitude_min = -1.3642872919117792
latitude_max = 52.2326886590528
latitude_min = 49.3413453409472
I query the db like so:
return Postcode.objects.filter(latitude__range=(latitude_min, latitude_max), longitude__range=(longitude_min, longitude_max))
But I don't get anything back? I should at least get the result I extrapolated the ranges from!
If you are filtering with those parameters for that coordinate, the queryset will not contain that item. The latitude is outside of the range provided.
In Ruby I have this function query.by_distance where as by_distance is something like
def_dataset_method(:by_distance) do |from, meters|
point = Geocoding.as_point(from)
query = "ST_DWithin(location, ST_SetSRID(ST_Point(?, ?), 4326)::geography, ?)"
where(query, point.lng, point.lat, meters)
end
in a ruby endpoint user is passing 2 values which are is_near_to which is mostly name of a city or a country.. Through which Geecoding is getting its points and 2 value is within_distance which are meters for getting cameras within this distance.
The above is happening in Ruby.
What am doing in Elixir to replicate it is
def by_distance(query, is_near_to, within_distance) do
[%{"lat" => lat, "lng" => lng}] = fetch(is_near_to)
latitude = lat
longitude = lng
query
|> where([cam], st_dwithin(cam.location, st_set_srid(st_point_from_text(^"#{latitude},#{longitude}"), 4326), ^within_distance))
end
for getting lat and long am doing something as
defmodule EvercamMedia.Geocode do
def fetch(address) do
response = HTTPotion.get "http://maps.googleapis.com/maps/api/geocode/json?address=#{URI.encode(address)}&sensor=false"
{:ok, results} = Poison.decode response.body
get_in(results, ["results", Access.all(), "geometry", "location"])
end
end
now I have same scenarios. I have both values is_near_to and within_distance. But am totally unaware of this that how I can replicate the same thing in a query as we are using geo in our project and by the docs its clearly possible but am not getting the way how to do it.
This is part of an app I'm working on, I believe it does what you need, it uses a fragment to pass the ST_distance_sphere call directly to postgis.
Using the library https://github.com/bryanjos/geo and postgis
In the model
def near_by(query, point, distance) do
from place in query,
where: fragment("ST_distance_sphere(?,?)", place.location, ^point) >= ^distance,
order_by: fragment("ST_distance_sphere(?,?)", place.location, ^point),
select: {place, fragment("ST_distance_sphere(?,?)", place.location, ^point)}
end
In the controller
def near_by(conn, %{"latitude" => latitude, "longitude" => longitude, "distance" => distance}) do
point = %Geo.Point{coordinates: {String.to_float(latitude), String.to_float(longitude)}, srid: 4326}
places = Place
|> Place.near_by(point, String.to_float(distance))
|> Repo.all
render(conn, "near_by.json", places: places)
end
In the view
def render("near_by_place.json", %{place: place}) do
{ place, distance } = place
{lat, lon} = place.location.coordinates
%{id: place.id,
name: place.name,
latitude: lat,
longitude: lon,
distance: distance}
end