web-dev-qa-db-fra.com

Coordonnée du point le plus proche sur une ligne

Il existe une polyligne avec une liste de coordonnées des sommets = [(x1, y1), (x2, y2), (x3, y3), ...] et un point (x, y). Dans Shapely, geometry1.distance(geometry2) renvoie la distance la plus courte entre les deux géométries. 

>>> from shapely.geometry import LineString, Point
>>> line = LineString([(0,0),(5,7),(12,6)])         #geometry2
>>> list(line.coords)
[(0.0, 0.0), (5.0, 7.0), (12.0, 6.0)]
>>> p = Point(4,8)     #geometry1
>>> list(p.coords)
[(4.0, 8.0)]
>>> p.distance(line)
1.4142135623730951

Mais je dois aussi trouver la coordonnée du point sur la ligne la plus proche du point (x, y). Dans l'exemple ci-dessus, il s'agit de la coordonnée du point de l'objet LineString distante de 1,4142135623730951 par rapport au point (4,8). La méthode distance () devrait avoir les coordonnées lors du calcul de la distance. Y a-t-il un moyen de le faire revenir avec cette méthode?

21
a r

Le terme SIG que vous décrivez est référencement linéaire , et Shapely a ces méthodes .

# Length along line that is closest to the point
print(line.project(p))

# Now combine with interpolated point on line
np = line.interpolate(line.project(p))
print(np)  # POINT (5 7)
28
Mike T

Dans le cas où vous avez un seul segment (par exemple: une ligne, en référence au titre) plutôt qu'une liste de segments, voici ce que j'ai fait et avec un scénario de test réussi. Veuillez considérer que certains utilisateurs de cette page recherchent cela uniquement en regardant le titre, provenant d'une recherche Google. 

Code Python:

def sq_shortest_dist_to_point(self, other_point):
    dx = self.b.x - self.a.x
    dy = self.b.y - self.a.y
    dr2 = float(dx ** 2 + dy ** 2)

    lerp = ((other_point.x - self.a.x) * dx + (other_point.y - self.a.y) * dy) / dr2
    if lerp < 0:
        lerp = 0
    Elif lerp > 1:
        lerp = 1

    x = lerp * dx + self.a.x
    y = lerp * dy + self.a.y

    _dx = x - other_point.x
    _dy = y - other_point.y
    square_dist = _dx ** 2 + _dy ** 2
    return square_dist

def shortest_dist_to_point(self, other_point):
    return math.sqrt(self.sq_shortest_dist_to_point(other_point))

Un cas de test:

def test_distance_to_other_point(self):
    # Parametrize test with multiple cases:
    segments_and_point_and_answer = [
        [Segment(Point(1.0, 1.0), Point(1.0, 3.0)), Point(2.0, 4.0), math.sqrt(2.0)],
        [Segment(Point(1.0, 1.0), Point(1.0, 3.0)), Point(2.0, 3.0), 1.0],
        [Segment(Point(0.0, 0.0), Point(0.0, 3.0)), Point(1.0, 1.0), 1.0],
        [Segment(Point(1.0, 1.0), Point(3.0, 3.0)), Point(2.0, 2.0), 0.0],
        [Segment(Point(-1.0, -1.0), Point(3.0, 3.0)), Point(2.0, 2.0), 0.0],
        [Segment(Point(1.0, 1.0), Point(1.0, 3.0)), Point(2.0, 3.0), 1.0],
        [Segment(Point(1.0, 1.0), Point(1.0, 3.0)), Point(2.0, 4.0), math.sqrt(2.0)],
        [Segment(Point(1.0, 1.0), Point(-3.0, -3.0)), Point(-3.0, -4.0), 1],
        [Segment(Point(1.0, 1.0), Point(-3.0, -3.0)), Point(-4.0, -3.0), 1],
        [Segment(Point(1.0, 1.0), Point(-3.0, -3.0)), Point(1, 2), 1],
        [Segment(Point(1.0, 1.0), Point(-3.0, -3.0)), Point(2, 1), 1],
        [Segment(Point(1.0, 1.0), Point(-3.0, -3.0)), Point(-3, -1), math.sqrt(2.0)],
        [Segment(Point(1.0, 1.0), Point(-3.0, -3.0)), Point(-1, -3), math.sqrt(2.0)],
        [Segment(Point(-1.0, -1.0), Point(3.0, 3.0)), Point(3, 1), math.sqrt(2.0)],
        [Segment(Point(-1.0, -1.0), Point(3.0, 3.0)), Point(1, 3), math.sqrt(2.0)],
        [Segment(Point(1.0, 1.0), Point(3.0, 3.0)), Point(3, 1), math.sqrt(2.0)],
        [Segment(Point(1.0, 1.0), Point(3.0, 3.0)), Point(1, 3), math.sqrt(2.0)]
    ]

    for i, (segment, point, answer) in enumerate(segments_and_point_and_answer):
        result = segment.shortest_dist_to_point(point)
        self.assertAlmostEqual(result, answer, delta=0.001, msg=str((i, segment, point, answer)))

Remarque: je suppose que cette fonction est dans une classe Segment . Si votre ligne est infinie, ne limitez pas la lerp de 0 à 1, mais indiquez au moins deux points a et b distincts. 

0