Last active
December 21, 2015 08:39
-
-
Save rudyryk/6279782 to your computer and use it in GitHub Desktop.
Approximate longitude & latitude rectangle for specified location and distance.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import math | |
def georange(origin, distance): | |
""" | |
Returns approximate longitude & latitude rectangle range for | |
specified origin and distance in km. Works good when: | |
distance is far smaller 6400 km (approximate Earth radius) | |
origin isn't too close to North and South poles | |
Parameters: | |
origin -- tuple (lat, lng) in degrees | |
distance -- float > 0 in km | |
Result: | |
((lat1, lat2), (lng1, lng2), (lng3, lng4)) | |
We should produce 2 ranges for longitude as it may have | |
gap at edges, e.g. [-190, -170] is invalid range and it's | |
splitted into [-180, -170] and [170, 180] ranges. | |
Examples: | |
>>> georange((55.45, 37.37), 2.0)[0] | |
(55.432013567881626, 55.46798643211838) | |
>>> georange((55.45, 37.37), 2.0)[1] | |
(37.33828490093818, 37.40171509906182) | |
>>> georange((55.45, 37.37), 2.0)[2] | |
(37.33828490093818, 37.40171509906182) | |
>>> georange((200.0, 0), 2.0) | |
Traceback (most recent call last): | |
... | |
Exception: Latitude must be in range [-90.0, 90.0] | |
>>> georange((0.0, 190), 2.0) | |
Traceback (most recent call last): | |
... | |
Exception: Longitude must be in range [-180.0, 180.0] | |
NO COPYRIGHT | |
------------ | |
You can copy, modify, distribute and perform the work, even for | |
commercial purposes, all without asking permission. | |
See Other Information below. | |
CC0 1.0 Universal (CC0 1.0) Public Domain Dedication | |
http://creativecommons.org/publicdomain/zero/1.0/ | |
""" | |
radius = 6371.0 # in km | |
lat, lng = map(float, origin) | |
# Validate values range | |
if not (-90.0 <= lat <= 90.0): | |
raise ValueError("Latitude must be in range [%s, %s]" % (-90.0, 90.0)) | |
if not (-180.0 <= lng <= 180.0): | |
raise ValueError("Longitude must be in range [%s, %s]" % (-180.0, 180.0)) | |
# Range for latitude | |
dlat = math.degrees(distance / radius) | |
lat1, lat2 = max(-90.0, lat - dlat), min(90.0, lat + dlat) | |
# Ranges for longitude | |
r = radius * math.cos(math.radians(lat)) | |
dlng = (r > 0) and min(180.0, math.degrees(distance / r)) or 180.0 | |
lng1, lng2 = lng - dlng, lng + dlng | |
lng3, lng4 = lng1, lng2 | |
# Split longitude range if required | |
if lng1 < -180.0: | |
lng3, lng4 = (360.0 + lng1, 180.0) | |
lng1, lng2 = (-180.0, lng2) | |
elif lng2 > 180.0: | |
lng3, lng4 = (-180.0, lng2 - 360) | |
lng1, lng2 = (lng1, 180.0) | |
return ((lat1, lat2), (lng1, lng2), (lng3, lng4)) | |
if __name__ == "__main__": | |
import doctest | |
doctest.testmod() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment