Understanding the distance relationships in a Longitude and Latitude equation from an SQL query - sql

In a PHP program that I did not develop I am able to enter, via a form, distance from a given US zipcode (radius in miles) from which to do a proximity search.
Let's take, for example, the city Gastonia, NC with a zipcode of 28054 and a radius distance of 10 miles.
The PHP code generates the SQL query dynamically. Before it gets to that point it does its calculations behind the scenes and gives these values:
:minlat (Float) 35.084832880851
:maxlat (Float) 35.374297119149
:minlon (Float) -81.305653802747
:maxlon (Float) -80.951286197253
It also gives this distance:
:distance (Float) 16093.47
I cannot see or manipulate the code that generates these values given the distance I entered into the form. However, I can override the values of each of these calculated variables.
I understand the :minlat and :minlon, it's the central point of my zipcode. What I don't understand is, what is :distance and what relationship does it have to :maxlat and :maxlon?
What type of measurement is :distance given that it started as 10 miles in the form?
Obviously :distance added to :maxlat or :maxlon doesn't make any sense.
What I ultimately want to be able to do is take a :minlat and :minlon, which I have a database of point, and then search a certain :distance.
So, if I wanted to search 20 miles, that would be :distance 32186 ish, but how does that affect maxlat and maxlon?
If you are interested in the entire SQL query, it's:
SELECT node.title AS node_title,
node.nid AS nid,
node.created AS node_created,
'node' AS field_data_field_item_photos_node_entity_type,
(COALESCE(ACOS(0.81684734668492*COS(RADIANS(location.latitude))*(0.15421945466762*COS(RADIANS(location.longitude)) + -0.9880366186544*SIN(RADIANS(location.longitude))) + 0.57685389156511*SIN(RADIANS(location.latitude))), 0.00000)*6370997.0816549) AS location_distance
FROM
{node} node
LEFT JOIN {location_instance} location_instance ON node.vid = location_instance.vid
LEFT JOIN {location} location ON location_instance.lid = location.lid
WHERE (( (node.status = '1')
AND (location.latitude > '35.084832880851'
AND location.latitude < '35.374297119149'
AND location.longitude > '-81.305653802747'
AND location.longitude < '-80.951286197253')
AND ((COALESCE(ACOS(0.81684734668492*COS(RADIANS(location.latitude))*(0.15421945466762*COS(RADIANS(location.longitude)) + -0.9880366186544*SIN(RADIANS(location.longitude))) + 0.57685389156511*SIN(RADIANS(location.latitude))), 0.00000)*6370997.0816549) < '16093.47') ))
Table structure
+-----------+---------------+------+-----+----------+-------+
| Field | Type | Null | Key | Default | Extra |
+-----------+---------------+------+-----+----------+-------+
| zip | varchar(16) | NO | MUL | 0 | |
| city | varchar(30) | NO | | | |
| state | varchar(30) | NO | | | |
| latitude | decimal(10,6) | NO | MUL | 0.000000 | |
| longitude | decimal(10,6) | NO | MUL | 0.000000 | |
| timezone | tinyint(4) | NO | | 0 | |
| dst | tinyint(4) | NO | | 0 | |
| country | char(2) | NO | MUL | | |
+-----------+---------------+------+-----+----------+-------+

Related

postgres: statictics of column of user type "set"

I am accessing a table table that I recognised one column was defined like type list (if you come from python like I do). I retrieved its create statement via pg_dump
CREATE TABLE sensemyfeup.trips (
trip_id integer NOT NULL,
daily_user_id integer,
session_ids integer[],
seconds_start integer,
lat_start double precision,
lon_start double precision,
seconds_end integer,
lat_end double precision,
lon_end double precision,
distance double precision
);
I am referring to column session_ids. It contents look like:
SELECT * FROM trips LIMIT 5;
trip_id | daily_user_id | session_ids | seconds_start | lat_start | lon_start | seconds_end | lat_end | lon_end | distance
---------+---------------+---------------+---------------+------------+------------+----- --------+------------+------------+------------------
540797 | 2169 | {43350} | 1461056108 | 41.1250659 | -8.5993936 | 1461056424 | 41.1221733 | -8.6004883 | 412.658565594423
546128 | 3096 | {84659,84663} | 1461847953 | 41.1787939 | -8.6078294 | 1461849730 | 41.1840573 | -8.6033242 | 3469.92906971906
536069 | 1080 | {9837} | 1460293763 | 41.1836186 | -8.6001802 | 1460294099 | 41.1836725 | -8.6001787 | 47.7817179218928
537711 | 1373 | {17641,17689} | 1460590761 | 41.1477454 | -8.611109 | 1460593908 | 41.1477451 | -8.6111093 | 1081.61337507529
542407 | 2254 | {53112} | 1461173383 | 40.9853811 | -8.5205261 | 1461173677 | 40.9873266 | -8.5003848 | 2224.13368208515
As we can see in the session_ids column, some records have 1 value, some multiple.
How I get a summary statistics of rows with 1 session_ids value, with 2, etc..?
We can use cardinality to count the number of elements in session_ids and group by with the results.
select cardinality(session_ids) as number_of_session_id_values
,count(*)
from t
group by cardinality(session_ids)
number_of_session_id_values
count
2
2
1
3
Fiddle

How to get location from latitude and longitude in T-SQL?

I have a mapping table of locationId along with their center latitude and center longitude value like below-
| Location Id | Center_lat | Center_long |
|-------------|------------|-------------|
| 1 | 50.546 | 88.344 |
| 2 | 48.546 | 86.344 |
| 3 | 52.546 | 89.344 |
I have another table where I am getting continuous location data with latitude and longitude for user like below -
+---------+------------+-------------+
| User Id | Center_lat | Center_long |
+---------+------------+-------------+
| 101 | 50.446 | 88.314 |
| 102 | 48.446 | 86.314 |
| 103 | 52.446 | 89.314 |
+---------+------------+-------------+
I want to get the locationId of all users if their latitude and longitude values lies within 1000 meters of lat-long values corresponding to location id. How can I get it done in T-SQL?
Final table should like below -
+---------+------------+-------------+------------+
| User Id | Center_lat | Center_long | LocationId |
+---------+------------+-------------+------------+
| 101 | 50.546 | 88.344 | 1 |
| 102 | 48.546 | 86.344 | 2 |
| 103 | 52.546 | 89.344 | 3 |
+---------+------------+-------------+------------+
You can use convert the latitude/longitude pairs to geography objects, and then use stdistance():
select u.*, l.location_id
from users u
inner join locations l
on geography::point(u.center_lat, u.center_long, 4326).stdistance(geography::point(l.center_lat, l.center_long, 4326)) < 1000
Note that it would be much more efficient to store this information as points to start with.

Alphanumberic output from ST_MakeLine

I'm trying to convert lat/lon to linestring. Basically, grouping the columns lat and lon, making a point, and creating a linestring.
Table:
+------------+----------+-----------+------------+---------+--------+
| link_id | seq_num | lat | lon | z_coord | zlevel |
+------------+----------+-----------+------------+---------+--------+
| "16777220" | "0" | "4129098" | "-7192948" | | 0 |
| "16777220" | "999999" | "4129134" | "-7192950" | | 0 |
| "16777222" | "0" | "4128989" | "-7193030" | | 0 |
| "16777222" | "1" | "4128975" | "-7193016" | | 0 |
| "16777222" | "2" | "4128940" | "-7193001" | | 0 |
| "16777222" | "3" | "4128917" | "-7192998" | | 0 |
| "16777222" | "4" | "4128911" | "-7193002" | | 0 |
+------------+----------+-----------+------------+---------+--------+
My code:
select link_id, ST_SetSRID(ST_MakeLine(ST_MakePoint((lon::double precision / 100000), (lat::double precision / 100000))),4326) as geometry
from public.rdf_link_geometry
group by link_id
limit 50
geometry output column example:
"0102000020E6100000020000004F92AE997CFB51C021E527D53EA54440736891ED7CFB51C021020EA14AA54440"
^^ What is this? how did it get formatted in such a way? I expected a linestring, something like
geometry
7.123 50.123,7.321 50.321
7.321 50.321,7.321 50.321
Data format for link_id is bingint, and for geometry it says geometry
SOLUTION:
select link_id, ST_AsText(ST_SetSRID(ST_MakeLine(ST_MakePoint(
(lon::double precision / 100000), (lat::double precision / 100000))),4326)) as geometry
from public.rdf_link_geometry
group by link_id
limit 50
The output is a geometry, which you can display as text using st_asText
select st_asText('0102000020E6100000020000004F92AE997CFB51C021E527D53EA54440736891ED7CFB51C021020EA14AA54440');
st_astext
--------------------------------------------------
LINESTRING(-71.92948 41.29098,-71.9295 41.29134)
That being said, should you have more than 2 points, you could order them to create a meaningful line:
select st_makeline(geom ORDER BY seqID) from tbl;

PostgresQL ERROR: operator does not exist: integer = integer[]

I am doing an SQL JOIN...ON in which the column of the other table to join on is an array to a set of rows, and therefore encounter this error. Specifically, I'm doing the JOIN on the tables.
TABLE: location
+------------+------------+---------+----------+
| session_id | gpstime | lat | lon |
+------------+------------+---------+----------+
| 49 | 1458203595 | 39.7449 | -8.8052 |
| 59 | 1458203601 | 39.7438 | -8.8057 |
| 95 | 1458203602 | 39.7438 | -8.8056 |
| 49 | 1458203602 | 39.7438 | -8.8057 |
+------------+------------+---------+----------+
TABLE: trips
+-------------+-----------+---------+-----------+---------+-------------+
| session_ids | lat_start | lat_end | lon_start | lon_end | travel_mode |
+-------------+-----------+---------+-----------+---------+-------------+
| {49} | 39.7449 | 41.1782 | -8.8053 | -8.5946 | car |
| {59,60} | 41.1551 | 41.1542 | -8.6294 | -8.6247 | foot |
| {94,95} | 41.1545 | 40.7636 | -8.6273 | -8.1729 | bike |
+-------------+-----------+---------+-----------+---------+-------------+
Here's the query I used:
SELECT gpstime, lat, lon, travel_mode
FROM location
INNER JOIN trips
ON session_id = session_ids
WHERE (lat BETWEEN SYMMETRIC lat_start AND lat_end)
AND (lon BETWEEN SYMMETRIC lon_start AND lon_end);
Error:
ERROR: operator does not exist: integer = integer[]
LINE 4: ON session_id = session_ids
How do I fix the issue?
The = comparator can only compare two values of the same type. But here you are trying to compare an integer value with an array. So the value 1 cannot equal a value that look like [1,2].
You can use the = ANY(...) comparator which checks if the left value is part of the right array:
demo:db<>fiddle
ON session_id = ANY(session_ids)
S-Man is correct, although you can also use the ANY function as described here.
For more information on the differences between using IN and ANY/ALL, read this question.

Retrieve closest road when given (lat, long) using OSM in Postgres with Postgis using SQL query

