Last active
April 28, 2021 04:56
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# | |
# 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...' |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
information : http://www.grasshopper3d.com/forum/topics/geojson-python-script