I have a large JSON object returned from a Google Maps reverse geocode lookup.
in this object there are many possible location details returned.
Using jq how do I select the first returned location with a location_type "ROOFTOP" and get the formatted_address and place_id ?
in the below example the first entry has a location_type of "GEOMETRIC_CENTER"
The second entry has the "ROOFTOP" location_type. I want to ignore the entry with "GEOMETRIC_CENTER" and only return the fist entry with location_type "ROOFTOP"
Many thanks
Mike
[
{
"address_components": [
{
"long_name": "30",
"short_name": "30",
"types": [
"street_number"
]
},
{
"long_name": "Allée Jean de Lattre de Tassigny",
"short_name": "Allée Jean de Lattre de Tassigny",
"types": [
"route"
]
},
{
"long_name": "Montpellier",
"short_name": "Montpellier",
"types": [
"locality",
"political"
]
},
{
"long_name": "Hérault",
"short_name": "Hérault",
"types": [
"administrative_area_level_2",
"political"
]
},
{
"long_name": "Occitanie",
"short_name": "Occitanie",
"types": [
"administrative_area_level_1",
"political"
]
},
{
"long_name": "France",
"short_name": "FR",
"types": [
"country",
"political"
]
},
{
"long_name": "34000",
"short_name": "34000",
"types": [
"postal_code"
]
}
],
"formatted_address": "30 Allée Jean de Lattre de Tassigny, 34000 Montpellier, France",
"geometry": {
"bounds": {
"northeast": {
"lat": 43.6097932,
"lng": 3.8817559
},
"southwest": {
"lat": 43.6094097,
"lng": 3.881321299999999
}
},
"location": {
"lat": 43.6095516,
"lng": 3.881559199999999
},
"location_type": "GEOMETRIC_CENTER",
"viewport": {
"northeast": {
"lat": 43.6109504302915,
"lng": 3.882887580291503
},
"southwest": {
"lat": 43.6082524697085,
"lng": 3.880189619708498
}
}
},
"place_id": "ChIJ13k0paCvthIRcTgwBrisc10",
"types": [
"premise"
]
},
{
"address_components": [
{
"long_name": "8",
"short_name": "8",
"types": [
"street_number"
]
},
{
"long_name": "Rue Michelet",
"short_name": "Rue Michelet",
"types": [
"route"
]
},
{
"long_name": "Montpellier",
"short_name": "Montpellier",
"types": [
"locality",
"political"
]
},
{
"long_name": "Hérault",
"short_name": "Hérault",
"types": [
"administrative_area_level_2",
"political"
]
},
{
"long_name": "Occitanie",
"short_name": "Occitanie",
"types": [
"administrative_area_level_1",
"political"
]
},
{
"long_name": "France",
"short_name": "FR",
"types": [
"country",
"political"
]
},
{
"long_name": "34000",
"short_name": "34000",
"types": [
"postal_code"
]
}
],
"formatted_address": "8 Rue Michelet, 34000 Montpellier, France",
"geometry": {
"location": {
"lat": 43.60911189999999,
"lng": 3.8814264
},
"location_type": "ROOFTOP",
"viewport": {
"northeast": {
"lat": 43.61046088029149,
"lng": 3.882775380291502
},
"southwest": {
"lat": 43.60776291970849,
"lng": 3.880077419708498
}
}
},
"place_id": "ChIJ2UaGt6CvthIRJoNW7vS2Ibs",
"plus_code": {
"compound_code": "JV5J+JH Montpellier, France",
"global_code": "8FM5JV5J+JH"
},
"types": [
"street_address"
]
}
]
Rather than using .[0] to obtain the first object satisfying, it would be more efficient to use first/1. Here is a solution using first/1 and ..:
$ jq 'first(..|objects|select(.geometry.location_type == "ROOFTOP"))
| {formatted_address, place_id}' input.json
{
"formatted_address": "8 Rue Michelet, 34000 Montpellier, France",
"place_id": "ChIJ2UaGt6CvthIRJoNW7vS2Ibs"
}
Using firstin this way avoids having to compute the entire array before taking the first element.
Alternatively ...
Retrieving the first top-level object, without using ..:
first(.[] | select(.geometry.location_type=="ROOFTOP"))
Assuming the entries are enclosed in an array, you can use this jq filter that gives all objects having ROOFTOP as location:
jq '.[] | select(.geometry.location_type=="ROOFTOP")' file
If you want to select only the first one from all objects, use the following:
jq 'map(select(.geometry.location_type=="ROOFTOP"))|.[0]' file
And you only need need some of the values, pipe it another filter like this:
jq 'map(select(.geometry.location_type=="ROOFTOP"))|.[0]|.place_id, .formatted_address' file
Related
I'm using the Google Places API to find the address details of a small village in India, following this documentation from Google. My https request looks as follows:
https://maps.googleapis.com/maps/api/place/findplacefromtext/json?key=MY_API_KEY&inputtype=textquery&input=Dahigaon&fields=name,geometry,address_component
My problem is that the request above returns the following error:
Error while parsing 'fields' parameter: Unsupported field name 'address_component'.
I've tried a couple of other 'field' values, and they all seem to work except for address_component. Any thoughts on what I'm doing wrong?
Figured this one out. Apparently "Place Search, Nearby Search, and Text Search requests all return a subset of the fields that are returned by Place Details requests." See here for details: LINK
address_component is one of the fields that is not supported ...
I went through the same thing a while ago, basically you should make 2 calls, the first one to:
curl --request GET \
--url 'https://maps.googleapis.com/maps/api/place/findplacefromtext/json?input=360%20Lobelia%20Rd&inputtype=textquery&key=YOUR_KEY'
this will return the following.
{
"candidates": [
{
"place_id": "ChIJCesBrxmd5ogR5kyZiY35HLI"
}
],
"status": "OK"
}
you should take the place_id and use it in a second call like this:
curl --request GET \
--url 'https://maps.googleapis.com/maps/api/place/details/json?place_id=ChIJCesBrxmd5ogR5kyZiY35HLI&key=YOUR_KEY'
this will return the following.
{
"html_attributions": [],
"result": {
"address_components": [
{
"long_name": "360",
"short_name": "360",
"types": [
"street_number"
]
},
{
"long_name": "Lobelia Road",
"short_name": "Lobelia Rd",
"types": [
"route"
]
},
{
"long_name": "St. Augustine",
"short_name": "St. Augustine",
"types": [
"locality",
"political"
]
},
{
"long_name": "St. Johns County",
"short_name": "St Johns County",
"types": [
"administrative_area_level_2",
"political"
]
},
{
"long_name": "Florida",
"short_name": "FL",
"types": [
"administrative_area_level_1",
"political"
]
},
{
"long_name": "United States",
"short_name": "US",
"types": [
"country",
"political"
]
},
{
"long_name": "32086",
"short_name": "32086",
"types": [
"postal_code"
]
},
{
"long_name": "6516",
"short_name": "6516",
"types": [
"postal_code_suffix"
]
}
],
"adr_address": "<span class=\"street-address\">360 Lobelia Rd</span>, <span class=\"locality\">St. Augustine</span>, <span class=\"region\">FL</span> <span class=\"postal-code\">32086-6516</span>, <span class=\"country-name\">USA</span>",
"formatted_address": "360 Lobelia Rd, St. Augustine, FL 32086, USA",
"geometry": {
"location": {
"lat": 29.8358907,
"lng": -81.31464799999999
},
"viewport": {
"northeast": {
"lat": 29.8372717802915,
"lng": -81.31318596970848
},
"southwest": {
"lat": 29.8345738197085,
"lng": -81.31588393029149
}
}
},
"icon": "https://maps.gstatic.com/mapfiles/place_api/icons/v1/png_71/geocode-71.png",
"icon_background_color": "#7B9EB0",
"icon_mask_base_uri": "https://maps.gstatic.com/mapfiles/place_api/icons/v2/generic_pinlet",
"name": "360 Lobelia Rd",
"place_id": "ChIJCesBrxmd5ogR5kyZiY35HLI",
"reference": "ChIJCesBrxmd5ogR5kyZiY35HLI",
"types": [
"premise"
],
"url": "https://maps.google.com/?q=360+Lobelia+Rd,+St.+Augustine,+FL+32086,+USA&ftid=0x88e69d19af01eb09:0xb21cf98d89994ce6",
"utc_offset": -240,
"vicinity": "St. Augustine"
},
"status": "OK"
}
I don't know why the other way in the documentation, I imagine that at some point due to billing issues they have separated it into 2 calls.
From what I understood about billing, for every 1000 addresses searched in this way, you would be spending approximately 34 USD.
I need to get the city from a given US Zipcode, and i'm having trouble with Boroughs like Brooklyn
im using geocode:
https://maps.googleapis.com/maps/api/geocode/json?address=11214&key=KEY
it returns this results
{
"address_components": [
{
"long_name": "11214",
"short_name": "11214",
"types": [
"postal_code"
]
},
{
"long_name": "Brooklyn",
"short_name": "Brooklyn",
"types": [
"political",
"sublocality",
"sublocality_level_1"
]
},
{
"long_name": "Kings County",
"short_name": "Kings County",
"types": [
"administrative_area_level_2",
"political"
]
},
{
"long_name": "New York",
"short_name": "NY",
"types": [
"administrative_area_level_1",
"political"
]
},
{
"long_name": "United States",
"short_name": "US",
"types": [
"country",
"political"
]
}
],
"formatted_address": "Brooklyn, NY 11214, USA",
"geometry": {
"bounds": {
"northeast": {
"lat": 40.6163139,
"lng": -73.98259999999999
},
"southwest": {
"lat": 40.5788159,
"lng": -74.014775
}
},
"location": {
"lat": 40.6046106,
"lng": -73.9982756
},
"location_type": "APPROXIMATE",
"viewport": {
"northeast": {
"lat": 40.6163139,
"lng": -73.98259999999999
},
"southwest": {
"lat": 40.5788159,
"lng": -74.014775
}
}
},
"place_id": "ChIJLzmMV-BawokR4tFzc3LaBFI",
"types": [
"postal_code"
]
}
i need a way to link this to New York City, the only hint i see is
"long_name": "Brooklyn",
"short_name": "Brooklyn",
"types": [
"political",
"sublocality",
"sublocality_level_1"
]
maybe theres a way to link Brooklyn as a sublocality of NYC but i couldn't find it.
This is a tricky example. First of all you should be aware that Geocoding API returns in address components array only components used for address formatting. In case of New York city the address formatting is very specific. It is used sublocality_level_1 instead of locality to format the addresses.
This fact is mentioned in the official documentation of Google Maps API.
Note that you might need to use a different set of components to align with the postal address formats used in some regions. For example, the sample code selects the locality component, which often represents the city part of the address. Examples of how components can differ include:
In the UK and in Sweden, the component to display the city is postal_town.
In Japan, components differ across prefectures.
Brooklyn and other parts of New York City do not include the city as part of the address. Instead, they use sublocality_level_1.
source: https://developers-dot-devsite-v2-prod.appspot.com/maps/documentation/javascript/examples/places-autocomplete-addressform
The only workaround I can think of is using a two requests:
the first request for postal code and country to get coordinate of the postal code center
the second request is reverse geocoding for coordinate from the first request with type locality
E.g.
let's get coordinate of the postal code 11214
https://maps.googleapis.com/maps/api/geocode/json?components=postal_code%3A11214%7Ccountry%3AUS&key=YOUR_API_KEY
it will return coordinate in response as
"geometry":{
"location":{
"lat":40.6046106,"lng":-73.9982756
},
...
}
Now reverse geocode the coordinate with result type locality
https://maps.googleapis.com/maps/api/geocode/json?latlng=40.6046106%2C-73.9982756&result_type=locality&key=YOUR_API_KEY
it will return New York, so this way you can relate a postal code and New York city
{
"plus_code":{
"compound_code":"J232+RM New York, NY, USA","global_code":"87G8J232+RM"
},
"results":[
{
"address_components":[
{
"long_name":"New York",
"short_name":"New York",
"types":[
"locality","political"
]
},
{
"long_name":"New York",
"short_name":"NY",
"types":[
"administrative_area_level_1","political"
]
},
{
"long_name":"United States",
"short_name":"US",
"types":[
"country","political"
]
}
],
"formatted_address":"New York, NY, USA",
"geometry":{
"bounds":{
"northeast":{
"lat":40.9175771,"lng":-73.70027209999999
},
"southwest":{
"lat":40.4773991,"lng":-74.25908989999999
}
},
"location":{
"lat":40.7127753,"lng":-74.0059728
},
"location_type":"APPROXIMATE",
"viewport":{
"northeast":{
"lat":40.9175771,"lng":-73.70027209999999
},
"southwest":{
"lat":40.4773991,"lng":-74.25908989999999
}
}
},
"place_id":"ChIJOwg_06VPwokRYv534QaPC8g",
"types":[
"locality","political"
]
}
],
"status":"OK"
}
I hope this helps!
I'm working on a mapping project that involves using Tangram (an awesome webgl mapping library) to visualize buildings on a map. The Tangram buildings come from Open Street Maps, and use OSM id's:
As fortune would have it though, my data comes from Google's Location API, which has its own id system. Using a request like:
curl https://maps.googleapis.com/maps/api/geocode/json?address=Timble-Bridge,%20Yorkshire%20Britain&key=API_KEY_HERE
one gets back (note at the bottom the place id hash):
{
"results": [
{
"address_components": [
{
"long_name": "59",
"short_name": "59",
"types": [
"street_number"
]
},
{
"long_name": "Long Lane",
"short_name": "Long Ln",
"types": [
"route"
]
},
{
"long_name": "London",
"short_name": "London",
"types": [
"postal_town"
]
},
{
"long_name": "Greater London",
"short_name": "Greater London",
"types": [
"administrative_area_level_2",
"political"
]
},
{
"long_name": "England",
"short_name": "England",
"types": [
"administrative_area_level_1",
"political"
]
},
{
"long_name": "United Kingdom",
"short_name": "GB",
"types": [
"country",
"political"
]
},
{
"long_name": "EC1A 9EJ",
"short_name": "EC1A 9EJ",
"types": [
"postal_code"
]
}
],
"formatted_address": "59 Long Ln, London EC1A 9EJ, UK",
"geometry": {
"location": {
"lat": 51.5192377,
"lng": -0.0998815
},
"location_type": "ROOFTOP",
"viewport": {
"northeast": {
"lat": 51.52058668029149,
"lng": -0.09853251970849797
},
"southwest": {
"lat": 51.51788871970849,
"lng": -0.101230480291502
}
}
},
"partial_match": true,
"place_id": "ChIJ5UYg9FMbdkgRBOslATQK-ok",
"types": [
"cafe",
"establishment",
"food",
"point_of_interest"
]
}
],
"status": "OK"
}
I'm now wondering if there's any extant database that maps OSM id's to Google location id's. If anyone knows of any such service, or any higher order mapping one can use to deterministically map OSM and Google id's to a higher-order identification service, I'd be very grateful for any insight you can offer.
No, such a database doesn't exist and it can't exist.
Such a mapping would require that:
Both data sets are open. OSM is open but Google's data isn't.
Buildings are mapped in a similar way. This is not always the case. A building in Google may consist of several buildings in OSM, and vice versa.
IDs are stable. I don't know about Google's IDs but IDs in OSM can change.
I just discovered the Geocoder gem and my question is related to how to test it's use through Rspec.
As I have seen here How to test geocoder gem
I created a google_data.json to simulate the googlemaps response in order to no hit their API everytime and to not consume my daily request amount (moreover in this way test are really faster). My problem comes when I want to test a function like this one
def self.find_by_radius_from_location(location, radius=10)
coords = Geocoder.coordinates(location)
Garage.near(coords, radius, units: :km)
end
and here is my test (which works if I connect to gmaps api)
describe 'find_by_radius_from_location' do
let!(:garage_inside_radius) { FactoryGirl.create(:turin_garage) }
let!(:garage_outside_radius) { FactoryGirl.create(:rome_garage) }
context 'given a city' do
it 'should return one garage' do
garages = Garage.find_by_radius_from_location('Torino', 10)
garages.first.should eq(garage_inside_radius)
raise garages.inspect
ids = garages.collect { |g| g[:id] }
ids.count.should be(1)
end
end
end
The garages coord are right (the test pass if I connect to gmaps) what is wrong is my json, because I receive 2 garages instead of 1.
Here it is
{
"status": "OK",
"results": [ {
"types": [ "street_address" ],
"formatted_address": "Via Monginevro 162, Torino, Italy",
"address_components": [ {
"long_name": "Via Monginevro 162",
"short_name": "Via Monginevro 162",
"types": [ "route" ]
}, {
"long_name": "Torino",
"short_name": "Torino",
"types": [ "city", "political" ]
}, {
"long_name": "Italy",
"short_name": "IT",
"types": [ "country", "political" ]
} ],
"geometry": {
"location": {
"lat": 45.0647,
"lng": 7.63015
}
}
}, {
"types": [ "street_address" ],
"formatted_address": "Via G. Ferraris 2/4, Roma, Italy",
"address_components": [ {
"long_name": "Via G. Ferraris 2/4",
"short_name": "Via G. Ferraris 2/4",
"types": [ "route" ]
}, {
"long_name": "Roma",
"short_name": "Roma",
"types": [ "city", "political" ]
}, {
"long_name": "Italy",
"short_name": "IT",
"types": [ "country", "political" ]
} ],
"geometry": {
"location": {
"lat": 41.8084,
"lng": 12.3015
}
}
} ]
}
What's wrong with that? Is there any way to test my find_by_radius_from_location function ? I suppose I should receive only one Garage but I receive 2... Why?
EDIT
I have found a workaround but maybe there is a better way to achieve this.
let!(:garage_inside_radius) { FactoryGirl.create(:turin_garage) }
let!(:garage_outside_radius) { FactoryGirl.create(:rome_garage) }
let(:outside_coords) { { latitude: 41.8084, longitude: 12.3015 } }
before(:each) do
garage_outside_radius.update_attributes(outside_coords)
end
I wanted to create app that will longitude, latitude by zip and name of object (for example mcdonalds). I tried to do that via google maps geocoder but my idea was wrong because geocoder returned weird results. Maybe someone know a service or strategy of getting location by zip/name please let me know.
You can test out the Google Geocoder by using the same search string in the maps.google.com search box. For example "McDonalds 2000 Australia", returns appropriate geocoding results. Are you including the country? That might clarify the search results.
reverse-geocoding from your tags means geolocation with given latitude and longitude.
If you want to geocode something you should just use Google Maps Geocode API.
http://maps.google.com/maps/api/geocode/json?address=Sydney&sensor=false
In result you have:
{
"status": "OK",
"results": [ {
"types": [ "locality", "political" ],
"formatted_address": "Sydney Nowa Południowa Walia, Australia",
"address_components": [ {
"long_name": "Sydney",
"short_name": "Sydney",
"types": [ "locality", "political" ]
}, {
"long_name": "Nowa Południowa Walia",
"short_name": "NSW",
"types": [ "administrative_area_level_1", "political" ]
}, {
"long_name": "Australia",
"short_name": "AU",
"types": [ "country", "political" ]
} ],
"geometry": {
"location": {
"lat": -33.8671390,
"lng": 151.2071140
},
"location_type": "APPROXIMATE",
"viewport": {
"southwest": {
"lat": -33.8764033,
"lng": 151.1911066
},
"northeast": {
"lat": -33.8578737,
"lng": 151.2231214
}
},
"bounds": {
"southwest": {
"lat": -33.8797030,
"lng": 151.1970330
},
"northeast": {
"lat": -33.8559920,
"lng": 151.2229770
}
}
}
} ]
}
In results array you have result objects. Coordinates are just in geometry->location.
Nothing simplier.
Here is reference: http://code.google.com/intl/pl-PL/apis/maps/documentation/geocoding/#GeocodingRequests