如何区分地理定位折线上方和下方的点?

问题描述

我有两个地理数据框-一个带有折线,另一个带有点。直到现在,我仍在处理单个多段线和几个this点。我试图以某种方式类似于描述为here的结果。

import numpy as np
import math
import matplotlib.pyplot as plt
import matplotlib.mlab as mlab
import pandas as pd
import geopandas as gpd
from shapely import wkt

roadpd = pd.read_csv('roads.csv',delimiter=';')

roadpd['geom'] = roadpd['geom'].apply(wkt.loads)
roadgpd = gpd.GeoDataFrame(roadpd,geometry=roadpd['geom'])
print(roadgpd)

addresspd = pd.read_csv('addresses.csv',delimiter=';')

addresspd['geom'] = addresspd['geom'].apply(wkt.loads)
addressgpd = gpd.GeoDataFrame(addresspd,geometry=addresspd['geom'])
print(addressgpd)

   ROA_ID                                               geom                                           geometry
0      23  LInesTRING (16.8978861 52.417438,16.8971243 5...  LInesTRING (16.89789 52.41744,16.89712 52.417...
     ADG_ID                                         geom                   geometry
0   6641801   POINT (16.89397537268492 52.4186416491154)  POINT (16.89398 52.41864)
1   6641802  POINT (16.89458531052842 52.41814952579504)  POINT (16.89459 52.41815)
2   6641803  POINT (16.89499192732443 52.41803648483478)  POINT (16.89499 52.41804)
3   6641804  POINT (16.89532584370729 52.41794434305021)  POINT (16.89533 52.41794)
4   6641805  POINT (16.89553809175901 52.41786913837524)  POINT (16.89554 52.41787)
5   6641806  POINT (16.89429797664177 52.41856009093589)  POINT (16.89430 52.41856)
6   6641807  POINT (16.89397725037543 52.41832458465358)  POINT (16.89398 52.41832)
7   6641808  POINT (16.89376733989525 52.41815994989702)  POINT (16.89377 52.41816)
8   6641809    POINT (16.89507474142366 52.418304817806)  POINT (16.89507 52.41830)
9   6641810  POINT (16.89417332654704 52.41827195239621)  POINT (16.89417 52.41827)
10  6641811  POINT (16.89432223101175 52.41829509557626)  POINT (16.89432 52.41830)

到目前为止,我无法提出任何合理的解决方案。

under_curve = addressgpd['geom'] < roadgpd['geom']

与先前的帖子想法类似,但遇到明显的错误

Exception has occurred: ValueError
Can only compare identically-labeled Series objects

我刚刚开始使用Python进行编程,因此也许可以采用一种非常不同的方法解决问题。任何帮助或建议将不胜感激。

解决方法

此问题是由于数据帧没有相同的索引值引起的。在此link中,它说明了当“ .em不同时, 比较运算符引发ValueError时如何处理。 ”。可以尝试以下操作:

under_curve = addressgpd['geom'].values < roadgpd['geom'].values
,

其他答案中的Mel已解释了您发现错误的原因。要解决此问题,需要一些数据排序和密集化技术,我将在下面提供的代码中以注释的形式对其进行解释。

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import geopandas as gpd
from shapely import wkt
#from cartopy import crs as ccrs

def xy_to_linecode(x1,y1):
    """
    This creates a WKT linestring code
    """
    line_code = "LINESTRING("
    for ix,(x,y) in enumerate(zip(x1,y1)):
        line_code += str(x) + " " + str(y)
        if ix<len(x1)-1:
            line_code += ","
        if ix==len(x1)-1:
            line_code += ")"
    return line_code

def xy_to_pntlist(x2,y2):
    """
    This creates a list of WKT point codes
    """
    pnt_head = "POINT("
    all_pnts = []
    for ix,y) in enumerate(zip(x2,y2)):
        pnt_code = pnt_head + str(x) + " " + str(y) + ")"
        all_pnts.append(pnt_code)
    return all_pnts

# Part 1 (make-up data and create geoDataFrames)
# ------
# create data for a line-string
# x1 must be sorted small to large
x1 = [15,17,19]
y1 = [52,51,54]

d1 = {'col1': [1,],'wkt': [
         xy_to_linecode(x1,y1)
         #'LINESTRING (15 52,17 51,19 54)',]
    }

# this will be 1 row dataframe
d1f = pd.DataFrame( data=d1 )

# make a geodataframe from d1f (1 LineString)
# `shapely.wkt.loads` converts WKT code to geometry
d1f['geom'] = d1f['wkt'].apply(wkt.loads)
d1f_geo = gpd.GeoDataFrame(d1f,geometry=d1f['geom'])

# create dataframe for points
# x2 must be sorted small to large
x2 = [15.2,16.0,16.8,17.2,18.0]
y2 = [52,52,53,51]
ids = [101,102,103,104,105]
d2 = {'id': ids,'wkt': xy_to_pntlist(x2,y2)
      #[ 'POINT (15.2 52)',#  'POINT (16.0 51)',#  'POINT (16.8 52)',#  'POINT (17.2 53)',#  'POINT (18.0 51)']
    }
d2f = pd.DataFrame( data=d2 )

# make a geodataframe from d2f (several Points)
# `shapely.wkt.loads` converts WKT code to geometry
d2f['geom'] = d2f['wkt'].apply(wkt.loads)
d2f_geo = gpd.GeoDataFrame(d2f,geometry=d2f['geom'])

# Since (x1,y1) and (x2,y2) are intentionally avoided to use
#   assuming working with geodataframes from this steps on ...
#   we must get (x1,y2) from the dataframes

# Part 2 (make use of the geoDataFrames)
# ------
# This will get (x1,y1) from d1f_geo,equivalent with (x1,y1) of the original data
xss = []
yss = []
for ea in d1f_geo.geometry.values:
    # expect d1f_geo to have 'LineString' only
    xs,ys = [],[]
    if ea.geom_type == 'LineString':
        xs.append(ea.xy[0])
        ys.append(ea.xy[1])
    #print(xs)
    #print(ys)
    xss.append(xs[0])
    yss.append(ys[0])

# intentionally avoid using original (x1,y1)
x1 = xss[0]
y1 = yss[0]

# this will get (x2,y2) from d2f_geo,equivalent with (x2,y2) from the original data
x2s,y2s = [],[]
for ea in d2f_geo.geometry.values:
    # expect d2f_geo to cobtain 'POINT' only
    x2s.append(ea.x)
    y2s.append(ea.y)
    #print(ea.x,ea.y)

# intentionally avoid using original (x2,y2)
x2 = x2s
y2 = y2s

# Part 3 (visualization)
# ------
# if Cartopy is used to plot the data,...
# crs1 = ccrs.PlateCarree()
# fig,ax = plt.subplots(subplot_kw={'projection': crs1})
# fig,ax = plt.subplots(1,1,figsize=(8,5),subplot_kw={'projection': crs1})
# note: axes' labels will not be plotted in this case

fig,5))

