Get a address_components - google-maps

I'm using google places API with geocoding. I have a problem with address components type.
I want to get information about address which user typed in autocomplete in this format:
Street number/ street / City/ Province/ Country.
If user autocompleted "Street 12, SomeCity, SomeProvince, SomeCountry", I want to return in alert all of this information. But when user type only "someProvince, SomeCountry", I want to have only province and country address type.
Here is my code:
google.maps.event.addListener(autocomplete, 'place_changed', function () {
var place = autocomplete.getPlace();
alert('0: ' + place.address_components[0].long_name);
alert('1: ' + place.address_components[1].long_name);
alert('2: ' + place.address_components[2].long_name);
alert('3: ' + place.address_components[3].long_name);
alert('4: ' + place.address_components[4].long_name);
alert('5: ' + place.address_components[5].long_name);
alert('6: ' + place.address_components[6].long_name);
alert('7: ' + place.address_components[7].long_name);
)};
Problem is that when user autocomplete full address, it show properly all of this alerts. But when autocomplete only part of information - only country - it will show 7 times what country is typed.
http://gmaps-samples-v3.googlecode.com/svn/trunk/places/autocomplete-addressform.html
I want to have, that when street and city is not given, it will show alert ("street is null" etc). How to do it?

Made this function for a project. It parses
street
number
country
zip
city
from a google georesponse
function parseGoogleResponse(components) {
_.each(components, function(component) {
_.each(component.types, function(type) {
if (type === 'route') {
$("input[name=street]").val(component.long_name)
}
if (type === 'street_number') {
$("input[name=nr]").val(component.long_name)
}
if (type === 'locality') {
$("input[name=city]").val(component.long_name)
}
if (type === 'country') {
$("input[name=country]").val(component.long_name)
}
if (type === 'postal_code') {
$("input[name=zip]").val(component.long_name)
}
})
})
}

Address Types and Address Component Types
Use the types and map them to your address fields. Keep in mind that cities, counties, states etc. can have different meanings depending on their context. For example, North Hollywood comes up with type neighborhood since it is located in Los Angeles('locality').
function placeToAddress(place){
var address = {};
place.address_components.forEach(function(c) {
switch(c.types[0]){
case 'street_number':
address.StreetNumber = c;
break;
case 'route':
address.StreetName = c;
break;
case 'neighborhood': case 'locality': // North Hollywood or Los Angeles?
address.City = c;
break;
case 'administrative_area_level_1': // Note some countries don't have states
address.State = c;
break;
case 'postal_code':
address.Zip = c;
break;
case 'country':
address.Country = c;
break;
/*
* . . .
*/
}
});
return address;
}

As per the demo, you need to check each of the returned address components to see if a street / city has been returned:
google.maps.event.addListener(autocomplete, 'place_changed', function() {
var place = autocomplete.getPlace();
var components = place.address_components;
var street = null;
for (var i = 0, component; component = components[i]; i++) {
console.log(component);
if (component.types[0] == 'route') {
street = component['long_name'];
}
}
alert('Street: ' + street);
});

Made this function for AngularJS:
function getAddressComponentByPlace(place) {
var components;
components = {};
angular.forEach(place.address_components, function(address_component) {
angular.forEach(address_component.types, function(type) {
components[type] = address_component.long_name;
});
});
return components;
}
The result looks like this:
administrative_area_level_1: "Berlin"
country: "Deutschland"
locality: "Berlin"
political: "Deutschland"
postal_code: "10719"
route: "Kurfürstendamm"
street_number: "1"
sublocality: "Bezirk Charlottenburg-Wilmersdorf"
sublocality_level_1: "Bezirk Charlottenburg-Wilmersdorf"
ES6 Version
const parseGoogleAddressComponents = (addressComponents) => {
let components = {};
addressComponents.forEach((addressComponent) => {
addressComponent.types.forEach((type) => {
components[type] = addressComponent.long_name;
});
});
return components;
};

