Get definite City name in Google maps reverse geocoding - javascript

Using the Google Maps Geocoding API, i'm able to get the formatted address for a particular coordinate. To get the exact city name, I'm doing the following:
$.ajax({
url: 'http://maps.googleapis.com/maps/api/geocode/json?latlng='+lat+','+long+'&sensor=false',
success: function(data){
var formatted = data.results;
var address_array = formatted[6].formatted_address.split(',');
var city = address_array[0];
}
});
where lat and long are derived using the browser coordinates. My problem is the following:
From coordinates 19.2100 and 72.1800, I get the city as Mumbai, but from a similar set of coordinates about 3Km away, I get city as Mumbai Suburban. How can I get Mumbai without changing the success function of my code? It seems to me that the result array doesn't always stick to the same format which creates problems in my displaying of the city name.

So I was trying to figure this out today and I came to this solution if it helps anyone. Google maps comes with the Geocoder built in now so you just create a geocoder object once the API has loaded.
You can easily wrap that in a function and just return an object with the city, state, and postal code. This site was helpful in allowing me to see what the different 'types' mean: Reverse Geocoding
var geocoder = new google.maps.Geocoder,
latitude = 28.54, //sub in your latitude
longitude = -81.39, //sub in your longitude
postal_code,
city,
state;
geocoder.geocode({'location': {lat:latitude, lng:longitude}}, function(results, status) {
if (status === google.maps.GeocoderStatus.OK) {
results.forEach(function(element){
element.address_components.forEach(function(element2){
element2.types.forEach(function(element3){
switch(element3){
case 'postal_code':
postal_code = element2.long_name;
break;
case 'administrative_area_level_1':
state = element2.long_name;
break;
case 'locality':
city = element2.long_name;
break;
}
})
});
});
}
});

You need to look at the type of the result, not the absolute index in the array of results. Iterate through the results array looking for the entry which has the appropriate type. Looks like that would be:
locality indicates an incorporated city or town political entity
But data may vary with region.
related question: Grabbing country from google geocode jquery
Looks like you want the entry with both the 'locality' and the 'political' types:
{
"long_name" : "Mumbai",
"short_name" : "Mumbai",
"types" : [ "locality", "political" ]
}

For what it's worth, I was looking for something similar and am trying https://plus.codes/
If you strip the encoded bit it yields a fairly consistent city, state, country name:
const extractCityName = latlng => {
googleMapsClient.reverseGeocode({ latlng }, (err, response) => {
if (!err) {
return response.json.plus_code.compound_code.split(' ').slice(1).join(' ');
}
});
};
// examples:
console.log(extractCityName(40.6599718,-73.9817292));
// New York, NY, USA
console.log(extractCityName(37.386052, -122.083851));
// Mountain View, CA, USA
console.log(extractCityName(51.507351, -0.127758));
// Westminster, London, UK

$.get({
url: locAPI,
success: function(data)
{
data.results[0].address_components.forEach(function(element){
// console.log(element.types);
if(element.types[0] == 'locality' && element.types[1] == 'political')
{
console.log('City:')
console.log(element.long_name);
}
if(element.types[0] == 'country' && element.types[1] == 'political')
{
console.log('Country:')
console.log(element.long_name);
}
});
}
})
In My Case, I had to find City and Country Name.. That's what I did.

Related

Prioritize city matches against province matches in Nominatim

I installed Nominatim in combination with the Maplibre geocoder, but I am having troubles getting the results in the correct order of relevance or getting the correct one at all. In Italy many provinces have the same name as the capital of the province, for example Cagliari is both the name of the Province and the name of a city. When I search for
Via Roma, Cagliari
I an expecting to see the result for the street "Via Roma" located in the city of Cagliari, but I get results for "Via Roma" in other cities located in the province of Cagliari instead. This happens frequently with street names that are fairly common across cities in the same area. How can I make Nominatim to give precedence to the street located in the city with the same name or to not take the province into account at all when elaborating the query?
This is how I am calling the geocoder api
var geocoder_api = {
forwardGeocode: async (config) => {
const features = [];
try {
geocoder_bbox = map.getBounds().toArray().flat()
let request =
'https://my.server/search?q=' +
config.query +
'&format=geojson&addressdetails=1&viewbox='+geocoder_bbox;
const response = await fetch(request);
const geojson = await response.json();
for (let feature of geojson.features) {
let center = [
feature.bbox[0] +
(feature.bbox[2] - feature.bbox[0]) / 2,
feature.bbox[1] +
(feature.bbox[3] - feature.bbox[1]) / 2
];
let point = {
type: 'Feature',
geometry: {
type: 'Point',
coordinates: center
},
place_name: feature.properties.display_name,
properties: feature.properties,
text: feature.properties.display_name,
place_type: ['place'],
center: center
};
features.push(point);
}
} catch (e) {
console.error(`Failed to forwardGeocode with error: ${e}`);
}
return {
features: features
};
}
};

