PostGIS - Route matching solution - postgresql

We are building an application where I am a driver and i travel from point A to point B.On my way i can find passengers who travel in the same route.
We are using PostgreSQL with PostGIS extension.
After googling a lot i have found out that we can use linestring to achieve this.I am not fully sure whether this approach will work out.
Suppose I have co-ordinates of my source and destination.
var RouteCoordinates = [
{lat: 37.772, lng: -122.214},
{lat: 21.291, lng: -157.821},
{lat: -18.142, lng: 178.431},
{lat: -27.467, lng: 153.027}
];
I need to store this as linestring in my DB.
After stroring if a passenger is also going in this route but as we know his source and destination wont be exactly in my line string but they will be near.
For example around 1km radius
As you can see my source and destination is that line. And as I am travelling I want to pick all those whose (source and destination) are near to my routes (within particular radius)
If I want to find particular location in my DB within particular radius I will be querying like this
SELECT id, name, address, geom
FROM Seattle_Starbucks
WHERE ST_DWithin(geom, ST_MakePoint(-122.325959,47.625138)::geography, 1000);
Now I can achieve my solution as I am new to postGIS its little bit confusing
How to store all all my source and destination point in DB
ANS: I need to convert in to linestring using this function ST_MakeLine and then store, right?
How to query that based on my requirement as I have mentioned above
Can you please give me the insight on how to achieve this. Your help is greatly appreciated. Thanks

