我将一个带有ArcGIS arcpy的python脚本放在一起,用于创建多边形json多边形(http://code.google.com/p/polygonzo/)。这是我的python脚本......
import os, string, arcpy
arcpy.env.overwriteOutput = True
layer = "C:\\Other\\Shapefiles\\Geo500K_JSON\\GEOLOGY_500K_Project.shp"
output = "C:\\Other\\Shapefiles\\Geo500K_JSON\\"
outfile = output + "Geo500K.json"
jsonFile = open(outfile,'w')
jsonFile.write('var geo = {\n')
jsonFile.write('\t"type": "FeatureCollection",\n')
jsonFile.write('\t"features": [\n')
idfield = "ORIG_LABEL"
shape_field = arcpy.Describe(layer).shapeFieldName
rows = arcpy.SearchCursor(layer,"","","",idfield + " A")
row = rows.next()
while row:
geostring = '' #for each lat/lng pt
geolist = [] # array for storing individual geostrings
ringList = [] #array for storing geolist array with geostrings separated by commas
partList = [] #array for storing partlist, final array used
shapeString = ''
jsonFile.write('\t\t{"type": "Feature", ')
extent = row.Shape.extent
ne = str(extent.XMax) + ',' + str(extent.YMax)
sw = str(extent.XMin) + ',' + str(extent.YMin)
jsonFile.write('"bbox": [' + sw + ', ' + ne + '],')
jsonFile.write('"properties":{')
geoLabel = str(row.getValue(idfield))
jsonFile.write('"label": "' + geoLabel + '", ')
geoName = str(row.getValue("FM_NAME"))
jsonFile.write('"name": "' + geoName + '", ')
lithType = str(row.getValue("LithType"))
jsonFile.write('"lithType": "' + lithType + '", ')
rank = str(row.getValue("Rank"))
jsonFile.write('"rank": "' + rank + '", ')
lithName = str(row.getValue("LithName"))
jsonFile.write('"lithName": "' + lithName + '", ')
ageType = str(row.getValue("AgeType"))
jsonFile.write('"ageType": "' + ageType + '", ')
minAge = str(row.getValue("MinAge"))
jsonFile.write('"minAge": "' + minAge + '", ')
maxAge = str(row.getValue("MaxAge"))
jsonFile.write('"maxAge": "' + maxAge + '", ')
part = row.getValue(shape_field).centroid
jsonFile.write('"center":[' + str(part.X) + ',' + str(part.Y) + '],')
jsonFile.write('"centroid":[' + str(part.X) + ',' + str(part.Y) + ']},')
jsonFile.write('"geometry":{"type":"MultiPolygon","coordinates":[[[')
feat = row.shape
for p in range(feat.partCount):
pInt = p
part = feat.getPart(p)
pt = part.next()
while pt:
lat = str(round(pt.Y,6))
lon = str(round(pt.X,6))
geostring = '[' + lon + ',' + lat + ']'
geolist.append(geostring)
pt = part.next()
#if now following point go to the next part which should be an interior ring.
if not pt:
ringList.append(',' .join(geolist))
geostring = ''
geolist = []
pt = part.next()
if pt:
print 'Interior Ring: ' + geoLabel
partList.append(',' .join(ringList))
ringList = []
shapeString = ']], [[' .join(partList)
jsonFile.write(shapeString)
jsonFile.write(']]]}},\n')
row = rows.next()
#jsonFile.seek(-1, os.SEEK_END)
#jsonFile.truncate()
jsonFile.write('\t]\n')
jsonFile.write('}')
jsonFile.close()
del row, rows
当脚本遇到内环时,它只会打印一个警告。我不知道如何处理它们。不幸的是,我使用的许多多边形都有内环。我使用一个有内环的多边形组合了一个测试图。这是它的样子...... http://www.geology.ar.gov/test/test-polygonzo.html
多边形可以处理内圈吗?
更新: 我非常感谢Michael Geary先生的回复!但是,我无法使用json模块来运行你的python脚本。它有一些错误,我在上面进行了编辑,但它会发出一个空白文档。也许我没有努力。在回顾了你的内部环多面体在json格式中应该是什么样子的例子之后,我又回到了我的python脚本(是的,使用json模块获得json有点困难)。我添加了更多评论,所以也许,如果你有时间,你可以使用json模块让你的脚本工作 - 我希望看到一个有效的例子。这是我最后的python脚本......
import os, string, arcpy
arcpy.env.overwriteOutput = True
layer = "C:\\Other\\Shapefiles\\Geo500K_JSON\\GEOLOGY_500K_kn.shp"
output = "C:\\Other\\Shapefiles\\Geo500K_JSON\\"
outfile = output + "Geo500K_knTest.json"
jsonFile = open(outfile,'w')
jsonFile.write('var geo = {\n')
jsonFile.write('\t"type": "FeatureCollection",\n')
jsonFile.write('\t"features": [\n')
idfield = "ORIG_LABEL"
shape_field = arcpy.Describe(layer).shapeFieldName
rows = arcpy.SearchCursor(layer,"","","",idfield + " A")
row = rows.next()
#loop through the attribute table
while row:
jsonFile.write('\t\t{"type": "Feature", \n')
extent = row.Shape.extent
ne = str(extent.XMax) + ',' + str(extent.YMax)
sw = str(extent.XMin) + ',' + str(extent.YMin)
jsonFile.write('\t\t"bbox": [' + sw + ', ' + ne + '],\n')
jsonFile.write('\t\t"properties":{\n')
geoLabel = str(row.getValue(idfield))
jsonFile.write('\t\t\t"label": "' + geoLabel + '", \n')
geoName = str(row.getValue("FM_NAME"))
jsonFile.write('\t\t\t"name": "' + geoName + '", \n')
lithType = str(row.getValue("LithType"))
jsonFile.write('\t\t\t"lithType": "' + lithType + '", \n')
rank = str(row.getValue("Rank"))
jsonFile.write('\t\t\t"rank": "' + rank + '", \n')
lithName = str(row.getValue("LithName"))
jsonFile.write('\t\t\t"lithName": "' + lithName + '", \n')
ageType = str(row.getValue("AgeType"))
jsonFile.write('\t\t\t"ageType": "' + ageType + '", \n')
minAge = str(row.getValue("MinAge"))
jsonFile.write('\t\t\t"minAge": "' + minAge + '", \n')
maxAge = str(row.getValue("MaxAge"))
jsonFile.write('\t\t\t"maxAge": "' + maxAge + '", \n')
centroid = row.getValue(shape_field).centroid
jsonFile.write('\t\t\t"center":[' + str(centroid.X) + ',' + str(centroid.Y) + '], \n')
jsonFile.write('\t\t\t"centroid":[' + str(centroid.X) + ',' + str(centroid.Y) + '] \n')
jsonFile.write('\t\t\t}, \n') #end of properties
jsonFile.write('\t\t"geometry":{\n\t\t\t"type":"MultiPolygon",\n\t\t\t"coordinates":[\n')
feat = row.shape #get the shape/geography of the row in the attribute table
partnum = 1
#loop through the parts of the polygon (some may have more that one part)
for p in range(feat.partCount):
jsonFile.write('\t\t\t\t[\n\t\t\t\t\t[\n')
jsonFile.write('\t\t\t\t\t\t//Part ' + str(partnum) + '\n')
jsonFile.write('\t\t\t\t\t\t//Outer ring of Part ' + str(partnum) + '\n')
part = feat.getPart(p) #return an array of point objects for particular part
pt = part.next() #return specific pt object of array
innerRingNum = 1
#loop through each pt object/vertex of part
while pt:
lat = round(pt.Y,7) #get latitude of pt object and round to 7 decimal places
lon = round(pt.X,7) #get longitude of pt object and round to 7 decimal places
jsonFile.write('\t\t\t\t\t\t[' + str(lon) + ',' + str(lat) + '],\n') #assemble [lon,lat]
pt = part.next() #go to next pt object to continue loop
#if no following point go to the next part which should be an interior ring.
if not pt:
#we've got an interior ring so let's loop through the vertices of the ring
pt = part.next()
if pt:
jsonFile.seek(-3, os.SEEK_END)
jsonFile.truncate() #remove trailing comma
jsonFile.write('\n\t\t\t\t\t],\n')
jsonFile.write('\t\t\t\t\t[\n')
jsonFile.write('\t\t\t\t\t\t//Inner ring ' + str(innerRingNum) + ' of Part ' + str(partnum) + '\n')
print 'Interior Ring: ' + geoLabel
innerRingNum += 1
partnum += 1
jsonFile.seek(-3, os.SEEK_END)
jsonFile.truncate() #remove trailing comma
jsonFile.write('\n\t\t\t\t\t]\n\t\t\t\t],\n')
jsonFile.seek(-3, os.SEEK_END)
jsonFile.truncate() #remove trailing comma
jsonFile.write('\n\t\t\t]\n\t\t\t}\n\t\t},\n')
row = rows.next()
jsonFile.seek(-3, os.SEEK_END)
jsonFile.truncate() #remove trailing comma
jsonFile.write('\n\t]\n')
jsonFile.write('}')
jsonFile.close()
del row, rows
我还要补充一点,我对polygonzo印象深刻,并且愿意与他人分享。但是,您提供的javascript和python确实可以使用更多注释来更快地理解它。
答案 0 :(得分:0)
PolyGonzo的作者在这里,对不起我直到现在才遇到你的问题。
我不知道这是否仍然有用,但我看了你的测试页。
PolyGonzo确实支持内圈,但GeoJSON数据中没有内圈。
MultiPolygon example中的GeoJSON spec中有一个内环的示例。不幸的是它的格式很差,所以这是一个缩进和评论的版本:
{
"type": "MultiPolygon",
"coordinates": [
// First polygon of the multipolygon
[
// Outer ring of the first polygon (there is no inner ring)
[
[ 102.0, 2.0 ],
[ 103.0, 2.0 ],
[ 103.0, 3.0 ],
[ 102.0, 3.0 ],
[ 102.0, 2.0 ]
]
],
// Second polygon of the multipolygon
[
// Outer ring of the second polygon
[
[ 100.0, 0.0 ],
[ 101.0, 0.0 ],
[ 101.0, 1.0 ],
[ 100.0, 1.0 ],
[ 100.0, 0.0 ]
],
// Inner ring of the second polygon
[
[ 100.2, 0.2 ],
[ 100.8, 0.2 ],
[ 100.8, 0.8 ],
[ 100.2, 0.8 ],
[ 100.2, 0.2 ]
]
// You could have additional inner rings here
]
]
}
换句话说,MultiPolygon的coordinates
属性是一个多边形数组。每个多边形又是一个环阵列。这些环中的每一个都是一组坐标对(如果你有高度信息等,则为三元组)。
对于给定的多边形,第一个环是外环,任何其他环都是内环。
在你的MultiPolygon中,每个多边形只有一个环,因此PolyGonzo将其解释为外环。
在查看了你的数据之后,我可以看到发生了这种情况:对于MultiPolygon中的每个多边形,你都拥有外环和的所有点任何内圈中的点都在一个大阵列中。
文件中的第三个多边形就是一个很好的例子。这是希望以北的大区。通过这部分GeoJSON数据,我发现了四个内环。 (我通过在地图上缩放到具有虚假线条的地方,以及通过查看大跳跃的坐标来找到它们。)
我在GeoJSON中手动拆分数组,以便这些内环拥有自己的数组,并且它可以很好地修复。
这是fiddle,其中包含Hope以北地区的更正数据。 GeoJSON数据内嵌在小提琴的JavaScript代码中,因此您可以看到我更改的内容。我也在Arkadelphia附近看到类似的问题,但没有纠正它。
现在关于您生成JSON数据的方式......
我 强烈 建议您不要通过将代码正在执行的大量JSON文本粘贴在一起来生成JSON。
相反,在Python中创建一个表示整个GeoJSON结构的对象(dict),然后调用json.dump()
或json.dumps()
将整个结构转换为JSON。
这使事情变得更容易。它会自动保证你的JSON是有效的 - 它已经存在了,但我敢打赌你必须努力才能到达那里,对吗? ;-) json.dump()
使这一点变得微不足道。
它还应该更容易避免像这种情况一样的问题,你打算为外环和内环放出单独的数组,但是它们意外地被卡在一个阵列中。
这是您的代码部分转换为使用此技术。因为我不熟悉arcpy,所以我没有完成整个事情,但是这应该给你一个想法:
import os, string, arcpy, json
arcpy.env.overwriteOutput = True
layer = "C:\\Other\\Shapefiles\\Geo500K_JSON\\GEOLOGY_500K_kn.shp"
output = "C:\\Other\\Shapefiles\\Geo500K_JSON\\"
outfile = output + "Geo500K_knTest_C2.json"
idfield = "ORIG_LABEL"
shape_field = arcpy.Describe(layer).shapeFieldName
features = []
geojson = {
'type': 'FeatureCollection',
'features': features
}
rows = arcpy.SearchCursor(layer,"","","",idfield + " A")
for row in rows:
geostring = '' #for each lat/lng pt
geolist = [] # array for storing individual geostrings
ringList = [] #array for storing geolist array with geostrings separated by commas
partList = [] #array for storing partlist, final array used
shapeString = ''
extent = row.Shape.extent
centroid = row.getValue(shape_field).centroid
coordinates = []
feature = {
'type': 'Feature',
'bbox': [ extent.XMin, extent.YMin, extent.XMax, extent.YMax ],
'properties': {
'label': str(row.getValue(idfield)),
'name': str(row.getValue("FM_NAME")),
'lithType': str(row.getValue("LithType")),
'rank': str(row.getValue("Rank")),
'lithName': str(row.getValue("LithName")),
'ageType': str(row.getValue("AgeType")),
'minAge': str(row.getValue("MinAge")),
'maxAge': str(row.getValue("MaxAge")),
'center': [ centroid.X, centroid.Y ],
'centroid': [ centroid.X, centroid.Y ]
},
'geometry': {
'type': 'MultiPolygon',
'coordinates': coordinates
}
}
feat = row.shape
for p in range(feat.partCount):
part = feat.getPart(p)
pt = part.next()
while pt:
lat = str(round(pt.Y,6))
lon = str(round(pt.X,6))
geostring = '[' + lon + ',' + lat + ']'
geolist.append(geostring)
pt = part.next()
#if no following point go to the next part which should be an interior ring.
if not pt:
ringList.append(',' .join(geolist))
geostring = ''
geolist = []
pt = part.next()
if pt:
print 'Interior Ring: '
partList.append('],[' .join(ringList))
ringList = []
features.append( feature )
shapeString = ']],[[' .join(partList)
coordinates.append(shapeString)
with open(outfile,'wb') as jsonFile:
json.dump( geojson, jsonFile )