I wrote this function in pure Javascript to get the corresponding type from a google.maps.GeocoderAddressComponent object
//gets "street_number", "route", "locality", "country", "postal_code"
function getAddressComponents(components, type) {
for (var key in components) {
if (components.hasOwnProperty(key)) {
if (type == components[key].types[0]) {
return components[key].long_name;
}
}
}
}

Here is a simple suggestion:
function parseGoogleResponse (components) {
var newComponents = {}, type;
$.each(components, function(i, component) {
type = component.types[0];
newComponents[type] = {
long_name: component.long_name,
short_name: component.short_name
}
});
return newComponents;
}
And the usage will be:
var components = parseGoogleResponse( places[0].address_components );

Simple ES6 version:
const addComponents = gPlaceObj?.place?.address_components
And then select each component you want using a filter:
const country = addComponents.filter(x => x?.types?.includes('country'))[0];
Which would give you this console.log(country):
{"long_name": "United States", "short_name": "US", "types": ["country", "political"]}

Related

react-google-autocomplete only works after refresh

I made a "Profile" page to show address, zip code, latitude and longitude when user enters(and select) an address by using react-google-autocomplete module.
When a user starts to enter address name, some full addresses are displayed down below.
After selecting one of them, I can get enough information about the address.
But the problem is that it doesn't work at first but works after refreshing the page.
Again...
I open localhost:3000 and move to localhost:3000/profile page by clicking a navbar button.
Then it doesn't work with this error "Google maps places API must be loaded."
And after refreshing the page(F5) it works properly.
I can't find a way to solve this error. Please help me!
//////////////////////
-Parent
<Grid item xs={12} sm={12}>
<MapInput form={form} setForm={setForm} />
</Grid>
///////////////////////
import { usePlacesWidget } from "react-google-autocomplete";
import React from "react";
export default ({form, setForm}) => {
const { ref } = usePlacesWidget({
apiKey: process.env.REACT_APP_GOOGLE_API_KEY,
onPlaceSelected: (place) => {
const address = place.formatted_address;
let city, state, country, zipcode;
for (let i = 0; i < place.address_components.length; i++) {
for (let j = 0; j < place.address_components[i].types.length; j++) {
switch (place.address_components[i].types[j]) {
case "locality":
city = place.address_components[i].long_name;
break;
case "administrative_area_level_1":
state = place.address_components[i].long_name;
break;
case "country":
country = place.address_components[i].long_name;
break;
case "postal_code":
zipcode = place.address_components[i].long_name;
break;
}
}
}
setForm({...form, s_address: address, s_city: city, s_state: state, s_country: country, s_zipcode: zipcode, lat: place.geometry.location.lat(), lng: place.geometry.location.lng()});
},
options: {
types: ["(regions)"],
// componentRestrictions: { country: "ru" },
},
});
return <input ref={ref} style={{
width: "100%",
padding: "18.5px 14px",
margin: "20px 0px",
font: "inherit",
color: "grey"
}} defaultValue={form?.s_address} />;
};

How to get Email of the user who performed an activity in Google Drive?

