Fork me on GitHub

gdal笔记--chapter7

使用GR进行矢量分析

1.计算相交区域面积的两种方法(对要素或图层操作)

from osgeo import gdal, ogr

water_ds = ogr.Open(r'')   #打开矢量数据,沼泽数据
water_lyr = water_ds.GetLayer(0)  #打开图层
water_lyr.SetAttributeFilter('WaterbdyID=1011327')  #属性选择
marsh_feat = water_lyr.GetNextFeature() #获取要素
marsh_geom = marsh_feat.geometry().Clone() #获取要素的几何体并clone

nola_ds = ogr.Open(r'')  #打开城市数据
nola_lyr = nola_ds.GetLayer(0)
nola_feat = nola_lyr.GetNextFeature()
nola_geom = nola_feat.geometry().Clone()

#相交操作
intersection = marsh_geom.Intersection(nola_geom)

#遍历要素计算相交区域面积
water_lyr.SetAttributeFilter("Feature != 'LaKe'") #限定为非湖泊
water_lyr.SetSpatialFilter(nola_geom) #市区内空间过滤,减少不必要要素,减少后续处理时间
wetlands_area = 0
for feat in water_lyr:
    intersect = feat.geometry().Intersection(nola_geom) #交集部分
    wetlands_area += intersect.GetArea()
pcnt = wetlands_area/nola_geom.GetArea()
print('{:.1%} of New Orleans is wetland'.format(pcnt))

#图层相交计算相交区域面积
water_lyr.SetAttributeFilter("Feature!='Lake'")
memory_driver = ogr.GetDriverByName('Memory')  #内存数据为临时数据
temp_ds = memory_driver.CreateDataSource('temp')
temp_lyr = temp_ds.CreateLayer('temp')
nola_lyr.Intersection(water_lyr, temp_lyr)#图层相交执行空间过滤,结果存储在临时图层

sql = 'SELECT SUM(OGR_GEOM_AREA) AS area FROM temp'  #查询area的sql语句
lyr = temp_ds.ExecuteSQL(sql)
pcnt = lyr.GetFeature(0).GetField('area')/nola_geom.GetArea() #获取area属性
print('{:.1%} of New Orleans is wetland'.format(pcnt))

2.创建缓冲区确定火山附近城市

#确定火山附近城市数量的有缺陷方法
shp_ds = ogr.Open(r'')
volcano_lyr = shp_ds.GetLayer('')  #打开火山和城市shp
cities_lyr = shp_ds.GetLayer('cities_albers')

memory_driver = ogr.GetDriverByName('memory')
memory_ds = memory_driver.CreateDataSource('temp')
buff_lyr = memory_ds.CreateLayer('buffer')  #创建临时数据作为缓冲区
buff_feat = ogr.Feature(buff_lyr.GetLayerDefn()) #创建缓冲区要素

#直接SetGeometry会让不同火山的缓冲区有叠加的可能,使得城市可能位于多个火山缓冲区导致重复。
#逐要素添加缓冲区
for volcano_feat in volcano_lyr:
    buff_geom = volcano_feat.geometry().Buffer(16000) #缓冲区操作
    tmp = buff_feat.SetGeometry(buff_geom) #要素添加几何体
    tmp = buff_lyr.CreateFeature(buff_feat)  #图层添加要素

result_lyr = memory_ds.CreateLayer('result')
buff_lyr.Intersection(cities_lyr, result_lyr) #求交集
print('Cities:{}'.format(result_lyr.GetFeatureCount()))

#更好的方法
volcano_lyr.ResetReading()  #回到图层起始位置
multipoly = ogr.Geometry(ogr.wkbMultiPolygon) #创建复合多边形
for volcano_feat in volcano_lyr:
    buff_geom = volcano_feat.geometry().Buffer(16000)
    multipoly.AddGeometry(buff_geom) #复合多边形添加缓冲区

#UnionCascaded方法把缓冲区合在一起,相同部分合并,避免了缓冲区的重复
cities_lyr.SetSpatialFilter(multipoly.UnionCascaded()) #UnionCascaded合并多边形
print('Cities:{}'.format(cities_lyr.GetFeatureCount()))

3.计算某火山和某城市距离

#计算某火山和某城市的距离(round计算几何体的距离)
volcano_lyr.SetAttributeFilter("NAME='Rainier'")
feat = volcano_lyr.GetNextFeature()
rainier = feat.geometry().Clone()

