Skip to content

Instantly share code, notes, and snippets.

@Alliages
Last active April 28, 2021 04:56
Show Gist options
  • Save Alliages/8dbfce7cdd24383342b0 to your computer and use it in GitHub Desktop.
Save Alliages/8dbfce7cdd24383342b0 to your computer and use it in GitHub Desktop.
GEOJSON_export python script for Grasshopper : A Plugin that export surfaces and its attributes to a GEOJSON file
#
# GEOJSON_export: A Plugin that export surfaces and its attributes to a GEOJSON file by Guillaume Meunier
#
# -----------------------------------
# Need pygeoj.py It must be in your script's folder
# by Karim Bahgat https://github.com/karimbahgat
# -----------------------------------
# TODO :
# use something like : https://github.com/karimbahgat/PyCRS
# -----------------------------------
#
# Copyright (c) 2016, Guillaume Meunier <alliages@gmail.com>
# GEOJSON_export is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published
# by the Free Software Foundation; either version 3 of the License,
# or (at your option) any later version.
#
# GEOJSON_export is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with GEOJSON_export; If not, see <http://www.gnu.org/licenses/>.
#
# @license GPL-3.0+ <http://spdx.org/licenses/GPL-3.0+>
"""
export surfaces and its attributes to GEOJSON file
#######
pygeoj.py must be in your script's folder
#######
version 1.3 few bugfixes
version 1.2 create multipolygon (with holes and multiple objects)
version 1.1 resolved problem with geometry check in qgis by closing completely polygon
-
Args:
path: path where to export
filename: filename of your JSON without its extension (GEOJSON added)
using_keys: which key attributes to export, 'id' by default
s_names: values of 'id' key
s_keys: normally each surfaces user attributes' keys
s_values: normally each surfaces user attributes' values
s_vertex: vertex of each surfaces
run: do the export
Returns:
out: various information
coordiantes: coordinates in JSON format
properties: properties in JSON format
"""
ghenv.Component.Name = "GEOJSON_export"
ghenv.Component.NickName = 'geojson'
ghenv.Component.Message = 'VER 1.3\n31_5_2016'
ghenv.Component.Category = "Extra"
ghenv.Component.SubCategory = ""
try: ghenv.Component.AdditionalHelpFromDocStrings = "1"
except: pass
from System import Object
from Grasshopper import DataTree
from Grasshopper.Kernel.Data import GH_Path
import pygeoj #pygeoj.py must be in your script's folder
def tree_to_list(input, retrieve_base = lambda x: x):
"""Returns a list representation of a Grasshopper DataTree"""
"""by Giulio Piacentino, https://gist.github.com/piac/ef91ac83cb5ee92a1294"""
def extend_at(path, index, simple_input, rest_list):
target = path[index]
if len(rest_list) <= target: rest_list.extend([None]*(target-len(rest_list)+1))
if index == path.Length - 1:
rest_list[target] = list(simple_input)
else:
if rest_list[target] is None: rest_list[target] = []
extend_at(path, index+1, simple_input, rest_list[target])
all = []
for i in range(input.BranchCount):
path = input.Path(i)
extend_at(path, 0, input.Branch(path), all)
return retrieve_base(all)
def coordinates(c):
c_out = "(" + c[0] + "," + c[1] + ")"
if c[0] == c[1] == "inner" :#inner loop case
c_out = "],["
return c_out
def create_coordinates():
geom = []
ve = tree_to_list(s_vertex)
if len(ve[0]) == 1 : #case if only one object
ve = ve[0]
for i in range(len(ve)):
c_start = "" #first coordinate to close the polygon
ve_s = "[["
is_not_inner = True #check wether we have an inner geometry
for j in range(len(ve[i])):
#create coordinates
c = ve[i][j].split(",")
if c[0] == c[1] == "inner" :#inner loop case
is_not_inner = False
ve_s = ve_s[:-1]
ve_s = ve_s + coordinates(c) + ","
if j == 0:
c_start = coordinates(c) #create coordinate to close the polygon
#print c_start
if c[0] == c[1] == "inner" :#inner loop case
ve_s = ve_s[:-1]
if is_not_inner: #this is automatically done with inner geometry
ve_s = ve_s + c_start + "," #closing polygon
ve_s = ve_s[:-1]
ve_s = ve_s + "]]"
geom.append(ve_s)
return geom
def create_properties():
prop = []
my_values = [] #futur values excluding from list input
my_keys = [] #futur values excluding from list input
using_keys.insert(0,"id") #mandatory id attributes = object name
k = tree_to_list(s_keys)
va = tree_to_list(s_values)
if (k == [[[]]]) or (k == []): #if empty
k = [[]] * len(s_names)
va = [[]] * len(s_names)
for i in range(len(s_names)):
my_values[:] = []
my_keys[:] = []
pos = -1
#empty case
try:
k[i].insert(0,"id")
va[i].insert(0,s_names[i])
except:
k[i] = ["id"]
va[i] = [""]
for j in range(len(using_keys)): # delete attributes that are not in the list
try:
pos = k[i].index(using_keys[j])
except ValueError:
pos = -1
if pos <> -1:
my_keys.append(k[i][pos])
my_values.append(va[i][pos])
tmp = dict(zip(my_keys,my_values))
prop.append(tmp)
return prop
def parseTree(input,i): #get tree depth
if isinstance (input, list):
i = parseTree(input[0],i+1)
return(i)
else:
return(i)
def export(coord,prop,my_path,my_name):
newfile = pygeoj.new()
newfile.define_crs(type="name",name="urn:ogc:def:crs:EPSG::2154")
for i in range(len(coord)):
kl = 0 #just in case
exec("k="+coord[i])
kl = parseTree(k,0) #get k depth
if len(k) > 1 : #MULTIPOLYGON
if kl > 2 : #standard case
newfile.add_feature(geometry=pygeoj.Geometry(type="MultiPolygon", coordinates=k), properties=prop[i])
else : #sometimes (one object)
newfile.add_feature(geometry=pygeoj.Geometry(type="MultiPolygon", coordinates=[k]), properties=prop[i])
else : #POLYGON
if kl > 2 : #sometimes
newfile.add_feature(geometry=pygeoj.Geometry(type="Polygon", coordinates=k[0]), properties=prop[i])
else : #simple case
newfile.add_feature(geometry=pygeoj.Geometry(type="Polygon", coordinates=k), properties=prop[i])
z = newfile.save(my_path+my_name+".geojson")
return "saved"
##MULTIPOLYGON
def find_duplicates(prop):
seen = set()
pos_uniq = []
pos_dup = []
for i in range(len(prop)):
x = prop[i].get("id")
if x not in seen:
pos_uniq.append(i)
seen.add(x)
else:
pos_dup.append(i)
return [pos_uniq,pos_dup]
def remove_dup_properties(prop,pos_uniq):
prop_uniq = []
for i in range(len(pos_uniq)):
prop_uniq.append(prop[pos_uniq[i]])
return prop_uniq
def remove_dup_coordinates(coord,pos_uniq):
coord_uniq = []
for i in range(len(pos_uniq)):
exec ("k="+coord[pos_uniq[i]])#tranform back to array
j = str(k[0]) #remove one bracket and convert back to string
coord_uniq.append(coord[pos_uniq[i]])
return coord_uniq
def get_dup_coord(coord,prop,pos_dup,prop_uniq):
coord_to_add_tot = []
seen = set()
for i in range(len(prop_uniq)):
coord_to_add = []
id = prop_uniq[i].get("id")
for j in range(len(pos_dup)):
id_dup = prop[pos_dup[j]].get("id")
if id == id_dup:
exec("k="+coord[pos_dup[j]])#tranform back to array
r = str(k[0]) #remove one bracket and convert back to string
coord_to_add.append(coord[pos_dup[j]])
coord_to_add_tot.append(coord_to_add)
return coord_to_add_tot
def add_coord(coord_uniq,coord_to_add):
coord = []
for i in range(len(coord_uniq)):
c = coord_uniq[i]
exec("k="+c)#tranform back to array
r = [k]
c = str(r)
if coord_to_add[i]:
for j in range(len(coord_to_add[i])):
if coord_to_add[i][j]:
exec("l="+coord_to_add[i][j])
r.append(l)
c = str(r)
coord.append(c)
return coord
##END MULTIPOLYGON
if run:
if path == None:
msg_e = "You need a path name"
print msg_e
ghenv.Component.AddRuntimeMessage(gh.GH_RuntimeMessageLevel.Warning, msg_e)
else:
coord = create_coordinates()
prop = create_properties()
##MULTIPOLYGON
all_pos = find_duplicates(prop)
if len(all_pos[1]) > 0 :
pos_uniq = all_pos[0]
pos_dup = all_pos[1]
prop_uniq = remove_dup_properties(prop,pos_uniq)
coord_uniq = remove_dup_coordinates(coord,pos_uniq)
coord_to_add = get_dup_coord(coord,prop,pos_dup,prop_uniq)
coord_multi = add_coord(coord_uniq,coord_to_add)
coord = coord_multi
prop = prop_uniq
##END MULTIPOLYGON
if filename == None:
filename = "export"
print export(coord,prop,path,filename)
coordinates = str(coord)
properties = str(prop)
print "success"
else:
print 'Set run to true...'
@Alliages
Copy link
Author

Alliages commented Dec 3, 2015

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment