In my php application I want to get the nearest postal code of the given post code.
That means I enter a post code as 680721 I want to get the nearest post code of this from my database.
How can I do this?
This is the table I used for store postal codes.
Here varpin is the postal code field.
Having said all this, a quick browse through the “External Links” on the UK Postcodes Wikipedia entry, and I quickly found an article by Paul Jenkins entitled UK Post Code Distance Calculation in PHP, which is fantastic, you can even download it here (uk_postcode_calc.zip).
After a short examination it seems this does exactly what it says on the tin, and simply calculates the distance.
However, with a quick google for php distance calculation, you can quickly find that there are more refined equivalents of the distance calculation. I thought it might be a good idea to use one of those instead.
After a bit of tweaking, here’s what I came up with in the end:
function distance($lat1, $lon1, $lat2, $lon2, $u=’1′) {
$u=strtolower($u);
if ($u == ‘k’) { $u=1.609344; } // kilometers
elseif ($u == ‘n’) { $u=0.8684; } // nautical miles
elseif ($u == ‘m’) { $u=1; } // statute miles (default)
$d=sin(deg2rad($lat1))*sin(deg2rad($lat2))+cos(deg2rad($lat1))*cos(deg2rad($lat2))*cos(deg2rad($lon1-$lon2));
$d=rad2deg(acos($d));
$d=$d*60*1.1515;
$d=($d*$u); // apply unit
$d=round($d); // optional
return $d;
}
So, that’s the hard parts done (database and maths), next is simply a case of using this information to “find the closest” from the postcode we input to an array of postcodes we supply…
To find the “closest” postcode, effectively what we’re trying to do is find the “shortest” distance between the postcodes, or, simply the smallest number in the results, assuming we put the results into an array with the key as the postcode and the distance as the value.
All we have to do is create a simple script that will find the smallest number in a given array, then return the appropriate key. Simple!
function closest ($needle,$haystack) {
if (!$needle || !$haystack) { return; }
if (!is_array($haystack)) { return; }
$smallest=min($haystack); //smallest value
foreach ($haystack as $key => $val) {
if ($val == $smallest) { return $key; }
}
}
The above script does exactly what we want, using the “min” function we can quickly work out what we need to return.
The only task left is to bind all this together, we need to create two functions that will:
Get the distance using the postcode to get the longitude and latitude from the database.
Create an array with the postcodes as the keys, and the distance as the values.
Very simple!
Function 1, Postcode Distance
function postcode_distance ($from,$to) {
// Settings for if you have a different database structure
$table=’postcodes_uk’;
$lat=’lat’;
$lon=’lon’;
$postcode=’postcode’;
// This is a check to ensure we have a database connection
if (!#mysql_query(‘SELECT 0′)) { return; }
// Simple regex to grab the first part of the postcode
preg_match(‘/[A-Z]{1,2}[0-9R][0-9A-Z]?/’,strtoupper($from),$match);
$one=$match[0];
preg_match(‘/[A-Z]{1,2}[0-9R][0-9A-Z]?/’,strtoupper($to),$match);
$two=$match[0];
$sql = “SELECT `$lat`, `$lon` FROM `$table` WHERE `$postcode`=’$one’”;
$query = mysql_query($sql);
$one = mysql_fetch_row($query);
$sql = “SELECT `$lat`, `$lon` FROM `$table` WHERE `$postcode`=’$two’”;
$query = mysql_query($sql);
$two = mysql_fetch_row($query);
$distance = distance($one[0], $one[1], $two[0], $two[1]);
// For debug only…
//echo “The distance between postcode: $from and postcode: $to is $distance miles\n”;
return $distance;
}
Function 2, Postcode Closest
function postcode_closest ($needle,$haystack) {
if (!$needle || !$haystack) { return; }
if (!is_array($haystack)) { return; }
foreach ($haystack as $postcode) {
$results[$postcode]=postcode_distance($needle,$postcode);
}
return closest($needle,$results);
}
So, with that done, place the 4 above functions into a file such as “postcode.php”, ready for use in the real world…
Test case:
<?php
include_once(‘postcode.php’);
if ($_POST) {
include_once(‘db.php’);
$postcodes=array(‘TF9 9BA’,'ST4 3NP’);
$input=strtoupper($_POST['postcode']);
$closest=postcode_closest($input,$postcodes);
}
if (isset($closest)) {
echo “The closest postcode is: $closest”;
}
?>
<form action=”" method=”post”>
Postcode: <input name=”postcode” maxlength=”9″ /><br />
<input type=”submit” />
</form>
You can download this script here: postcode_search.phps
Note: In the above test case, I have a “db.php” file which contains my database details and starts a database connection. I suggest you do the same.
Ensure you have your database populated, you should be able to use Paul Jenkins’s UK Postcode csv, allowing you to use your own table structure.
Well, that’s all folks, I can now use this script to provide any locations that match the “closest” postcode.
Related
I am using Google Distance Matrix to calculate the distance between two locations for a taxi website. I need to test a condition to ascertain whether the one of the locations is a London airport and if so return a message rather than the calculating the cost of the trip.
It worked perfectly yesterday and then today ... the code has gone biserk. It is only returning the message if pick-up and drop off locations are both airports but if only one is, it is calculating the cost for the full distance which defeats my purpose. Below is the code structure and I am wondering whether I am using the wrong nested conditional syntax at line 39 when I start the strpos() test ....
if (isset($_POST['submitted'])):
$origin = urlencode($_POST['origin']);
$destination = urlencode($_POST['destination']);
// Insert encoded url variables
$url = "http://maps.googleapis.com/maps/api/distancematrix/json?origins=$origin&destinations=$destination&mode=driving&keyy={API KEY}";
$json = file_get_contents($url); // get the data from Google Maps API
$status = $result['rows'][0]['elements'][0]['status'];
if (status is OK):
// Calculate the distance
$status = ...;
$DistanceMetres = .....;
$Distance = .....; // converted to yards
// Calculate the Day Rate
....
// Calculate the Night Rate
....
// Calculate the Sunday Rate
....
// Calculate the Christmas & NYE Rate
....
// Set up variables for pick-up & drop-off locations
$toairport = $result['destination_addresses'][0];
$fromairport= $result['origin_addresses'][0];
if (distance is the minimum distance) {
echo the minimum cost of the trip
} else { //ie if the distance is more than the minimum distance
// Check to see if pick-up or drop-off destination is an airport
if (strpos($toairport, 'Heathrow') || strpos($toairport, 'Gatwick') || strpos($toairport, 'London Luton') || strpos($toairport, 'London City Airport') || strpos($fromairport, 'Heathrow') || strpos($fromairport, 'Gatwick') || strpos($fromairport, 'London Luton') || strpos($fromairport, 'London City Airport') === false) {
echo the cost of the trip
// But if at least one location is an airport
} else {
echo a message saying a special flat rate is available for airport transfers
}
}
else:
echo that status is not okay
endif;
else:
display input form
endif;
I suggest to implement the check as a function. For example
/**
* Check if the location is an airport.
*
* #param string $location
* #return bool
*/
function isAirport($location)
{
$airportList = ['Heathrow', 'Gatwick', 'London Luton', 'London City Airport'];
foreach ($airportList as $airport) {
if (strpos($location, $airport) !== false) {
return true;
}
}
return false;
}
Note that we compare strpos result with false using !== operator because strpos might return zero which is == to false.
Next, internal check might look like this
if (isAirport($toairport) || isAirport($fromairport)) {
echo 'a message saying a special flat rate is available for airport transfers'.PHP_EOL;
} else {
echo 'the cost of the trip'.PHP_EOL;
}
Note that we check that either $toairport are $fromairport` variables are airport locations. I guess this is what you want the script to do.
When we use function like this it makes significantly easy to read the script and understand what is it doing. Also it makes it easier to modify the script by adding new location or improving the logic. For example we might want to make the check case insensitive.
I have integrated Yelp reviews into my directory site with each venue that has a Yelp ID returning the number of reviews and overall score.
Following a successful MySQL query for all venue details, I output the results of the database formatted for the user. The Yelp element is:
while ($searchresults = mysql_fetch_array($sql_result)) {
if ($yelpID = $searchresults['yelpID']) {
require('yelp.php');
if ( $numreviews > 0 ) {
$yelp = '<img src="'.$ratingimg.'" border="0" /> Read '.$numreviews.' reviews on <img src="graphics/yelp_logo_50x25.png" border="0" /><br />';
} else {
$yelp = '';
}
} //END if ($yelpID = $searchresults['yelpID']) {
} //END while ($searchresults = mysql_fetch_array($sql_result)) {
The yelp.php file returns:
$yrating = $result->rating;
$numreviews = $result->review_count;
$ratingimg = $result->rating_img_url;
$url = $result->url;
If a venue has a Yelp ID and one or more reviews then the output displays correctly, but if the venue has no Yelp ID or zero reviews then it displays the Yelp review number of the previous venue.
I've checked the $numreviews variable type and it's an integer.
So far I've tried multiple variations of the "if ( $numreviews > 0 )" statement such as testing it against >=1, !$numreviews etc., also converting the integer to a string and comparing it against other strings.
There are no errors and printing all of the variables returned gives the correct number of reviews for each property with venues having no ID or no reviews returning nothing (as opposed to zero). I've also compared it directly against $result->review_count with the same problem.
Is there a better way to make the comparison or better format of variable to work with to get the correct result?
EDIT:
The statement if ($yelpID = $searchresults['yelpID']) { is not operating as it should. It is identical to other statements in the file, validating row contents which work correctly for their given variable, e.g. $fbID = $searchresults['fbID'] etc.
When I changed require('yelp.php'); to require_once('yelp.php'); all of the venue outputs changed to showing only the first iterated result. Looking through the venues outputted, the error occurs on the first venue after a successful result which makes me think there is a pervasive piece of code in the yelp.php file, causing if ($yelpID = $searchresults['yelpID']) { to be ignored until a positive result is found (a yelpID in the db), i.e. each venue is correctly displayed with a yelp number of reviews until a blank venue is encountered. The preceding venues' number of reviews is then displayed and this continues for each blank venue until a venue is found with a yelpID when it shows the correct number again. The error reoccurs on the next venue output with no yelpID and so on.
Sample erroneous output: (line 1 is var_dump)
string(23) "bayview-hotel-bushmills"
Bayview Hotel
Read 3 reviews on yelp
Benedicts
Read 3 reviews on yelp (note no var_dump output, this link contains the url for the Bayview Hotel entry above)
string(31) "bushmills-inn-hotel-bushmills-2"
Bushmills Inn Hotel
Read 7 reviews on yelp
I suspect this would be a new question rather than clutter/confuse this one further?
END OF EDIT
Note: I'm aware of the need to upgrade to mysqli but I have thousands of lines of legacy code to update. For now I'm working on functionality before reviewing the code for best practice.
Since the yelp.php is sort of a blackbox; the best explanation for this behavior would be that it only set's those variables if it finds a match. Updating your code to this should fix that:
unset($yrating, $numreviews, $ratingimg, $url);
require('yelp.php');
I also noticed this peculiar if-statement, do you realize that's an assignment or is this a copy/paste error? If you want to test (that's what if is for)
if ($yelpID == $searchresults['yelpID']) {
Do you know some utility or a web site where I can give US city,state and radial distance in miles as input and it would return me all the cities within that radius?
Thanks!
Here is how I do it.
You can obtain a list of city, st, zip codes and their latitudes and longitudes.
(I can't recall off the top of my head where we got ours)
edit: http://geonames.usgs.gov/domestic/download_data.htm
like someone mentioned above would probably work.
Then you can write a method to calculate the min and max latitude and longitudes based on a radius, and query for all cities between those min and max. Then loop through and calculate the distance and remove any that are not in the radius
double latitude1 = Double.parseDouble(zipCodes.getLatitude().toString());
double longitude1 = Double.parseDouble(zipCodes.getLongitude().toString());
//Upper reaches of possible boundaries
double upperLatBound = latitude1 + Double.parseDouble(distance)/40.0;
double lowerLatBound = latitude1 - Double.parseDouble(distance)/40.0;
double upperLongBound = longitude1 + Double.parseDouble(distance)/40.0;
double lowerLongBound = longitude1 - Double.parseDouble(distance)/40.0;
//pull back possible matches
SimpleCriteria zipCriteria = new SimpleCriteria();
zipCriteria.isBetween(ZipCodesPeer.LONGITUDE, lowerLongBound, upperLongBound);
zipCriteria.isBetween(ZipCodesPeer.LATITUDE, lowerLatBound, upperLatBound);
List zipList = ZipCodesPeer.doSelect(zipCriteria);
ArrayList acceptList = new ArrayList();
if(zipList != null)
{
for(int i = 0; i < zipList.size(); i++)
{
ZipCodes tempZip = (ZipCodes)zipList.get(i);
double tempLat = new Double(tempZip.getLatitude().toString()).doubleValue();
double tempLon = new Double(tempZip.getLongitude().toString()).doubleValue();
double d = 3963.0 * Math.acos(Math.sin(latitude1 * Math.PI/180) * Math.sin(tempLat * Math.PI/180) + Math.cos(latitude1 * Math.PI/180) * Math.cos(tempLat * Math.PI/180) * Math.cos(tempLon*Math.PI/180 -longitude1 * Math.PI/180));
if(d < Double.parseDouble(distance))
{
acceptList.add(((ZipCodes)zipList.get(i)).getZipCd());
}
}
}
There's an excerpt of my code, hopefully you can see what's happening. I start out with one ZipCodes( a table in my DB), then I pull back possible matches, and finally I weed out those who are not in the radius.
Oracle, PostGIS, mysql with GIS extensions, sqlite with GIS extensions all support this kind of queries.
If you don't have the dataset look at:
http://www.geonames.org/
Take a look at this web service advertised on xmethods.net. It requires a subscription to actually use, but claims to do what you need.
The advertised method in question's description:
GetPlacesWithin Returns a list of geo
places within a specified distance
from a given place. Parameters: place
- place name (65 char max), state - 2 letter state code (not required for
zip codes), distance - distance in
miles, placeTypeToFind - type of place
to look for: ZipCode or City
(including any villages, towns, etc).
http://xmethods.net/ve2/ViewListing.po?key=uuid:5428B3DD-C7C6-E1A8-87D6-461729AF02C0
You can obtain a pretty good database of geolocated cities/placenames from http://geonames.usgs.gov - find an appropriate database dump, import it into your DB, and performing the kind of query your need is pretty straightforward, particularly if your DBMS supports some kind of spatial queries (e.g. like Oracle Spatial, MySQL Spatial Extensions, PostGIS or SQLServer 2008)
See also: how to do location based search
I do not have a website, but we have implemented this both in Oracle as a database function and in SAS as a statistics macro. It only requires a database with all cities and their lat and long.
Maybe this can help. The project is configured in kilometers though. You can modify these in CityDAO.java
public List<City> findCityInRange(GeoPoint geoPoint, double distance) {
List<City> cities = new ArrayList<City>();
QueryBuilder queryBuilder = geoDistanceQuery("geoPoint")
.point(geoPoint.getLat(), geoPoint.getLon())
//.distance(distance, DistanceUnit.KILOMETERS) original
.distance(distance, DistanceUnit.MILES)
.optimizeBbox("memory")
.geoDistance(GeoDistance.ARC);
SearchRequestBuilder builder = esClient.getClient()
.prepareSearch(INDEX)
.setTypes("city")
.setSearchType(SearchType.QUERY_THEN_FETCH)
.setScroll(new TimeValue(60000))
.setSize(100).setExplain(true)
.setPostFilter(queryBuilder)
.addSort(SortBuilders.geoDistanceSort("geoPoint")
.order(SortOrder.ASC)
.point(geoPoint.getLat(), geoPoint.getLon())
//.unit(DistanceUnit.KILOMETERS)); Original
.unit(DistanceUnit.MILES));
SearchResponse response = builder
.execute()
.actionGet();
SearchHit[] hits = response.getHits().getHits();
scroll:
while (true) {
for (SearchHit hit : hits) {
Map<String, Object> result = hit.getSource();
cities.add(mapper.convertValue(result, City.class));
}
response = esClient.getClient().prepareSearchScroll(response.getScrollId()).setScroll(new TimeValue(60000)).execute().actionGet();
if (response.getHits().getHits().length == 0) {
break scroll;
}
}
return cities;
}
The "LocationFinder\src\main\resources\json\cities.json" file contains all cities from Belgium. You can delete or create entries if you want too. As long as you don't change the names and/or structure, no code changes are required.
Make sure to read the README https://github.com/GlennVanSchil/LocationFinder
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 4 years ago.
Improve this question
I saw on StackOverflow a "point in polygon" raytracing algorithm that I implemented in my PHP Code. Most of the time, it works well, but in some complicated cases, with complex polygons and vicious points, it fails and it says that point in not in polygon when it is.
For example:
You will find here my Polygon and Point classes: pointInPolygon method is in Polygon class. At the end of the file, there are two points that are supposed to lie inside the given polygon (True on Google Earth). The second one works well, but the first one is buggy :( .
You can easily check the polygon on Google Earth using this KML file.
Have been there :-) I also travelled through Stackoverflow's PiP-suggestions, including your reference and this thread. Unfortunately, none of the suggestions (at least those I tried) were flawless and sufficient for a real-life scenario: like users plotting complex polygons on a Google map in freehand, "vicious" right vs left issues, negative numbers and so on.
The PiP-algorithm must work in all cases, even if the polygon consists of hundreds of thousands of points (like a county-border, nature park and so on) - no matter how "crazy" the polygon is.
So I ended up building a new algorithm, based on some source from an astronomy-app:
//Point class, storage of lat/long-pairs
class Point {
public $lat;
public $long;
function Point($lat, $long) {
$this->lat = $lat;
$this->long = $long;
}
}
//the Point in Polygon function
function pointInPolygon($p, $polygon) {
//if you operates with (hundred)thousands of points
set_time_limit(60);
$c = 0;
$p1 = $polygon[0];
$n = count($polygon);
for ($i=1; $i<=$n; $i++) {
$p2 = $polygon[$i % $n];
if ($p->long > min($p1->long, $p2->long)
&& $p->long <= max($p1->long, $p2->long)
&& $p->lat <= max($p1->lat, $p2->lat)
&& $p1->long != $p2->long) {
$xinters = ($p->long - $p1->long) * ($p2->lat - $p1->lat) / ($p2->long - $p1->long) + $p1->lat;
if ($p1->lat == $p2->lat || $p->lat <= $xinters) {
$c++;
}
}
$p1 = $p2;
}
// if the number of edges we passed through is even, then it's not in the poly.
return $c%2!=0;
}
Illustrative test :
$polygon = array(
new Point(1,1),
new Point(1,4),
new Point(4,4),
new Point(4,1)
);
function test($lat, $long) {
global $polygon;
$ll=$lat.','.$long;
echo (pointInPolygon(new Point($lat,$long), $polygon)) ? $ll .' is inside polygon<br>' : $ll.' is outside<br>';
}
test(2, 2);
test(1, 1);
test(1.5333, 2.3434);
test(400, -100);
test(1.01, 1.01);
Outputs :
2,2 is inside polygon
1,1 is outside
1.5333,2.3434 is inside polygon
400,-100 is outside
1.01,1.01 is inside polygon
It is now more than a year since I switched to the above algorithm on several sites. Unlike the "SO-algorithms" there have not been any complaints so far. See it in action here (national mycological database, sorry for the Danish). You can plot a polygon, or select a "kommune" (a county) - ultimately compare a polygon with thousands of points to thousands of records).
Update
Note, this algorithm is targeting geodata / lat,lngs which can be very precise (n'th decimal), therefore considering "in polygon" as inside polygon - not on border of polygon. 1,1 is considered outside, since it is on the border. 1.0000000001,1.01 is not.
I have a rectangular map, stored as multidimensional array (ie $map[row][col]) and I have to track down which squares are seen by a player, placed anywhere on this map.
Player visibility is circular with unknown radius (but given at run-time) and I only need integer solutions.
I know that circumference formula is
x^2 + y^2 <= r^2
but how can I store everything?
I need these values since then I can "reveal" map squares.
The best would be a multidimesional array (ie __$sol[x][y]__).
This is a piece of code that I'm using. It's not corrected since it assumes that vision is a square and not a circle.
Calculating the square
$this->vision_offsets_2 = array();
//visibility given as r^2
$mx = (int)(sqrt($this->viewradius2));
$mxArr = range($mx * -1, $mx + 1);
foreach ($mxArr as $d_row)
{
foreach ($mxArr as $d_col)
{
$this->vision_offsets_2[] = array($d_row, $d_col);
}
}
This is how I apply that
foreach($player as $bot)
{
foreach($visibility as $offset)
{
$vision_row = $offset[0] + $bot[0];
$vision_col = $offset[1] + $bot[1];
if(isset($map[$vision_row][$vision_col]))
{
if( $map[$vision_row][$vision_col] == UNSEEN) {
$map[$vision_row][$vision_col] = LAND; }
}
}
}
Here you can find the bot view: as you can see is a non perfect circle.
How can I track it? By the way, in this example radius^2 is 55, the orange circle is the player, brown squares are visible ones.
Structure
You're already referencing terrain in a grid. Store terrain objects in those grid values. Apply attributes to those objects. Check with something like
$map[$x][$y]->isVisible($player);
You'll need some methods in there for setting vision and tests for checking the user that is passed against a list of users who can see it. While you're at it, setup other related methods in those objects (I see references to land... isLand() and isWater() perhaps?).
You can even have vision cascade within objects such that you only need to move the position of a user and the object takes care of triggering off all the code to set nearby plots of land to visible.
Math
We are given circumference.
double diameter = circumference / 3.14159
double radius = diameter / 2 //Normally done in one step / variable
Now we must know the distance between two points to compare it. Let's use map[4][7] and map[3][9].
int x0 = 4;
int y0 = 7;
int x1 = 3;
int y1 = 9;
double distance = Math.sqrt(
Math.pow(x0 - x1, 2) +
Math.pow(y0 - y1, 2)
);
System.out.println(distance); //2.23606797749979
Test distance > radius.
Testing each square
Put the above in a method: visibleFrom(Square target)
radius should be a globally accessible static variable when comparing.
Your Square object should be able to hand over its coordinates.
target.getX()
target.getY()
Some optimizations can be had
Only checking things for circular distance when they're in the square.
Not checking anything for circular distance when purely along the x or y axis.
Figuring out the largest square that fits inside the circle and not checking boxes in that range for circular distance.
Remember that premature optimization and over optimization are pitfalls.
A function like this would tell you if a map square is visible (using the distance of the centers of the squares as a metric; if you want to define visibility in another manner, which you probably would, things get much more complicated):
function is_visible($mapX, $mapX, $playerX, $playerY, $r) {
return sqrt(pow($mapX - $playerX, 2) + pow($mapY - $playerY, 2)) <= $r;
}
You probably don't really need to store these values since you can easily calculate them on demand.
I think that Bresenham's circle drawing algorithm is what you're looking for.
I don't know exactly what you want, but here's some things that should help you along. As a warning these are untested, but the logic is sound.
//You mentioned circumference, this will find out the circumference but I don't
//think you actually need it.
$circumference_length = 2 * $visibility_range * 3.1415;
//Plug in the player and target coordinates and how far you can see, this will
//tell you if the player can see it. This can be optimized using your object
//and player Objects.
function canSee($player_x, $player_y, $vision_length, $target_x, $target_y){
$difference_x = $target_x - $player_x;
$difference_y = $target_y - $player_y;
$distance = sqrt((pow($difference_x,2) + pow($difference_y, 2));
if($vision < $distance){
return false;
} else {
return true;
}
}
Edit: In response to your clarification, you can use the above function to figure out if you should show the terrain objects or not.
foreach($player as $bot)
{
foreach($terrain_thing as $terrain)
{
//ASSUMING THAT [0] IS ALWAYS X AND [1] IS ALWAYS y, set a third variable
//to indicate visibility
$terrain["is_visible"] = canSee($bot[0], $bot[1], $visibility_range, $terrain[0], $terrain[1])
}
}