#属性过滤获取lyr范围,读取要素,读取几何体并Clone
cities_lyr.SetAttributeFilter("NAME='Seattle'")
feat = cities_lyr.GetNextFeature()
seattle = feat.geometry().Clone()

meters = round(rainier.Distance(seattle)) #计算几何体距离
miles = meters/1600 #英里
print('{} meters {} miles'.format(meters, miles))

4.各种操作忽略Z值

pt1_2d = ogr.Geometry(ogr.wkbPoint)
pt1_2d.AddPoint(15, 15)
pt2_2d = ogr.Geometry(ogr.wkbPoint)
pt2_2d.AddPoint(15, 19)
print(pt1_2d.Distance(pt2_2d))

pt1_25d = ogr.Geometry(ogr.wkbPoint)
pt1_25d.AddPoint(15, 15, 0)
pt2_25d = ogr.Geometry(ogr.wkbPoint)
pt2_25d.AddPoint(15, 19, 3)
print(pt1_25d.Distance(pt2_25d))
#结果相同,未对Z运算

ring = ogr.Geometry(ogr.wkbLinearRing) #创建环
ring.AddPoint(10,10)
ring.AddPoint(10,20)
ring.AddPoint(20,20)
ring.AddPoint(20,10)
poly_2d = ogr.Geometry(ogr.wkbPolygon) #创建多边形
poly_2d.AddGeometry(ring)
poly_2d.CloseRings() #多边形闭合
print(poly_2d.GetArea())
#2.5d同忽略Z
ring = ogr.Geometry(ogr.wkbLinearRing)
ring.AddPoint(10, 10, 0)
ring.AddPoint(10, 20, 0)
ring.AddPoint(20, 20, 10)
ring.AddPoint(20, 10, 10)
poly_25d = ogr.Geometry(ogr.wkbPolygon25D)
poly_25d.AddGeometry(ring)
poly_25d.CloseRings()
print(poly_25d.GetArea())

poly_2d.Contains(pt1_2d)
poly_25d.Contains(pt1_2d)
#叠加操作忽略Z

5.案例1:风力场选址

#7.3风力场选址
census_fn = r'' #人口普查数据
census_ds = ogr.Open(census_fn, True)
census_lyr = census_ds.GetLayer()
density_field = ogr.FieldDefn('popsqkm', ogr.OFTReal) #字段定义
census_lyr.CreateField(density_field) #给图层创建新字段
for row in census_lyr: #遍历图层要素
    pop = row.GetField('HD01_S001') #读取pop字段值
    sqkm = row.Geometry().GetArea()/1000000 #要素几何体获取面积
    row.SetField('popsqkm', pop/sqkm) #设置popsqkm字段值
    census_lyr.SetFeature(row) #图层添加要素

country_fn = r''
country_ds = ogr.Open(r'')
country_lyr = country_ds.GetLayer()
country_lyr.SetAttributeFilter("COUNTRY='Imperial Country'")
country_row = next(country_lyr) #读取要素
country_geom = country_row.geometry().Clone()
del country_ds

country_geom.TransformTo(census_lyr.GetSpatialRef())  #投影转换
census_lyr.SetSpatialFilter(country_geom)
census_lyr.SetAttributeFilter('popsqkm<0.5')
#空间和属性过滤完的census_lyr

wind_fn = r''
wind_ds = ogr.Open(wind_fn)
wind_lyr = wind_ds.GetLayer()
wind_lyr.SetAttributeFilter('WPC>=3')  #打开风力数据属性过滤

out_fn = r''
out_ds = ogr.GetDriverByName('ESRI Shapefile').CreateDataSource(out_fn) #创建输出文件
out_lyr = out_ds.CreateLayer('wind_farm', wind_lyr.GetSpatialRef(), ogr.wkbPolygon)
out_lyr.CreateField(ogr.FieldDefn('wind', ogr.OFTInteger)) #创建字段
out_lyr.CreateField(ogr.FieldDefn('popsqkm', ogr.OFTReal))
out_row = ogr.Feature(out_lyr.GetLayerDfen()) #新建空要素