I am trying to get data from Drive Activity API. The data needs to have the following 4 arguments:
User Information
Filename
Activity Type (Edit, Delete, Copy)
Timestamp
I used the following code to get the required data:
function listDriveActivity() {
var arr = [];
const request = {
pageSize: 10
};
try {
const response = DriveActivity.Activity.query(request);
const activities = response.activities;
if (!activities || activities.length === 0) {
Logger.log('No activity.');
return;
}
for (const activity of activities) {
// get time information of activity.
var time = getTimeInfo(activity);
// get the action details/information
var action = getActionInfo(activity.primaryActionDetail);
// get the actor's details of activity
var actors = activity.actors.map(getActorInfo);
// get target information of activity.
var targets = activity.targets.map(getTargetInfo);
arr.push(actors,targets,[action],[time]);
}
Logger.log(arr);
} catch (err) {
Logger.log('Failed with an error %s', err.message);
}
}
function getOneOf(object) {
for (const key in object) {
return key;
}
return 'unknown';
}
function getTimeInfo(activity) {
if ('timestamp' in activity) {
return activity.timestamp;
}
if ('timeRange' in activity) {
return activity.timeRange.endTime;
}
return 'unknown';
}
function getActionInfo(actionDetail) {
return getOneOf(actionDetail);
}
function getUserInfo(user) {
if ('knownUser' in user) {
const knownUser = user.knownUser;
const isMe = knownUser.isCurrentUser || false;
return isMe ? 'people/me' : knownUser.personName;
}
return getOneOf(user);
}
function getActorInfo(actor) {
if ('user' in actor) {
return getUserInfo(actor.user);
}
return getOneOf(actor);
}
function getTargetInfo(target) {
if ('driveItem' in target) {
const title = target.driveItem.title || 'unknown';
return 'driveItem:"' + title + '"';
}
if ('drive' in target) {
const title = target.drive.title || 'unknown';
return 'drive:"' + title + '"';
}
if ('fileComment' in target) {
const parent = target.fileComment.parent || {};
const title = parent.title || 'unknown';
return 'fileComment:"' + title + '"';
}
return getOneOf(target) + ':unknown';
}
This is the example output, when there is some activity in the drive by a user:
User Information: people/107464693787053536449
Filename: Timesheet
Activity Type: Edit
Timestamp: 2022-04-05T04:51:41.862Z
Now, I want to get the user email in User information rather than user id. Can you please guide me how should I do it? Is there any method/function that I could follow?
You have to use People API if you want to get more information about the user, including the email address:
personName: The identifier for this user that can be used with the People API to get more information. The format is people/ACCOUNT_ID. See https://developers.google.com/people/.
More specifically, call people.get with personFields set to emailAddresses:
function getEmailAddress(resourceName = "people/ACCOUNT_ID") {
const optionalArgs = {
personFields: "emailAddresses",
fields: "emailAddresses(value)"
}
const contact = People.People.get(resourceName, optionalArgs);
const emailAddress = contact.emailAddresses[0].value;
return emailAddress;
}

How to load json from file and set it as global variable in Vue?

