├── .gitignore ├── Images └── esri2open-logo_32x32.png ├── Install ├── esri2open │ ├── __init__.py │ ├── esri2open.py │ ├── parseGeometry.py │ ├── parseRow.py │ ├── prepare.py │ ├── topojson │ │ ├── __init__.py │ │ ├── arcs.py │ │ ├── bounds.py │ │ ├── clockwise.py │ │ ├── coordinatesystems.py │ │ ├── hashtable.py │ │ ├── line.py │ │ ├── mytypes.py │ │ ├── quantize.py │ │ ├── simplify.py │ │ ├── stitchpoles.py │ │ ├── topology.py │ │ └── utils.py │ ├── utilities.py │ ├── wkb.py │ └── wkt.py ├── esriopenaddin_addin.py ├── merge.py ├── multiple.py └── single.py ├── LICENSE.md ├── README.md ├── config.xml ├── esri2open.tbx └── makeaddin.py /.gitignore: -------------------------------------------------------------------------------- 1 | .c9revisions 2 | *~ 3 | node_modules 4 | *.pyc -------------------------------------------------------------------------------- /Images/esri2open-logo_32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/project-open-data/esri2open/1d7b2cca6e30ecbc77830c381fa58e24e98d28f6/Images/esri2open-logo_32x32.png -------------------------------------------------------------------------------- /Install/esri2open/__init__.py: -------------------------------------------------------------------------------- 1 | from esri2open import toOpen, writeFile, closeUp, closeJSON,closeTOPOJSON 2 | from prepare import prepareFile, prepareGeoJSON,prepareTOPO 3 | from utilities import getExt,getName -------------------------------------------------------------------------------- /Install/esri2open/esri2open.py: -------------------------------------------------------------------------------- 1 | # --------------------------------------------------------------------------- 2 | # esri2open.py 3 | # Created on: March 11, 2013 4 | # Created by: Michael Byrne 5 | # Federal Communications Commission 6 | # exports the feature classes for any feature class to 7 | # a csv file, JSON file or geoJSON file 8 | # also adding edits from sgillies, Shaun Walbridge, and Calvin Metcalf 9 | # updates include using the python json.dumps method and indentation issues 10 | # merge of Calvin's esri2geo and export to sqlite 11 | # last edits made 7/26/2013 12 | # --------------------------------------------------------------------------- 13 | #imports 14 | from arcpy import AddMessage, GetCount_management 15 | from utilities import getExt 16 | from parseRow import parse 17 | from prepare import prepareFile 18 | 19 | #---- 20 | #close file 21 | #---- 22 | def closeJSON(out): 23 | out.write("""]}""") 24 | out.close() 25 | return True 26 | 27 | def closeSqlite(out): 28 | out[2].commit() 29 | out[1].close() 30 | return True 31 | 32 | def closeCSV(out): 33 | out[1].close() 34 | return True 35 | 36 | def closeTOPOJSON(out): 37 | file = open(out['out'],'w') 38 | out['topo'].dump(file) 39 | file.close() 40 | return True 41 | 42 | def closeUp(out,fileType): 43 | if fileType == "geojson": 44 | return closeJSON(out) 45 | elif fileType == "csv": 46 | return closeCSV(out) 47 | elif fileType == "json": 48 | return closeJSON(out) 49 | elif fileType == "topojson": 50 | return closeTOPOJSON(out) 51 | else: 52 | return False 53 | 54 | #this is the meat of the function, we could put it into a seperate file if we wanted 55 | def writeFile(outArray,featureClass,fileType,includeGeometry, first=True, outName = False): 56 | parser = parse(outArray,featureClass,fileType,includeGeometry,first,outName) 57 | #wrap it in a try so we don't lock the database 58 | try: 59 | for row in parser.rows: 60 | #parse row 61 | parser.parse(row) 62 | except Exception as e: 63 | #using chrome has rubbed off on me 64 | AddMessage("OH SNAP! " + str(e)) 65 | finally: 66 | #clean up 67 | return parser.cleanUp(row) 68 | 69 | #this is the main entry point into the module 70 | def toOpen(featureClass, outJSON, includeGeometry="geojson"): 71 | #check the file type based on the extention 72 | fileType=getExt(outJSON) 73 | #some sanity checking 74 | #valid geojson needs features, seriously you'll get an error 75 | if not int(GetCount_management(featureClass).getOutput(0)): 76 | AddMessage("No features found, skipping") 77 | return 78 | elif not fileType: 79 | AddMessage("this filetype doesn't make sense") 80 | return 81 | #geojson needs geometry 82 | if fileType in ("geojson", "topojson"): 83 | includeGeometry="geojson" 84 | elif fileType=="sqlite": 85 | includeGeometry="well known binary" 86 | else: 87 | includeGeometry=includeGeometry.lower() 88 | #open up the file 89 | outFile=prepareFile(outJSON,featureClass,fileType,includeGeometry) 90 | #outFile will be false if the format isn't defined 91 | if not outFile: 92 | AddMessage("I don't understand the format") 93 | return 94 | #write the rows 95 | writeFile(outFile,featureClass,fileType,includeGeometry) 96 | #go home 97 | closeUp(outFile,fileType) 98 | -------------------------------------------------------------------------------- /Install/esri2open/parseGeometry.py: -------------------------------------------------------------------------------- 1 | from wkt import getWKTFunc 2 | from wkb import getWKBFunc 3 | 4 | def getPoint(pt): 5 | return [pt.X,pt.Y] 6 | def parseLineGeom(line): 7 | out=[] 8 | lineCount=line.count 9 | if lineCount ==1: 10 | return ["Point",getPoint(line[0])] 11 | i=0 12 | while i1: 175 | out = {} 176 | out["type"]="GeometryCollection" 177 | outGeo = [] 178 | if polys: 179 | outGeo.append(polyGeo) 180 | if points: 181 | outGeo.append(pointGeo) 182 | if lines: 183 | outGeo.append(lineGeo) 184 | out["geometries"]=outGeo 185 | return out 186 | else: 187 | return {} 188 | def parseMultiPatch(): 189 | #noop at the moment 190 | return {} 191 | 192 | #this should probobly be a class 193 | def getParseFunc(shpType, geo): 194 | if geo == "none": 195 | return False 196 | elif geo=="well known binary": 197 | return getWKBFunc(shpType) 198 | else: 199 | if shpType == "point": 200 | fun = parsePoint 201 | elif shpType == "multipoint": 202 | fun = parseMultiPoint 203 | elif shpType == "polyline": 204 | fun = parseMultiLineString 205 | elif shpType == "polygon": 206 | fun = parseMultiPolygon 207 | else: 208 | fun = parseMultiPatch 209 | 210 | if geo=="well known text": 211 | return getWKTFunc(fun) 212 | else: 213 | return fun -------------------------------------------------------------------------------- /Install/esri2open/parseRow.py: -------------------------------------------------------------------------------- 1 | from utilities import listFields, getShp, getOID, statusMessage, parseProp, makeInter,getName 2 | from arcpy import SpatialReference, SearchCursor 3 | from parseGeometry import getParseFunc 4 | from json import dump 5 | 6 | #really the only global 7 | wgs84="GEOGCS['GCS_WGS_1984',DATUM['D_WGS_1984',SPHEROID['WGS_1984',6378137.0,298.257223563]],PRIMEM['Greenwich',0.0],UNIT['Degree',0.0174532925199433]];-400 -400 1000000000;-100000 10000;-100000 10000;8.98315284119522E-09;0.001;0.001;IsHighPrecision" 8 | 9 | class parse: 10 | def __init__(self,outFile,featureClass,fileType,includeGeometry, first=True, outName=False): 11 | self.outFile = outFile 12 | self.fileType = fileType 13 | #first we set put the local variables we'll need 14 | [self.shp,self.shpType]=getShp(featureClass) 15 | self.fields=listFields(featureClass) 16 | self.oid=getOID(self.fields) 17 | sr=SpatialReference() 18 | sr.loadFromString(wgs84) 19 | #the search cursor 20 | self.rows=SearchCursor(featureClass,"",sr) 21 | #don't want the shape field showing up as a property 22 | del self.fields[self.shp] 23 | self.first=first 24 | self.status = statusMessage(featureClass) 25 | #define the correct geometry function if we're exporting geometry 26 | self.parseGeo = getParseFunc(self.shpType,includeGeometry) 27 | self.i=0 28 | if fileType=="geojson": 29 | self.parse = self.parseGeoJSON 30 | elif fileType=="csv": 31 | self.parse = self.parseCSV 32 | elif fileType=="json": 33 | self.parse = self.parseJSON 34 | elif fileType=="sqlite": 35 | self.parse = self.parseSqlite 36 | elif fileType=="topojson": 37 | self.parse = self.parseTOPOJSON 38 | if outName: 39 | self.oName=outName 40 | else: 41 | self.oName = getName(featureClass) 42 | self.topo = self.outFile['topo'].object_factory(self.oName) 43 | 44 | def cleanUp(self,row): 45 | del row 46 | del self.rows 47 | return True 48 | 49 | def parseCSV(self,row): 50 | #more messages 51 | self.status.update() 52 | fc=parseProp(row,self.fields, self.shp) 53 | if self.parseGeo: 54 | try: 55 | fc["geometry"]=self.parseGeo(row.getValue(self.shp)) 56 | except: 57 | return 58 | for key in fc: 59 | if isinstance(fc[key],unicode): 60 | fc[key] = fc[key].encode('utf_8') 61 | self.outFile[0].writerow(fc) 62 | 63 | def parseGeoJSON(self,row): 64 | #more messages 65 | self.status.update() 66 | fc={"type": "Feature"} 67 | if self.parseGeo: 68 | try: 69 | fc["geometry"]=self.parseGeo(row.getValue(self.shp)) 70 | except: 71 | return 72 | else: 73 | raise NameError("we need geometry for geojson") 74 | fc["id"]=row.getValue(self.oid) 75 | fc["properties"]=parseProp(row,self.fields, self.shp) 76 | if fc["geometry"]=={}: 77 | return 78 | if self.first: 79 | self.first=False 80 | dump(fc,self.outFile) 81 | else: 82 | #if it isn't the first feature, add a comma 83 | self.outFile.write(",") 84 | dump(fc,self.outFile) 85 | 86 | def parseTOPOJSON(self,row): 87 | #more messages 88 | self.status.update() 89 | fc={"type": "Feature"} 90 | if self.parseGeo: 91 | try: 92 | fc["geometry"]=self.parseGeo(row.getValue(self.shp)) 93 | except: 94 | return 95 | else: 96 | raise NameError("we need geometry for topojson") 97 | fc["id"]=row.getValue(self.oid) 98 | fc["properties"]=parseProp(row,self.fields, self.shp) 99 | if fc["geometry"]=={}: 100 | return 101 | self.topo(fc) 102 | 103 | def parseJSON(self,row): 104 | #more messages 105 | self.status.update() 106 | fc=parseProp(row,self.fields, self.shp) 107 | if self.parseGeo: 108 | try: 109 | fc["geometry"]=self.parseGeo(row.getValue(self.shp)) 110 | except: 111 | return 112 | if self.first: 113 | self.first=False 114 | dump(fc,self.outFile) 115 | else: 116 | self.outFile.write(",") 117 | dump(fc,self.outFile) 118 | 119 | def parseSqlite(self,row): 120 | #more messages 121 | self.status.update() 122 | fc=parseProp(row,self.fields, self.shp) 123 | self.i=self.i+1 124 | fc["OGC_FID"]=self.i 125 | if self.parseGeo: 126 | try: 127 | fc["GEOMETRY"]=self.parseGeo(row.getValue(self.shp)) 128 | except: 129 | return 130 | keys = fc.keys() 131 | values = fc.values() 132 | [name,c,conn]=self.outFile 133 | c.execute("""insert into {0}({1}) 134 | values({2}) 135 | """.format(name,", ".join(keys),makeInter(len(values))),values) 136 | conn.commit() -------------------------------------------------------------------------------- /Install/esri2open/prepare.py: -------------------------------------------------------------------------------- 1 | from csv import DictWriter 2 | from utilities import listFields,getShp, parseFieldType 3 | from sqlite3 import Connection 4 | from os.path import splitext, split 5 | from topojson import Topology 6 | 7 | def prepareCSV(outJSON,featureClass,fileType,includeGeometry): 8 | shp=getShp(featureClass)[0] 9 | fields=listFields(featureClass) 10 | fieldNames = [] 11 | out = open(outJSON,"wb") 12 | for field in fields: 13 | if (fields[field] != u'OID') and field.lower() !=shp.lower(): 14 | fieldNames.append(field) 15 | if includeGeometry!="none": 16 | fieldNames.append("geometry") 17 | outCSV=DictWriter(out,fieldNames,extrasaction='ignore') 18 | fieldObject = {} 19 | for fieldName in fieldNames: 20 | fieldObject[fieldName]=fieldName 21 | outCSV.writerow(fieldObject) 22 | return [outCSV,out] 23 | 24 | def prepareSqlite(out,featureClass,fileType,includeGeometry): 25 | [shp,shpType]=getShp(featureClass) 26 | if shpType == "point": 27 | gType = 1 28 | elif shpType == "multipoint": 29 | gType = 4 30 | elif shpType == "polyline": 31 | gType = 5 32 | elif shpType == "polygon": 33 | gType = 6 34 | fields=listFields(featureClass) 35 | fieldNames = [] 36 | fieldNames.append("OGC_FID INTEGER PRIMARY KEY") 37 | if includeGeometry: 38 | fieldNames.append("GEOMETRY blob") 39 | for field in fields: 40 | if (fields[field] != u'OID') and field.lower() !=shp.lower(): 41 | fieldNames.append(parseFieldType(field,fields[field])) 42 | 43 | conn=Connection(out) 44 | c=conn.cursor() 45 | name = splitext(split(out)[1])[0] 46 | c.execute("""CREATE TABLE geometry_columns ( f_table_name VARCHAR, f_geometry_column VARCHAR, geometry_type INTEGER, coord_dimension INTEGER, srid INTEGER, geometry_format VARCHAR )""") 47 | c.execute("""insert into geometry_columns( f_table_name, f_geometry_column, geometry_type, coord_dimension, srid, geometry_format) values(?,?,?,?,?,?)""",(name,"GEOMETRY",gType,2,4326,"WKB")) 48 | c.execute("""CREATE TABLE spatial_ref_sys ( srid INTEGER UNIQUE, auth_name TEXT, auth_srid TEXT, srtext TEXT)""") 49 | c.execute("insert into spatial_ref_sys(srid ,auth_name ,auth_srid ,srtext) values(?,?,?,?)",(4326, u'EPSG', 4326, u'GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]]')) 50 | c.execute("create table {0}({1})".format(name,", ".join(fieldNames))) 51 | return [name,c,conn] 52 | def prepareGeoJSON(outJSON,*args): 53 | out = open(outJSON,"wb") 54 | out.write("""{"type":"FeatureCollection","features":[""") 55 | return out 56 | 57 | def prepareJSON(outJSON,*args): 58 | out = open(outJSON,"wb") 59 | out.write("""{"docs":[""") 60 | return out 61 | 62 | def prepareTOPO(outJSON,*args): 63 | topo = Topology() 64 | return {"topo":topo,'out':outJSON} 65 | 66 | def prepareFile(outJSON,featureClass,fileType,includeGeometry): 67 | if fileType == "geojson": 68 | return prepareGeoJSON(outJSON,featureClass,fileType,includeGeometry) 69 | elif fileType == "csv": 70 | return prepareCSV(outJSON,featureClass,fileType,includeGeometry) 71 | elif fileType == "json": 72 | return prepareJSON(outJSON,featureClass,fileType,includeGeometry) 73 | elif fileType == "sqlite": 74 | return prepareSqlite(outJSON,featureClass,fileType,includeGeometry) 75 | elif fileType == "topojson": 76 | return prepareTOPO(outJSON,featureClass,fileType,includeGeometry) 77 | else: 78 | return False -------------------------------------------------------------------------------- /Install/esri2open/topojson/__init__.py: -------------------------------------------------------------------------------- 1 | from topology import Topology -------------------------------------------------------------------------------- /Install/esri2open/topojson/arcs.py: -------------------------------------------------------------------------------- 1 | from hashtable import Hashtable 2 | from os import remove 3 | import shelve 4 | from tempfile import mkdtemp 5 | from hashlib import sha1 6 | from utils import point_compare 7 | class Arcs: 8 | def __init__(self,Q): 9 | self.coincidences = Hashtable(Q * 10) 10 | self.arcsByPoint = Hashtable(Q * 10) 11 | self.pointsByPoint = Hashtable(Q * 10) 12 | self.arc_db_path=mkdtemp()+'/arc_db' 13 | self.arcs= shelve.open(self.arc_db_path) 14 | #self.arcs={} 15 | self.length=0 16 | self.storage_path = mkdtemp()+'/db' 17 | self.db = shelve.open(self.storage_path) 18 | #self.db={} 19 | def get_index(self,point): 20 | return self.pointsByPoint.get(point) 21 | def get_point_arcs(self,point): 22 | return self.arcsByPoint.get(point) 23 | def coincidence_lines(self,point): 24 | return self.coincidences.get(point) 25 | def peak(self,point): 26 | return self.coincidences.peak(point) 27 | def push(self,arc): 28 | self.arcs[str(self.length)]=arc 29 | self.length+=1 30 | return self.length 31 | def close(self): 32 | #pass 33 | self.db.close() 34 | remove(self.storage_path) 35 | self.arcs.close() 36 | remove(self.arc_db_path) 37 | def get_hash(self,arc): 38 | ourhash = sha1() 39 | ourhash.update(str(arc)) 40 | return ourhash.hexdigest() 41 | def check(self,arcs): 42 | a0 = arcs[0] 43 | a1 = arcs[-1] 44 | point = a0 if point_compare(a0, a1) < 0 else a1 45 | point_arcs = self.get_point_arcs(point) 46 | h = self.get_hash(arcs) 47 | if h in self.db: 48 | return int(self.db[h]) 49 | else: 50 | index = self.length 51 | point_arcs.append(arcs) 52 | self.db[h]=index 53 | self.db[self.get_hash(list(reversed(arcs)))]=~index 54 | self.push(arcs) 55 | return index 56 | 57 | -------------------------------------------------------------------------------- /Install/esri2open/topojson/bounds.py: -------------------------------------------------------------------------------- 1 | from mytypes import Types 2 | class Bounds(Types): 3 | def __init__(self): 4 | self.x0=self.y0=float('inf') 5 | self.x1=self.y1=-float('inf') 6 | def point (self,point): 7 | x = point[0] 8 | y = point[1] 9 | if x < self.x0: 10 | self.x0 = x 11 | if x > self.x1: 12 | self.x1 = x 13 | if y < self.y0: 14 | self.y0 = y 15 | if y > self.y1: 16 | self.y1 = y 17 | def bound(objects): 18 | b=Bounds() 19 | b.obj(objects) 20 | return b 21 | -------------------------------------------------------------------------------- /Install/esri2open/topojson/clockwise.py: -------------------------------------------------------------------------------- 1 | class Clock: 2 | def __init__(self,area): 3 | self.area=area 4 | def clock(self,feature): 5 | if 'geometries' in feature: 6 | feature['geometries'] = map(self.clock_geometry,feature['geometries']) 7 | elif 'geometry' in feature: 8 | feature['geometry']=self.clock_geometry(feature['geometry']) 9 | return feature 10 | def clock_geometry(self,geo): 11 | if 'type' in geo: 12 | if geo['type']=='Polygon' or geo['type']=='MultiLineString': 13 | geo['coordinates'] = self.clockwise_polygon(geo['coordinates']) 14 | elif geo['type']=='MultiPolygon': 15 | geo['coordinates'] = map(lambda x:self.clockwise_polygon(x),geo['coordinates']) 16 | elif geo['type']=='LineString': 17 | geo['coordinates'] = self.clockwise_ring(geo['coordinates']) 18 | return geo 19 | def clockwise_polygon(self,rings): 20 | return map(lambda x:self.clockwise_ring(x),rings) 21 | def clockwise_ring(self,ring): 22 | if self.area(ring) > 0: 23 | return list(reversed(ring)) 24 | else: 25 | return ring 26 | -------------------------------------------------------------------------------- /Install/esri2open/topojson/coordinatesystems.py: -------------------------------------------------------------------------------- 1 | # coding=utf8 2 | from math import sqrt, pi, cos, sin, atan2, tan, atan, asin 3 | 4 | PI4 = pi / 4 5 | RADIANS = pi / 180 6 | 7 | 8 | class BaseCoordinateSystem(object): 9 | name = "BaseCoordinateSystem" 10 | 11 | def format_distance(self, distance): 12 | return str(distance) 13 | 14 | def ring_area(self, ring): 15 | raise Exception('Not implemented') 16 | 17 | def triangle_area(self, triangle): 18 | raise Exception('Not implemented') 19 | 20 | def distance(self, x0, y0, x1, y1): 21 | raise Exception('Not implemented') 22 | 23 | def absolute_area(self, area): 24 | return abs(area) 25 | 26 | 27 | class Cartesian(BaseCoordinateSystem): 28 | name = "cartesian" 29 | 30 | def ring_area(self, ring): 31 | calc_area = lambda p1, p2: p1[1] * p2[0] - p1[0] * p2[1] 32 | # last and first 33 | area = calc_area(ring[-1], ring[0]) 34 | for i, p in enumerate(ring[1:]): # skip first so p is current and 35 | area += calc_area(p, ring[i]) # ring[i] is the previous 36 | return area * 0.5 37 | 38 | def triangle_area(self, triangle): 39 | return abs( 40 | (triangle[0][0] - triangle[2][0]) * (triangle[1][1] - triangle[0][1]) - 41 | (triangle[0][0] - triangle[1][0]) * (triangle[2][1] - triangle[0][1]) 42 | ) 43 | 44 | def distance(self, x0, y0, x1, y1): 45 | dx = x0 - x1 46 | dy = y0 - y1 47 | return sqrt(dx * dx + dy * dy) 48 | 49 | 50 | class Spherical(BaseCoordinateSystem): 51 | name = 'spherical' 52 | 53 | def haversin(self, x): 54 | return sin(x / 2) ** 2 55 | 56 | def format_distance(self, distance): 57 | km = distance * 6371.0 58 | if km > 1: 59 | return u"{:0.03f}km".format(km) 60 | else: 61 | return u"{:0.03f} ({0.03f}°)".format(km * 1000, distance * 180 / pi) 62 | 63 | def ring_area(self, ring): 64 | if len(ring) == 0: 65 | return 0 66 | area = 0 67 | p = ring[0] 68 | PI4 = 1 69 | lambda_ = p[0] * RADIANS 70 | phi = p[1] * RADIANS / 2.0 + PI4 71 | lambda0 = lambda_ 72 | cosphi0 = cos(phi) 73 | sinphi0 = sin(phi) 74 | for pp in ring[1:]: 75 | lambda_ = pp[0] * RADIANS 76 | phi = pp[1] * RADIANS / 2.0 + PI4 77 | # Spherical excess E for a spherical triangle with vertices: south pole, 78 | # previous point, current point. Uses a formula derived from Cagnoli’s 79 | # theorem. See Todhunter, Spherical Trig. (1871), Sec. 103, Eq. (2). 80 | dlambda = lambda_ - lambda0 81 | cosphi = cos(phi) 82 | sinphi = sin(phi) 83 | k = sinphi0 * sinphi 84 | u = cosphi0 * cosphi + k * cos(dlambda) 85 | v = k * sin(dlambda) 86 | area += atan2(v, u) 87 | #Advance the previous point. 88 | lambda0 = lambda_ 89 | cosphi0 = cosphi 90 | sinphi0 = sinphi 91 | return 2 * area 92 | 93 | def absolute_area(self, area): 94 | return area + 4 * pi if area < 0 else area 95 | 96 | def triangle_area(self, triangle): 97 | def distance(a, b): # why 2 implementations? I don't know, original has the same question in comments 98 | x0, y0, x1, y1 = [(n * RADIANS) for n in (a + b)] 99 | delta_lambda = x1 - x0 100 | return atan2( 101 | sqrt( 102 | (cos(x1) * sin(delta_lambda)) ** 2 + 103 | (cos(x0) * sin(x1) - sin(x0) * cos(x1) * cos(delta_lambda)) ** 2 104 | ), 105 | sin(x0) * sin(x1) + cos(x0) * cos(x1) * cos(delta_lambda) 106 | ) 107 | a = distance(triangle[0], triangle[1]) 108 | b = distance(triangle[1], triangle[2]) 109 | c = distance(triangle[2], triangle[0]) 110 | s = (a + b + c) / 2.0 111 | 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)))) 112 | 113 | def distance(self, x0, y0, x1, y1): 114 | x0, y0, x1, y1 = [(n * RADIANS) for n in [x0, y0, x1, y1]] 115 | return 2.0 * asin(sqrt(self.haversin(y1 - y0) + cos(y0) * cos(y1) * self.haversin(x1 - x0))) 116 | 117 | 118 | systems = {'cartesian': Cartesian(), 'spherical': Spherical()} -------------------------------------------------------------------------------- /Install/esri2open/topojson/hashtable.py: -------------------------------------------------------------------------------- 1 | from math import ceil, log 2 | 3 | def hasher(size): 4 | mask = int(size) - 1; 5 | def retFunc(point): 6 | if type(point)==type([]) and len(point)==2: 7 | key = (int(point[0]) + 31 * int(point[1])) | 0 8 | return (~key if key < 0 else key) & mask 9 | return retFunc 10 | 11 | class Hashtable: 12 | def __init__(self,size): 13 | self.size = 1 << int(ceil(log(size)/log(2))) 14 | self.table = map(lambda x:False,range(0,int(size))) 15 | self.h = hasher(size) 16 | 17 | def peak(self,key): 18 | matches = self.table[self.h(key)] 19 | if matches: 20 | for match in matches: 21 | if equal(match['key'], key): 22 | return match['values'] 23 | return None 24 | def get(self,key): 25 | index = self.h(key) 26 | if not index: 27 | return [] 28 | matches = self.table[index] 29 | if (matches): 30 | for match in matches: 31 | if equal(match['key'], key): 32 | return match['values'] 33 | else: 34 | matches = self.table[index] = [] 35 | values = [] 36 | matches.append({'key': key, 'values': values}) 37 | return values 38 | def equal(keyA, keyB): 39 | return keyA[0] == keyB[0] and keyA[1] == keyB[1] 40 | -------------------------------------------------------------------------------- /Install/esri2open/topojson/line.py: -------------------------------------------------------------------------------- 1 | from arcs import Arcs 2 | from utils import point_compare,is_point,Strut,mysterious_line_test 3 | 4 | 5 | class Line: 6 | def __init__(self,Q): 7 | self.arcs = Arcs(Q) 8 | def arc(self,current_arc, last=False): 9 | n = len(current_arc) 10 | if last and not len(self.line_arcs) and n == 1: 11 | point = current_arc[0] 12 | index = self.arcs.get_index(point) 13 | if len(index): 14 | self.line_arcs.append(index[0]) 15 | else: 16 | index.append(self.arcs.length) 17 | self.line_arcs.append(index[0]) 18 | self.arcs.push(current_arc) 19 | elif n > 1: 20 | self.line_arcs.append(self.arcs.check(current_arc)) 21 | 22 | def line(self,points,opened): 23 | self.line_arcs = []; 24 | n = len(points) 25 | current_arc = Strut() 26 | k = 0 27 | p=False 28 | t=False 29 | if not opened: 30 | points.pop() 31 | n-=1 32 | while k < n: 33 | t = self.arcs.peak(points[k]) 34 | if opened: 35 | break 36 | if p and not mysterious_line_test(p, t): 37 | tInP = all(map(lambda line:line in p,t)) 38 | pInT = all(map(lambda line:line in t,p)) 39 | if tInP and not pInT: 40 | k-=1 41 | break 42 | p = t 43 | k+=1 44 | # If no shared starting point is found for closed lines, rotate to minimum. 45 | if k == n and isinstance(p,list) and len(p) > 1: 46 | point0 = points[0] 47 | i = 2 48 | k=0 49 | while i 0: 52 | point0 = point 53 | k = i 54 | i+=1 55 | i = -1 56 | if opened: 57 | m = n-1 58 | else: 59 | m = n 60 | while i < m: 61 | i+=1 62 | point = points[(i + k) % n] 63 | p = self.arcs.peak(point) 64 | if not mysterious_line_test(p, t): 65 | tInP = all(map(lambda line: line in p,t)) 66 | pInT = all(map(lambda line: line in t,p)) 67 | if tInP: 68 | current_arc.append(point); 69 | self.arc(current_arc) 70 | if not tInP and not pInT and len(current_arc): 71 | self.arc(Strut([current_arc[-1], point])) 72 | if pInT and len(current_arc): 73 | current_arc = Strut([current_arc[-1]]) 74 | else: 75 | current_arc = Strut(); 76 | if not len(current_arc) or point_compare(current_arc[-1], point): 77 | current_arc.append(point) # skip duplicate points 78 | t = p 79 | self.arc(current_arc, True) 80 | return self.line_arcs 81 | def line_closed(self,points): 82 | return self.line(points,False) 83 | def line_open(self,points): 84 | return self.line(points,True) 85 | def map_func (self,arc): 86 | if len(arc)==2 and type(arc[0])==type(1): 87 | arc= [arc] 88 | i = 1 89 | n = len(arc) 90 | point = arc[0] 91 | x1 = point[0] 92 | x2= dx =y2 = dy=False 93 | y1 = point[1] 94 | points = [[int(x1), int(y1)]] 95 | while i < n: 96 | point = arc[i] 97 | if not is_point(point): 98 | i+=1 99 | continue 100 | x2 = point[0] 101 | y2 = point[1] 102 | dx = int(x2 - x1) 103 | dy = int(y2 - y1) 104 | if dx or dy: 105 | points.append([dx, dy]) 106 | x1 = x2 107 | y1 = y2 108 | i+=1 109 | return points 110 | def get_arcs (self): 111 | for num in range(0,self.arcs.length): 112 | yield self.map_func(self.arcs.arcs[str(num)]) 113 | self.arcs.close() 114 | -------------------------------------------------------------------------------- /Install/esri2open/topojson/mytypes.py: -------------------------------------------------------------------------------- 1 | 2 | GEOMETRY_TYPES = ( 3 | 'LineString', 4 | 'MultiLineString', 5 | 'MultiPoint', 6 | 'MultiPolygon', 7 | 'Point', 8 | 'Polygon', 9 | 'GeometryCollection' 10 | ) 11 | 12 | class Types: 13 | def Feature(self,feature): 14 | if 'geometry' in feature: 15 | self.geometry(feature['geometry']) 16 | def FeatureCollection(self,collection): 17 | for feature in collection['features']: 18 | self.Feature(feature) 19 | def GeometryCollection(self,collection): 20 | if 'geometry' in collection: 21 | for geometry in collection['geometries']: 22 | self.geometry(geometry) 23 | def LineString(self,lineString): 24 | self.line(lineString['coordinates']) 25 | def MultiLineString(self,multiLineString): 26 | for coordinate in multiLineString['coordinates']: 27 | self.line(coordinate) 28 | def MultiPoint(self,multiPoint): 29 | for coordinate in multiPoint['coordinates']: 30 | self.point(coordinate); 31 | def MultiPolygon(self,multiPolygon): 32 | for coordinate in multiPolygon['coordinates']: 33 | self.polygon(coordinate); 34 | def Point(self,point): 35 | self.point(point['coordinates']) 36 | def Polygon(self,polygon): 37 | self.polygon(polygon['coordinates']) 38 | def obj(self,obj): 39 | if obj == None : 40 | self.outObj = None 41 | elif not ('type' in obj): 42 | self.outObj = {} 43 | for fName in obj: 44 | self.outObj[fName]=self.FeatureCollection(obj[fName]) 45 | elif obj['type']=='Feature': 46 | self.outObj = self.Feature(obj) 47 | elif obj['type']=='FeatureCollection': 48 | self.outObj = self.FeatureCollection(obj) 49 | elif obj['type'] in GEOMETRY_TYPES: 50 | self.outObj = self.geometry(obj) 51 | return self.outObj 52 | def geometry(self,geometry): 53 | if not (geometry != None and geometry['type'] in GEOMETRY_TYPES): 54 | return None 55 | elif geometry['type']== 'LineString': 56 | return self.LineString(geometry) 57 | elif geometry['type']== 'MultiLineString': 58 | return self.MultiLineString(geometry) 59 | elif geometry['type']== 'MultiPoint': 60 | return self.MultiPoint(geometry) 61 | elif geometry['type']== 'MultiPolygon': 62 | return self.MultiPolygon(geometry) 63 | elif geometry['type']== 'Point': 64 | return self.Point(geometry) 65 | elif geometry['type']== 'Polygon': 66 | return self.Polygon(geometry) 67 | elif geometry['type']== 'GeometryCollection': 68 | return self.GeometryCollection(geometry) 69 | def point(*arguments): 70 | pass 71 | def line(self,coordinates): 72 | for coordinate in coordinates: 73 | self.point(coordinate) 74 | def polygon(self,coordinates): 75 | for coordinate in coordinates: 76 | self.line(coordinate) 77 | -------------------------------------------------------------------------------- /Install/esri2open/topojson/quantize.py: -------------------------------------------------------------------------------- 1 | from mytypes import Types 2 | 3 | class Quantize(Types): 4 | def __init__(self,x0,y0,kx,ky,distance): 5 | self.x0=x0 6 | self.y0=y0 7 | self.kx=kx 8 | self.ky=ky 9 | self.emax=0 10 | self.distance=distance 11 | def point(self,point): 12 | x1 = point[0] 13 | y1 = point[1] 14 | x = ((x1 - self.x0) * self.kx) 15 | y =((y1 - self.y0) * self.ky) 16 | ee = self.distance(x1, y1, x / self.kx + self.x0, y / self.ky + self.y0) 17 | if ee > self.emax: 18 | self.emax = ee 19 | point[0] = int(x) 20 | point[1] = int(y) -------------------------------------------------------------------------------- /Install/esri2open/topojson/simplify.py: -------------------------------------------------------------------------------- 1 | #from https://github.com/omarestrella/simplify.py 2 | from mytypes import Types 3 | 4 | def getSquareDistance(p1, p2): 5 | """ 6 | Square distance between two points 7 | """ 8 | dx = p1[0] - p2[0] 9 | dy = p1[1] - p2[1] 10 | 11 | return dx * dx + dy * dy 12 | 13 | 14 | def getSquareSegmentDistance(p, p1, p2): 15 | """ 16 | Square distance between point and a segment 17 | """ 18 | x = p1[0] 19 | y = p1[1] 20 | 21 | dx = p2[0] - x 22 | dy = p2[1] - y 23 | 24 | if dx != 0 or dy != 0: 25 | t = ((p[0] - x) * dx + (p[1] - y) * dy) / (dx * dx + dy * dy) 26 | 27 | if t > 1: 28 | x = p2[0] 29 | y = p2[1] 30 | elif t > 0: 31 | x += dx * t 32 | y += dy * t 33 | 34 | dx = p[0] - x 35 | dy = p[1] - y 36 | 37 | return dx * dx + dy * dy 38 | 39 | 40 | def simplifyRadialDistance(points, tolerance): 41 | length = len(points) 42 | prev_point = points[0] 43 | new_points = [prev_point] 44 | 45 | for i in range(length): 46 | point = points[i] 47 | 48 | if getSquareDistance(point, prev_point) > tolerance: 49 | new_points.append(point) 50 | prev_point = point 51 | 52 | if prev_point != point: 53 | new_points.append(point) 54 | 55 | return new_points 56 | 57 | 58 | def simplifyDouglasPeucker(points, tolerance): 59 | length = len(points) 60 | markers = [0] * length # Maybe not the most efficent way? 61 | 62 | first = 0 63 | last = length - 1 64 | 65 | first_stack = [] 66 | last_stack = [] 67 | 68 | new_points = [] 69 | 70 | markers[first] = 1 71 | markers[last] = 1 72 | 73 | while last: 74 | max_sqdist = 0 75 | 76 | for i in range(first, last): 77 | sqdist = getSquareSegmentDistance(points[i], points[first], points[last]) 78 | 79 | if sqdist > max_sqdist: 80 | index = i 81 | max_sqdist = sqdist 82 | 83 | if max_sqdist > tolerance: 84 | markers[index] = 1 85 | 86 | first_stack.append(first) 87 | last_stack.append(index) 88 | 89 | first_stack.append(index) 90 | last_stack.append(last) 91 | 92 | # Can pop an empty array in Javascript, but not Python, so check 93 | # the length of the list first 94 | if len(first_stack) == 0: 95 | first = None 96 | else: 97 | first = first_stack.pop() 98 | 99 | if len(last_stack) == 0: 100 | last = None 101 | else: 102 | last = last_stack.pop() 103 | 104 | for i in range(length): 105 | if markers[i]: 106 | new_points.append(points[i]) 107 | 108 | return new_points 109 | 110 | 111 | def simplify(points, tolerance=0.1, highestQuality=True): 112 | sqtolerance = tolerance * tolerance 113 | 114 | if not highestQuality: 115 | points = simplifyRadialDistance(points, sqtolerance) 116 | 117 | points = simplifyDouglasPeucker(points, sqtolerance) 118 | 119 | return points 120 | 121 | 122 | class Simplify(Types): 123 | def __init__(self, tolerance): 124 | self.tolerance = tolerance 125 | def Feature(self,feature): 126 | if 'geometry' in feature: 127 | feature['geometry'] = self.geometry(feature['geometry']) 128 | return feature 129 | def line(self,points): 130 | return simplify(points,self.tolerance) 131 | def polygon(self,coordinates): 132 | return map(self.line,coordinates) 133 | def GeometryCollection(self,collection): 134 | if collection.has_key('geometries'): 135 | collection['geometries'] = map(self.geometry,collection['geometries']) 136 | def LineString(self,lineString): 137 | lineString['coordinates'] = self.line(lineString['coordinates']) 138 | def MultiLineString(self,multiLineString): 139 | multiLineString['coordinates']=map(self.line,multiLineString['coordinates']) 140 | def MultiPolygon(self,multiPolygon): 141 | multiPolygon['coordinates'] = map(self.polygon,multiPolygon['coordinates']) 142 | def Polygon(self,polygon): 143 | polygon['coordinates']=self.polygon(polygon['coordinates']) 144 | -------------------------------------------------------------------------------- /Install/esri2open/topojson/stitchpoles.py: -------------------------------------------------------------------------------- 1 | from mytypes import Types 2 | 3 | class Stitch(Types): 4 | def polygon(self,polygon): 5 | for line in polygon: 6 | n = len(line) 7 | a = False 8 | b = False 9 | c = False 10 | i0 = -1 11 | i=0 12 | while i 180 + E or self.bounds.y0 < -90 - E or self.bounds.y1 > 90 + E 52 | if not self.system: 53 | if oversize: 54 | self.system = systems["cartesian"] 55 | else: 56 | self.system = systems["spherical"] 57 | if self.system == systems['spherical']: 58 | if self.bounds.x0 < -180 + E: 59 | self.bounds.x0 = -180 60 | if self.bounds.x1 > 180 - E: 61 | self.bounds.x1 = 180 62 | if self.bounds.y0 < -90 + E: 63 | self.bounds.y0 = -90 64 | if self.bounds.y1 > 90 - E: 65 | self.bounds.y1 = 90; 66 | if is_infinit(self.bounds.x0): 67 | self.bounds.x0 = 0 68 | if is_infinit(self.bounds.x1): 69 | self.bounds.x1 = 0; 70 | if is_infinit(self.bounds.y0): 71 | self.bounds.y0 = 0; 72 | if is_infinit(self.bounds.y1): 73 | self.bounds.y1 = 0; 74 | if not self.quantization: 75 | self.quantization = self.bounds.x1 + 1 76 | self.bounds.x0 = self.bounds.y0 = 0 77 | [self.kx,self.ky]=make_ks(self.quantization,self.bounds.x0,self.bounds.x1,self.bounds.y0,self.bounds.y1) 78 | self.quant = Quantize(self.bounds.x0,self.bounds.y0,self.kx,self.ky,self.system.distance) 79 | self.clock = Clock(self.system.ring_area) 80 | #Convert features to geometries, and stitch together arcs. 81 | class Topo(Types): 82 | def __init__(self,ln,id_func,property_transform): 83 | self.ln = ln 84 | self.id_func = id_func 85 | self.property_transform=property_transform 86 | def Feature (self,feature): 87 | geometry = feature["geometry"] 88 | if feature['geometry'] == None: 89 | geometry = {}; 90 | if 'id' in feature: 91 | geometry['id'] = feature['id'] 92 | if 'properties' in feature: 93 | geometry['properties'] = feature['properties'] 94 | return self.geometry(geometry); 95 | def FeatureCollection(self,collection): 96 | collection['type'] = "GeometryCollection"; 97 | collection['geometries'] = map(self.Feature,collection['features']) 98 | del collection['features'] 99 | return collection 100 | def GeometryCollection(self,collection): 101 | collection['geometries'] = map(self.geometry,collection['geometries']) 102 | def MultiPolygon(self,multiPolygon): 103 | multiPolygon['arcs'] = map(lambda poly:map(self.ln.line_closed,poly),multiPolygon['coordinates']) 104 | def Polygon(self,polygon): 105 | polygon['arcs'] = map(self.ln.line_closed,polygon['coordinates']) 106 | def MultiLineString(self,multiLineString): 107 | multiLineString['arcs'] = map(self.ln.line_open,multiLineString['coordinates']) 108 | def LineString(self,lineString): 109 | lineString['arcs'] = self.ln.line_open(lineString['coordinates']) 110 | def geometry(self,geometry): 111 | if geometry == None: 112 | geometry = {}; 113 | else: 114 | Types.geometry(self,geometry) 115 | geometry['id'] = self.id_func(geometry) 116 | if geometry['id'] == None: 117 | del geometry['id'] 118 | properties0 = geometry['properties'] 119 | if properties0: 120 | properties1 = {} 121 | del geometry['properties'] 122 | for key0 in properties0: 123 | if self.property_transform(properties1, key0, properties0[key0]): 124 | geometry['properties'] = properties1 125 | if 'arcs' in geometry: 126 | del geometry['coordinates'] 127 | return geometry; 128 | self.topo = Topo(self.ln,self.id_func,self.property_transform) 129 | AddMessage('looping through ' +str(self.feature_length)+' features again') 130 | for db in self.feature_db: 131 | for i in self.feature_db[db]: 132 | #AddMessage('on '+str(i)) 133 | self.tweak(self.feature_db[db],i) 134 | def dump(self,f): 135 | self.start() 136 | #print('writing') 137 | f.write('{"type":"Topology","bbox":') 138 | dump([self.bounds.x0, self.bounds.y0, self.bounds.x1, self.bounds.y1],f) 139 | f.write(',"transform":') 140 | dump({ 141 | 'scale': [1.0 / self.kx, 1.0 / self.ky], 142 | 'translate': [self.bounds.x0, self.bounds.y0] 143 | },f) 144 | #print('dumping objects') 145 | f.write(',"objects":') 146 | i =0 147 | AddMessage('one last time') 148 | for thing in self.get_objects(): 149 | i+=1 150 | #AddMessage('on ' + str(i) + ' for the last time') 151 | f.write(thing) 152 | #print('dumping arcs') 153 | f.write(',"arcs":[') 154 | first = True 155 | for arc in self.ln.get_arcs(): 156 | if first: 157 | first=False 158 | else: 159 | f.write(',') 160 | dump(arc,f) 161 | f.write(']}') 162 | def add(self,object,feature): 163 | if not (object in self.feature_db): 164 | path = self.feature_dir+'/'+object 165 | self.feature_path.append(path) 166 | self.feature_db[object]=shelve.open(path) 167 | storage = self.feature_db[object] 168 | if self.simplify: 169 | feature = self.simplify.Feature(feature) 170 | if self.stitch_poles: 171 | self.stitch_poles.Feature(feature) 172 | self.bounds.Feature(feature) 173 | self.feature_db[object][str(self.feature_length)]=feature 174 | self.feature_length+=1 175 | def tweak(self,db,i): 176 | feature = db[i] 177 | self.quant.Feature(feature) 178 | feature=self.clock.clock(feature) 179 | self.coincidences.Feature(feature) 180 | db[i] = feature 181 | def get_objects(self): 182 | firstDB = True 183 | yield '{' 184 | for db in self.feature_db: 185 | if firstDB: 186 | firstDB=False 187 | else: 188 | yield ',' 189 | first = True 190 | yield '"'+db+'":{"type":"GeometryCollection","geometries":[' 191 | for object in self.feature_db[db]: 192 | if first: 193 | first = False 194 | else: 195 | yield ',' 196 | yield dumps(self.topo.Feature(self.feature_db[db][object])) 197 | self.feature_db[db].close() 198 | yield ']}' 199 | for path in self.feature_path: 200 | remove(path) 201 | yield '}' 202 | def object_factory(self,object): 203 | return partial(self.add,object) 204 | 205 | def make_ks(quantization,x0,x1,y0,y1): 206 | [x,y]=[1,1] 207 | if quantization: 208 | if x1 - x0: 209 | x= (quantization - 1.0) / (x1 - x0) 210 | if y1 - y0: 211 | y=(quantization - 1.0) / (y1 - y0) 212 | return [x,y] 213 | -------------------------------------------------------------------------------- /Install/esri2open/topojson/utils.py: -------------------------------------------------------------------------------- 1 | def point_compare(a, b): 2 | if is_point(a) and is_point(b): 3 | return a[0] - b[0] or a[1] - b[1] 4 | #def is_point(p): 5 | # try: 6 | # float(p[0]), float(p[1]) 7 | # except (TypeError, IndexError): 8 | # return False 9 | is_point = lambda x : isinstance(x,list) and len(x)==2 10 | class Strut(list): 11 | def __init__(self,ite=[]): 12 | self.index=0 13 | list.__init__(self,ite) 14 | def is_infinit(n): 15 | return abs(n)==float('inf') 16 | E = 1e-6 17 | def mysterious_line_test(a, b): 18 | for arg in (a, b): 19 | if not isinstance(arg, list): 20 | return True 21 | return a == b 22 | -------------------------------------------------------------------------------- /Install/esri2open/utilities.py: -------------------------------------------------------------------------------- 1 | from arcpy import ListFields,Describe,SetProgressorLabel,SetProgressorPosition,GetCount_management, SetProgressor, AddMessage 2 | from os.path import splitext, split 3 | 4 | def getName(feature): 5 | name = splitext(split(feature)[1]) 6 | if name[1]: 7 | if name[1]==".shp": 8 | return name[0] 9 | else: 10 | return name[1][1:] 11 | else: 12 | return name[0] 13 | #utility functions we will call more then once 14 | 15 | #takes the input feature class and returns a dict with 16 | # the field name as key and field types as values 17 | def listFields(featureClass): 18 | fields=ListFields(featureClass) 19 | out=dict() 20 | for fld in fields: 21 | if (fld.name.lower() not in ('shape_length','shape_area','shape.len','shape.length','shape_len','shape.area') and fld.name.find(".")==-1): 22 | out[fld.name]=fld.type 23 | return out 24 | 25 | #takes the input geometry object and returns 26 | # a list of [name of shape field, name of shape type] 27 | def getShp(shp): 28 | desc = Describe(shp) 29 | return [desc.ShapeFieldName,desc.shapeType.lower()] 30 | 31 | #takes the fields object from above, tells you which is the OID 32 | def getOID(fields): 33 | for key, value in fields.items(): 34 | if value== u'OID': 35 | return key 36 | 37 | #for putting a % based status total up 38 | class statusMessage: 39 | def __init__(self,featureClass): 40 | self.featureCount = int(GetCount_management(featureClass).getOutput(0)) 41 | SetProgressor("step", "Found {0} features".format(str(self.featureCount)), 0, 100,1) 42 | AddMessage("Found "+str(self.featureCount)+" features") 43 | self.percent = 0 44 | self.current=0 45 | def update(self): 46 | self.current+=1 47 | newPercent = int((self.current*100)/self.featureCount) 48 | if newPercent != self.percent: 49 | self.percent=newPercent 50 | SetProgressorLabel("{0}% done".format(str(self.percent))) 51 | SetProgressorPosition(self.percent) 52 | 53 | #parse the properties, get rid of ones we don't want, i.e. null, or the shape 54 | def parseProp(row,fields, shp): 55 | out=dict() 56 | for field in fields: 57 | if (fields[field] != u'OID') and field.lower() not in ('shape_length','shape_area','shape.len','shape.length','shape_len','shape.area',shp.lower()) and row.getValue(field) is not None: 58 | if fields[field] == "Date": 59 | value = str(row.getValue(field).date()) 60 | elif fields[field] == "String": 61 | value = row.getValue(field).strip() 62 | else: 63 | value = row.getValue(field) 64 | if value != "": 65 | out[field]=value 66 | return out 67 | 68 | def getExt(fileName): 69 | split=splitext(fileName) 70 | if split[1]: 71 | return split[1][1:].lower() 72 | else: 73 | return False 74 | def getFileName(path): 75 | if path.endswith('.topojson'): 76 | return split(path)[1][:-9] 77 | def parseFieldType(name, esriType): 78 | if esriType.lower() in ("text","string","date"): 79 | return name+" text" 80 | elif esriType.lower() in ("short","long","integer"): 81 | return name+" integer" 82 | elif esriType.lower() in ("float","single","double"): 83 | return name+" real" 84 | else: 85 | return name+" blob" 86 | 87 | def zm(shp): 88 | desc = Describe(shp) 89 | return [desc.hasZ,desc.hasM] 90 | 91 | def makeInter(n): 92 | out = [] 93 | i = 0 94 | while i1: 105 | values = ["1: 117 | values = [" 3 | Esri2Open Add-In 4 | {dd6dff0f-4d31-4659-b1e0-cbcfe6a192d7} 5 | Export ArcGIS data to web-friendly formats: GeoJSON, 6 | CSV, SQLite, and JSON. 7 | 0.1 8 | Images\esri2open-logo_32x32.png 9 | 10 | 11 | 06/27/2013 12 | 13 | 14 | 15 | 17 | 18 | 19 | 24 | 30 | 36 | 37 | 38 | 39 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 50 | 51 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /esri2open.tbx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/project-open-data/esri2open/1d7b2cca6e30ecbc77830c381fa58e24e98d28f6/esri2open.tbx -------------------------------------------------------------------------------- /makeaddin.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | import zipfile 4 | 5 | current_path = os.path.dirname(os.path.abspath(__file__)) 6 | 7 | out_zip_name = os.path.join(current_path, 8 | os.path.basename(current_path) + ".esriaddin") 9 | 10 | BACKUP_FILE_PATTERN = re.compile(".*_addin_[0-9]+[.]py$", re.IGNORECASE) 11 | 12 | def looks_like_a_backup(filename): 13 | return bool(BACKUP_FILE_PATTERN.match(filename)) 14 | 15 | zip_file = zipfile.ZipFile(out_zip_name, 'w') 16 | for filename in ('config.xml', 'README.md', 'makeaddin.py'): 17 | zip_file.write(os.path.join(current_path, filename), filename) 18 | dirs_to_add = ['Images', 'Install'] 19 | for directory in dirs_to_add: 20 | for (path, dirs, files) in os.walk(os.path.join(current_path, directory)): 21 | archive_path = os.path.relpath(path, current_path) 22 | found_file = False 23 | for file in (f for f in files if not looks_like_a_backup(f)): 24 | archive_file = os.path.join(archive_path, file) 25 | print archive_file 26 | zip_file.write(os.path.join(path, file), archive_file) 27 | found_file = True 28 | if not found_file: 29 | zip_file.writestr(os.path.join(archive_path, 'placeholder.txt'), 30 | "(Empty directory)") 31 | zip_file.close() 32 | --------------------------------------------------------------------------------