#将人口普查数据与风力数据相交
for census_row in census_lyr:
    census_geom = census_row.geometry()
    census_geom = census_geom.Intersection(country_geom) #人口普查数据和县界相交
    wind_lyr.SetSpatialFilter(census_geom)#空间过滤风力lyr

    print('Intersecting census tract with {0} wind polygons'.format(wind_lyr.GetFeatureCount()))

    if wind_lyr.GetFeatureCount()>0:
        out_row.SetField('popsqkm', census_row.GetField('popsqkm'))
        for wind_row in wind_lyr:
            wind_geom = wind_row.geometry()
            if census_geom.Intersect(wind_geom):  #人口普查数据和风力多边形相交
                new_geom = census_geom.Intersection(wind_geom)
                out_row.SetField('wind', wind_row.GetField('WPC'))
                out_row.SetGeometry(new_geom)
                out_lyr.CreateFeature(out_row)

#将小的多边形合并成大的多边形
folder = ''
ds = ogr.Open(folder, True)
in_lyr = ds.GetLayerByName('wind_farm') #源图层
out_lyr = ds.CreateLayer('wind_farm2', in_lyr.GetSpatialRef(), ogr.wkbPolygon) #输出图层
out_row = ogr.Feature(out_lyr.GetLayerDefn()) #新建输出空要素

multipoly = ogr.Geometry(ogr.wkbMultiPolygon) #创建复合多边形
for in_row in in_lyr: #遍历图层每个要素
    in_geom = in_row.geometry().Clone()
    in_geom_type = in_geom.GetGeometryType() #查询类型是多边形还是复合多边形
    if in_geom_type == ogr.wkbPolygon:
        multipoly.AddGeometry(in_geom)  #多边形则添加到新复合多边形
    elif in_geom_type == ogr.wkbMultiPolygon:
        for i in range(in_geom.GetGeometryCount()): #复合多边形,则in_geom.GetGeometryCount()遍历
            multipoly.AddGeometry(in_geom.GetGeometryRef(i)) #打断成多边形后添加到新复合多边形

multipoly = multipoly.UnionCascaded() #合并操作

for i in range(multipoly.GetGeometryCount()):  #遍历新复合多边形
    poly = multipoly.GetGeometryRef(i)
    if poly.GetArea() > 1000000:  #保留面积大于...的多边形几何体
        out_row.SetGeometry(poly) #输出要素添加多边形几何体
        out_lyr.CreateFeature(out_row)  #图层添加要素

del ds

6.案例2:动物跟踪数据

#7.4动物跟踪数据

#从csv文件新建shapefile文件
from osgeo import ogr, osr

csv_fn = r''
shp_fn = r''
sr = osr.SpatialReference(osr.SRS_WKT_WGS84) #设置shpfile投影信息

#新建shpfile文件并创建字段存储时间、日期,新建要素
shp_ds = ogr.GetDriverByName('ESRI Shapefile').CreateDataSource(shp_fn)
shp_lyr = shp_ds.CreateLayer('albatross_dd', sr, ogr.wkbPoint)
shp_lyr.CreateField(ogr.FieldDefn('tag_id', ogr.OFTString))
shp_lyr.CreateField(ogr.FieldDefn('timestamp', ogr.OFTString))
shp_row = ogr.Feature(shp_lyr.GetLayerDefn())

csv_ds = ogr.Open(csv_fn)  #ogr打开scv文件
csv_lyr = csv_ds.GetLayer() #csv文件获取图层
for csv_row in csv_lyr:
    x = csv_row.GetFieldAsDouble('location-long')
    y = csv_row.GetFieldAsDouble('location-lat')
    shp_pt = ogr.Geometry(ogr.wkbPoint)
    shp_pt.AddPoint(x, y)
    tag_id = csv_row.GetField('individual-local-identifier')
    timestamp = csv_row.GetField('timestamp')
    shp_row.SetGeometry(shp_pt)
    shp_row.SetField('tag_id', tag_id)
    shp_row.SetField('timestamp', timestamp)
    shp_lyr.CreateFeature(shp_row)
del csv_ds, shp_ds

#有坏点需要删除
shp_ds = ogr.Open(shp_fn, True)
shp_lyr = shp_ds.GetLayer()
shp_lyr.SetSpatialFilterRect(-1, -1, 1, 1) #设置坏点的空间范围
for shp_row in shp_lyr:
    shp_lyr.DeleteFeature(shp_row.GetFID()) #获取FID删除要素
shp_lyr.SetSpatialFilter(None) #不再设置空间过滤
shp_ds.ExecuteSQL('REPACK ' + shp_lyr.GetName()) #回收空间
shp_ds.ExecuteSQL('RECOMPUTE EXTENT ON' + shp_lyr.GetName())#更新元数据空间范围
del shp_ds

