【问题标题】:Can polygonzo for google maps handle interior rings?谷歌地图的多边形可以处理内环吗?
【发布时间】:2014-01-10 22:58:06
【问题描述】:

我已经将一个 python 脚本与 ArcGIS arcpy 放在一起,用于创建 polygonzo 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

polygonzo 可以处理内环吗?

更新: 我非常感谢您的回复 Michael Geary 先生!但是,我无法使用 json 模块让您的 python 脚本工作。其中有一些错误,我在上面对其进行了编辑,但它吐出了一个空白文档。可能是我不够努力。在查看了带有内环的多面体在 json 格式中应该是什么样子的示例之后,我回到了我的 python 脚本的工作上(是的,如果不使用 json 模块,让 json 有效有点困难)。我添加了更多的 cmets,所以如果你有时间,你可以使用 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 确实可以使用更多的 cmets 来更快地理解这一切。

【问题讨论】:

    标签: python json google-maps geojson arcpy


    【解决方案1】:

    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,其中包含希望以北地区的更正数据。 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 )
    

    【讨论】:

    • 我从您的 Python 脚本 Michael Geary 中编辑了一些错误。你的意思是用 json.dumps 代替 json.dump 吗?
    • @razor_nate - 感谢修复,非常感谢。我的意思是使用json.dump()(不是dumps()),因为我的想法是写一个文件。但我忘了包括文件参数!现在添加了。
    • Michael Geary - 我用修改后的 python 脚本更新了我的问题。
    • @razor_nate - 很酷,所以你现在可以使用了吗?这比使用哪种方法生成 JSON 数据更重要。但是关于生成空 JSON 文件的脚本,你有没有看到我对最后一行的更改,所以它调用了json.dump(geojson,jsonfile)?如果它只是在那里调用json.dumps,你确实会得到一个空文件。不幸的是,我没有 ArcGIS,因此无法调试此代码,否则我很乐意帮助您使用 json 模块调试版本。此外,我听到您大声而清晰地听到 PolyGonzo 文档的缺乏,并将解决该问题! :-)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-07-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-03
    相关资源
    最近更新 更多