Python Module for Distance between UK postcodes - python

I need to calculate distances between UK postcodes.
I don't want to use a web api.
Does a python module/ library exist for this?
Or do I have to put together something of my own using data from the OrdnanceSurvey?
Thanks,

1/You can use any rest geolocation api eg google maps, that would provide you accurate distance based on the pincodes.
2/ You can use any updated database which has post codes and latitude/longitude information, and use that information to calculate distance between the two points.
Helpful links:
i) http://blog.acmultimedia.co.uk/2008/03/uk-post-code-distance-calculator-using-phpmysql/
ii) Django - how can I find the distance between two locations?

This question is a bit old now but maybe worth pointing out the difference between polar coordinates (Long/Lat) used by Google and SatNavs and Eastings & Northings provided by the Ordnance Survey which shows how far a given postcode centroid is from a given datum point somewhere off to the South West of the Scilly Isles.
If you just want to work out distances it's a one-liner using Pythagoras. EG Stonehenge to House of Parliament
>>> # Coordinates of Stonehenge and Westminster in E/Northings
... s = (412183, 142346)
... p = (530268, 179545)
... d = ((s[0] - p[0]) ** 2 + (s[1] - p[1]) ** 2) ** 0.5
... print (d)
123805.62517914927
I was looking for a library to convert from polar to Cartesian coordinates which is relatively tricky even on a perfect sphere which the world most certainly isn't. I'm currently trying, and mostly failing, to get my head around this:
https://scipython.com/book/chapter-2-the-core-python-language-i/additional-problems/converting-between-an-os-grid-reference-and-longitudelatitude/

I don't know of a directly usable module, but you could use GRASS or QGIS, both of which support python scripting so the functionality can be used as python modules. You would still need to figure out how to do it manually in either of these tools though, but that's not really very difficult.

See below for simple code using the postcodes module for python along with the google maps distance matrix API.
from postcodes import PostCoder
import requests
import json
import pprint
pc = PostCoder()
origin_postcode = str.upper(raw_input("Enter origin PostCode:"))
dest_postcode = str.upper(raw_input("Enter destination PostCode:"))
origin = str(pc.get('%s' % origin_postcode)['geo']['lat'])+','+str(pc.get('%s' % origin_postcode)['geo']['lng'])
dest = str(pc.get('%s' % dest_postcode)['geo']['lat'])+','+str(pc.get('%s' % dest_postcode)['geo']['lng'])
data = requests.get('https://maps.googleapis.com/maps/api/distancematrix/json?units=imperial&origins=%s&destinations=%s&key=AIzaSyDfTip2PrdaRkF1muCLP8REAk3FsLjmBrU' % (origin, dest))
new = json.loads(data.text)
miles = str(new['rows'][0]['elements'][0]['distance']['text']).replace('mi','miles')
duration = str((new['rows'][0]['elements'][0]['duration']['text']))
print '\n\nThe distance from %s to %s is %s, taking approximately %s (driving).' % (origin_postcode, dest_postcode, miles, duration)

Related

Convert LineString / MultiLineString geometries to lat lon