#投影转换,转换成兰伯特投影米制单位
#ogr2ogr -f 'ESRI Shapefile' -t_srs '+proj=lcc.....' albatross_lambert.shp yuanshi.shp

#从属性列获取唯一值(从图层获取指定字段值)
def get_unique(datasource, layer_name, field_name):
    sql = 'SELECT DISTINCT {0} FROM {1}'.format(field_name, layer_name) #搜寻图层中对应的某一字段值
    lyr = datasource.ExecuteSQL(sql)
    values = []  #存储图层要素对应的字段值
    for row in lyr:
        values.append(row.GetField(field_name))  #获取该鸟所有时间的字段值
    datasource.ReleaseResultSet(lyr)  #ReleaseResultSet(<result_layer>)是将查询结果释放,在执行下一条SQL语句之前一定要先释放。
    return values

#计算相邻点间距离
ds = ogr.Open(r'')
lyr = ds.GetLayerByName('albatross_lambert')
lyr.CreateField(ogr.FieldDefn('distance', ogr.OFTReal))  #创建举例子段

tag_ids = get_unique(ds, 'albatross_lambert', 'tag_id') #获取对应ID鸟的字段值
for tag_id in tag_ids:  #遍历所有鸟种tag_id
    print('Processing ' + tag_id)
    lyr.SetAttributeFilter("tag_id='{}'".format(tag_id)) #属性查询
    row = next(lyr) #获取第一个要素
    previous_pt = row.geometry().Clone() #要素几何体
    previous_time = row.GetField('timestamp') #该要素对应时间
    for row in lyr: #遍历图层要素(单个鸟的点位)
        current_time = row.GetField('timestamp') #获取当前时间
        if current_time < previous_time:
            raise Exception('Timestamps out of order')
        current_pt = row.geometry().Clone() 
        distance = current_pt.Distance(previous_pt) #计算距离
        row.SetField('distance', distance) #设置distance字段值
        lyr.SetFeature(row) #图层添加要素
        previous_pt = current_pt #更新点位信息
        previous_time = current_time
del ds

#查找地点间最大飞行速度和所耗时间
from datetime import datetime
from osgeo import ogr

date_format = '%Y-%M-%d %H:%M:%S.%f'
ds = ogr.Open(r'')
lyr = ds.GetLayerByName('albatross_lambert')
for tag_id in get_unique(ds, 'albatross', 'tag_id'):  #遍历鸟种
    max_speed = 0  #初始化最大速度
    lyr.SetAttributeFilter("tag_id={}".format(tag_id)) #属性查询
    row = next(lyr)  #第一个时间点
    ts = row.GetField('timestamp')
    previous_time = datetime.strptime(ts, date_format)
    for row in lyr:
        ts = row.GetField('timestamp')
        current_time = datetime.strptime(ds, date_format) #转换成可计算的时间格式
        elapsed_time = current_time - previous_time
        hours = elapsed_time.total_seconds()/3600
        distance = row.GetField('distance')
        speed = distance/hours  #计算speed
        max_speed = max(max_speed, speed) #更新max_speed
    print('Max speed for {0}:{1}'.format(tag_id, max_speed))

#为每只鸟创建凸包多边形
ds = ogr.Open(r'')
pt_lyr = ds.GetLayerByName('albatross_lambert')
poly_lyr = ds.CreateLayer('albatross_ranges', pt_lyr.GetSpatialRef(), ogr.wkbPoint)  #输出图层
id_field = ogr.FieldDefn('tag_id', ogr.OFTString)
area_field = ogr.FieldDefn('tag_id', ogr.OFTString)
area_field.SetWidth(30)
area_field.SetPrecision(4)
poly_lyr.CreateFields([id_field, area_field])  #新建id\area字段
poly_row = ogr.Feature(poly_lyr.GetLayerDefn())

for tag_id in get_unique(ds, 'albatross_lambert', 'tag_id'):
    print('Processing ' + tag_id)
    pt_lyr.SetAttributeFilter("tag_id = '{}'".format(tag_id))  #为lyr进行属性过滤
    locations = ogr.Geometry(ogr.wkbMultiPoint) #为每只鸟(tag_id)创建复合多边形几何体
    for pt_row in pt_lyr: #遍历原图层要素,把单个ID的所有点添加到一个几何对象
        locations.AddGeometry(pt_row.geometry().Clone())  #把原要素几何对象添加到新几何对象

    homerange = locations.ConvexHull()  #所有点创建凸包多边形
    poly_row.SetGeometry(homerange) #poly要素设置几何体
    poly_row.SetField('tag_id', tag_id)
    poly_row.SetField('area', homerange.GetArea())
    poly_lyr.CreateFeature(poly_row) #创建poly要素
