Get all the users within radius from MySQL with Spatial Point - php

I have a MySQL table on my local server. The table includes latitude, longitude and POINT(latitude, longitude) for each user. I am trying to get all the id user within 10 kilometres from center.
I would like not to use latitude and longitude but only the Point. But I don't know how to do it. I also heard about SRID to modify in the POINT but I did not quite understand
Here is my table for the moment
# Name Type
1 lat decimal(10,8)
2 lng decimal(11,8)
3 latlng point
4 id_user int(10)
Indexes
Keyname Type Unique Packed Column Cardinality Collation Null Comment
latlng SPATIAL No No latlng (32) A No
How could I do that in PHP?
Just for information, what is the best thing between using a Point and latitude/longitude?

I am a postgres/postgis user, but they have equivalents. If you have the ability to add columns to your table you should convert the lat/lons to Point() objects maybe using something like ST_PointFromText and then you can compare them to each other using ST_Distance()
Ref: https://dev.mysql.com/doc/refman/8.0/en/gis-data-formats.html
Edit: apparently mySql does not have the more efficient ST_DWithin() (See: Whats the equivalent of ST_DWithin (PostGIS - PostgreSQL) for MySQL?) so you probably stuck with ST_Distance().
SRID 4326 is a common one for these types of lat/lon coordinate systems.
Ref: https://desktop.arcgis.com/en/arcmap/10.3/manage-data/using-sql-with-gdbs/what-is-an-srid.htm

Related

Working with coordinates in CodeIgniter 4 (or PHP)

I am programming in CodeIgniter 4, and I don't know how to deal with the following problem using either CodeIgniter 4 or directly with PHP.
I have a database table places with one column coord with the position (coordinates) of each place, a MySQL column of type point. Thanks to CodeIgniter 4 I get all the columns, but in order to use the latitude and longitude separately I need to do:
select (*, st_x(coord) as lat, st_y(coord) as lon)
from ...
That way, I can use lat and lon. If I just use coord I get something without meaning.
Is there a way to do one of the following:
Work with that coord variable to get the latitude and longitude in PHP?
Add a function in the MySQL table to make that select(*) return also the lat and lon columns (calculated from st_x(coord) and st_y(coord))?
You could add virtual generated columns for lat and lon -
ALTER TABLE places
ADD COLUMN lat DOUBLE GENERATED ALWAYS AS (ST_X(coord)) VIRTUAL,
ADD COLUMN lon DOUBLE GENERATED ALWAYS AS (ST_Y(coord)) VIRTUAL;

Laravel + Mysql: Retrieve POI's within a bounding box

I have a database table with POI's that i want to connect with a record in my Geo table (containing cities, countries).
Within the POI table i have a Lat & Lon field.
Within the Geo table i have 4 coordinates, for West/East and North/South
I'm using Laravel 5 / Eloquent and Mysql. The coordinates are stored as a decimal (11,8). I can add multiple columns if needed.
How can i check if a POI is located within those 4 coordinates?
Mysql supports spatial data types and functions that were designed to make such calculations easy.
Define your POIs as POINT and your rectangle as GEOMETRY. Use the ST_Contains() function to test whether the point is within the rectangle:
select * from yourtable
where st_contains(rectangle_field, point_field)
As far as I know, Laravel does not directly support these daya types and functions, so you have to resort to raw sql.

What algorithm is used for Reverse Geocoding using Geonames.org downloads?

I would use the API Geonames, but my application won't have access to the internet. So it has to run stand-alone. I see there are many files from Geonames.org to download, but I don't see any software to aid in being able to do Reverse Geocoding. I want to provide the lat/lon and have it return the country code. I'm thinking of downloading the data and put it into a MySQL database.
What algorithm is used for Reverse Geocoding so I can make use of it with the geonames.org downloads. My project is being written in PHP. Thanks!
Geonames provides data for most countries. This data can be imported into a MySQL database. This can be searched for using lat lng.
The problem is that there is probably too much data in the files for your requirements. You can eliminate all the fields except lat,lng&country code for all the countries required and then combine them in 1 record.
MySQL query
SELECT country code FROM XX WHERE lat BETWEEN 55 AND 55.5 AND lng BETWEEN -2
AND -1.5
This will pull in data over a "Box" of 111kM. You can change the range to suit.
From the table below you can see that the 111kM "box" is only valid at the equator(0°). for other latitudes the lng range would need to be increased.
lat lng
0° 110.574 km 111.320 km
15° 110.649 km 107.551 km
30° 110.852 km 96.486 km
45° 111.132 km 78.847 km
60° 111.412 km 55.800 km
75° 111.618 km 28.902 km
90° 111.694 km 0.000 km
geonames downloads has mainly in .txt files, these can be imported via SQLYog to the mysql database
Table Structure
CREATE DATABASE geonames;
USE geonames;
CREATE TABLE geoname (
geonameid int PRIMARY KEY,
name varchar(200),
asciiname varchar(200),
alternatenames varchar(4000),
latitude decimal(10,7),
longitude decimal(10,7),
fclass char(1),
fcode varchar(10),
country varchar(2),
cc2 varchar(60),
admin1 varchar(20),
admin2 varchar(80),
admin3 varchar(20),
admin4 varchar(20),
population int,
elevation int,
gtopo30 int,
timezone varchar(40),
moddate date
) CHARACTER SET utf8;
MYsql query to import data to this table
LOAD DATA INFILE '/path/to/your/file/geoname.txt' INTO TABLE `geoname`;
After all is set you can query this table against lat/long to get country name
Here is the link you can refer to
http://sgowtham.net/blog/2009/07/29/importing-geonames-org-data-into-mysql/
http://forum.geonames.org/gforum/posts/list/732.page
Hope this helps!
The algorithm you are looking for is more a data structure then a complicated formula. Mysql supports R-tree and spatial index queries. The concept is a hierarchical tree with the lat long pairs as a bounding box the root of the tree has the entire world in the bounding box. A Kd-tree can be also good. In rare case you want to look for a quadkey or a space filling curve. It's a fractal and reduces the dimension to a number. The formula is H(x,y) = H(x) + H(y). The fractal also preseve some proximity information which I don't know how it's solved with a R-tree.

