[postgis-users] project a point onto a line and get x, y, offset of the point of projection
Stephen Woodbridge
woodbri at swoodbridge.com
Sun Jul 3 12:51:23 PDT 2005
Hi Strk,
This is great news, you guys really work fast :) Well I guess my
function will help people that aren't yet on HEAD CVS. ;)
-Steve
strk at refractions.net wrote:
> In the head branch of postgis we already have functions able to
> do this:
>
> -- position is a float between 0 and 1
> line_interpolate_point(line, position)
>
> -- return a float between 0 and 1 representing
> -- position of closest point on line
> line_locate_point(line, point)
>
> So functionality of your function would be obtained with:
>
> SELECT line_interpolate_point(geom, line_locate_point(geom, 'POINT..'));
>
> --strk;
>
> On Fri, Jul 01, 2005 at 10:58:36PM -0400, Stephen Woodbridge wrote:
>
>>Hi all,
>>
>>Someone on the list asked if they could get the point location of where
>>on a line object the shortest distance to that line was from a point.
>>The distance(line, point) will tell you the shortest distance, but it
>>does not project the point onto the line at the point where the shortest
>>distance is located.
>>
>>Then I needed the same thing for a project at Where2GetIt.com and it
>>seemed like this functionality should be a part of PostGIS. So attached
>>is the code I wrote for our needs. There are some examples of its use.
>>It has an assumption that you are working in decimal degrees, but it
>>could be modified easily to work in any space. I will leave that up to
>>others.
>>
>>The algorithm is very straight forward. Given a linestring and a point,
>>project the point onto each segment of the linestring and save the
>>projection of the closest point. Then compute the distance from the
>>start of the linestring to the projection point and return an array of
>>pointx, pointy, distance.
>>
>>Paul, please feel free to add this or something like it to PostGIS.
>>
>>Enjoy, I would be open to any feedback or improvements any might suggest.
>>
>>-Steve
>
>
>>CREATE OR REPLACE FUNCTION x_y_offset_from_line_pnt (geometry, geometry) RETURNS float[] AS '
>>/*
>> * {x, y, offset} = x_y_offset_from_line_pnt(LINESTRING, POINT($lon $lat))
>> */
>>DECLARE
>> line ALIAS FOR $1;
>> pnt ALIAS FOR $2;
>> nline geometry;
>> ipnt geometry;
>> bestp geometry;
>> pa geometry;
>> pb geometry;
>> npts integer;
>> besti integer;
>> seglen float;
>> dist float;
>> best float;
>> offset float;
>> U float;
>>--
>>BEGIN
>> IF NOT (GeometryType(line) = ''LINESTRING'' ||
>> GeometryType(line) = ''MULTILINESTRING'' ) AND
>> GeometryType(pnt) != ''POINT'' THEN
>> RETURN NULL;
>> END IF;
>> /*
>> * iterate over the linestring segments and find the segment and
>> * point on the linestring that are closest to the pnt
>> */
>> npts := npoints(line);
>> best := 360.0;
>> FOR i IN 1 .. npts-1 LOOP
>> pa := PointN(line,i);
>> pb := PointN(line,i+1);
>> seglen := distance(pa, pb);
>> IF (seglen > 0.0) THEN
>> U := ( (X(pnt)-X(pa)) * (X(pb)-X(pa)) +
>> (Y(pnt)-Y(pa)) * (Y(pb)-Y(pb)) ) /
>> (seglen*seglen);
>> IF U < 0.0 THEN
>> ipnt := pa;
>> ELSIF U > 1.0 THEN
>> ipnt := pb;
>> ELSE
>> ipnt := MakePoint(
>> X(pa) + U*(X(pb) - X(pa)),
>> Y(pa) + U*(Y(pb) - Y(pa)) );
>> END IF;
>> dist := distance(pnt, ipnt);
>> IF dist < best THEN
>> best := dist;
>> besti := i;
>> bestp := ipnt;
>> END IF;
>> END IF;
>> END LOOP;
>> /*
>> * we should now have the best point so calcuate the offset
>> * and return the results.
>> */
>> IF besti = 1 THEN
>> nline = MakeLine(PointN(line,1), bestp);
>> ELSE
>> nline = MakeLine(PointN(line,1), PointN(line,2));
>> IF besti > 2 THEN
>> FOR i IN 3 .. besti LOOP
>> pa := PointN(line,i);
>> nline := AddPoint(nline, pa, -1);
>> END LOOP;
>> END IF;
>> nline := AddPoint(nline, bestp, -1);
>> END IF;
>> offset := length_spheroid(nline,''SPHEROID["GRS_1980", 6378137, 298.257222101]'');
>> RETURN ARRAY[X(bestp), Y(bestp), offset];
>>END;
>>' LANGUAGE plpgsql;
>>
>>-- here are some test calls to the function
>>
>>select x_y_offset_from_line_pnt(GeomFromText('LINESTRING(0 0,1 0)',-1), GeomFromText('POINT(0.5 0.1)', -1));
>>
>>select x_y_offset_from_line_pnt(GeomFromText('LINESTRING(0 0,0.1 0,0.3 .1,.4 .15,.6 .2)',-1), GeomFromText('POINT(0.5 0.1)', -1));
>>
>>select link_id, the_geom, min(distance(the_geom, GeomFromText('POINT(-71.38900904888 42.619402684661)', -1))) as dist
>> from
>> rgeo
>> where
>> expand(GeomFromText('POINT(-71.38900904888 42.619402684661)', -1), 0.0013) &&
>> the_geom
>> group by the_geom order by dist limit 1;
>>
>>select (x_y_offset_from_line_pnt(
>> (select the_geom from rgeo where link_id=$segment_id),
>> GeomFromText('POINT(-71.38900904888 42.619402684661)', -1))
>> )[3] as offset;
>
>
>>_______________________________________________
>>postgis-users mailing list
>>postgis-users at postgis.refractions.net
>>http://postgis.refractions.net/mailman/listinfo/postgis-users
>
>
> _______________________________________________
> postgis-users mailing list
> postgis-users at postgis.refractions.net
> http://postgis.refractions.net/mailman/listinfo/postgis-users
>
More information about the postgis-users
mailing list