Skip to content

Commit

Permalink
Merge pull request #39 from SteffenHaeussler/add_line_intersect
Browse files Browse the repository at this point in the history
Add line-intersect module
  • Loading branch information
diogomatoschaves committed Jun 5, 2020
2 parents 75fb99b + e191bc7 commit 98152b6
Show file tree
Hide file tree
Showing 21 changed files with 1,382 additions and 54 deletions.
17 changes: 13 additions & 4 deletions .github/workflows/pythonapp.yml
Original file line number Diff line number Diff line change
@@ -1,22 +1,30 @@
name: build

on:
push:
branches: [ master ]
pull_request:
branches: [ master ]

jobs:
build:

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- name: Set up Python 3.8
uses: actions/setup-python@v1
with:
python-version: 3.8
- name: Install libspatialindex
run: |
sudo apt-get update
sudo apt-get install curl
sudo apt-get install g++
sudo apt-get install make
curl -L http://download.osgeo.org/libspatialindex/spatialindex-src-1.8.5.tar.gz | tar xz
cd spatialindex-src-1.8.5
./configure
sudo make
sudo make install
sudo ldconfig
- name: Install dependencies
run: |
python -m pip install --upgrade pip
Expand All @@ -38,3 +46,4 @@ jobs:
flags: unittests
name: codecov-umbrella
fail_ci_if_error: false

