[postgis-users] Closest point problem AND How to find the nearest road (geom) for the point which is not exactly located on the road.
Ben Madin
ben at remoteinformation.com.au
Sun Aug 9 21:58:01 PDT 2009
G'day all
inspired by Stephen's suggestion I set off with my laptop and the
postgres manual, and spent a trip to South Africa learning how to use
PL/pgsql
I don't think my attempt is rocket science, but if this helps anyone,
I hope that is of some use - if anyone can see anything that would
make it faster, I'd be stoked!
cheers
Ben
-- This function requires a point geometry and a nominated return value,
-- and will return the gid, source or target value for the closest
line segment
-- work on the un-ambiguity principle, you can choose to spell out the
-- return value you want, or just the first letter.
-- Some of the conditions are :
-- both geometries in the same srid
-- geometry in the th_geom column
-- geometry table already contains a gid, source and target field
-- obviously dynamic nomination of the table would be nice, but I was
on a plane,
-- and vaguely under the impression that it stops caching and slows it
down,
-- and I was looking through 150 000 rows
-- this is my first ever plpgsql function, so don't expect too much.
CREATE OR REPLACE FUNCTION find_nearest_road(
point geometry, sf varchar(6), OUT value int
) AS
$BODY$
DECLARE
max_search_radius real := 5.0; -- this is assuming working in
degrees I guess!
search_radius real := 0.01;
rec mrwa_net%ROWTYPE; -- this has to match your lookup table
BEGIN
LOOP
SELECT * INTO rec
FROM mrwa_net m -- and you might want to change this
WHERE st_expand(point, search_radius) && m.the_geom
ORDER BY st_distance(point, m.the_geom)
LIMIT 1;
IF FOUND THEN -- you could really simplify this, it might speed
it up, but I kind of like it.
IF substring(sf from 1 for 1) iLIKE 's' --start or Source
THEN
value := rec.source;
ELSIF substring(sf from 1 for 1) iLIKE 't' --target
OR substring(sf from 1 for 1) iLIKE 'f' --finish
THEN
value := rec.target;
ELSIF substring(sf from 1 for 1) iLIKE 'g' --gid
OR substring(sf from 1 for 1) iLIKE 'i' --id
THEN
value := rec.gid;
END IF;
EXIT;
END IF;
search_radius := search_radius * 2.0;
EXIT WHEN search_radius > max_search_radius;
END LOOP;
END;
$BODY$ LANGUAGE plpgsql;
On 04/08/2009, at 4:35 PM, Stephen Woodbridge wrote:
> Thurber, Fred wrote:
>> I have a database full of point data. When our user stops at a
>> certain lat/lon, how do I find the closest point in our database?
>> We have at least 10,000 points, maybe a lot more so the search has
>> to be efficient, but I have not found a way. It seems a little
>> surprising considering how common this query must be.
>> The obvious way is to scan the entire table and find the closest
>> point. Let’s say our user stops at (40,70), I could do this:
>> SELECT * FROM foo
>> ORDER BY distance(point_geom, GeomFromText('POINT(70.0 40.1)', 4326))
>> LIMIT 1
>> When I run this through EXPLAIN ANALYZE, it shows a full-table
>> scan. Is there a way I can use a GIST or is there some other way
>> to speed it up?
>
> Thilani also wrote:
>> Hi,
>> I’m developing a shortest path navigation application on GIS. I
>> came across
>> a problem when we need to find a shortest path from the location
>> which is
>> not exactly located on the road. In that case I need to find what
>> is the
>> nearest road (geom.) to that particular point 1st. Is there any
>> available
>> post GIS function to do that? If so can you kindly explain that
>> using an
>> example.
>> Thanks and Best Regards,
>> Thilani
>
> Hi Fred and Thilani,
>
> This question is asked often on the list You can check the archives
> for more comments. Search for "Nearest Neighbor"
>
> Basically, you can not currently do this efficiently with a single
> query. Most people solve this problem if a plpgsql function. The key
> to using an index requires a radius. Typical you should start with a
> small radius and if you fail to get a result, double the radius and
> try again. So something like this:
>
> mac_search_radius := 1.0;
> raduis := .0013;
> loop
> select into rec * from mytable
> where expand(setsrid(makepoint(70.0, 40.1),4326), radius) &&
> the_geom
> order by distance(setsrid(makepoint(70.0, 40.1),4326),the_geom)
> limit 1;
> if found then
> exit;
> end if;
> radius := radius * 2.0;
> if radius > max_search_radius then
> return null;
> end if;
> end loop;
> -- do something with rec
> return some_result;
>
> -Steve W
> _______________________________________________
> postgis-users mailing list
> postgis-users at postgis.refractions.net
> http://postgis.refractions.net/mailman/listinfo/postgis-users
--
Ben Madin
REMOTE INFORMATION
t : +61 8 9192 5455
f : +61 8 9192 5535
m : 0448 887 220
Broome WA 6725
ben at remoteinformation.com.au
Out here, it pays to know...
More information about the postgis-users
mailing list