【问题标题】: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
    相关资源
    最近更新 更多