I am using this Mapillary endpoint: https://tiles.mapillary.com/maps/vtp/mly1_public/2/{zoom_level}/{x}/{y}?access_token={} and getting such responses back (see photo). Also, here is the Mapillary documentation.
It is not quite clear to me what the nested coordinate lists in the response represent. By the looks of it, I initially thought it may have to do with pixel coordinates. But judging by the context (the API documentation) and the endpoint I am using, I would say that is not the case. Also, I am not sure if the json response you see in the picture is valid geojson. Some online formatters did not accept it as valid.
I would like to find the bounding box of the "sequence". For context, that would be the minimal-area rectangle defined by two lat, lon positions that fully encompasses the geometry of the so-called "sequence"; and a "sequence" is basically a series of photos taken during a vehicle/on-foot trip, together with the metadata associated with the photos (metadata is available using another endpoint, but that is just for context).
My question is: is it possbile to turn the coordinates you see in the pictures into (lat,lon)? Having those, it would be easy for me to find the bounding box of the sequence. And if so, how? Also, please notice that some of the nested lists are of type LineString while others are MultiLineString (which I read about the difference here: help.arcgis.com, hope this helps)
Minimal reproducible code snippet:
import json
import requests
import mercantile
import mapbox_vector_tile as mvt
ACCESS_TOKEN = 'XXX' # can be provided from here: https://www.mapillary.com/dashboard/developers
z6_tiles = list(mercantile.tiles( #us_west_coast_bbox
west=-125.066423,
south=42.042594,
east=-119.837770,
north=49.148042,
zooms=6
))
# pprint(z6_tiles)
vector_tiles_url = 'https://tiles.mapillary.com/maps/vtp/mly1_public/2/{}/{}/{}?access_token={}'
for tile in z6_tiles:
res = requests.get(vector_tiles_url.format(tile.z,tile.x,tile.y,ACCESS_TOKEN))
res_json = mvt.decode(res.content)
with open('idea.json','w+') as f:
json.dump(res_json, f, indent=4)
I think this get_normalized_coordinates is the solution I was looking for. Please take this with a grain of salt, as I did not fully test it yet. Will try to and then I will update my answer. Also, please be cautious, because for tiles closer to either the South or the North Pole, the Z14_TILE_DMD_WIDTH constant will not be the one you see, but something more like: 0.0018958715374282065.
Z14_TILE_DMD_WIDTH = 0.02197265625
Z14_TILE_DMD_HEIGHT = 0.018241950298914844
def get_normalized_coordinates(bbox: mercantile.LngLatBbox,
target_lat: int,
target_lon: int,
extent: int=4096): # 4096 is Mapillary's default
"""
Returns lon,lat tuple representing real position on world map of a map feature.
"""
min_lon, min_lat, _, _ = bbox
return min_lon + target_lon / extent * Z14_TILE_DMD_WIDTH,
min_lat + target_lat / extent * Z14_TILE_DMD_HEIGHT
And if you are wondering how I came with the constants that you see, I simply iterated over the list of tiles that I am interested in and checked to make sure they all have the same width/height size (this might have not been the case, keeping in mind what I mentioned above about tiles closer to one of the poles - I think this is called "distortion", not sure). Also, for context: these tiles I iterated over are within this bbox: (-125.024414, 31.128199, -108.896484, 49.152970) (min_lon, min_lat, max_lon, max_lat; US west coast) which I believe is also why all the tiles have the same width/height sizes.
set_test = set()
for tile in relevant_tiles_set:
curr_bbox = mercantile.bounds(list_relevant_tiles_set[i])
dm_width_diff: float = curr_bbox.east - curr_bbox.west
dm_height_diff: float = curr_bbox.north - curr_bbox.south
set_test.add((dm_width_diff, dm_height_diff))
set_test
output:
{(0.02197265625, 0.018241950298914844}
UPDATE: forgot to mention that you actually do not need to compute those WIDTH, HEIGHT constants. You just replace those with (max_lon - min_lon) and (max_lat - min_lat) respectively. What I did with those constants was something for testing purposes only

Generating Random Cordinates for Specific Country

Am Trying to Generate Random Coordinates for a Country
I used this library Faker
def geo_point():
"""make random cordinates"""
faker = factory.Faker('local_latlng', country_code = 'IN')
coords = faker.generate()
return (coords[1], coords[0])
But the problem in this is, it has a very limited set of coordinates around 30-40 we require at least 10,000 for testing.
I tried a simple approach
def random_geo_cordinate():
"""make random geocordinates"""
x, y = uniform(-180,180), uniform(-90, 90)
return (y, x)
But then only 10-20 coordinates for Specific Country Comes.
There were a lot of references I found that through shape_files we can generate but in all of them only geom parameters are only available.
I found a method through which I can check that these coordinates lie in that country or not via the Geom column.
But am still missing something in generating random coordinates for a country.
Is there any simple and direct approach.
Am using
POST GIS Database
GeoDjango Server
Note:
I used GDAL for getting shapefiles for a country
You could use Overpass API, which queries the OSM database, so you get real coordinates.
For example fetching all villages in India:
import requests
import json
overpass_url = "http://overpass-api.de/api/interpreter"
overpass_query = """
[out:json];area[name="India"];(node[place="village"](area););out;
"""
response = requests.get(
overpass_url,
params={'data': overpass_query}
)
coords = []
if response.status_code == 200:
data = response.json()
places = data.get('elements', [])
for place in places:
coords.append((place['lat'], place['lon']))
print ("Got %s village coordinates!" % len(coords))
print (coords[0])
else:
print("Error")
Output:
Got 102420 village coordinates!
(9.9436615, 77.8978759)
Note: Overpass API is rate limited, so you should save the all coordinates locally and extract your random set from there!
Additionally, you can play around with places parameter fetching just cities or towns, or fetch restaurant locations for a specific district, ...
https://3geonames.org/randomland.IN is a free API that returns random locations in any country of the world.

Get an object's position in another object's coordinate system

Is there a way in MEL or Python in Maya to get one object's position in the coordinate system of another object? I have a camera in a scene that may be rotated in any direction and am trying to measure the distance in its local Z axis to the vertices of various objects in the scene. This obviously needs to be fast, since it will likely be run thousands of times across the scene.
In Maxscript the command would be something like
" in coordsys $camera "
but I have yet to find something like this in Maya. If there's no direct command to do this, does anyone have a way to calculate it using matrix math?
There is no one liner similar to the MXS idiom -- and no easy way to do it in mel. However in Python you can do this fairly easily.
First you need to get the matrix for the coordinate system you want as an MMatrix, which is part of the OpenMaya api. Then get the position you want to check as an MPoint, which is another api class. Here's the cheap way to get them (there are faster methods but they're much wordier):
from maya.api.OpenMaya import MVector, MMatrix, MPoint
import maya.cmds as cmds
def world_matrix(obj):
"""'
convenience method to get the world matrix of <obj> as a matrix object
"""
return MMatrix( cmds.xform(obj, q=True, matrix=True, ws=True))
def world_pos(obj):
"""'
convenience method to get the world position of <obj> as an MPoint
"""
return MPoint( cmds.xform(obj, q=True, t=True, ws=True))
Once you have the matrix and the point, the relative position is simply point times the inverse of the matrix:
relative_position = world_pos('pSphere1') * world_matrix('pCube1').inverse()
print relative_position
# (0.756766, -0.0498943, 3.38499, 1)
The result will be an MPoint, which has 4 numbers (x, y, z and w); the 4th will always be 1 so you can just ignore it, although the math needs it to account for scales and shears.
Use this MEL script to calculate the distance from camera1 to nurbsSphere1 primitive:
vector $p1 = `getAttr camera1.translate`;
vector $p2 = `getAttr nurbsSphere1.translate`;
vector $result = $p1 - $p2;
print (mag($result))
Printed result must be like this:
# MEL 40.1965
Or use this Python script to calculate the distance from camera1 to nurbsSphere1 primitive:
import maya.cmds as cmds
import math
distance = math.sqrt(pow((float)(cmds.getAttr("nurbsSphere1.tx") - cmds.getAttr("camera1.tx")),2) +
pow((float)(cmds.getAttr("nurbsSphere1.ty") - cmds.getAttr("camera1.ty")),2) +
pow((float)(cmds.getAttr("nurbsSphere1.tz") - cmds.getAttr("camera1.tz")),2) )
print(distance)
Printed result must be like this:
# Python 40.1964998512

