analysis/webservice/algorithms/doms/geo.py (87 lines of code) (raw):
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import math
MEAN_RADIUS_EARTH_METERS = 6371010.0
EQUATORIAL_RADIUS_EARTH_METERS = 6378140.0
POLAR_RADIUS_EARTH_METERS = 6356752.0
FLATTENING_EARTH = 298.257223563
MEAN_RADIUS_EARTH_MILES = 3958.8
class DistanceUnit(object):
METERS = 0
MILES = 1
# Haversine implementation for great-circle distances between two points
def haversine(x0, y0, x1, y1, units=DistanceUnit.METERS):
if units == DistanceUnit.METERS:
R = MEAN_RADIUS_EARTH_METERS
elif units == DistanceUnit.MILES:
R = MEAN_RADIUS_EARTH_MILES
else:
raise Exception("Invalid units specified")
x0r = x0 * (math.pi / 180.0) # To radians
x1r = x1 * (math.pi / 180.0) # To radians
xd = (x1 - x0) * (math.pi / 180.0)
yd = (y1 - y0) * (math.pi / 180.0)
a = math.sin(xd / 2.0) * math.sin(xd / 2.0) + \
math.cos(x0r) * math.cos(x1r) * \
math.sin(yd / 2.0) * math.sin(yd / 2.0)
c = 2.0 * math.atan2(math.sqrt(a), math.sqrt(1.0 - a))
d = R * c
return d
# Equirectangular approximation for when performance is key. Better at smaller distances
def equirectangularApprox(x0, y0, x1, y1):
R = 6371000.0 # Meters
x0r = x0 * (math.pi / 180.0) # To radians
x1r = x1 * (math.pi / 180.0)
y0r = y0 * (math.pi / 180.0)
y1r = y1 * (math.pi / 180.0)
x = (y1r - y0r) * math.cos((x0r + x1r) / 2.0)
y = x1r - x0r
d = math.sqrt(x * x + y * y) * R
return d
class BoundingBox(object):
def __init__(self, north=None, south=None, west=None, east=None, asString=None):
if asString is not None:
bboxParts = asString.split(",")
self.west = float(bboxParts[0])
self.south = float(bboxParts[1])
self.east = float(bboxParts[2])
self.north = float(bboxParts[3])
else:
self.north = north
self.south = south
self.west = west
self.east = east
def toString(self):
return "%s,%s,%s,%s" % (self.west, self.south, self.east, self.north)
def toMap(self):
return {
"xmin": self.west,
"xmax": self.east,
"ymin": self.south,
"ymax": self.north
}
'''
Constrains, does not expand.
'''
class BoundsConstrainer(BoundingBox):
def __init__(self, north=None, south=None, west=None, east=None, asString=None):
BoundingBox.__init__(self, north, south, west, east, asString)
def testNorth(self, v):
if v is None:
return
self.north = max([self.north, v])
def testSouth(self, v):
if v is None:
return
self.south = min([self.south, v])
def testEast(self, v):
if v is None:
return
self.east = max([self.east, v])
def testWest(self, v):
if v is None:
return
self.west = min([self.west, v])
def testCoords(self, north=None, south=None, west=None, east=None):
self.testNorth(north)
self.testSouth(south)
self.testWest(west)
self.testEast(east)
def testOtherConstrainer(self, other):
self.testCoords(north=other.north, south=other.south, west=other.west, east=other.east)