del ds

#创建地理区域分割的凸包多边形(有点难)
#核心思想:不断读入要素,pt_locations用于存储连续相同位置的点,一旦位置发生改变pt_locations置0。
#此外,用poly_lyr不断添加凸多边形。
#疑惑:这样看起来如果点位是3个陆地、岛屿、陆地的情况下,pt_locations会新建,那么先前同类型的点就清空了。这样添加到最后每侧应该是若干个的凸包多边形吧?没有成为整体。这是为什么?
#自问自答:可能因为点位本身就是按照时间顺序添加的,不可能一下子在岛屿,一下又飞到了陆地,所以点位置是连续的,这样最后就是两个大凸多边形。
land_lyr = ds.GetLayer('land_lambert')
land_row = next(land_lyr)
land_poly = land_row.geometry().Buffer(100000)  #创建陆地缓冲区

for tag_id in get_unique(ds, 'albatross_lambert', 'tag_id'): #遍历每只鸟
    print('Processing ' + tag_id)
    pt_lyr.SetAttributeFilter("tag_id = '{}".format(tag_id))  #属性过滤id
    pt_locations = ogr.Geometry(ogr.wkbMultiPoint) #创建多点
    last_location = None 
    for pt_row in pt_lyr:  #遍历图层的每个点
        pt = pt_row.geometry().Clone()
        if not land_poly.Contains(pt):  #如果点不在缓冲区,跳过这个点
            continue
        if pt.GetX() < -2800000:  #岛屿区域
            location = 'island'
        else:  #大陆区域
            location = 'mainland'
        if location != last_location:  #如果当前点位不同于上个点位
            if pt_locations.GetGeometryCount() > 2:  #除当前点以外的点,至少有3个,能组成多边形时(位置发生变化时,创建多边形)
                homerange = pt_locations.ConvexHull() #凸包多边形
                poly_row.SetGeometry(homerange) #设置几何体和属性值
                poly_row.SetField('tag_id', tag_id)
                poly_row.SetField('area', homerange.GetArea())
                poly_row.SetField('location', last_location)
                poly_lyr.CreateFeature(poly_row) #添加要素,这里poly_lyr把相同的点组成的凸多边形不断地添加到里面
            #点数不够3直接新建多点对象,添加点并更新点位
            pt_locations = ogr.Geometry(ogr.wkbMultiPoint) #新建操作用于在点位发生变化时重新计算凸包多边形,避免不同位置点存在一起
            last_location = location
        #点位相同,那么继续添加多点对象,直到不再相同再进行凸包操作
        pt_locations.AddGeometry(pt)

    if pt_locations.GetGeometryCount() > 2:  #由于上面只在点位发生变化的时候创建多边形,所以需要单独保存最后一个组点创建多边形
        homerange = pt_locations.ConvexHull()
        poly_row.SetGeometry(homerange)
        poly_row.SetField('tag_id', tag_id)
        poly_row.SetField('area', homerange.GetArea())
        poly_row.SetField('location', last_location)
        poly_lyr.CreateFeature(poly_row)

#计算所有岛屿访问时公共区域所占面积的比例
ds = ogr.Open(r'')
lyr = ds.GetLayerByName('albatross_ranges2')
lyr.SetAttributeFilter("tag_id = '1163-1163' and location = 'island'")  #属性过滤,id和island区域
row = next(lyr) #读取第一个要素
all_areas = row.geometry().Clone() #读取第一个要素,初始化all_areas\common_areas
common_areas = row.geometry().Clone()
for row in lyr: #遍历图层的要素,作交集和并集运算
    all_areas = all_areas.Union(row.geometry())  #更新并集区域
    common_areas = common_areas.Intersection(row.geometry())  #更新交集区域
percent = common_areas.GetArea()/all_areas.GetArea()*100  #面积百分比
print('Percent of all area used in every visit: {0}'.format(percent))

 

posted @ 2020-04-08 22:15  Rser_ljw  阅读(1118)  评论(0编辑  收藏  举报