Working out which points lat/lon coordinates are closest to

I currently have a list of coordinates
[(52.14847612092221, 0.33689512047881015),
(52.14847612092221, 0.33689512047881015),
(52.95756796776235, 0.38027099942700493),
(51.78723479900971, -1.4214854900618064)
...]
I would like to split this list into 3 separate lists/datafames corresponding to which city they are closest to (in this case the coordinates are all in the UK and the 3 cities are Manchester, Cardiff and London)
So at the end result I would like the current single list of coordinates to be split into either separate lists ideally or it could be a dataframe with 3 columns would be fine eg:
leeds cardiff london
(51.78723479900971, (51.78723479900971, (51.78723479900971,
-1.4214854900618064) -1.4214854900618064) -1.4214854900618064)
(those are obiously not correct coordinates!)
-Hope that makes sense. It doesn't have to be overly accurate (don't need to take into consideration the curvature of the earth or anything like that!)
I'm really not sure where to start with this - I'm very new to python and would appreciate any help!
Thanks in advance
This will get you started:
from geopy.geocoders import Nominatim
geolocator = Nominatim()
places = ['london','cardiff','leeds']
coordinates = {}
for i in places:
coordinates[i] = ((geolocator.geocode(i).latitude, geolocator.geocode(i).longitude))
>>>print coordinates
{'cardiff': (51.4816546, -3.1791933), 'leeds': (53.7974185, -1.543794), 'london': (51.5073219, -0.1276473)}
You can now hook up the architecture for putting this in a pandas dataframe, calculating the distance metric between your coordinates and the above.
Ok so now we want to do distances between what is a very small array (the coordinates).
Here's some code:
import numpy as np
single_point = [3, 4] # A coordinate
points = np.arange(20).reshape((10,2)) # Lots of other coordinates
dist = (points - single_point)**2
dist = np.sum(dist, axis=1)
dist = np.sqrt(dist)
From here there is any number of things you can do. You can sort it using numpy, or you can place it in a pandas dataframe and sort it there (though that's really just a wrapper for the numpy function I believe). Whichever you're more comfortable with.
This is a pretty brute force approach, and not too adaptable. However, that can be the easiest to understand and might be plenty efficient for the problem at hand. It also uses only pure python, which may help you to understand some of python's conventions.
points = [(52.14847612092221, 0.33689512047881015), (52.14847612092221, 0.33689512047881015), (52.95756796776235, 0.38027099942700493), (51.78723479900971, -1.4214854900618064), ...]
cardiff = (51.4816546, -3.1791933)
leeds = (53.7974185, -1.543794)
london = (51.5073219, -0.1276473)
def distance(pt, city):
return ((pt[0] - city[0])**2 + (pt[1] - city[1])**2)**0.5
cardiff_pts = []
leeds_pts = []
london_pts = []
undefined_pts = [] # for points equidistant between two/three cities
for pt in points:
d_cardiff = distance(pt, cardiff)
d_leeds = distance(pt, leeds)
d_london = distance(pt, london)
if (d_cardiff < d_leeds) and (d_cardiff < d_london):
cardiff_pts.append(pt)
elif (d_leeds < d_cardiff) and (d_leeds < d_london):
leeds_pts.append(pt)
elif (d_london < d_cardiff) and (d_london < d_leeds):
london_pts.append(pt)
else:
undefined_pts.append(pt)
Note that this solution assumes the values are on a cartesian reference frame, which latitude longitude pairs are not.

pyephem FixedObject() for given RA/Dec

I'm looking to determine the alt/az of (un-famous) stars at given RA/Dec at specific times from Mauna Kea. I'm trying to compute these parameters using pyephem, but the resulting alt/az don't agree with other sources. Here's the calculation for HAT-P-32 from Keck:
import ephem
telescope = ephem.Observer()
telescope.lat = '19.8210'
telescope.long = '-155.4683'
telescope.elevation = 4154
telescope.date = '2013/1/18 10:04:14'
star = ephem.FixedBody()
star._ra = ephem.degrees('02:04:10.278')
star._dec = ephem.degrees('+46:41:16.21')
star.compute(telescope)
print star.alt, star.az
which returns -28:43:54.0 73:22:55.3, though according to Stellarium, the proper alt/az should be: 62:26:03 349:15:13. What am I doing wrong?
EDIT: Corrected latitude and longitude, which were formerly reversed.
First, you've got long and latitude backwards; second, you need to provide the strings in hexadecimal form; and third, you need to provide the RA as hours, not degrees:
import ephem
telescope = ephem.Observer()
# Reversed longitude and latitude for Mauna Kea
telescope.lat = '19:49:28' # from Wikipedia
telescope.long = '-155:28:24'
telescope.elevation = 4154.
telescope.date = '2013/1/18 00:04:14'
star = ephem.FixedBody()
star._ra = ephem.hours('02:04:10.278') # in hours for RA
star._dec = ephem.degrees('+46:41:16.21')
star.compute(telescope)
This way, you get:
>>> print star.alt, star.az
29:11:57.2 46:43:19.6
PyEphem always uses UTC for time, so that programs operate the same and give the same output wherever they are run. You simply need to convert the date you are using to UTC, instead of using your local time zone, and the results agree fairly closely with Stellarium; use:
telescope.date = '2013/1/18 05:04:14'
The result is this alt/az:
62:27:19.0 349:26:19.4
To know where the small remaining difference comes from, I would have to look into how the two programs handle each step of their computation; but does this get you close enough?

Categories

Resources