A few thougths on your question:
I need to convert in to linestring using this function ST_MakeLine and
then store, right?
Yes, to merge multiple points into a LINESTRING you can use ST_MakeLine:
SELECT ST_AsText(
ST_MakeLine(ARRAY[ST_MakePoint(-122.21,37.77),
ST_MakePoint(-157.82,21.29),
ST_MakePoint(178.43,-18.14),
ST_MakePoint(153.02,-27.46)]));
st_astext
---------------------------------------------------------------------
LINESTRING(-122.21 37.77,-157.82 21.29,178.43 -18.14,153.02 -27.46)
(1 Zeile)
How to query that based on my requirement as I have mentioned above
Create a buffer on each point of your LINESTRING and check wether other geometries are inside of it.
First, you have to split your rout LINESTRING into POINTs using ST_DumpPoints ...
db=# SELECT ST_AsText((ST_DumpPoints('LINESTRING(-122.21 37.77,-157.82 21.29,178.43 -18.14,153.02 -27.46)'::GEOMETRY)).geom);
st_astext
----------------------
POINT(-122.21 37.77)
POINT(-157.82 21.29)
POINT(178.43 -18.14)
POINT(153.02 -27.46)
(4 Zeilen)
.. and then use ST_Buffer to create a buffer around each point. ST_Buffer returns a geometry with the area surrounding your point (or any other geometry type). For instance, taking the first point of your route (if I didn't switch x and y, it's somewhere in San Francisco) POINT(-122.21 37.76):
db=# SELECT ST_AsText(
ST_Buffer('POINT(-122.21 37.76)'::GEOMETRY,0.0001, 'quad_segs=16'));
Returns this geometry:
POLYGON((-122.2099 37.76,-122.209900481527 37.759990198286,-122.209901921472 37.7599804909678,-122.209904305966 37.7599709715323,-122.209907612047 37.7599617316568,-122.209911807874 37.7599528603263,-122.209916853039 37.7599444429767,-122.209922698955 37.7599365606716,-122.209929289322 37.7599292893219,-122.209936560672 37.7599226989547,-122.209944442977 37.7599168530388,-122.209952860326 37.7599118078736,-122.209961731657 37.7599076120467,-122.209970971532 37.7599043059664,-122.209980490968 37.759901921472,-122.209990198286 37.7599004815273,-122.21 37.7599,-122.210009801714 37.7599004815273,-122.210019509032 37.759901921472,-122.210029028468 37.7599043059664,-122.210038268343 37.7599076120467,-122.210047139674 37.7599118078736,-122.210055557023 37.7599168530388,-122.210063439328 37.7599226989547,-122.210070710678 37.7599292893219,-122.210077301045 37.7599365606716,-122.210083146961 37.7599444429767,-122.210088192126 37.7599528603263,-122.210092387953 37.7599617316568,-122.210095694034 37.7599709715323,-122.210098078528 37.7599804909678,-122.210099518473 37.759990198286,-122.2101 37.76,-122.210099518473 37.760009801714,-122.210098078528 37.7600195090322,-122.210095694034 37.7600290284677,-122.210092387953 37.7600382683432,-122.210088192126 37.7600471396737,-122.210083146961 37.7600555570233,-122.210077301045 37.7600634393284,-122.210070710678 37.7600707106781,-122.210063439328 37.7600773010453,-122.210055557023 37.7600831469612,-122.210047139674 37.7600881921264,-122.210038268343 37.7600923879533,-122.210029028468 37.7600956940336,-122.210019509032 37.760098078528,-122.210009801714 37.7600995184727,-122.21 37.7601,-122.209990198286 37.7600995184727,-122.209980490968 37.760098078528,-122.209970971532 37.7600956940336,-122.209961731657 37.7600923879533,-122.209952860326 37.7600881921264,-122.209944442977 37.7600831469612,-122.209936560672 37.7600773010453,-122.209929289322 37.7600707106781,-122.209922698955 37.7600634393284,-122.209916853039 37.7600555570233,-122.209911807874 37.7600471396737,-122.209907612047 37.7600382683432,-122.209904305966 37.7600290284677,-122.209901921472 37.7600195090322,-122.209900481527 37.760009801714,-122.2099 37.76))
If you're wondering about the shape of this buffer, please read this answer.
And using this query you can check if another geometry is inside of this buffer (ST_Within):
db=# SELECT
ST_Within('POINT(-122.21 37.76)'::GEOMETRY,
ST_Buffer('POINT(-122.21 37.76)'::GEOMETRY,0.0001, 'quad_segs=16'));
st_within
-----------
t
(1 Zeile)
To put it all together, you can use a CTE (aka WITH clause) and write something like this:
WITH j AS (
SELECT
(ST_DumpPoints('LINESTRING(-122.21 37.77,-157.82 21.29,178.43 -18.14,153.02 -27.46)'::GEOMETRY)).geom AS g)
SELECT id, name, address, geom
FROM Seattle_Starbucks
WHERE ST_Within(geom,ST_Buffer(j.g,0.0001, 'quad_segs=16'))

Related

How can I obtain all of the street names under a polygon in Overpass?

I am new to overpass (after only discovering it last night). I have a polygon I drew on QGIS and I plan to obtain its coordinates (long, lat). I'd then like to use these coordinates in overpass to obtain all of the road names in that area. I found a query online that obtains all road names in a city:
[out:csv ("name")][timeout:2500];
{{geocodeArea:Roma}}->.searchArea;
(
way["highway"]["name"](area.searchArea);
);
for (t["name"])
{
make street name=_.val;
out;
}
How can I adjust the following query so that I can specify a polygon function instead of city/area name?. I'm mindful of the syntax:
(poly:"latitude_1 longitude_1 latitude_2 longitude_2 latitude_3 longitude_3 …");
I'm just not sure where it would go in the query. I tried a few times but I was receiving errors or just blank results. Hopefully if I see an example I should be able to carry out my task effectively.
After doing some research I found the answer. This would give me a list of road names:
[out:csv ("name")][timeout:2500];
way["highway"]["name"]
(poly:"51.5566 0.0763 51.5734 0.0724 51.5203 0.0293");
out tags;
And this would give me a map view:
way["highway"]["name"]
(poly:"51.5566 0.0763 51.5734 0.0724 51.5203 0.0293");
out geom;