How to correctly find objects by proximity using sequelize.js and PostGIS?

I'm trying to perform a simple ST_Dwithin search using sequelize.js and PostGIS.
In my database I have 3 tables of interest: Users, Neighborhoods and Addresses. All geo data is stored inside addresses table, which has references to users and neighborhoods.
return Neighborhood.findById(id).then(neighborhood => {
return neighborhood.getAddress().then(address => {
return Address.findAll({
where: sequelize.where(
sequelize.fn(
'ST_DWithin',
sequelize.fn(
'ST_Transform',
address.position,
26986),
sequelize.fn('ST_Transform',
sequelize.col('position'),
26986),
distance),
true
)
})
})
}).catch(err => new Error(err));
First I get the address of a neighborhood and then use sequelize.fn to query with PostGIS ST_DWithin function. However this throws an error TypeError: val.replace is not a function. I believe it is something with line address.position. The column position in the table Addresses stores geometry points with type GEOMETRY and srid 4326.
The function works correctly if instead of address.position I hard code something like sequelize.fn('ST_GeomFromText', 'POINT(39.807222 -76.984722)', 4326)
In my case, the geometry(point) attribute is in the User entity. This is what I got working:
var lat = parseFloat(json.lat);
var lng = parseFloat(json.lng);
var attributes = Object.keys(User.attributes);
var distanceAttribute =
sequelize.fn('ST_Distance_Sphere',
sequelize.literal('geolocation'),
sequelize.literal('ST_MakePoint('+lat+','+lng+')'));
var distanceAlias = [distanceAttribute, 'distance'];
attributes.push(distanceAlias);
var query = {
attributes: attributes,
where: sequelize.where(distanceAttribute, {$lte: 100000}),
logging: console.log
}
User.findAll(query)
.then(function(instance){
console.log(instance);
});
Which produces a SQL like this:
SELECT "user_id", "user_name" ... ST_Distance_Sphere(geolocation,
ST_MakePoint(-22.4149023,-47.56513940000002)) AS "distance"
FROM "user" AS "User"
WHERE ST_Distance_Sphere(geolocation,
ST_MakePoint(-22.4149023,-47.56513940000002)) <= 100000;
I think this should work for you too by changing User to Address

using $.getJSON in a loop to append to global variable

I am writing a small script that takes an array of locations and geocodes them to add lat long to another object. I have it working so it geocodes and writes to the console flawlessly. How can I take that output and put it into a predefined variable outside of the function? I am not vary familiar with jquery and do not intend on using it anyplace else in the script.
Just started learning JavaScript this weekend so don't shoot me if Something else is messed up.
Thanks for any help!!
Also, I changed my Google API key so it isn't stolen.
//test array of locations to geocode
var locationsToGeoCode = ["China: Shandong: Jinan",
"China: Shandong: Jinan",
"United States: Washington: La Conner",
"United States: Texas: Dallas",
"United States: California: Walnut"
];
//empty object to place geocoded places
var coordinates;
for (var i = 0; i < locationsToGeoCode.length; i += 1) {
$.getJSON("https://maps.googleapis.com/maps/api/geocode/json?address=" + locationsToGeoCode[i] + "&key=MIzaSyCywKsD_50EI9rheDLyqPOUdUzi6s6u-q8", function (geocodeResult) {
console.log(geocodeResult);
});
}
Modifying your code like such should work:
//test array of locations to geocode
var locationsToGeoCode = ["China: Shandong: Jinan",
"China: Shandong: Jinan",
"United States: Washington: La Conner",
"United States: Texas: Dallas",
"United States: California: Walnut"
];
//empty object to place geocoded places
var coordinates = {};
function fetchGeoCode(location){
$.getJSON("https://maps.googleapis.com/maps/api/geocode/json?address=" + location + "&key=MIzaSyCywKsD_50EI9rheDLyqPOUdUzi6s6u-q8", function (geocodeResult) {
coordinates[location] = geocodeResult;
});
}
for (var i = 0; i < locationsToGeoCode.length; i += 1) {
fetchGeoCode(locationsToGeoCode[i]);
}

Merge JSON values into a variable or string