I'm new to Vue. I want to read employeeId from a login form and ust it to load some json files named according as employeeId.json like (10000001.json, 20000001.json) and set the json object as a global variable so I can easily access it in all components.
Firstly, I don't know how to dynamically load json files. Using import sees not work. Some one suggested using require should work. But there are not many examples, I don't know where to put require...
Secondly, how do I set the json as global after the employeeId props in? I'm very confused where to put it (inside the export default or not? inside methods or not? or inside created/mounted or not?) and where to use this or not...
This is the script section of my headerNav.vue file.
<script>
//**I placed them here now, it works, but employeeId is hard coded...
import json10000001 from "./json/10000001.json";
import json20000001 from "./json/20000001.json";
import json30000001 from "./json/30000001.json";
// var employeeId = employeeIdFromLogin;
var jsonForGlobal;
var employeeId = 10000001;
var jsonFileCurrentObj;
if (employeeId == "10000001") {
jsonForGlobal = jsonFileCurrentObj = json10000001;
} else if (employeeId == "20000001") {
jsonForGlobal = jsonFileCurrentObj = json20000001;
} else if (employeeId == "30000001") {
jsonForGlobal = jsonFileCurrentObj = json30000001;
}
export default {
// props:{
// employeeIdFromLogin: String,
// },
props:['employeeIdFromLogin'],
jsonForGlobal,
// employeeIdFromLogin,
data() {
return {
docked: false,
open: false,
position: "left",
userinfo: {},
jsonFileCurrent: jsonFileCurrentObj,
// employeeIdFromLogin: this.GLOBAL3.employeeIdFromLogin
// jsonFile: currentJsonFile
};
},
mounted() {
//**I tried put it here, not working well...
// var employeeId = this.employeeIdFromLogin;
// // var jsonForGlobal;
// console.log("headernav.employeeIdFromLogin="+this.employeeIdFromLogin);
// // var employeeId = 10000001;
// var jsonFileCurrentObj;
// if (employeeId == "10000001") {
// this.jsonForGlobal = this.jsonFileCurrentObj = json10000001;
// } else if (employeeId == "20000001") {
// this.jsonForGlobal = this.jsonFileCurrentObj = json20000001;
// } else if (employeeId == "30000001") {
// this.jsonForGlobal = this.jsonFileCurrentObj = json30000001;
// }
},
methods: {
switchPage(pageName) {
this.$emit("switchPage", pageName);
}
//**I don't know how to use the require...
// var employeeId = 10000001;
// getJsonFile(employeeId) {
// this.currentJsonFile = require("../assets/json/" + employeeId + ".json");
// }
}
};
You might want to use vuex to manage global store. But if you don't want includes Vuex, there is a simpler way to have global state:
Define globalStore.js
// globalStore.js
export const globalStore = new Vue({
data: {
jsonForGlobal: null
}
})
then import it and use in component:
import {globalStore} from './globalStore.js'
export default {
props: ['employeeIdFromLogin'],
data: function ()
return {
jsonLocal: globalStore.jsonForGlobal,
jsonFileCurrent: null
}
},
watch: {
employeeIdFromLogin: {
handler(newVal, oldVal) {
const data = require('./json/' + this.employeeIdFromLogin + '.json')
this.jsonFileCurrent = data
globalStore.jsonForGlobal = data
}
}
}
}

How to restrict Google Maps search results to only one country properly?

I am developing an application, in Ionic, where you can plan a trip with a start address and and end address. However I want to limit this feature to only one country. Before writing I have been searching for solutions on the internet, but none of them worked for me.
Have tried these suggestions:
https://stackoverflow.com/a/8282093/8130808
https://stackoverflow.com/a/10170421/8130808
Here is how I have tried to approach it:
//Places markers and displays a route, so the user can accept the current placing
newRoutePlaceMarks(place: any): void {
var googleDiplay = new google.maps.DirectionsRenderer({ draggable: true });
var route = this.directionsDisplay;
//A bit of a hack, sadly Typescript and google maps arent the best of buddies
this.directionsService.route({
origin: this.routeStart,
destination: this.routeEnd,
travelMode: 'DRIVING',
}, function (response, status) {
if (status === 'OK') {
console.log("status is OK trying to put directions up");
//The reason why I've set the addListener before the actual route is so it gets triggered
//on the creation of the route. Had some problem with figuring out how to actually handle
//the data when on the route creation, as this response function is in strict mode, and outside
google.maps.event.addListener(route, "directions_changed", function () {
console.log("Route changed");
this.global = ShareService.getInstance();
this.directions = route.getDirections();
this.metersToDist = this.directions.routes[0].legs[0].distance.value;
this.timeToDist = this.directions.routes[0].legs[0].duration.value;
this.startAddress = this.directions.routes[0].legs[0].start_address;
this.startCord = this.directions.routes[0].legs[0].start_location;
this.endAddress = this.directions.routes[0].legs[0].end_address;
this.endCord = this.directions.routes[0].legs[0].end_location;
this.global.setMetersToDist(this.metersToDist);
this.global.setTimeToDist(this.timeToDist);
this.global.setStartAddress(this.startAddress);
this.global.setStartCord(this.startCord);
this.global.setEndAddress(this.endAddress);
this.global.setEndCord(this.endCord);
var options = {
types: ['geocode'],
componentRestrictions: { country: "dk" }
};
google.maps.places.Autocomplete(this.startAddress, options);
});
//The actual initialiser for the route
route.setDirections(response);
} else {
alert('Could not display route ' + status);
}
});
}
My problem is that the input is HTTPELEMENT, I get the input from an alert dialog
newRouteInput() {
let alert = this.alertCtrl.create({
title: 'New route',
inputs: [
{
name: 'routeStart',
placeholder: 'Start of route'
},
{
name: 'routeEnd',
placeholder: 'End of route'
}
],
buttons: [
{
text: 'Cancel',
role: 'cancel',
handler: data => {
console.log('Cancel clicked');
}
},
{
text: 'Debug start and end',
handler: data => {
if (data.username !== "undefined") {
console.log(data.routeStart + " " + data.routeEnd);
this.routeStart = "Brøndby Strand";
this.routeEnd = "Hvidovre";
this.newRoutePlaceMarks(this.map);
this.routeButton = false;
} else {
return false;
}
}
},
{
text: 'Place route markers',
handler: data => {
if (data.username !== "undefined") {
this.routeStart = data.routeStart;
this.routeEnd = data.routeEnd;
this.newRoutePlaceMarks(this.map);
this.routeButton = false;
} else {
console.log(data.routeStart + " " + data.routeEnd);
return false;
}
}
}
]
});
alert.present();
}
When I run this I get an error because of this.startAddress. It's not null, it contains the start address:
InvalidValueError: not an instance of HTMLInputElement