# plot the single line in `d1f_geo`
kwargs={'linewidth': 1.5,'linestyles': '--','alpha': 0.6}
d1f_geo.plot(color='black',ax=ax,**kwargs)

# xfill will contain all positions in x1 and x2
# that is,x1 or x2 is subset of xfill
xfill = np.sort(np.concatenate([x1,x2]))

# densify y1,y2 data to get equal shape data
y1fill = np.interp(xfill,x1,y1)  # for the lineString's vertices
y2fill = np.interp(xfill,x2,y2)  # for the series of points

# at this state,shapes of the arrays are equal,# xfill.shape: (8,)
# y1fill.shape: (8,)
# y2fill.shape: (8,)

# the densified `line` data (xfill,y1fill) now have 8 vertices
# the densified `points` data (xfill,y2fill) also have 8 points

# filled color is painted here to visualize how the points/vertices are created
# if you dont need it,change True to False
if True:
    ax.fill_between(xfill,y1fill,y2fill,where=y1fill < y2fill,interpolate=True,color='dodgerblue',alpha=0.1)
    ax.fill_between(xfill,where=y1fill > y2fill,color='crimson',alpha=0.1)

# plot numbers at linestring's vertices
# if you dont need this,change True to False
if True:
    id = 0
    for x,y in zip(xfill,y1fill):
        id += 1
        ax.text(x,y,str(id),color='blue')

# point is under or above the line ?
# this only works if `y2fill` and `y1fill` have equal shape
under_curve = None
if y2fill.shape == y1fill.shape:
    under_curve = y2fill < y1fill

# prep list of colors,red (above line),green (below line)
colors = np.array(['green' if b else 'red' for b in under_curve])

# select only original points from (xfill,y2fill) to plot as green and red dots
# the extra (densified) points are plotted in gray
# label: "original" or "densified" are plotted at the points
for x,tof,cl in zip(xfill,under_curve,colors):
    tf1 = x in x2 and y in y2
    if tf1:
        ax.scatter(x,c=cl)  # red or green
        ax.text(x,"original",color=cl,alpha=0.65)
    else:
        # the (additional/densified) interpolated point created by `np.interp()`
        # plot them in gray color
        ax.scatter(x,c='lightgray',edgecolor='gray',linewidth=0.2)
        ax.text(x,"densified",color='gray',alpha=0.75)

plt.show()

输出图:

point_under