Permalink
Please sign in to comment.
Browse files
Merge pull request #21 from calvinmetcalf/topo
TopoJSON support
- Loading branch information...
Showing
with
969 additions
and 36 deletions.
- +1 −0 .gitignore
- +3 −2 Install/esri2open/__init__.py
- +12 −4 Install/esri2open/esri2open.py
- +5 −4 Install/esri2open/parseGeometry.py
- +29 −2 Install/esri2open/parseRow.py
- +7 −0 Install/esri2open/prepare.py
- +1 −0 Install/esri2open/topojson/__init__.py
- +56 −0 Install/esri2open/topojson/arcs.py
- +20 −0 Install/esri2open/topojson/bounds.py
- +25 −0 Install/esri2open/topojson/clockwise.py
- +118 −0 Install/esri2open/topojson/coordinatesystems.py
- +39 −0 Install/esri2open/topojson/hashtable.py
- +113 −0 Install/esri2open/topojson/line.py
- +76 −0 Install/esri2open/topojson/mytypes.py
- +20 −0 Install/esri2open/topojson/quantize.py
- +143 −0 Install/esri2open/topojson/simplify.py
- +34 −0 Install/esri2open/topojson/stitchpoles.py
- +212 −0 Install/esri2open/topojson/topology.py
- +21 −0 Install/esri2open/topojson/utils.py
- +13 −2 Install/esri2open/utilities.py
- +14 −5 Install/merge.py
- +1 −11 Install/multiple.py
- +6 −6 README.md
- BIN esri2open.tbx
@@ -1,3 +1,4 @@ | ||
.c9revisions | ||
*~ | ||
node_modules | ||
+*.pyc |
@@ -1,2 +1,3 @@ | ||
-from esri2open import toOpen, writeFile, closeUp, closeJSON | ||
-from prepare import prepareFile, prepareGeoJSON | ||
+from esri2open import toOpen, writeFile, closeUp, closeJSON,closeTOPOJSON | ||
+from prepare import prepareFile, prepareGeoJSON,prepareTOPO | ||
+from utilities import getExt,getName |
@@ -0,0 +1 @@ | ||
+from topology import Topology |
@@ -0,0 +1,56 @@ | ||
+from hashtable import Hashtable | ||
+from os import remove | ||
+import shelve | ||
+from tempfile import mkdtemp | ||
+from hashlib import sha1 | ||
+from utils import point_compare | ||
+class Arcs: | ||
+ def __init__(self,Q): | ||
+ self.coincidences = Hashtable(Q * 10) | ||
+ self.arcsByPoint = Hashtable(Q * 10) | ||
+ self.pointsByPoint = Hashtable(Q * 10) | ||
+ self.arc_db_path=mkdtemp()+'/arc_db' | ||
+ self.arcs= shelve.open(self.arc_db_path) | ||
+ #self.arcs={} | ||
+ self.length=0 | ||
+ self.storage_path = mkdtemp()+'/db' | ||
+ self.db = shelve.open(self.storage_path) | ||
+ #self.db={} | ||
+ def get_index(self,point): | ||
+ return self.pointsByPoint.get(point) | ||
+ def get_point_arcs(self,point): | ||
+ return self.arcsByPoint.get(point) | ||
+ def coincidence_lines(self,point): | ||
+ return self.coincidences.get(point) | ||
+ def peak(self,point): | ||
+ return self.coincidences.peak(point) | ||
+ def push(self,arc): | ||
+ self.arcs[str(self.length)]=arc | ||
+ self.length+=1 | ||
+ return self.length | ||
+ def close(self): | ||
+ #pass | ||
+ self.db.close() | ||
+ remove(self.storage_path) | ||
+ self.arcs.close() | ||
+ remove(self.arc_db_path) | ||
+ def get_hash(self,arc): | ||
+ ourhash = sha1() | ||
+ ourhash.update(str(arc)) | ||
+ return ourhash.hexdigest() | ||
+ def check(self,arcs): | ||
+ a0 = arcs[0] | ||
+ a1 = arcs[-1] | ||
+ point = a0 if point_compare(a0, a1) < 0 else a1 | ||
+ point_arcs = self.get_point_arcs(point) | ||
+ h = self.get_hash(arcs) | ||
+ if h in self.db: | ||
+ return int(self.db[h]) | ||
+ else: | ||
+ index = self.length | ||
+ point_arcs.append(arcs) | ||
+ self.db[h]=index | ||
+ self.db[self.get_hash(list(reversed(arcs)))]=~index | ||
+ self.push(arcs) | ||
+ return index | ||
+ |
@@ -0,0 +1,20 @@ | ||
+from mytypes import Types | ||
+class Bounds(Types): | ||
+ def __init__(self): | ||
+ self.x0=self.y0=float('inf') | ||
+ self.x1=self.y1=-float('inf') | ||
+ def point (self,point): | ||
+ x = point[0] | ||
+ y = point[1] | ||
+ if x < self.x0: | ||
+ self.x0 = x | ||
+ if x > self.x1: | ||
+ self.x1 = x | ||
+ if y < self.y0: | ||
+ self.y0 = y | ||
+ if y > self.y1: | ||
+ self.y1 = y | ||
+def bound(objects): | ||
+ b=Bounds() | ||
+ b.obj(objects) | ||
+ return b |
@@ -0,0 +1,25 @@ | ||
+class Clock: | ||
+ def __init__(self,area): | ||
+ self.area=area | ||
+ def clock(self,feature): | ||
+ if 'geometries' in feature: | ||
+ feature['geometries'] = map(self.clock_geometry,feature['geometries']) | ||
+ elif 'geometry' in feature: | ||
+ feature['geometry']=self.clock_geometry(feature['geometry']) | ||
+ return feature | ||
+ def clock_geometry(self,geo): | ||
+ if 'type' in geo: | ||
+ if geo['type']=='Polygon' or geo['type']=='MultiLineString': | ||
+ geo['coordinates'] = self.clockwise_polygon(geo['coordinates']) | ||
+ elif geo['type']=='MultiPolygon': | ||
+ geo['coordinates'] = map(lambda x:self.clockwise_polygon(x),geo['coordinates']) | ||
+ elif geo['type']=='LineString': | ||
+ geo['coordinates'] = self.clockwise_ring(geo['coordinates']) | ||
+ return geo | ||
+ def clockwise_polygon(self,rings): | ||
+ return map(lambda x:self.clockwise_ring(x),rings) | ||
+ def clockwise_ring(self,ring): | ||
+ if self.area(ring) > 0: | ||
+ return list(reversed(ring)) | ||
+ else: | ||
+ return ring |
@@ -0,0 +1,118 @@ | ||
+# coding=utf8 | ||
+from math import sqrt, pi, cos, sin, atan2, tan, atan, asin | ||
+ | ||
+PI4 = pi / 4 | ||
+RADIANS = pi / 180 | ||
+ | ||
+ | ||
+class BaseCoordinateSystem(object): | ||
+ name = "BaseCoordinateSystem" | ||
+ | ||
+ def format_distance(self, distance): | ||
+ return str(distance) | ||
+ | ||
+ def ring_area(self, ring): | ||
+ raise Exception('Not implemented') | ||
+ | ||
+ def triangle_area(self, triangle): | ||
+ raise Exception('Not implemented') | ||
+ | ||
+ def distance(self, x0, y0, x1, y1): | ||
+ raise Exception('Not implemented') | ||
+ | ||
+ def absolute_area(self, area): | ||
+ return abs(area) | ||
+ | ||
+ | ||
+class Cartesian(BaseCoordinateSystem): | ||
+ name = "cartesian" | ||
+ | ||
+ def ring_area(self, ring): | ||
+ calc_area = lambda p1, p2: p1[1] * p2[0] - p1[0] * p2[1] | ||
+ # last and first | ||
+ area = calc_area(ring[-1], ring[0]) | ||
+ for i, p in enumerate(ring[1:]): # skip first so p is current and | ||
+ area += calc_area(p, ring[i]) # ring[i] is the previous | ||
+ return area * 0.5 | ||
+ | ||
+ def triangle_area(self, triangle): | ||
+ return abs( | ||
+ (triangle[0][0] - triangle[2][0]) * (triangle[1][1] - triangle[0][1]) - | ||
+ (triangle[0][0] - triangle[1][0]) * (triangle[2][1] - triangle[0][1]) | ||
+ ) | ||
+ | ||
+ def distance(self, x0, y0, x1, y1): | ||
+ dx = x0 - x1 | ||
+ dy = y0 - y1 | ||
+ return sqrt(dx * dx + dy * dy) | ||
+ | ||
+ | ||
+class Spherical(BaseCoordinateSystem): | ||
+ name = 'spherical' | ||
+ | ||
+ def haversin(self, x): | ||
+ return sin(x / 2) ** 2 | ||
+ | ||
+ def format_distance(self, distance): | ||
+ km = distance * 6371.0 | ||
+ if km > 1: | ||
+ return u"{:0.03f}km".format(km) | ||
+ else: | ||
+ return u"{:0.03f} ({0.03f}°)".format(km * 1000, distance * 180 / pi) | ||
+ | ||
+ def ring_area(self, ring): | ||
+ if len(ring) == 0: | ||
+ return 0 | ||
+ area = 0 | ||
+ p = ring[0] | ||
+ PI4 = 1 | ||
+ lambda_ = p[0] * RADIANS | ||
+ phi = p[1] * RADIANS / 2.0 + PI4 | ||
+ lambda0 = lambda_ | ||
+ cosphi0 = cos(phi) | ||
+ sinphi0 = sin(phi) | ||
+ for pp in ring[1:]: | ||
+ lambda_ = pp[0] * RADIANS | ||
+ phi = pp[1] * RADIANS / 2.0 + PI4 | ||
+ # Spherical excess E for a spherical triangle with vertices: south pole, | ||
+ # previous point, current point. Uses a formula derived from Cagnoli’s | ||
+ # theorem. See Todhunter, Spherical Trig. (1871), Sec. 103, Eq. (2). | ||
+ dlambda = lambda_ - lambda0 | ||
+ cosphi = cos(phi) | ||
+ sinphi = sin(phi) | ||
+ k = sinphi0 * sinphi | ||
+ u = cosphi0 * cosphi + k * cos(dlambda) | ||
+ v = k * sin(dlambda) | ||
+ area += atan2(v, u) | ||
+ #Advance the previous point. | ||
+ lambda0 = lambda_ | ||
+ cosphi0 = cosphi | ||
+ sinphi0 = sinphi | ||
+ return 2 * area | ||
+ | ||
+ def absolute_area(self, area): | ||
+ return area + 4 * pi if area < 0 else area | ||
+ | ||
+ def triangle_area(self, triangle): | ||
+ def distance(a, b): # why 2 implementations? I don't know, original has the same question in comments | ||
+ x0, y0, x1, y1 = [(n * RADIANS) for n in (a + b)] | ||
+ delta_lambda = x1 - x0 | ||
+ return atan2( | ||
+ sqrt( | ||
+ (cos(x1) * sin(delta_lambda)) ** 2 + | ||
+ (cos(x0) * sin(x1) - sin(x0) * cos(x1) * cos(delta_lambda)) ** 2 | ||
+ ), | ||
+ sin(x0) * sin(x1) + cos(x0) * cos(x1) * cos(delta_lambda) | ||
+ ) | ||
+ a = distance(triangle[0], triangle[1]) | ||
+ b = distance(triangle[1], triangle[2]) | ||
+ c = distance(triangle[2], triangle[0]) | ||
+ s = (a + b + c) / 2.0 | ||
+ return 4 * atan(sqrt(max(0, tan(s / 2.0) * tan((s - a) / 2.0) * tan((s - b) / 2.0) * tan((s - c) / 2.0)))) | ||
+ | ||
+ def distance(self, x0, y0, x1, y1): | ||
+ x0, y0, x1, y1 = [(n * RADIANS) for n in [x0, y0, x1, y1]] | ||
+ return 2.0 * asin(sqrt(self.haversin(y1 - y0) + cos(y0) * cos(y1) * self.haversin(x1 - x0))) | ||
+ | ||
+ | ||
+systems = {'cartesian': Cartesian(), 'spherical': Spherical()} |

Oops, something went wrong.
0 comments on commit
2400d16