Storing multiple GPS coordinates in MySQL

I'm making an Android app that tracks a user and displays their location in real time. I have it working, but I'm having issues storing the coordinates in a database properly. Right now, the user's location will update every second, and it stores the location in a database and then the web app pulls the most recent from the database. I want to be able to store the list of locations in one row for a particular user. I read some about GeoSpatial information in MySQL, and I think that the linestring datatype would work, but I can't seem to find enough information about how to implement the query in PHP. Can someone provide an example of how to keep appending coordinates to the database in a linestring type using PHP? Or provide a suggestion of how to continually store coordinates using one row of a database.
Thanks
Simply store each point the user is located at into a table, along with an ID and timestamp. You can then assemble the points with a query.
Don't store an entire track in one row, or you won't be able to do much with the data later.
Edit: Here is what your table will look like:
gps_points
id (bigint)
user_id (int)
timestamp (timestamp or datetime, depending on your needs)
lat (double)
lon (double)
A GPS coordinate is a set of X,Y and Z float value, not a set of points to interpolate a curve (which essentially is what the linestring datatype is for). So I would store the points in 3 float columns with the additional information like a timestamp. If you need, you can extrapolate the linestring afterwards from the given data to show on a map.
Or you can just simply use Firebase for your database which is very flexible and you can easily work in firebase
My experience is that the UTM format is easier to store in a database since its orthogonal and has a really convenient syntax. And it is suitable for single line string too. You can find information and a handy class that easily converts between GPS and UTM here:
http://www.ibm.com/developerworks/java/library/j-coordconvert/index.html

PHP/MySQL - Performance considerations querying (select only) 48k rows

I am currently attempting to build a web application that relies quite heavily on postcode data (supplied from OS CodePoint Open). The postcode database has 120 tables which breaks down the initial postcode prefix (i.e. SE, WS, B). Inside these tables there are between 11k - 48k rows with 3 fields (Postcode, Lat, Lng).
What I need to be able to do is for a user to come online, enter their postcode i.e. SE1 1LD which then selects the SE table, and converts the postcode into a lat / lng.
I am fine with doing this on a PHP level. My concern is.. well the huge number of rows that will be queried and whether it is going to grind my website to a halt?
If there are any techniques that I should know about, please do let me know.. I've never worked with tables with big numbers in!
Thanks :)
48K are not big numbers. 48 million is. :) If your tables are properly indexed (put indexes on the fields you use in the WHERE clause) it won't be a problem at all.
Avoid LIKE, and use INNER JOINS instead of LEFT JOINs if possible.
selecting from 48k rows in mysql is not big, in fact its rather small. index it properly and you are fine.
If I understand correct, there is a SE table, a WS one, a B one, etc. In all, 120 tables with same structure (Postcode, Lat, Lng).
I strongly propose you normalize the tables.
You can have either one table:
postcode( prefix, postcode, lat, lng)
or two:
postcode( prefixid , postcode, lat, lng )
prefix( prefixid, prefix )
The postcode table will be slighly bigger than 11K-48K rows, about 30K x 120 = 3.6M rows but it will save you time for writing different queries for every prefix and quite complex ones if, for example, you want to search for latitude and longitude (imagine a query that searches in 120 tables).
If you are not convinced try to add a person table so you can add data for your users. How this table will be related to the postcode table(s) ?
EDIT
Since the prefix is just the first characters of the postcode which is also the primary key, there is no need for extra field or second table. I would simply combine the 120 tables into one:
postcode( postcode, lat, lng )
Then queries like:
SELECT *
FROM postode
WHERE postcode = 'SE11LD'
or
SELECT *
FROM postode
WHERE postcode LIKE 'SE%'
will be fast, as they will be using the primary key index.
As long as you have indexes on the appropriate columns, there should be no problem. One of my customers has the postcode database stored in a table like :
CREATE TABLE `postcode_geodata` (
`postcode` varchar(8) NOT NULL DEFAULT '',
`x_coord` float NOT NULL DEFAULT '0',
`y_coord` float NOT NULL DEFAULT '0',
UNIQUE KEY `postcode_idx` (`postcode`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 |
And we have no problems (from a performance point of view) in querying that.
If your table did become really large, then you could always look at using MySQL's partitioning support - see http://dev.mysql.com/doc/refman/5.1/en/partitioning.html - but I wouldn't look at that until you've done the easier things first (see below).
If you think performance is an issue, turn on MySQL's slow_query_log (see /etc/mysql/my.cnf) and see what it says (you may also find the command 'mysqldumpslow' useful at this point for analysing the slow query log).
Also try using the 'explain' syntax on the MySQL cli - e.g.
EXPLAIN SELECT a,b,c FROM table WHERE d = 'foo' and e = 'bar'
These steps will help you optimise the database - by identifying which indexes are (or aren't) being used for a query.
Finally, there's the mysqltuner.pl script (see http://mysqltuner.pl) which helps you optmise the MySQL server's settings (e.g. query cache, memory usage etc which will affect I/O and therefore performance/speed).

Categories