Daniel Andrews
Daniel Andrews

Reputation: 35

Wordpress Distance Search SQL

I have two tables, one with all of the postcodes in the UK together with their latitudes and longitudes. The second is a table with store locations.

I need to create a fast search query that returns all stores(posts) within a user specified radius, together with their distances, in order from nearest to furthest away.

The table structure is:

Postcodes Table Fields: Id, Postcode, Latitude, Longitude

Locations Table Fields: Store Name, PostID, Lat, Lng

The search will return the post name and an extract of information relating to that store.

A query that gets the postcode the user submitted, gets the latitude and longitude of that from the Postcodes table, then runs a distance calculation between the Postcodes table latitude/longitude, and each of the Lat/Long in the locations, returning those within the user specified distance is what i imagine is required, but no luck on creating the distance calculations between the two sets of lat/lng.

Distances required are up to 10 miles, up to 20 miles, up to 50 miles, up to 100 miles, and over 100 miles.

Any help much appreciated.

Many thanks,

Dan

Upvotes: 1

Views: 823

Answers (1)

eggyal
eggyal

Reputation: 126035

As @OllieJones commented, you need to use the haversine formula to calculate great circle distance. Adapting from Google Code's Creating a Store Locator with PHP, MySQL & Google Maps in order to cache as much as possible:

ALTER TABLE Postcodes
  ADD tc DOUBLE COMMENT 'cosine of latitude',
  ADD ts DOUBLE COMMENT   'sine of latitude',
  ADD gr DOUBLE COMMENT 'longitude in radians',
  ADD UNIQUE INDEX (Postcode);

CREATE TRIGGER Postcode_insert BEFORE INSERT ON Postcodes FOR EACH ROW SET
  NEW.tc = cos(radians(NEW.Latitude)),
  NEW.ts = sin(radians(NEW.Latitude)),
  NEW.gr =     radians(NEW.Longitude);

CREATE TRIGGER Postcode_update BEFORE UPDATE ON Postcodes FOR EACH ROW SET
  NEW.tc = cos(radians(NEW.Latitude)),
  NEW.ts = sin(radians(NEW.Latitude)),
  NEW.gr =     radians(NEW.Longitude);

UPDATE Postcodes SET tc = NULL; -- trigger will do the rest

CREATE VIEW distance AS
  SELECT s.Id AS PostID, u.Postcode,
         acos(s.tc*u.tc*cos(s.gr-u.gr) + s.ts*u.ts) AS radii
  FROM   Postcodes s, Postcodes u;

Then all you need to do is:

SELECT   Locations.*, 3959 * distance.radii AS miles
FROM     Locations JOIN distance USING (PostID)
WHERE    distance.Postcode = ?
HAVING   miles < ?

See it on sqlfiddle.

Upvotes: 1

Related Questions