source: lambert/lambert.py @ d5dacc74a117f96a578399fc0a885e9b09731390

Revision d5dacc74a117f96a578399fc0a885e9b09731390, 6.5 KB checked in by flupke <flupke@…>, 5 years ago (diff)

fixed projection code

git-svn-id:  http://kawa.selfip.org/svn/projects@444 532e76a9-45ae-c241-9c6d-8309ac6440cd

  • Property mode set to 100644
Line 
1#-*- encoding: utf-8 -*-
2from math import *
3
4
5# Some constants
6X = 0         # Easting
7Y = 1         # Northing
8PHI = 0       # Latitude
9LAMBDA = 1    # Longitude
10EPS10 = 1.e-10
11HALFPI = pi / 2.0
12QUARTERPI = pi / 4.0
13
14def gradesToRadians(grades):
15  return grades / 200.0 * pi
16
17
18def sexagesimalToDecimal(degrees=0.0, minutes=0.0, seconds=0.0):
19  """ Convert a sexagesimal (degrees, minutes, seconds) to a decimal representation """
20  return degrees + (minutes / 60.0) + (seconds / (60.0*60.0))
21
22
23def clampPositive(number):
24  """ Clamp negative numbers to 0.0 """
25  if number < 0.0:
26    return 0.0
27  else:
28    return number
29
30
31class Ellipsoid:
32  """ An ellipsoid ; can be specified using semi minor axis length or inverse flattening """
33 
34  def __init__(self, semiMajor, semiMinor=None, inverseFlattening=None):
35    self.semiMajor = semiMajor
36    self.semiMinor = semiMinor
37    # Some precalculated properties
38    if inverseFlattening is None:
39      self.inverseFlattening = self.semiMajor / (self.semiMajor - self.semiMinor)
40    else:
41      self.inverseFlattening = inverseFlattening
42      self.semiMinor = self.semiMajor - (self.semiMajor / self.inverseFlattening)
43    self.flattening = 1.0 / self.inverseFlattening
44    self.squaredExcentricity = 2.0 * self.flattening - self.flattening * self.flattening
45    self.excentricity = sqrt(self.squaredExcentricity)
46
47
48STD_ELLIPSOIDS = {
49    "International 1924": Ellipsoid(6378388.0, inverseFlattening=297.0),
50    "Clarke 1866": Ellipsoid(6378206.400, inverseFlattening=294.97870),
51    "Clarke 1880 (IGN)": Ellipsoid(6378249.2, semiMinor=6356515.0),
52    "GRS 1980": Ellipsoid(6378137.0, inverseFlattening=298.257222101),
53    }
54
55
56class LambertProjectionParameters2SP:
57  """ Lambert conic conformal projection (2 standard parallels) parameters """
58 
59  def __init__(self, ellipsoid, firstParallel, secondParallel, origin, shift):
60    self.ellipsoid = ellipsoid
61    self.firstParallel = firstParallel
62    self.secondParallel = secondParallel
63    self.origin = origin
64    self.shift = shift
65 
66  def __str__(self):
67    return "first parallel: %f\nsecond_parallel: %f" % \
68        (degrees(self.firstParallel), degrees(self.secondParallel))
69
70
71def msfn(sinphi, cosphi, es):
72  return (cosphi / sqrt(1.0 - es * sinphi * sinphi))
73
74
75def tsfn(phi, sinphi, e):
76  sinphi *= e;
77  return (tan(0.5 * ((pi / 2.0) - phi)) /
78      pow((1. - sinphi) / (1. + sinphi), .5 * e))
79
80
81class LambertConicConformalProjection2SP:
82  """ Lambert conic conformal projection (2 standard parallels) """
83 
84  _thetaAdjustment = 0.0          # Theta adjustment for belgium modified case
85 
86  def __init__(self, parameters):
87    self.setParameters(parameters)
88
89  def setParameters(self, parameters):
90    self.parameters = parameters
91    projectionLatitude1 = parameters.firstParallel
92    projectionLatitude2 = parameters.secondParallel
93    projectionLatitude = parameters.origin[1]
94    e = self.parameters.ellipsoid.excentricity
95    es = self.parameters.ellipsoid.squaredExcentricity
96    self.n = sinphi = sin(projectionLatitude1)
97    cosphi = cos(projectionLatitude1)
98    secant = abs(projectionLatitude1 - projectionLatitude2) >= 1e-10
99    spherical = (es == 0.0)
100    if not spherical:
101      m1 = msfn(sinphi, cosphi, es)
102      ml1 = tsfn(projectionLatitude1, sinphi, e)
103      if secant:
104        sinphi = sin(projectionLatitude2)
105        self.n = log(m1 /
106            msfn(sinphi, cos(projectionLatitude2), es))
107        self.n /= log(ml1 / tsfn(projectionLatitude2, sinphi, e))
108      self.c = self.rho0 = m1 * pow(ml1, -self.n) / self.n
109      if abs(abs(projectionLatitude) - HALFPI) < 1e-10:
110        self.rho0 *= 0.0
111      else:
112        self.rho0 *= pow(tsfn(projectionLatitude, sin(projectionLatitude), e), self.n)
113    else:
114      if secant:
115        self.n = log(cosphi / cos(projectionLatitude2)) / \
116            log(tan(QUARTERPI + .5 * projectionLatitude2) / \
117            tan(QUARTERPI + .5 * projectionLatitude1))
118      self.c = cosphi * pow(tan(QUARTERPI + .5 * projectionLatitude1), self.n) / self.n
119      if abs(abs(projectionLatitude) - HALFPI) < 1e-10:
120        self.rho0 = 0.0
121      else:
122        self.rho0 = self.c * pow(tan(QUARTERPI + .5 * projectionLatitude), -n)
123
124  def project(self, coordinates):
125    """ Projects geographical coordinates to (x, y) using Lambert projection. """
126    coordinates = (coordinates[0] - self.parameters.origin[0], coordinates[1])
127    if fabs(fabs(coordinates[1]) - (pi / 2.0)) < EPS10:
128      if coordinates[1] * self.n <= 0.0:
129        raise Exception
130      rho = 0.0
131    else:
132      if self.parameters.ellipsoid.squaredExcentricity == 0.0:
133        rho = self.c * pow(tan((pi / 4.0) + 0.5 * coordinates[1]), -n)
134      else:
135        rho = self.c * pow(tsfn(coordinates[1], sin(coordinates[1]), self.parameters.ellipsoid.excentricity), self.n)
136    a = coordinates[0] * self.n
137    x = self.parameters.ellipsoid.semiMajor * (rho * sin(a)) + self.parameters.shift[0]
138    y = self.parameters.ellipsoid.semiMajor * (self.rho0 - rho * cos(a)) + self.parameters.shift[1]
139    return (x, y)
140
141
142  def inverse_project(self, coordinates):
143    """ Projects geographical coordinates to (x, y) using Lambert projection """
144
145
146class LambertConicConformalProjection2SPBelgium(LambertConicConformalProjection2SP):
147  """ Lambert conic conformal projection (2 standard parallels Belgium) """
148 
149  _thetaAdjustment = radians(sexagesimalToDecimal(seconds=29.2985))
150
151
152STD_PROJECTIONS_PARAMETERS = {
153  "Lambert II Carto": LambertProjectionParameters2SP(
154      ellipsoid=STD_ELLIPSOIDS["Clarke 1880 (IGN)"],
155      firstParallel=radians(sexagesimalToDecimal(degrees=45.0, minutes=53.0, seconds=56.108)),   # 45°53'56.108"
156      secondParallel=radians(sexagesimalToDecimal(degrees=47.0, minutes=41.0, seconds=45.652)),  # 47°41'45.652"
157      origin=(radians(sexagesimalToDecimal(2.0, 20.0, 14.025)), radians(sexagesimalToDecimal(46.0, 48.0))),
158      shift=(600000.0, 2200000.0)),
159  }
160
161STD_PROJECTIONS = {
162  "Lambert II Carto": LambertConicConformalProjection2SP(STD_PROJECTIONS_PARAMETERS["Lambert II Carto"]),
163  }
164     
165
166def toDegrees(vector):
167  return [degrees(x) for x in vector]
168
169if __name__ == "__main__":
170  projection = STD_PROJECTIONS["Lambert II Carto"]
171  print projection.parameters
172  coords = (radians(5.82), radians(43.95))
173  result = projection.project(coords)
174  #result = ((result[0] - 840005) / 16.666 , (result[1] - 1889995) / 16.666)
175  print "coords: %s,\nprojected: %s" % (coords, result)
176  #print "...", [degrees(x) for x in projection.unProject((840005, 1889995))]
Note: See TracBrowser for help on using the repository browser.