1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ Currently, the following modules have been implemented:
- [great circle](https://github.com/diogomatoschaves/pyturf/tree/master/turf/great_circle)
- [helpers](https://github.com/diogomatoschaves/pyturf/tree/master/turf/helpers)
- [length](https://github.com/diogomatoschaves/pyturf/tree/master/turf/length)
- [line-intersect](https://github.com/diogomatoschaves/pyturf/tree/master/turf/line_intersect)
- [midpoint](https://github.com/diogomatoschaves/pyturf/tree/master/turf/midpoint)
- [nearest-point](https://github.com/diogomatoschaves/pyturf/tree/master/turf/nearest_point)
- [point-on-feature](https://github.com/diogomatoschaves/pyturf/tree/master/turf/point_on_feature)
Expand Down
5 changes: 5 additions & 0 deletions docs/source/modules/misc.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,8 @@
Misc
====


line-intersect
--------------

.. autofunction:: turf.line_intersect
49 changes: 2 additions & 47 deletions environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ dependencies:
- python=3.8.1=h359304d_1
- readline=7.0=h1de35cc_5
- regex=2020.2.20=py38h1de35cc_0
- rtree=0.8.3=py38_1
- setuptools=46.0.0=py38_0
- six=1.14.0=py38_0
- sqlite=3.31.1=ha441bb4_0
Expand All @@ -46,52 +47,6 @@ dependencies:
- xz=5.2.4=h1de35cc_4
- zlib=1.2.11=h1de35cc_3
- pip:
- alabaster==0.7.12
- babel==2.8.0
- bleach==3.1.4
- chardet==3.0.4
- commonmark==0.9.1
- coverage==5.0.4
- dendroid==0.4.2
- docutils==0.16
- idna==2.9
- imagesize==1.2.0
- jinja2==2.11.2
- keyring==21.2.0
- m2r==0.2.1
- markdown-table==2019.4.13
- markupsafe==1.1.1
- martinez==0.4.0
- mistune==0.8.4
- mkdir==2019.4.13
- orderdict==2019.9.25
- pkginfo==1.5.0.1
- prioq==0.1.1
- public==2019.4.13
- pybind11==2.5.0
- pygments==2.6.1
- pytest-cov==2.8.1
- pytz==2020.1
- readme-renderer==25.0
- recommonmark==0.6.0
- reprit==0.2.2
- requests==2.23.0
- requests-toolbelt==0.9.1
- setupcfg==2019.4.13
- snowballstemmer==2.0.0
- sphinx==3.0.3
- sphinx-rtd-theme==0.4.3
- sphinxcontrib-applehelp==1.0.2
- sphinxcontrib-devhelp==1.0.2
- sphinxcontrib-htmlhelp==1.0.3
- sphinxcontrib-jsmath==1.0.1
- sphinxcontrib-qthelp==1.0.3
- sphinxcontrib-serializinghtml==1.1.4
- tqdm==4.45.0
- twine==3.1.1
- urllib3==1.25.8
- values==2019.4.13
- webencodings==0.5.1
- write==2019.4.13
- -r file:requirements.txt
prefix: /Users/diogomatoschaves/.conda/envs/py-turf

1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ regex==2020.2.20
reprit==0.2.2
requests==2.23.0
requests-toolbelt==0.9.1
rtree>=0.8,<0.9
setupcfg==2019.4.13
six==1.14.0
snowballstemmer==2.0.0
Expand Down
1 change: 1 addition & 0 deletions turf/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from turf.great_circle import great_circle
from turf.helpers import *
from turf.length import length
from turf.line_intersect import line_intersect
from turf.midpoint import midpoint
from turf.nearest_point import nearest_point
from turf.point_on_feature import point_on_feature
Expand Down
4 changes: 2 additions & 2 deletions turf/invariant/_invariant.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ def get_coords_from_geometry(
if not allowed_types:
allowed_types = allowed_types_default

if isinstance(geometry, list):
if isinstance(geometry, (list, tuple)):
return _process_list_input(
allowed_types, geometry, get_coords_from_geometry, raise_exception
)
Expand Down Expand Up @@ -145,7 +145,7 @@ def get_geometry_from_features(features: Any, allowed_types: Sequence = None) ->
if not allowed_types:
allowed_types = allowed_types_default

if isinstance(features, list):
if isinstance(features, (list, tuple)):
return _process_list_input(
[*allowed_types, "Feature"], features, get_geometry_from_features
)
Expand Down
1 change: 1 addition & 0 deletions turf/line_intersect/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from turf.line_intersect._line_intersect import line_intersect
171 changes: 171 additions & 0 deletions turf/line_intersect/_line_intersect.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
from typing import Dict, Sequence, TypeVar, Union
from collections import deque

from rtree import index

from turf.helpers import (
Feature,
FeatureCollection,
LineString,
MultiLineString,
MultiPolygon,
Point,
Polygon,
)

from turf.envelope._envelope import envelope
from turf.polygon_to_line import polygon_to_line
from turf.helpers import feature, feature_collection, line_string, point
from turf.invariant import get_coords_from_features

from turf.utils.error_codes import error_code_messages
from turf.utils.exceptions import InvalidInput


GeoJSON = TypeVar("GeoJSON", Feature, FeatureCollection)

LinePolyFeature = TypeVar(
"LineFeature",
Dict,
Feature,
FeatureCollection,
LineString,
MultiLineString,
MultiPolygon,
Polygon,
)


def line_intersect(input_1: LinePolyFeature, input_2: LinePolyFeature) -> Feature:
"""
Takes any LineString or Polygon GeoJSON and returns the intersecting point(s).
:param line_1: {GeoJSON} line1 any LineString or Polygon
:param line_2:{GeoJSON} line2 any LineString or Polygon
:returns: {FeatureCollection<Point>} point(s) that intersect both
"""
line_1_segments = get_line_segments(input_1)
line_2_segments = get_line_segments(input_2)

possible_intersects = []
intersects = []

possible_intersects.extend(spatial_filtering(line_1_segments, line_2_segments))

for i in possible_intersects:
pnt = calculate_intersect(*i)

if pnt:
intersects.append(pnt)

return feature_collection(intersects)


def spatial_filtering(line_1: Sequence, line_2: Sequence) -> Sequence:
"""
Filters possible intersections of the lines via their bounding box
:param line_1: line_1 coordinates of segments
:param line_2: line_2 coordinates of segments
:returns: list of line segments that possibly intersect with each other
"""
possible_intersects = []
rtree_index = index.Index()

for i, seg in enumerate(line_1):

bbox = envelope(seg)
rtree_index.insert(i, bbox["bbox"])

for j, seg in enumerate(line_2):

bbox = envelope(seg)
seg_intersection_idx = deque(rtree_index.intersection(bbox["bbox"]))

while seg_intersection_idx:

seg_idx = seg_intersection_idx.pop()

possible_intersects.append([*line_1[seg_idx], *line_2[j]])

return possible_intersects


def calculate_intersect(
seg_1_start: Sequence,
seg_1_end: Sequence,
seg_2_start: Sequence,
seg_2_end: Sequence,
) -> Union[None, Point]:
"""
Calculates the intersection point of two segments otherwise None
:param seg_1_start: coordinates of segment 1 start
:param seg_1_end: coordinates of segment 1 end
:param seg_2_start: coordinates of segment 2 start
:param seg_2_end: coordinates of segment 2 end
:returns: {Point} if intersection, otherwise None
"""
x1 = seg_1_start[0]
y1 = seg_1_start[1]
x2 = seg_1_end[0]
y2 = seg_1_end[1]
x3 = seg_2_start[0]
y3 = seg_2_start[1]
x4 = seg_2_end[0]
y4 = seg_2_end[1]

pnt = None

denom = ((y4 - y3) * (x2 - x1)) - ((x4 - x3) * (y2 - y1))
numeA = ((x4 - x3) * (y1 - y3)) - ((y4 - y3) * (x1 - x3))
numeB = ((x2 - x1) * (y1 - y3)) - ((y2 - y1) * (x1 - x3))

if denom == 0:
return pnt

uA = numeA / denom
uB = numeB / denom

if uA >= 0 and uA <= 1 and uB >= 0 and uB <= 1:
x = round(x1 + (uA * (x2 - x1)), 6)
y = round(y1 + (uA * (y2 - y1)), 6)
pnt = point([x, y])

return pnt


def get_line_segments(line: LinePolyFeature) -> Sequence:
"""
Gets segments from a line feature
:param line: any LineString or Polygon
:return: sequence of segmetns
"""
segments = []

try:
line_geometry = line.get("geometry").get("type")
except AttributeError:
try:
line_geometry = line["type"]
except KeyError:
raise InvalidInput(
error_code_messages["InvalidGeometry"](
["LineString", "MultiLineString", "Polygon", "MultiPolygon"]
)
)

if line_geometry in ["MultiPolygon", "Polygon"]:
line = polygon_to_line(line)
line_geometry = line["geometry"]["type"]

line_coords = get_coords_from_features(line, ("LineString", "MultiLineString"))

if line_geometry in ["LineString"]:
line_coords = [line_coords]

for line_coord in line_coords:
segments.extend(list(zip(line_coord, line_coord[1:])))

return segments
45 changes: 45 additions & 0 deletions turf/line_intersect/tests/in/2-vertex-segment.geojson
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {
"stroke": "#F00",
"stroke-width": 6
},
"geometry": {
"type": "LineString",
"coordinates": [
[
124.584961,
-12.768946
],
[
126.738281,
-17.224758
]
]
}
},
{
"type": "Feature",
"properties": {
"stroke": "#00F",
"stroke-width": 6
},
"geometry": {
"type": "LineString",
"coordinates": [
[
123.354492,
-15.961329
],
[
127.22168,
-14.008696
]
]
}
}
]
}

0 comments on commit 98152b6

Please sign in to comment.