Given a set (lat, long) I am trying to find the maximum speed using "max_speed" and street type using "highway".
I have loaded my database (Postgres and Postgis) as follows:
$ osm2pgsql -c -d gis --slim -C 50000 /var/lib/postgresql/data/germany-latest.osm.pbf
The closest related question I could find was How to query all shops around a certain longitude/latitude using osm-postgis?. I have taken the query, and plugged in a (lat, long) that I found in google maps for the city center of Munich (as the post was also related to city center Munich and I have the map for Germany). The result turns up empty.
gis=# SELECT name, shop FROM planet_osm_point WHERE ST_DWithin(way ,ST_SetSrid(ST_Point(48.137969, 11.573829), 900913), 100);
name | shop
------+------
(0 rows)
Also when looking into the planet_osm_nodes, which contains (lat, long) pairs directly, I end up with no results:
gis=# SELECT * FROM planet_osm_nodes WHERE ((lat BETWEEN 470000000 AND 490000000) AND (lon BETWEEN 100000000 AND 120000000)) LIMIT 10;
id | lat | lon | tags
----+-----+-----+------
(0 rows)
I verified the data is in my database:
gis=# SELECT COUNT(*) FROM planet_osm_point;
count
---------
9924531
(1 row)
and
gis=# SELECT COUNT(*) FROM planet_osm_nodes;
count
-----------
288597897
(1 row)
So ideally, my question would be
Q: How can I find the "max speed" and "highway" given a set (lat, lon)
alternatively, my questions is:
Q: How do I get the query from the other stack overflow post to work?
My best guess is that I need to transform my (lat, lon) in some way, or that I simply have the wrong data for whatever reason.
Edit: added sample data as requested:
gis=# SELECT * FROM planet_osm_point LIMIT 1;
osm_id | access | addr:housename | addr:housenumber | addr:interpolation | admin_level | aerialway | aeroway | amenity | area | barrier | bicycle | brand | bridge | boundary | building | capital | construction | covered | culvert |
cutting | denomination | disused | ele | embankment | foot | generator:source | harbour | highway | historic | horse | intermittent | junction | landuse | layer | leisure | lock | man_made | military | motorcar | name | natural | off
ice | oneway | operator | place | poi | population | power | power_source | public_transport | railway | ref | religion | route | service | shop | sport | surface | toll | tourism | tower:type | tunnel | water | waterway | wetland | wi
dth | wood | z_order | way
-----------+--------+----------------+------------------+--------------------+-------------+-----------+---------+---------+------+---------+---------+-------+--------+----------+----------+---------+--------------+---------+---------+
---------+--------------+---------+-----+------------+------+------------------+---------+----------+----------+-------+--------------+----------+---------+-------+---------+------+----------+----------+----------+------+---------+----
----+--------+----------+-------+-----+------------+-------+--------------+------------------+---------+-----+----------+-------+---------+------+-------+---------+------+---------+------------+--------+-------+----------+---------+---
----+------+---------+----------------------------------------------------
304070863 | | | | | | | | | | | | | | | | | | | |
| | | | | | | | crossing | | | | | | | | | | | | | |
| | | | | | | | | | | | | | | | | | | | | | | |
| | | 010100002031BF0D0048E17A94F19F2941CDCCCCDCC60D5741
(1 row)
and
gis=# SELECT * FROM planet_osm_nodes LIMIT 1;
id | lat | lon | tags
--------+-----------+----------+------
234100 | 666501948 | 80442755 |
(1 row)
Edit 2: There was a mention regarding "SRID", so I added example data from another table:
gis=# SELECT * FROM spatial_ref_sys LIMIT 1;
srid | auth_name | auth_srid | srtext
| proj4text
------+-----------+-----------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------
3819 | EPSG | 3819 | GEOGCS["HD1909",DATUM["Hungarian_Datum_1909",SPHEROID["Bessel 1841",6377397.155,299.1528128,AUTHORITY["EPSG","7004"]],TOWGS84[595.48,121.69,515.35,4.115,-2.9383,0.853,-3.408],AUTHORITY["EPSG","1024"]],PR
IMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","3819"]] | +proj=longlat +ellps=bessel +towgs84=595.48,121.69,515.35,4.115,-2.9383,0.853,-3.408 +no_defs
(1 row)
Geometry in PostGIS has a different ordering of (lat long) first is going longitude then latitude.
Also if you want to transform a point from one SRID to another use st_transfrom(), not ST_SetSrid.
ST_Transform relly transform your data from one coordinates system to another.
select st_astext(st_transform(ST_SetSrid(ST_Point(11.573829,48.137969), 4326),900913))
ST_SetSrid - just change SRID for the object.
select st_astext((ST_SetSrid(ST_Point(11.573829,48.137969),900913)
So, you have to change your SQL that way
SELECT name, shop
FROM planet_osm_point
WHERE ST_DWithin(way,st_transform(ST_SetSrid(ST_Point(11.573829,48.137969), 4326),900913), 100);