-
Notifications
You must be signed in to change notification settings - Fork 17
Expand file tree
/
Copy pathGeoJson2Rhino.py
More file actions
276 lines (241 loc) · 8.91 KB
/
GeoJson2Rhino.py
File metadata and controls
276 lines (241 loc) · 8.91 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
"""
Allows for the translation of GeoJSON data to Rhino objects
GeoJSON _does_ support 3d, so this can take 3d coordinates for 3d GeoJSONs
The GeoJSON Format Specification can be found here:
http://geojson.org/geojson-spec.html
The RhinoCommon SDK (where all the Rhino.Geometry objects are documented) is
here:
http://www.rhino3d.com/5/rhinocommon/
I have decided to extend the GeoJSON specification by adding support for one
more type of geometry that would be really useful in Rhino (and elsewhere),
the Mesh. Here is an example of a json Mesh:
{"type": "Feature",
"geometry": {
"type": "Mesh",
"coordinates": [
[3.43, 54.234, 2343.23],
[...],
[...],
...,
]
"faces": [
[0,3,2],
[5,32,1],
...,
]
}
"properties": {"prop0": "value0"}
}
Example of Use:
>>> import GeoJson2Rhino as geoj
>>> myGeoJson = '''
{ "type": "FeatureCollection",
"features": [
{ "type": "Feature",
"geometry": {"type": "Point", "coordinates": [102.0, 0.5]},
"properties": {"prop0": "value0"}
},
{ "type": "Feature",
"geometry": {
"type": "LineString",
"coordinates": [
[102.0, 0.0], [103.0, 1.0], [104.0, 0.0], [105.0, 1.0]
]
},
"properties": {
"prop0": "value0",
"prop1": 0.0
}
},
{ "type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [
[ [100.0, 0.0], [101.0, 0.0], [101.0, 1.0],
[100.0, 1.0], [100.0, 0.0] ]
]
},
"properties": {
"prop0": "value0",
"prop1": {"this": "that"}
}
}
]
}'''
>>> guidList = geoj.load(myGeoJson) #stores guids of new rhino objects
"""
# Import standard library modules
import json
# Import Rhino modules
import Rhino
from Rhino.Geometry import *
from scriptcontext import doc
# import .NET libraries
import System
def addRhinoLayer(layerName, layerColor=System.Drawing.Color.Black):
"""Creates a Layer in Rhino using a name and optional color. Returns the
index of the layer requested. If the layer
already exists, the color is updated and no new layer is created."""
docLyrs = doc.Layers
layerIndex = docLyrs.Find(layerName, True)
if layerIndex == -1:
layerIndex = docLyrs.Add(layerName,layerColor)
else: # it exists
layer = docLyrs[layerIndex] # so get it
if layer.Color != layerColor: # if it has a different color
layer.Color = layerColor # reset the color
return layerIndex
def PointToRhinoPoint(coordinates):
if len(coordinates) > 2:
z = coordinates[2]
else:
z = 0.0
x, y = coordinates[0], coordinates[1]
return Point3d(x, y, z)
def MultiPointToRhinoPoint(coordinates):
rhPointList = []
for point in coordinates:
rhPointList.append(PointToRhinoPoint(point))
return rhPointList
def MeshToRhinoMesh(coordinates, faces):
rhMesh = Mesh()
for point in coordinates:
rhPoint = PointToRhinoPoint(point)
rhMesh.Vertices.Add(rhPoint)
for face in faces:
i, j, k = tuple(face)
mFace = MeshFace(i, j, k)
rhMesh.Faces.AddFace(mFace)
rhMesh.Normals.ComputeNormals()
rhMesh.Compact()
return rhMesh
def LineStringToRhinoCurve(coordinates):
rhPoints = MultiPointToRhinoPoint(coordinates)
return Curve.CreateControlPointCurve(rhPoints, 1)
def MultiLineStringToRhinoCurve(coordinates):
rhCurveList = []
for lineString in coordinates:
rhCurveList.append(LineStringToRhinoCurve(lineString))
return rhCurveList
def PolygonToRhinoCurve(coordinates):
# each ring is a separate list of coordinates
ringList = []
for ring in coordinates:
ringList.append(LineStringToRhinoCurve(ring))
return ringList
def MultiPolygonToRhinoCurve(coordinates):
polygonList = []
for polygon in coordinates:
polygonList.append(PolygonToRhinoCurve(polygon))
return polygonList
def GeometryCollectionToParser(geometries):
pass # I need to figure this one out still
def addPoint(rhPoint, objAtt):
return doc.Objects.AddPoint(rhPoint, objAtt)
def addPoints(rhPoints, objAtt):
guidList = []
for rhPoint in rhPoints:
guidList.append(doc.Objects.AddPoint(rhPoint, objAtt))
return guidList
def addCurve(rhCurve, objAtt):
return doc.Objects.AddCurve(rhCurve, objAtt)
def addCurves(rhCurves, objAtt):
guidList = []
for curve in rhCurves:
guidList.append(addCurve(curve, objAtt))
return guidList
def addPolygon(ringList, objAtt):
# for now this just makes curves
# but maybe it should make TrimmedSrfs
# or should group the rings
return addCurves(ringList, objAtt)
def addPolygons(polygonList, objAtt):
guidList = []
for polygon in polygonList:
# !! Extending the guid list !!!
guidList.extend(addPolygon(polygon, objAtt))
return guidList
def addMesh(rhMesh, objAtt):
return doc.Objects.AddMesh(rhMesh, objAtt)
geoJsonGeometryMap = {
'Point':(PointToRhinoPoint, addPoint),
'MultiPoint':(MultiPointToRhinoPoint, addPoints),
'LineString':(LineStringToRhinoCurve, addCurve),
'MultiLineString':(MultiLineStringToRhinoCurve, addCurves),
'Polygon':(PolygonToRhinoCurve, addPolygon),
'MultiPolygon':(MultiPolygonToRhinoCurve, addPolygons),
'Mesh':(MeshToRhinoMesh, addMesh),
'GeometryCollection':(GeometryCollectionToParser),
}
def setUserKeys(properties, objAttributes):
for key in properties:
objAttributes.SetUserString(key, str(properties[key]))
return objAttributes
def jsonToRhinoCommon(jsonFeature):
# deal with the geometry
geom = jsonFeature['geometry']
geomType = geom['type'] # this will return a mappable string
coordinates = geom['coordinates']
# if this is a mesh, pass the faces
if geomType == 'Mesh':
faces = geom['faces']
rhFeature = geoJsonGeometryMap[geomType][0](coordinates, faces)
# translate the coordinates to Rhino.Geometry objects
else:
rhFeature = geoJsonGeometryMap[geomType][0](coordinates)
return rhFeature
def addJsonFeature(jsonFeature, objAttributes):
# deal with the properties
if jsonFeature['properties']:
objAttributes = setUserKeys(jsonFeature['properties'], objAttributes)
geomType = jsonFeature['geometry']['type']
rhFeature = jsonToRhinoCommon(jsonFeature)
# return the GUID(s) for the feature
return geoJsonGeometryMap[geomType][1](rhFeature, objAttributes)
def processGeoJson(parsedGeoJson,
destinationLayer=None,
destinationLayerColor=System.Drawing.Color.Black):
# get the features
jsonFeatures = parsedGeoJson['features']
guidResults = []
# set up object attributes
for jsonFeature in jsonFeatures: # for each feature
att = Rhino.DocObjects.ObjectAttributes()
# setup layer if requested
if destinationLayer != None:
att.LayerIndex = addRhinoLayer(destinationLayer,
destinationLayerColor)
guidResults.append(addJsonFeature(jsonFeature, att))
# return all the guids
return guidResults
def load(rawJsonData,
destinationLayer=None,
destinationLayerColor=System.Drawing.Color.Black):
# if the data already appears to be a dict literal ...
if type(rawJsonData) == dict:
jsonData = rawJsonData
else: # otherwise, just try to load it
jsonData = json.loads(rawJsonData)
# if this is just a GeoJSON ...
if jsonData["type"] == "FeatureCollection":
# process the GeoJSON, pass the layer and color in
return processGeoJson(jsonData, destinationLayer,
destinationLayerColor)
# or if this is a set of layers from PostSites ...
elif jsonData["type"] == "LayerCollection":
# make a list for all the guids
allResults = []
layersList = jsonData['layers']
for layer in layersList: # for each layer
name = layer['name'] # get the name
if 'color' in layer: # get the color if it exists
color = layer['color']
else:
color = destinationLayerColor # or just make it black
geoJson = layer['contents'] # get the GeoJSON for this layer
# make it
layerResults = processGeoJson( geoJson, name, color )
allResults.append(layerResults)
return allResults
else:
return "This doesn't look like correctly formatted GeoJSON data.\nI'm not sure what to do with it, sorry."