diff --git a/IV/tests.sql b/IV/tests.sql index 630fcf1..1f13350 100644 --- a/IV/tests.sql +++ b/IV/tests.sql @@ -16,8 +16,9 @@ create table figures (name text, way geometry); insert into figures (name, way) values ('fig3',ST_GeomFromText('LINESTRING(0 0,12 0,13 4,20 2,20 0,32 0,33 10,38 16,43 15,44 10,44 0,60 0)')); insert into figures (name, way) values ('fig3-1',ST_GeomFromText('LINESTRING(0 0,12 0,13 4,20 2,20 0,32 0,33 10,38 16,43 15,44 10,44 0)')); insert into figures (name, way) values ('fig5',ST_GeomFromText('LINESTRING(0 39,19 52,27 77,26 104,41 115,49 115,65 103,65 75,53 45,63 15,91 0,91 0)')); -insert into figures (name, way) values ('inflection-1',ST_GeomFromText('LINESTRING(100 14,114 20,133 20,145 15,145 0,136 5,123 7,114 7,111 2)')); +insert into figures (name, way) values ('inflection-1',ST_GeomFromText('LINESTRING(110 24,114 20,133 20,145 15,145 0,136 5,123 7,114 7,111 2)')); +-- `bends` is for manual inspection using, say, qgis drop table if exists bends; create table bends (way geometry); insert into bends select unnest(detect_bends((select way from figures where name='fig3'))); @@ -26,17 +27,27 @@ insert into bends select unnest(detect_bends((select way from figures where name do $$ declare - bends geometry[]; + vbends geometry[]; begin - select detect_bends((select way from figures where name='fig3')) into bends; - perform assert_equals(5, array_length(bends, 1)); - perform assert_equals('LINESTRING(0 0,12 0,13 4)', st_astext(bends[1])); - perform assert_equals('LINESTRING(12 0,13 4,20 2,20 0)', st_astext(bends[2])); - perform assert_equals('LINESTRING(20 2,20 0,32 0,33 10)', st_astext(bends[3])); - perform assert_equals('LINESTRING(32 0,33 10,38 16,43 15,44 10,44 0)', st_astext(bends[4])); + -- DETECT BENDS + select detect_bends((select way from figures where name='fig3')) into vbends; + perform assert_equals(5, array_length(vbends, 1)); + perform assert_equals('LINESTRING(0 0,12 0,13 4)', st_astext(vbends[1])); + perform assert_equals('LINESTRING(12 0,13 4,20 2,20 0)', st_astext(vbends[2])); + perform assert_equals('LINESTRING(20 2,20 0,32 0,33 10)', st_astext(vbends[3])); + perform assert_equals('LINESTRING(32 0,33 10,38 16,43 15,44 10,44 0)', st_astext(vbends[4])); perform assert_equals(4, array_length(detect_bends((select way from figures where name='fig3-1')), 1)); - select detect_bends((select way from figures where name='fig5')) into bends; - perform assert_equals(3, array_length(bends, 1)); + select detect_bends((select way from figures where name='fig5')) into vbends; + perform assert_equals(3, array_length(vbends, 1)); + + -- FIX BEND INFLECTIONS + select detect_bends((select way from figures where name='inflection-1')) into vbends; + + drop table if exists inflections; + create table inflections (way geometry); + --insert into inflections select unnest(fix_gentle_inflections(vbends)); + select fix_gentle_inflections(vbends); + end $$ language plpgsql; diff --git a/IV/wm.sql b/IV/wm.sql index aa10e0c..1f3fe5d 100644 --- a/IV/wm.sql +++ b/IV/wm.sql @@ -73,19 +73,21 @@ $$ language plpgsql; -- -- The text does not specify how many vertices can be "adjusted"; it can -- equally be one or many. This function is adjusting many, as long as the --- commulative inflection angle is less than pi/6 (30 deg). +-- commulative inflection angle small (see variable below). create or replace function fix_gentle_inflections(INOUT bends geometry[]) as $$ declare - prev_bend geometry; - bend geometry; - p geometry; - p1 geometry; - p2 geometry; - p3 geometry; + small_angle real; + phead geometry; -- head point of head bend + ptail geometry[]; -- 3 head points of tail bend + i int4; -- bends[i] is the current head begin - foreach bend in array bends loop - if prev_bend is null then - prev_bend = bend; + -- the threshold when the angle is still "small", so gentle inflections can + -- be joined + small_angle := radians(30); + + <> + for i in select generate_subscripts(bends, 1) loop + if i = 1 then continue; end if; @@ -101,31 +103,36 @@ begin -- -- Assume this curve (figure `inflection-1`): -- - -- A______B - -- ---' `-------. C - -- | - -- G___ F | - -- / `-----.____+ D - -- E + -- \______B + -- A `-------. C + -- | + -- G___ F | + -- / `-----.____+ D + -- E -- -- After processing the curve following the definition of a bend, the bend -- [A-E] would be detected. Assuming inflection point E and F are "small", -- the bend needs to be extended by two edges to [A,G]. - -- - -- Assuming the direction in this example is clock-wise, the first set of - -- `p` variables will be: p1=C, p2=B, p3=A. - for p in (select geom from st_dumppoints(prev_bend) order by path[1] desc) loop - p3 = p2; - p2 = p1; - p1 = p; - if p3 is null then - continue; + select geom from st_dumppoints(bends[i]) order by path[1] desc limit 1 into phead; + while true loop + -- copy last 3 points of bends[i-1] (tail) to ptail + select array_agg(geom) from st_dumppoints(bends[i-1]) order by path[1] desc limit 3 into ptail; + + -- if inflection angle between ptail[1:3] "large", stop processing this bend + if abs(st_angle(ptail[1], ptail[2], ptail[2], ptail[3]) - pi) > small_angle then + exit bend_loop; end if; - -- (p2, p1) is shared with the current bend. + -- distance from last vertex should be larger than second-last vertex + if st_distance(phead, ptail[2]) < st_distance(phead, ptail[3]) then + exit bend_loop; + end if; + + -- detected a gentle inflection. Move head of the tail to the tail of head + bends[i] = st_addpoint(bends[i], ptail[3]); + bends[i-1] = st_removepoint(bends[i-1], st_npoints(bends[i-1])-1); end loop; - prev_bend = bend; end loop; end $$ language plpgsql;