How can I get a list of bridges with their location (latitude and longitude) from an OSM file?

maybe this query may be a bit trivial or perhaps laborious, but for a project I need to obtain the bridges that exist in an osm file along with its location (latitude and longitude).
Reading the openstreetmap wiki, I see that there is a procedure using osmosis but I do not know if I will actually get the information as follows:
Name of the bridge | latitude | longitude
bin / osmosis.bat --rx brandenburg.osm.bz2 --bp file = "city.poly" --tf accept-ways highway=motorway_link,motorway --way-key-value keyValueList="bridge.yes" --used-node --write-xml brdg_autob.osm
Thanks in advance
Pablo
The output will be OSM XML and not plaintext.
Also, most bridges in OSM are mapped as ways. A way consists of multiple lat/lons represented as nodes. If you need a single lat,lon pair then you have to calculate the bridge center yourself.
Additionally, not all bridges are tagged as bridge=yes. See bridge in the OSM wiki for a list of commonly used tags, such as bridge=viaduct, bridge=aqueduct, bridge=boardwalk and so on.
You won't exactly get the format you described. However with some little work you can transform OSM XML into your format.

geom format in postGIS use to print in google maps android

a greeting, a friend happened to me a route of transportation and I step in the following format:
0102000020E610000049020000F2D077B7B23A53C0F03504C7651428C07E703E75AC3A53C07E37DDB2431428C07E8AE3C...
He told me it was a MultiLineString. I used the PostGIS function:
SELECT St_asewkt ('0102000020E6100000810200 .....');
with this i supposedly should be able to get the wkt format that I can
get the coordinates and use it on google maps api to paint a path in android, but when using it throws me empty. Help me please
I need to get something like MULTILINESTRING((0 0,1 1,1 2),(2 3,3 2,5 4)) ... to get lat lng coordinates to paint it in google maps api with android.
To summarize what I have is this value
0102000020E6100000810200 ..... and would like to paint it in android with google maps api, that's why I try to get the coordinates that function.
What you have is Well know binary, WKB, which is explained on this page along with its companion well known text (WKT), but in an ASCII (hex) representation, see the docs
If you do something like:
select st_setsrid(st_makepoint(50, -2),4326);
you will see 0101000020E6100000000000000000494000000000000000C0, similar to what you have.
You can insert these directly into a db with
create table test (g geometry);
insert into test(g) values(ST_GeomFromEWKB(E'\\x0101000020E6100000000000000000494000000000000000C0')
where the E'\\x indicates that you are inserted a hex string, see binary format docs.
If you now do
select g from test;
you will get your WKB back and if you do
select st_astext(g) from test;
you will see the more human-readable WKT format.
The best way to load data in the correct format is to use:
COPY table_name FROM your_file.csv CSV;
where your wkb would be, as is, unquoted, in the your_file.csv.

How to add a method in DataMapper so I can find "all points within x distance of this latitude and longitude"?

I'm using DataMapper to manage a database that has points of interest (POIs) located by latitude and longitude. I would like to do a query and find all POIs within x distance of a given latitude and longitude. For example, all POIs within 1000m of latitude 45, longitude 90.
I set this up:
class POI
include DataMapper::Resource
property :id, String, :key => true
property :title, String, :required => true
property :lat, Float, :required => true
property :lon, Float, :required => true
def distance(latitude, longitude)
# Taken from https://github.com/almartin/Ruby-Haversine
earthRadius = 6371 # Earth's radius in km
# convert degrees to radians
def convDegRad(value)
unless value.nil? or value == 0
value = (value/180) * Math::PI
end
return value
end
deltaLat = (self.lat - latitude)
deltaLon = (self.lon - longitude)
deltaLat = convDegRad(deltaLat)
deltaLon = convDegRad(deltaLon)
# Calculate square of half the chord length between latitude and longitude
a = Math.sin(deltaLat/2) * Math.sin(deltaLat/2) +
Math.cos((self.lat/180 * Math::PI)) * Math.cos((latitude/180 * Math::PI)) *
Math.sin(deltaLon/2) * Math.sin(deltaLon/2);
# Calculate the angular distance in radians
c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a))
distance = earthRadius * c
return distance
end
end
I'd like to be able to find records with a call similar to this:
pois = POI.all(distance(45,90).lte => 1000)
But that gives an error:
./poi-provider.rb:44:in `<main>': undefined method `distance' for main:Object (NoMethodError)
I read dkubb's answer about defining complex queries in a method but this is different because I need to pass parameters in and I'm trying to use the method as a condition.
How can I do that---or, is there a better way to use DataMapper to find points near a given latitude and longitude without breaking down and just using raw SQL?
Whenever you're dealing with latitudes and longitudes, I would very strongly recommend that you use a spatial database rather than just two Float columns. Otherwise your queries will become crazily slow as soon as you have more than a trivial quantity of data.
MySQL does have spatial extensions, but the de facto standard for open-source spatial databases is the PostGIS extension to Postgres. Fortunately, you can use this with DataMapper fairly easily.
Installing
You'll need the dm-postgis plugin. The main code appears to be abandonware, but someone has updated it to work with current DataMapper. Clone dm-postgis from github, apply this pull request, and then gem install from the local directory.
You'll also find life easier if you install dm-ar-finders, which lets you use SQL queries and get DataMapper objects back.
Data model
You can then define your co-ordinate property as a PostGIS geometry type like this:
property :location, PostGISGeometry
and set it this way:
location=GeoRuby::SimpleFeatures::Point.from_x_y(longitude,latitude)
You'll probably want to create an index on your location column. Rather than Postgres's default B-tree, you want a GIST index for geographic data. dm-postgis unfortunately isn't smart enough to create this, so do it from the psql command line:
CREATE INDEX index_poi_location ON poi USING GIST (location);
The proximity query
Ok. So now you have a location field. How do you run your proximity query?
The key is PostGIS's ST_DWithin function. As the docs say, this "returns true if the geometries are within the specified distance of one another". So in your case, you'd do something like this:
POI.find_by_sql "SELECT id,name FROM pois WHERE ST_DWithin(location, ST_GeomFromText('POINT(45 90)') , 0.05 )"
The 0.05 is the distance required. Note that this is in degrees, not metres. (Talking about projections and SRIDs is way beyond the scope of this answer, but if, for example, all your data was in the UK, you could use the OSGB projection and get metres pretty easily. Alternatively, someone could improve dm-postgis to cope with PostGIS geographies as well as geometries, which cope with metre distances natively. It's on my list... one day.)
The really great thing about all this is that PostGIS puts much more complex geographical queries within your grasp. For example, in a project I'm currently working on, I'm using it like this:
shops=Shop.find_by_sql [<<-EOS,route.id]
SELECT listings.id, listings.name, listings.provides
FROM listings
JOIN routes ON ST_DWithin(listings.location, routes.linestring, 0.05)
WHERE routes.id=? AND type='Shop'
ORDER BY ST_Line_Locate_Point(routes.linestring, listings.location)
EOS
That finds all the shops along a particular route (a polyline) and orders them start-to-finish... and does it remarkably quickly.
You're calling an instance method without instantiating. That's what is causing your error.
You can't use DataMapper methods as conditions. This means you're going to have to write some SQL.
Something like:
class POI
...
def self.all_within_distance(distance, longitude, latitude)
repository.adapter.query "SELECT *
FROM pois
WHERE " + distance.to_s + " > ** Complicated math coded in SQL that gets uses poi.lon and poi.lat to get the distance from the given longitude and latitude ** "
end
Then call
POI.all_within_distance(1000, 45, 90)

postgis shape file import problems

Hi I'm trying to import a shape file from
http://www.nyc.gov/html/dcp/html/bytes/bytesarchive.shtml
into a postgis database. the above files creates MULTIPOLYGONS when i import using shp2pgsql.
then i'm trying to simply determine if lat/long points are contained in my multipolygons
however my select's are not working, and when i print out the poitns of my the_geom column it seems to be very broken.
select st_astext(geom) from (select (st_dumppoints(the_geom)).* from nybb where borocode =1) foo;
gives the result...
st_astext
------------------------------------------
POINT(1007193.83859999 257820.786899999)
POINT(1007209.40620001 257829.435100004)
POINT(1007244.8654 257833.326199993)
POINT(1007283.3496 257839.812399998)
POINT(1007299.3502 257851.488900006)
POINT(1007320.1081 257869.218500003)
POINT(1007356.64669999 257891.055800006)
POINT(1007385.6197 257901.432999998)
POINT(1007421.94509999 257894.084000006)
POINT(1007516.85959999 257890.406100005)
POINT(1007582.59110001 257884.7861)
POINT(1007639.02150001 257877.217199996)
POINT(1007701.29170001 257872.893099993)
...
for points in nyc, this is very off.. what am i doing wrong?
The points are not of. The spatial data that is referred to is NOT in lat/long. This is why numbers are different from what you expect. If you need it to be in long/lat it must be reprojected. See more here: http://postgis.refractions.net/news/20020108/
The projection of the data seems to be in the NAD_1983_StatePlane_New_York_Long_Island_FIPS_3104_Feet coordinate system (according to the metadata - see code.).
<spref>
<horizsys>
<planar>
<planci>
<plance Sync="TRUE">coordinate pair</plance>
<coordrep>
<absres Sync="TRUE">0.000000</absres>
<ordres Sync="TRUE">0.000000</ordres>
</coordrep>
<plandu Sync="TRUE">survey feet</plandu>
</planci>
<mapproj><mapprojn Sync="TRUE">Lambert Conformal Conic</mapprojn><lambertc><stdparll Sync="TRUE">40.666667</stdparll><stdparll Sync="TRUE">41.033333</stdparll><longcm Sync="TRUE">-74.000000</longcm><latprjo Sync="TRUE">40.166667</latprjo><feast Sync="TRUE">984250.000000</feast><fnorth Sync="TRUE">0.000000</fnorth></lambertc></mapproj></planar>
<geodetic>
<horizdn Sync="TRUE">North American Datum of 1983</horizdn>
<ellips Sync="TRUE">Geodetic Reference System 80</ellips>
<semiaxis Sync="TRUE">6378137.000000</semiaxis>
<denflat Sync="TRUE">298.257222</denflat>
</geodetic>
<cordsysn>
<geogcsn Sync="TRUE">GCS_North_American_1983</geogcsn>
<projcsn Sync="TRUE">NAD_1983_StatePlane_New_York_Long_Island_FIPS_3104_Feet</projcsn>
</cordsysn>
</horizsys>
</spref>
If you work much with spatial data I suggest that you read more about map projection.
I think this is not issue with PostGIS. I checked input esri Shape file nybb.shp with AvisMap Free Viewer and as you see points are weird itself:
However there is something interesting in nybb.shp.xml metadata file:
<spdom>
<bounding>
<westbc Sync="TRUE">-74.257465</westbc>
<eastbc Sync="TRUE">-73.699450</eastbc>
<northbc Sync="TRUE">40.915808</northbc>
<southbc Sync="TRUE">40.495805</southbc>
</bounding>
<lboundng>
<leftbc Sync="TRUE">913090.770096</leftbc>
<rightbc Sync="TRUE">1067317.219904</rightbc>
<bottombc Sync="TRUE">120053.526313</bottombc>
<topbc Sync="TRUE">272932.050103</topbc>
</lboundng>
</spdom>
I am not familiar with those toolkit (ESRI ArcCatalog), but most probably you need to rescale your points after import using that metadata.