I am analyzing a relative big amount of coordinates in PHP within a for cycle and I make a reverse geocoding calling the following function:
function getLocation( $coordinates )
{
$url = 'http://maps.googleapis.com/maps/api/geocode/json?latlng=' . trim($coordinates) . '&sensor=false';
$json = #file_get_contents($url);
$data = json_decode($json);
$status = $data->status;
//print_r ( $data->results[ 0 ]);
if ($status == "OK")
{
return $data->results[ 0 ]->address_components[ 1 ]->long_name . ", " . $data->results[ 0 ]->address_components[ 2 ]->long_name;
}
else
{
return false;
}
}
The problem arised is that quite often Google (logically) answers me back with a null json_array and I don't have the reverse geocoding of each coordinate.
how can I bypass this issue? Do you know any other service?
I saw many libraries to reverse geocode but I would prefer to use them as last resource in order not to make heavier my small project.
Ok, well google will normally give you a reason why it failed. I know from past experience that it will fail if you give it too many in a certain time so hence you need to put sleep(500) or something so you don't overload google.
The docs say that it has a tolerance - of perhaps it's giving you an address but those fields aren't populated. Some debug code would help here.
Equally there is some rules you can add e.g
https://maps.googleapis.com/maps/api/geocode/json?latlng=40.714224,-73.961452&location_type=ROOFTOP&result_type=street_address&key=YOUR_API_KEY
Would limit you results down and may give you a better address.
It's probably a good idea to store any addresses you have.
Related
I have lat and long coordinates and I want to convert them to the facility name or place name that is listed on google maps (or at least the closest facility).
I am not trying to plot anything on a map, I am just looking for the facility name. In the example below originally posted here> Reverse Geocoding with Google Map API by ifaour, that I am using, it is returning the street name and I am not sure how to return the place name. I have tried using "premise" as per google maps api but I am not too familiar with the sytnax as I am a beginner.
<?php
$data = json_decode(file_get_contents("http://maps.googleapis.com/maps/api/geocode/json?latlng=43.97513954,-78.1601639&sensor=true&radius=300"));
if($data->status == "OK") {
if(count($data->results)) {
foreach($data->results[0]->address_components as $component) {
if(in_array("premise",$component->types) || in_array("colloquial_area",$component->types) || in_array("route",$component->types) || in_array("park",$component->types)) {
echo $component->long_name . "<br />";
}
}
}
} else {
// error
}
?>
I am trying to get the co-ordinates to return "Cobourg Community Center" but it returns "D'Arcy Street" instead.
It would be great to pull the closest name within 300meters too.
Can anyone see where I am going wrong?
So after many hours of testing research it turns out that this code actually works as intended. The fault is that the facilities are not actually tagged as a "premise" by Google, which is why the names are not being returned. I tested with lat and long from a known Google "premise" and it worked correctly. Thank you for all suggestions. I will now need to post a new question based on Google Maps "nearby" functionality
I'm using the Netsuite PHP Toolkit to try to obtain a list of invoices for a customer. I can do the call (using a TransactionSearch) with no problem, but I'm struggling to understand how I'm supposed to get all details for an invoice - i.e. the invoice "header" details (e.g. grand total, currency, main menu line etc) as well as details for each line item (net value, taxable value, item etc).
I have tried a couple of approaches:
TransactionSearchAdvanced, with return columns specified and returnSearchColumns preference set to "false". This gives back all the separate lines (woo!) but things like currency and term aren't expanded out - you just get internalId specified and not the actual text (or the symbol). Also, with TSA, do you really have to specify every column you want? i.e. is the default really just an empty set of fields? Isn't there a way of just saying "give me all the details for all lines of each invoice?
TransactionSearch, with returnSearchColumns preference set to "true". This gives a list of single Invoice type records, with all the currency and term stuff correctly populated, but frustratingly, none of the individual line items. It's more of a summary.
So I am left with a couple of options, neither of which are very palatable, namely:
Do both calls for all invoices and combine the data. These searches take a long time (performance is another bugbear for me, so I really don't want to do this.
or
Figure out a way of requesting the data for terms, currency etc and also a way of obtaining invoice lines.
I have no idea how you're supposed to do this, and can't find anything on the internet about it. This is one of the worst interfaces I've used (and I've used some pretty bad ones).
Any help would be hugely appreciated.
Just like you I started out trying to do things with the Web Services API (aka SuiteTalk). Mostly it was an exercise in frustration because eventually what I found out was that I plain couldn't do what I wanted with them. That and the performance was pretty bad, which would have killed my project even if it had worked properly.
Like Faz, I've found it much easier and faster to use a combination of RESTlets and Saved Searches than deal with the web services framework.
Basically break your problem down into these parts:
Saved Search that returns the results that you want (keep track of the internal ID you'll need it later)
RESTlet it's just a Javascript file that defines the function you will use to return the results from the search
Client code to call the RESTlet and get the results.
Part I:
So the saved search is pretty straightforward. I'm going to assume you can make that happen and also that you can actually get all the fields you want in one place. That hasn't always been the case in my experience.
Part II:
The RESTlet involves a lot more steps even though it's really a very simple thing. What makes it complicated is getting it uploaded and deployed on your NetSuite site. If you don't already have the NetSuite IDE installed I highly recommend it if only to make deploying the scripts a little easier. The autocompletion and tooltips are extremely useful as well.
For instance here is code I use to get results from a search I cared about. This was adapted from some kind soul's posting somewhere on the internet but I forget where:
function getSearchResults(){
var max_rows = 1000;
var search_id = 1211;
var search = nlapiLoadSearch(null, search_id);
var results = search.runSearch();
var rows = [];
// add starting point for usage
var context = nlapiGetContext();
startingUsage = context.getRemainingUsage();
rows.push(["beginning usage", startingUsage]);
// now create the collection of result rows in 1000 row chunks
var index = 0;
do{
var chunk = results.getResults(index, index+1000);
if( ! chunk ) break;
chunk.forEach( function(row){
rows.push(row);
index++;
});
}while( chunk.length === max_rows);
// add a line that returns the remaining usage for this RESTlet
context = nlapiGetContext();
var remainingUsage = context.getRemainingUsage();
rows.push(["remaining usage",remainingUsage]);
// send back the rows
return rows;
}
This is where you get things primed by passing in your Saved Search Internal ID:
var search = nlapiLoadSearch(null, SEARCH_ID);
var resultSet = search.runSearch();
Then the code repeatedly calls getResults() to get chunks of 1000 results, this is a NetSuite limitation. Once you have this written you have to upload the script to NetSuite and configure and deploy it. The most important part is telling it what function to assign to each verb. In this case I assigned GET to execute the getSearchResults. There is a lot of work to do here, and I'm not going to type all of it out because it is worth your time to learn this part. At least enough to get the IDE to do it for you =D. You can read all about it in the "Introduction to RESTlets" guide.
Part III.
Client code can be in whatever you want that does REST the way you like to. Personally I like Python for this because the requests library is fantastic.
Here's some example Python code:
import requests
import json
url = 'https://rest.sandbox.netsuite.com/app/site/hosting/restlet.nl?script=123&deploy=1'
headers = {'Content-Type': 'application/json', 'Authorization':'NLAuth nlauth_account=1234567, nlauth_email=someone#somewhere.com, nlauth_signature=somepassword, nlauth_role=3'}
resp = requests.get(url, headers=headers)
data = resp.json()
The URL is going to be displayed to you as part of the deployment of the RESTlet. Then it's up to you to do what you want with the data that comes back.
So the things I would suggest you spend time with would be
Setting up the NetSuite IDE
Getting and reading the SuiteScript developer reference docs
Finding a good way to create REST client code in you language of choice.
I hope that helps.
I created a saved search in Netsuite and call that search using restlet. With this it is pretty lightweight and you can call the data as it is in the saved search.
Performance wise Restlet is much better than webservices.
Create a new suitelet script and deploy
Below script will give you invoice list by customer internal id
function customSearch(request, response) {
var rows = [];
var result;
var filters = [];
//9989 is customer internal id you can add more
// by pushing additional ids to array
filters.push(new nlobjSearchFilter('entity', null, 'anyOf', [9989] ));
var invoiceList = nlapiSearchRecord('invoice', null, filters, []);
// by default record limit is 1000
// taking 100 records
for (var i = 0; i < Math.min(100, invoiceList.length); i++)
{
if (parseInt(invoiceList[i].getId()) > 0) {
recordid = invoiceList[i].getId();
try {
result= nlapiLoadRecord(invoiceList[i].getRecordType(), recordid);
// pushing in to result
rows.push(result);
} catch (e) {
if (e instanceof nlobjError) {
nlapiLogExecution('DEBUG', 'system error', e.getCode() + '\n' + e.getDetails());
} else {
nlapiLogExecution('DEBUG', 'unexpected error', e.toString());
}
}
}
}
response.setContentType('JSON');
response.write(JSON.stringify({'records' : rows}));
return;
}
}
}
response.setContentType('JSON');
response.write(JSON.stringify({'records' : rows}));
return;
}
Here is what I have for getting a customer's invoices:
public function getCustomerInvoices($customer_id)
{
$service = new NetSuiteService($this->config);
$customerSearchBasic = new CustomerSearchBasic();
$searchValue = new RecordRef();
$searchValue->type = 'customer';
$searchValue->internalId = $customer_id;
$searchMultiSelectField = new SearchMultiSelectField();
setFields($searchMultiSelectField, array('operator' => 'anyOf', 'searchValue' => $searchValue));
$customerSearchBasic->internalId = $searchMultiSelectField;
$transactionSearchBasic = new TransactionSearchBasic();
$searchMultiSelectEnumField = new SearchEnumMultiSelectField();
setFields($searchMultiSelectEnumField, array('operator' => 'anyOf', 'searchValue' => "_invoice"));
$transactionSearchBasic->type = $searchMultiSelectEnumField;
$transactionSearch = new TransactionSearch();
$transactionSearch->basic = $transactionSearchBasic;
$transactionSearch->customerJoin = $customerSearchBasic;
$request = new SearchRequest();
$request->searchRecord = $transactionSearch;
$searchResponse = $service->search($request);
return $searchResponse->searchResult->recordList;
}
I need to get the state and country from the visitor IP. I will be using the country info to showcase custom made products. As for the state info it will not be used for the same purpose but only for record keeping to track the demand.
I have found on this site an instance of using the ipinfo.io API with this example code:
function ip_details($ip) {
$json = file_get_contents("http://ipinfo.io/{$ip}/json");
$details = json_decode($json);
return $details;
}
However, since I do not need the full details, I see that the site does allow to just grab single fields. So I am considering using these 2:
1) ipinfo.io/{ip}/region
2) ipinfo.io/{ip}/country
like so:
function ip_details($ip) {
$ip_state = file_get_contents("http://ipinfo.io/{$ip}/region");
$ip_country = file_get_contents("http://ipinfo.io/{$ip}/country");
return $ip_state . $ip_country;
}
OR would I be better off going with:
function ip_details($ip) {
$json = file_get_contents("http://ipinfo.io/{$ip}/geo");
$details = json_decode($json);
return $details;
}
The last one has the "/geo" in the url to slim down the selection from the first one with "/json". Currently I am leaning to the second option above by using 2 file_get_contents but wanted to know if it is slower than the last one having it in an array. Just want to minimize the load time. Or if any other method can be given it would be much appreciated.
In short, go for your second option, with a single request (file_get_contents makes a get request when parsed a url):
The result is a simple array, access the details you want via its key:
function ip_details($ip) {
$json = file_get_contents("http://ipinfo.io/{$ip}/geo");
$details = json_decode($json);
return $details;
}
$ipinfo = ip_details('86.178.xxx.xxx');
echo $ipinfo['country']; //GB
//etc
Regarding speed difference - 99% of the overhead is network latency, so making ONE request and parsing the details you need will be much faster than making 2 separate requests for individual details
I'm trying to get the country from which the user is browsing the website so I can work out what currency to show on the website. I have tried using the GET scripts available from: http://api.hostip.info but they just return XX when I test it.
If anyone knows any better methods please share.
Thanks.
I use this:
$_SESSION['ip'] = $_SERVER['REMOTE_ADDR'];
$ip = $_SESSION['ip'];
$try1 = "http://ipinfodb.com/ip_query.php?ip=".$ip."&output=xml";
$try2 = "http://backup.ipinfodb.com/ip_query.php?ip=".$ip."&output=xml";
$XML = #simplexml_load_file($try1,NULL,TRUE);
if(!$XML) { $XML = #simplexml_load_file($try2,NULL,TRUE); }
if(!$XML) { return false; }
//Retrieve location, set time
if($XML->City=="") { $loc = "Localhost / Unknown"; }
else { $loc = $XML->City.", ".$XML->RegionName.", ".$XML->CountryName; }
$_SESSION['loc'] = $loc;
Try these:
http://ip-to-country.webhosting.info/
http://www.ip2location.com/
Both are IP address-to-country databases, which allow you to look up the country of origin of a given IP address.
However it's important to note that these databases are not 100% accurate. They're a good guide, but you will get false results for a variety of reasons.
Many people use proxying to get around country-specific blocks and filters.
Many IP ranges are assigned to companies with large geographic spread; you'll just get the country where they're based, not where the actual machine is (this always used to be a big problem for tracking AOL users, because they were all apparently living in Virginia)
Control of IP ranges are sometimes transferred between countries, so you may get false results from that (especially for smaller/less well-connected countries)
Keeping your database up-to-date will mitigate some of these issues, but won't resolve them entirely (especially the proxying issue), so you should always allow for the fact that you will get false results.
You should use the geoip library.
Maxmind provides free databases and commercial databases, with a difference in the date of last update and precision, the commercial being of better quality.
See http://www.maxmind.com/app/geolitecountry for the free database.
I think it should be sufficient for basic needs.
You can use Geolocation to get the Coordinates and then some Service to get the Country from that, but the geolocation API is browser based so you can only access it via JavaScript and then have to pass theese Informations to PHP somehow, i wrote something on the JS Part once:
http://www.lautr.com/utilizing-html5-geolocation-api-and-yahoo-placefinder-example
When it comes to getting the Location via the IP, there are a bazillion Services out there who offer databases for that, some free, some for charge, some with a lot of IP's stored and much data, some with less, for example the one you mentioned, works just fine:
http://api.hostip.info/?ip=192.0.32.10
So You can ether go with the Geolocation API which is pretty neat, but requires the users permission, works via JS and doesnt work in IE (so far) or have to look for a IPÜ Location Service that fits your needs :)
Try these:
$key="9dcde915a1a065fbaf14165f00fcc0461b8d0a6b43889614e8acdb8343e2cf15";
$ip= "198.168.1230.122";
$url = "http://api.ipinfodb.com/v3/ip-city/?key=$key&ip=$ip&format=xml";
// load xml file
$xml = simplexml_load_file($url);
// print the name of the first element
echo $xml->getName() . "";
// create a loop to print the element name and data for each node
foreach($xml->children() as $child)
{
echo $child->getName() . ": " . $child . "<br />";
}
There are many ways to do it as suggested by those earlier. But I suggest you take a look at the IP2 PHP library available at https://github.com/ip2iq/ip2-lib-php which we developed.
You can use it like below:
<?php
require_once("Ip2.php");
$ip2 = new \ip2iq\Ip2();
$country_code = $ip2->country('8.8.8.8');
//$country_code === 'US'
?>
It doesn't need any SQL or web service lookup just a local data file. It is faster than almost all other methods out there. The database is updated monthly you can download it for free.
The only thing you will need to do for now if you need the country name in your language is map it to an associative array from something like this https://gist.github.com/DHS/1340150
I am working on a PHP code for my social network, what I am planning to do is on user signup or profile update
I will take there zipcode that they enter and use yahoo to lookup there lattitude and logitude from there zipcode
I will then save these value to there user mysql table, this will be used later on to find users located within X amount
of miles from them.
Here is my code so far to get this info before storing into DB
<?PHP
function yahoo_geo($location) {
$q = 'http://api.local.yahoo.com/MapsService/V1/geocode';
q .= '?appid=rlerdorf&location='.rawurlencode($location);
echo $q;
libxml_use_internal_errors(true);
$xml = simplexml_load_file($q);
if(!is_object($xml)) return false;
$ret['precision'] = (string)$xml->Result['precision'];
foreach($xml->Result->children() as $key=>$val) {
if(strlen($val)){
$ret[(string)$key] = (string)$val;
}
return $ret;
}
$_REQUEST['location'] = 32744; // my zip code
$a = yahoo_geo($_REQUEST['location']);
// set retunred data to some variables for storage into MySQL
$latitude = $a[Latitude];
$longitude = $a[Longitude];
// MySQL code will go here
// Output for demo
echo 'Lattitude' .$latitude. '<BR><BR>';
echo 'Longitude' .$longitude. '<BR><BR>';
?>
This works great except if a long. and lat. is not found it returns an error to the browser
Warning: simplexml_load_file(http://api.local.yahoo.com/MapsService/V1/geocode?appid=rlerdorf&location=some non zipcode) [function.simplexml-load-file]: failed to open stream: HTTP request failed! HTTP/1.1 400 Bad Request in C:\webserver\htdocs\test\geolocation\index.php on line 16
Note; I could make sure that zipcode is numeric only but I would rather not as yahoo will let me search with other fields as well, so If a user does not enter a zipcode I will make it use a city or state or even country at the worse case so every user should have some sort of longitude and lat. saved
Is there a good way to avoid the error showing or even better is there a way to detect id there is an error and if an error occurs I could insert a default value for that user?
Not exactly sure what your question is, but I think you're looking for the error control operator #
xml = #simplexml_load_file($q);
Your function will then return false as normal.
You need to check that later though (and add some quotes):
if (!$a)
{
// Do something
}
else
{
$latitude = $a['Latitude'];
$longitude = $a['Longitude'];
}
With the # operator :
you have to use it everytime you need it
when you want to debug, you have to remove it from everywhere (or use the scream extension -- which you cannot always install, depending on your hosting service)
if there is an error somewhere you didn't put #, it will still be displayed to the user.
Another solution would be to use something like this at the beginning of your script, when you are on the production server :
ini_set('display_errors', 'Off');
That way, no error is ever displayed to the end-user (they don't need to see those, and probably wouldn't understand), even in situations you didn't think about ; and it's easy to get all the errors displayed on your development machine : just on line to comment ;-)
Note : in production, if you can set the configuration like you want, there is the possibility to log errors to a file ; that way, you can check those, and not have them displayed.