I JSON nodes with the same title, but different latitude and longitude values in each node. I need to check for the same title value, but then merge together the latitude and longitude values into a url for a map API. I need it to be in this order latitude, longitude, latitude, longitude, etc... I just don't know what to do at this point. Thanks for any help or suggestions.
JS VAR
<img class="detail_map" src="http://open.mapquestapi.com/staticmap/v4/getplacemap?size=320,240&zoom=15&location=' + data.nodes.Latitude + ',' + data.nodes.Longitude + '&imagetype=jpeg&showicon=blue-1">
JSON Object
var data = fl({
"nodes":[
{"node":{
"title":"180","Address":"555 Market St. San Francisco, CA United States See map: Google Maps","
Latitude":"37.789952","
Longitude":"-122.400158"}},
{"node":{
"title":"180","Address":"Epic Roasthouse (399 Embarcadero) San Francisco, CA United States See map: Google Maps","
Latitude":"37.797677","
Longitude":"-122.394339"}},
{"node":{
"title":"180","Address":"Mason & California Streets (Nob Hill) San Francisco, CA United States See map: Google Maps","
Latitude":"37.791556","
Longitude":"-122.410766"}},
{"node":{
"title":"180","Address":"Justin Herman Plaza San Francisco, CA United States See map: Google Maps","
Latitude":"37.774930","
Longitude":"-122.419416"}},
{"node":{
"title":"180","Address":"200 block Market Street San Francisco, CA United States See map: Google Maps","
Latitude":"37.793133","
Longitude":"-122.396560"}}
]});
});
You can use $.extend() function for merge two objects, the different properties will be replaced.
Example:
var newData = $.extend(data1, data2);
// Assuming data1, data2 are objects;
If you need convert string JSON to object, use $.parseJSON()
you can try to do something like this where var url_part is used to replace "data.nodes.Latitude + ',' + data.nodes.Longitude" from the img src:
var url_part = '';
$.each(data.nodes, function(key,val){
url_part += val.node.Latitude+","+val.node.Longitude+",";
});
but before useding url_part, you need to remove the last coma...
Use jQuery's $.map(). Compare the object's node.title value, and then from there return the string as you would expect. When you're done, join the array on ,.
var locs = $.map(fl.nodes, function(obj,i){
return obj.node.title == '180' ? 'latitude='+obj.node.Latitude+'&longitude='+obj.node.Longitude : '';
}).join(',');
would return lat1,long1,lat2,long2,lat3,long3,lat4,long4,lat5,long5
Working jsFiddle
Edit 1
Added support for dynamic titles.
function getNodesByTitle(title){
return $.map(fl.nodes, function(obj,i){
return obj.node.title == title ? 'latitude='+obj.node.Latitude+'&longitude='+obj.node.Longitude : '';
}).join(',');
}
In theoretical practice:
var locString = getNodesByTitle('your title here');
I had to create a function to merge addresses and latitude and longitude. Check out the jsFiddle to see it in action.
function fl(data){
//data = JSON.parse(data);
//Array to hold movie titles
var movieTitles = [];
//Assign Movies to to movies Array and ensure unique
for (var i=0; i < data.nodes.length; i++)
{
//Look for the current title in the movieTitles array
var movieIndex = movieTitles.indexOf(data.nodes[i].node.title);
if (movieIndex>-1) //If the title already exists
{
//Merge all the properties you want here
movies[movieIndex].Address += ", " + data.nodes[i].node.Address;
if(!movies[movieIndex].Coords) movies[movieIndex].Coords = [];
movies[movieIndex].Coords.push(
data.nodes[i].node.Latitude + "," + data.nodes[i].node.Longitude
);
}
else
{
//var address = movies[movieIndex].Address; movies[movieIndex].Address = address.replace(/^\s*/,'');
//Add movie to movies array
movies.push(data.nodes[i].node);
//Add movie title to movieTitles array
movieTitles.push(data.nodes[i].node.title);
}
}
displayLinks(); //Load all the links
//showCast(0); //Display details for first item
}
//});

String Mapping in Javascript

Currently I have implemented a Javascript Ajax search where if user types a city name I am populating the name in the search result. Problem is there are cities which have alternate names (for example Mumbai has Bombay as alternate name.....Bangalore has Bengaluru as alternate name). I want to map the alternate names of the city to the correct name using javascript. ALSO there might be multiple alternate names for the city not only one. so how can I map alternate cities name to correct one using javascript.
var duplicateNameCities = {
"Bengaluru": "Bangalore",
"Mumbai": "Bombay"
};
// when you're given a string, run it through this map first
city = duplicateNameCities[city] || city;
However, it feels like the server side should handle this
var alternates = {
Bombay : "Mumbai",
Bengaluru : "Bangalore",
}
var userCity = obtainUserInputSomehow()
if (alternates.hasOwnProperty(userCity)) { userCity = alternates[userCity] }
// after this userCity will have "Mumbai" if user entered "Bombay" or "Mumbai".
// add as many aliases as you need.

Categories