Map JSON data to Knockout observableArray with specific view model type

Is there a way to map a JSON data object to an observable array and then in turn have each item of the observable array be initialized into a specific type of view model?
I've looked at all of knockout's documentation along with the knockout and mapping examples here and I can't find any answer that works for what I'm after.
So, I have the following JSON data:
var data = {
state : {
name : 'SD',
cities : [{
name : 'Sioux Falls',
streets : [{
number : 1
}, {
number : 3
}]
}, {
name : 'Rapid City',
streets : [{
number : 2
}, {
number : 4
}]
}]
}
};
And I have the following view models:
var StateViewModel = function(){
this.name = ko.observable();
this.cities = ko.observableArray([new CityViewModel()]);
}
var CityViewModel = function(){
this.name = ko.observable();
this.streets = ko.observableArray([new StreetViewModel()]);
}
var StreetViewModel = function(){
this.number = ko.observable();
}
Is it possible, with the given data structure and using knockout's mapping plugin, to have the resulting StateViewModel contain an observableArray populated with 2 CityViewModels, and each CityViewModel containing an observableArray populated with 2 StreetViewModels?
Currently using the mapping plugin I'm able to get it to map to a StateViewModel, but the 'cities' and 'streets' collections are populated with generic objects instead of instances of my City and Street view models.
They end up with the correct observable properties and values on them, they're just not instances of my view models, which is what I'm after.
Check this http://jsfiddle.net/pTEbA/268/
Object.prototype.getName = function() {
var funcNameRegex = /function (.{1,})\(/;
var results = (funcNameRegex).exec((this).constructor.toString());
return (results && results.length > 1) ? results[1] : "";
};
function StateViewModel(data){
this.name = ko.observable();
ko.mapping.fromJS(data, mapping, this);
}
function CityViewModel(data) {
this.name = ko.observable();
ko.mapping.fromJS(data, mapping, this);
}
function StreetViewModel(data) {
this.name = ko.observable();
ko.mapping.fromJS(data, mapping, this);
}
var mapping = {
'cities': {
create: function(options) {
return new CityViewModel(options.data);
}
},
'streets': {
create: function(options) {
return new StreetViewModel(options.data);
}
}
}
var data = { state: {name:'SD', cities:[{name:'Sioux Falls',streets:[{number:1},{number:3}]},
{name:'Rapid City',streets:[{number:2},{number:4}]}]}};
var vm = new StateViewModel(data.state)
console.log(vm);
console.log(vm.getName());
console.log(vm.cities());
console.log(vm.cities()[0].getName());
console.log(vm.cities()[0].streets());
console.log(vm.cities()[0].streets()[0].getName());
​