Extracting individual address components from Google geocoder results in Google Apps Script - google-apps-script

There seems to be some difficulty about getting individual address components out of a JSON Google geocoder result. For example, I want to get county names based on addresses. Could someone please explain why, for example, this code doesn't work?
function county(){
var geo = Maps.newGeocoder().geocode('address');
for (var i=0; i<geo.results.address_components.length; i++)
for (var j=0; j<geo.results.address_components[i].types.length; j++)
if (geo.results.address_components[i].types[j]=="administrative_area_level_2")
var county = geo.results.address_components[i].long_name;
}
I know this is overly simplistic, but it can't even recognize the results.address_components
Any suggestions?

You can use the Logger object to look at intermediary data like this:
Logger.log(geo)
You'll see something like this in the view menu, under logs:
{results=[{address_components=[{long_name=Address, short_name=Address, types=[point_of_interest, establishment]}, {long_name=Madrid, short_name=Madrid, types=[locality, political]}, {long_name=Madrid, short_name=M, types=[administrative_area_level_2, political]}, {long_name=Community of Madrid, short_name=Community of Madrid, types=[administrative_area_level_1, political]}, {long_name=Spain, short_name=ES, types=[country, political]}, {long_name=28039, short_name=28039, types=[postal_code]}], formatted_address=Address, 28039 Madrid, Spain, types=[hospital, point_of_interest, establishment], geometry={viewport={southwest={lng=-3.708366880291502, lat=40.44598801970851}, northeast={lng=-3.705668919708498, lat=40.44868598029151}}, location_type=APPROXIMATE, location={lng=-3.7070179, lat=40.447337}}}, {address_components=[{long_name=Address:, short_name=Address:, types=[point_of_interest, establishment]}, {long_name=Pineville, short_name=Pineville, types=[locality, political]}, {long_name=1, Charlotte, short_name=1, Charlotte, types=[administrative_area_level_3, political]}, {long_name=Mecklenburg County, .........
If you run
function myFunction() {
var geo = Maps.newGeocoder().geocode('1600 pennsylvania ave, washington, dc');
Logger.log(geo.results[0].address_components);
}
myFunction();
You can see the components in the log, it will look like this:
{long_name=1600, short_name=1600, types=[street_number]}, {long_name=President's Park, short_name=President's Park, types=[establishment]}, {long_name=Pennsylvania Avenue Northwest, short_name=Pennsylvania Ave NW, types=[route]}, {long_name=Washington, short_name=Washington, types=[locality, political]}, {long_name=District of Columbia, short_name=DC, types=[administrative_area_level_1, political]}, {long_name=United States, short_name=US, types=[country, political]}, {long_name=20500, short_name=20500, types=[postal_code]}
In this case, none of components are "administrative_area_level_2", but in your case they might be.
You should also consider using Array.forEach.
Hopefully this will help you nail down what the more specific problem is.

To add on to Aaron and Micah's answers, you're really close, just mostly need to select the first result. geocode produces an array of results, but if you're geocoding a single address, grabbing the first will work. Their answer uses the White House address, which appears not to have a county, but using Google HQ (as Google tends to do in examples) produces a valid result.
Here's a generic function for selecting any component of the geocoding responses, also leveraging indexOf to check if the desired component is in the types array (saving a for loop):
function getAddressComponent(address, component) {
var geo = Maps.newGeocoder().geocode(address);
var resultComponents = geo.results[0].address_components;
for (var i = 0; i < resultComponents.length; i++) {
if (resultComponents[i].types.indexOf(component) > -1) {
return resultComponents[i].long_name;
}
}
}
Testing with Logger, we get the correct value of 'Santa Clara County':
function getAddressComponent_test() {
var address = '1600 Amphitheatre Parkway, Mountain View, CA';
Logger.log(getAddressComponent(address, 'administrative_area_level_2'));
}

Related

Exception: The number of rows in the range must be at least 1

I am having trouble writing my json data to a google sheet - I get the exception "The number of rows in the data does not match the number of rows in the range."
I think the problem is with how the range is set - but I don't know how to fix it. I include a sample of the json data after this script:
function getApi() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Sheet1");
var url = "https://www.lexology.com/api/v1/track/clients/articles?companyId=1013419&limit=100";
var params = {
"contentType": "application/json",
"headers":{"ApiKey": "MYAPIKEY"
},
};
var response = UrlFetchApp.fetch(url, params);
var dataAll = JSON.parse(response.getContentText());
var dataSet = dataAll;
var rows = [],
data;
for (i = 0; i < dataSet.length; i++) {
data = dataSet[i];
rows.push([data.companyID, data.companyName, data.articles ]);
}
Logger.log(dataSet)
dataRange = sheet.getRange(3, 1, rows.length, 3);
dataRange.setValues(rows);
}
The json data looks like this:
{
"companyId": 1013419,
"companyName": "Cisco Systems Inc",
"jurisdictions": [],
"workAreas": [],
"tags": [],
"fromDate": "2021-01-10T00:00:00+00:00",
"toDate": "2021-02-10T00:00:00+00:00",
"articles": [
{
"date": "2021-01-04T00:00:00",
"articleUrl": "https://www.lexology.com/library/detail.aspx?g=253372ab-8cce-47c9-853f-4a9953becc0d",
"title": "USPTO and IMPI Announce Parallel Patent Grant Initiative",
"summary": "Under the terms of the MoU, a parallel patent grant framework was instituted which allowed IMPI to leverage search and examination results from the USPTO when issuing a counterpart Mexican patent.",
"firmName": "Casimir Jones SC",
"topic": null,
"jurisdictions": [
"Mexico",
"USA"
],
"workAreas": [
"Patents"
],
"reads": 3,
"prints": 0,
"authorNameOrPhotoClick": 0,
"articleContentLink": 0,
"forwardedUsingMailTo": 0,
"viewOriginal": 0
},
{
"date": "2020-12-23T00:00:00",
"articleUrl": "https://www.lexology.com/library/detail.aspx?g=2c23065f-8479-4343-a889-2a072465bedc",
"title": "Why Everyone Is Patenting Software Inventions",
"summary": "Finding the correct balance requires the assistance of a patent strategist with knowledge of what the patent examiner is likely to accept, and who is skilled in crafting the patent in a way that covers what competitors will need in order to compete.",
"firmName": "Finnegan, Henderson, Farabow, Garrett & Dunner, LLP",
"topic": null,
"jurisdictions": [
"USA"
],
"workAreas": [
"IT & Data Protection",
"Patents"
],
"reads": 3,
"prints": 0,
"authorNameOrPhotoClick": 0,
"articleContentLink": 0,
"forwardedUsingMailTo": 0,
"viewOriginal": 0
}
............EDIT.........
I have fixed the typos in the code as pointed out, thank you.
The output I am looking for is set out in this sheet:
https://docs.google.com/spreadsheets/d/1c3_7D7KOwzR6mPNhKuCoGMNXODr9zgQG7qLzmpFfq6E/edit#gid=0&range=A1:F16
ALSO - when I do Logger.log(dataAll) it is not empty. I get the following:
12:58:42 AM Info Logging output too large. Truncating output. {workAreas=[], articles=[{jurisdictions=[Mexico, USA], forwardedUsingMailTo=0.0, topic=null, prints=0.0, date=2021-01-04T00:00:00, authorNameOrPhotoClick=0.0, viewOriginal=0.0, workAreas=[Patents], title=USPTO and IMPI Announce Parallel Patent Grant Initiative, firmName=Casimir Jones SC, summary=Under the terms of the MoU, a parallel patent grant framework was instituted which allowed IMPI to leverage search and examination results from the USPTO when issuing a counterpart Mexican patent., reads=3.0, articleContentLink=0.0, articleUrl=https://www.lexology.com/library/detail.aspx?g=253372ab-8cce-47c9-853f-4a9953becc0d}, {articleUrl=https://www.lexology.com/library/detail.aspx?g=2c23065f-8479-4343-a889-2a072465bedc, authorNameOrPhotoClick=0.0, prints=0.0, jurisdictions=[USA], viewOriginal=0.0, topic=null, date=2020-12-23T00:00:00, reads=3.0, firmName=Finnegan, Henderson, Farabow, Garrett & Dunner, LLP, forwardedUsingMailTo=0.0, articleContentLink=0.0, summary=Finding the correct balance requires the assistance of a patent strategist with knowledge of what the patent examiner is likely to accept, and who is skilled in crafting the patent in a way that covers what competitors will need in order to compete., title=Why Everyone Is Patenting Software Inventions, workAreas=[IT & Data Protection, Patents]}, {date=2021-01-12T00:00:00, prints=0.0, reads=3.0, title=Election Ballot Verification - A Patent Subject Matter Eligibility Analysis, summary=v. CLS Bank Int., the court (1) determined that the claims were drawn to an abstract idea of “voting, verifying the vote, and submitting the vote for tabulation” and (2) found “no inventive concept in the claims sufficient to transform them into patent-eligible subject matter.” Recited at a high level of abstraction, the claims at issue before the court included one or more voting stations, each including a computer, a display, a printer, an input device, a ballot scanning machine, and a means for tabulating printed ballots., workAreas=[Litigation, Patents], jurisdictions=[USA], authorNameOrPhotoClick=0.0, forwardedUsingMailTo=0.0, topic=null, viewOriginal=0.0, articleContentLink=0.0, articleUrl=https://www.lexology.com/library/detail.aspx?g=544fc695-b8eb-4951-a19e-c2df4b7039b6, firmName=Baker & Hostetler LLP}, {authorNameOrPhotoClick=0.0, topic=null, forwardedUsingMailTo=0.0, date=2021-02-04T00:00:00, reads=3.0, articleUrl=https://www.lexology.com/library/detail.aspx?g=64b1aa84-d37f-4e80-9805-3509b48c1824, workAreas=[IT & Data Protection, Litigation], jurisdictions=[USA], title=Successful Dismissal of PayPal Class Action Over Breach Disclosures Serves as Risks Reminder, summary=Companies should consult closely with counsel when making a public announcement regarding a potential or confirmed data security incident to ensure they are thinking through the potential regulatory and litigation risks, whether a trading blackout period is appropriate during the period of investigation, and whether existing cybersecurity risk disclosures in the company’s public filings should be amended., firmName=Sheppard Mullin Richter & Hampton LLP, articleContentLink=0.0, prints=0.0, viewOriginal=0.0}, {topic=null, firmName=Wilmer Cutler Pickering Hale and Dorr LLP, articleUrl=https://www.lexology.com/library/detail.aspx?g=6cad7e90-ff4a-413f-9a4d-079a23c12a44, prints=0.0, reads=3.0, date=2020-12-22T00:00:00, title=Quick Takeaways: SolarWinds Cybersecurity Incident, viewOriginal=0.0, authorNameOrPhotoClick=0.0, articleContentLink=0.0, forwardedUsingMailTo=0.0, workAreas=[IT & Data Protection], summary=Certain versions of SolarWinds software updates that assist organizations in managing their computer networks were compromised by what some officials, such as the US Secretary of State and the US Attorney General, have indicated is a Russian intelligence agency., jurisdictions=[USA]}, {firmName=Reed Smith LLP, forwardedUsingMailTo=0.0, authorNameOrPhotoClick=0.0, summary=The Guidelines set out the law applicable to operations consisting in reading or writing data on users’ devices and clarify the CNIL’s approach, while the Recommendation aims to guide organisations in their compliance efforts by providing examples of practical methods that can be used, notably to collect consent., date=2021-01-22T00:00:00, title=Adtech regulatory round-up January 2021, articleUrl=https://www.lexology.com/library/detail.aspx?g=8973269a-306a-48d3-9b19-0e50a6647d96, articleContentLink=0.0, viewOriginal=1.0, jurisdictions=[Germany, France, United Kingdom, USA], topic=null, workAreas=[IT & Data Protection, Litigation], reads=3.0, prints=0.0}, {firmName=Sheppard Mullin Richter & Hampton LLP, reads=3.0, articleUrl=https://www.lexology.com/library/detail.aspx?g=8e3a8f46-982e-41ba-a4bc-9e8af57ac7db, forwardedUsingMailTo=0.0, summary=The second carve-out from the definition of protected information was for information known to the employee before he joined TGS, “as evidenced by Employee’s written records.” The Brown court took a particularly dim view of this exception, because it would mean that information which, by definition was not confidential before Brown began working at TSG, would become so unless Brown had written records proving he knew of it beforehand., authorNameOrPhotoClick=0.0, title=California Court Strikes Down Overbroad Confidentiality Agreement as a de facto Non-Compete, articleContentLink=0.0, prints=0.0, topic=null, viewOriginal=0.0, date=2021-01-05T00:00:00, workAreas=[Employment & Labor, Litigation], jurisdictions=[USA]},
Issue:
Apparently you can't get a range with 0 rows. In this method getRange(row, column, numRows, numColumns), every parameter should be a number larger than 0.
Why is rows.length 0?
The issue has to do with the fact that your JSON dataSet is not an array, therefore the for loop is terminated because dataSet.length is undefined. As a result row.length is zero because no values were added.
Updated Solution:
Assuming the first row (headers) in your sheet is:
companyId companyName articles.date articles.articleUrl articles.title articles.summary
function getApi() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = ss.getSheetByName("Sheet1");
const url = "https://www.lexology.com/api/v1/track/clients/articles?companyId=1013419&limit=100";
const params = {
"contentType": "application/json",
"headers":{"ApiKey": "MYAPIKEY"
},
};
const response = UrlFetchApp.fetch(url, params);
const dataSet = JSON.parse(response.getContentText());
const data =
dataSet.articles.map((r,i)=>
i==0?[dataSet.companyId,dataSet.companyName,r.date,r.articleUrl,r.title,r.summary]:
["","",r.date,r.articleUrl,r.title,r.summary]
);
sheet.getRange(sheet.getLastRow()+1,1,data.length,data[0].length).setValues(data);
}
The simplest solution is to move the line from fig1the line under the first row to fig2the line under the second row. It works for me!

Google Directions Service API 3 Return Wrong Result to Map

Am Using google Direction Service to find map the Two Address stated Below
var start = "2115 First Avenue SE Unit 1306, Cottage Grove Place, Cedar Rapids, IA, 52402"; //Set the source/ origin
var end = "6126 Rockwell Dr. Apt 128, Keystone Place, Cedar Rapids, IA, 52402";
On select it Via Google maps Direction directly it works fine
Link :https://www.google.co.in/maps/dir/Keystone+Place-Blair's+Ferry,+6126+Rockwell+Drive+Northeast,+Cedar+Rapids,+IA+52402,+United+States/Cottage+Grove+Place,+2115+1st+Avenue+Southeast,+Cedar+Rapids,+IA+52402,+United+States/#42.0179234,-91.6739759,13z/data=!3m1!4b1!4m13!4m12!1m5!1m1!1s0x87e4f0459aacb105:0x63c416fc0edc61d6!2m2!1d-91.6475339!2d42.039779!1m5!1m1!1s0x87e4f0d795c1b96b:0xa1ccbbf6400b01aa!2m2!1d-91.6362972!2d41.9959428?hl=en
It not working as same while using Direction Service.
Find below example
http://jsfiddle.net/MRHQ4/291/ (Credits to Author :Shreerang Patwardhan)
It works fine when i remove Address2 from Source and Destination
Unit and apartment information can cause parsing issues in the geocoder. If you can, try to remove them as best as possible. Change the addresses to
var start = "2115 First Avenue SE, Cedar Rapids, IA, 52402"; //Set the source/ origin
var end = "6126 Rockwell Dr, Cedar Rapids, IA, 52402";
and you'll see it working better.
Reduced Address Information
With Neighborhood Information
With Apt/Unit Information
'Cottage Grove Place' and 'Keystone Place' are not the address2, these are place names (or you can say place title), just move them to start of the address (no other change) like following, and it is working fine:
var start = "Cottage Grove Place, 2115 First Avenue SE Unit 1306, Cedar Rapids, IA, 52402"; //Set the source/ origin
var end = "Keystone Place, 6126 Rockwell Dr. Apt 128, Cedar Rapids, IA, 52402"; //Set the destination
I tested and it is working fine, please review, thanks.

how to filter a JSON file? (NYC Census Tracts)

I am adapting an existing D3JS map-drawing file to work with a different JSON file. In the file 'ny.json' there are things called "objects" and inside of those there are things called "tracts" so I get how the references work for the current (working) code, which comes from here and looks like:
d3.json("./shapefiles/ny.json", function(error, ny) {
if (error) throw error;
var tracts = ny.objects.tracts;
tracts.geometries = tracts.geometries
.filter(function(d) { return (d.id / 10000 | 0) !== 99; });
svg.append("g")
.selectAll("path")
.data(topojson.feature(ny, tracts).features)
.enter().append("path")
.attr("class", "tract")
.attr("d", path)
However, the new JSON file doesn't have those 'objects' or 'tracts' and I'm trying to figure out how to filter it so that it can be used to draw a map not of New York State (as in the first one) but just of the Bronx county. It's possible that the two files are too different in structure that an easy filter change isn't possible, but I thought I would ask in case there's another solution. I haven't seen the 'meta' features in json before.
I am afraid it is not possible to display only Bronx in the map because census tract ids in ny.json are unique only at borough level. For instance, census tract 014300 appears in Bronx, Brooklyn, Manhattan, and Queens. ny.json stores only census tract ids in ny.objects.tracts.id, and not their FIPS County Codes.
This makes filtering on Borough name impossible: https://bl.ocks.org/memoryfull/ba6121d19f80f85f0beb
Even if the filtering was feasible, it would be suboptimal. Instead, I would create own GeoJSON of Bronx in any GIS software with census tract ids and then call it in your d3 code.

MeteorJS and UnderscoreJS: grouping results to plot points on a Google Map

I am working on a small MeteorJS app that plots points on a map based on popular areas for work.
I have this:
Template.list.jobs = function() {
if(Session.get('currentIndustryOnet')) {
jobs = Jobs.find({onet: Session.get('currentIndustryOnet')}).fetch();
// Session.set('jobCount', jobs.count());
var cnt = _.groupBy(jobs, 'address');
console.log(cnt);
return Pagination.collection(jobs);
} else {
jobs = Jobs.find()
Session.set('jobCount', jobs.count());
return Pagination.collection(jobs.fetch());
}
}
The cnt variable returns a properly grouped array (the key of the array is an address like Allentown, PA). I have a collection of Cities which have ever city in the USA along with their LAT/LONGs to plot on a Google Map. So I will take the top 100 from the grouped array, find the lat/long in the Cities collection and plot those points on a map.
I am not familiar with working with a groupedBy method to sort the list based on the length and then pull out the key to use as my search.
I'm not 100% certain about the data structure... but assuming jobs have an address field, and you want them sorted by frequency of occurrence and capped, you could do something like this:
var addresses = _.chain(jobs)
.countBy('address')
.pairs()
.sortBy(function(j) {return -j[1];})
.map(function(j) {return j[0];})
.first(100)
.value();
Note there may be a more clever way to use underscore to arrive at this result. Once you have the capped, sorted list of addresses, you can probably get the lat/long values via a find like:
Cities.find({address: {$in: addresses}}).fetch();

Return list of possible address matches from google?

I am trying to add functionality to a web app of mine that allows a user to enter an address estimation, for example "McDonalds, Detroit, MI", and then select the exact address from a list of possible matches.
I am already familiar with direct geocoding (address -> lat, lng) and reverse geocoding (lat, lng -> address). Does anyone know how I can accomplish the above using just the google geocoding API?
Thanks!
When you geocode the address google returns a list of possible matches in the response
A geocode request for main st returns 2 results in new york, 1 in utah, 1 in maine, etc...
You just parse the response to get the different locations
You can't. Google's Geocoder will not match on business locations in the majority of cases. What you want is a combination of local search + geocoding, or a address suggest service, which Google's Geocoding service doesn't offer.
Galen is right, here's working code against V3 - I found the suggestions in the Placemarks attribute of the results. Try address = "Broad Street" in the below example.
if (geocoder) {
geocoder.getLocations(
address,
function (results) {
if (!results) {
alert(address + " no suggestions");
} else {
$('#output').html('');
if (results.Placemark) {
for (var i = 0; i < results.Placemark.length; i++) {
$('#output').append(i + ': ' + results.Placemark[i].address + '<br/>');
}
}
}
});
}