diff --git a/notes.txt b/notes.txt index b94a775..61c8b2c 100644 --- a/notes.txt +++ b/notes.txt @@ -27,13 +27,12 @@ after a few bends have been skipped. E.g. ends of A<->B cross the line, but -If a bend with 180+ deg inflection is found, its line between inflection angles -(AB in our examples) must be crossed with all the other bends to detect a -possible line-crossing. This is O(N*M), where N is the total number of line -segments, and M is the number of qualifying bends. In other words, can be very -computationally expensive. +If a bend with 180+ deg sum of inflection angles is found, its line between +inflection angles (AB in our examples) must be crossed with all the other bends +to detect a possible line-crossing. This is O(N*M), where N is the total number +of line segments, and M is the number of qualifying bends. It is expensive. This may be simplified: if other bend's endpoints (A' and B') are in the same sub-plane as divided by AB, then the bend can be skipped from checking if it -intersects with AB. Some intersections may be missed (see the example), but -they will be eliminated by just joining A and B. +intersects with AB. Some intersections may be missed (see the last example), +but they will be eliminated by joining A and B anyway. diff --git a/tests.sql b/tests.sql index 877a696..5ab4143 100644 --- a/tests.sql +++ b/tests.sql @@ -47,7 +47,7 @@ drop table if exists inflections, demo_inflections; create table inflections (name text, ways geometry[]); insert into inflections select name, fix_gentle_inflections(ways) from bends; create table demo_inflections (name text, i bigint, way geometry); -insert into demo_inflections select a.name, generate_subscripts(a.ways, 1), unnest(a.ways) from inflections a; +insert into demo_inflections select name, generate_subscripts(ways, 1), unnest(ways) from inflections; do $$ declare @@ -67,3 +67,10 @@ begin perform assert_equals('LINESTRING(114 20,133 20,145 15,145 0,136 5,123 7,114 7)', st_astext(vinflections[2])); perform assert_equals('LINESTRING(123 7,114 7,111 2)', st_astext(vinflections[3])); end $$ language plpgsql; + +-- SELF-LINE CROSSING +drop table if exists selfcrossing, demo_selfcrossing; +create table selfcrossing (name text, ways geometry[]); +insert into selfcrossing select name, self_crossing(ways) from inflections; +create table demo_selfcrossing (name text, i bigint, way geometry); +insert into demo_selfcrossing select name, generate_subscripts(ways, 1), unnest(ways) from selfcrossing; diff --git a/wm.sql b/wm.sql index bcb0379..11a1afa 100644 --- a/wm.sql +++ b/wm.sql @@ -170,9 +170,35 @@ $$ language plpgsql; create or replace function self_crossing(INOUT bends geometry[]) as $$ declare pi real; + angle real; + p geometry; + p1 geometry; + p2 geometry; + p3 geometry; + bend geometry; begin + pi = radians(180); + -- go through the bends and find one where sum of inflection angle is >180 + foreach bend in array bends loop + angle = 0; + p3 = null; + p2 = null; + p1 = null; + for p in (select geom from st_dumppoints(bend) order by path[1] asc) loop + p3 = p2; + p2 = p1; + p1 = p; + if p3 is null then + continue; + end if; + angle = angle + abs(pi - st_angle(p1, p2, p3)); + end loop; + if abs(angle) > pi then + raise notice 'maybe self-crossing bend %: %', st_astext(bend), round(degrees(abs(angle))); + end if; + end loop; end $$ language plpgsql;