Here's my issue... I have two selections for my user, use current location, or use a zip code. When the user selects a zip code I make a call to the Google geocode API and retrieve the central point for that zip code. I want to be able to put these coordinates into my Vue model and then execute a method within Vue called refresh which retrieves some data from my database and calls a function that sets up the map with markers and bounds. Since the callback function is decoupled from the model, I cannot seem to set the Vue properties, nor can I call the method. How do I handle the callback?
Please note that the refresh method works properly when using the selection for current location.
getLocation is called when the user selects "Current Location"
checkZip is called when the user selects "Use Zip Code"
<script>
var app = new Vue({
el: '#app-content',
data: {
locationType: "CurrentLocation",
lat: "",
lng: "",
radiusInMiles: 10,
filters: [],
zipCode: "",
geoError: "",
error: "",
results: []
},
methods: {
getLocation: function () {
this.zipCode = "";
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(this.storeLocation, this.locationError);
} else {
this.locationType = "ZipLocation";
console.log("Geolocation does not appear to be supported by the browser.");
this.geoError = "Unable to obtain location. Please make sure location services are turned on and try again.";
}
},
storeLocation: function (position) {
this.lat = position.coords.latitude;
this.lng = position.coords.longitude;
this.refresh();
},
locationError: function (err) {
this.locationType = "ZipLocation";
this.results = [];
console.warn(err);
this.geoError = "Unable to obtain location. Please make sure location services are turned on and try again.";
},
refresh: function () {
if (!(this.lat && this.lng && this.radiusInMiles && this.filters)) {
console.log("Location and filters are undefined.");
}
else {
//https://github.com/axios/axios
axios
.post('xyxyxyxyx', {
lat: this.lat,
lng: this.lng,
radiusInMiles: this.radiusInMiles,
filters: this.filters.toString()
})
.then(response => {
this.results = response.data.d;
//Send to map function...
loadMap(this.lat, this.lng, this.results);
})
.catch (error => console.log(error))
}
},
checkZip: function () {
if (this.zipCode.length == 5 && !isNaN(this.zipCode)) {
var geocoder = new google.maps.Geocoder();
geocoder.geocode({ 'address': 'zipcode ' + this.zipCode }, function (results, status) {
if (status == google.maps.GeocoderStatus.OK) {
//Here's my issue...
//How do I store to the model and then call this.refresh
this.lat = results[0].geometry.location.lat();
this.lng = results[0].geometry.location.lng();
this.refresh();
} else {
console.error("Request failed.")
}
});
}
}
}
})
</script>
I was able to get this to work by copying the Vue model into a variable (self).
How can I update a Vue app's or component's property in a promise call back?
checkZip: function () {
if (this.zipCode.length == 5 && !isNaN(this.zipCode)) {
var self = this;
var geocoder = new google.maps.Geocoder();
geocoder.geocode({ 'address': 'zipcode ' + this.zipCode }, function (results, status) {
if (status == google.maps.GeocoderStatus.OK) {
self.lat = results[0].geometry.location.lat();
self.lng = results[0].geometry.location.lng();
self.refresh();
} else {
console.error("Request failed.")
}
});
}
}
Related
I'm trying to implement the OverlappingMarkerSpiderfier for my Google Maps, and it works because my markers are able to "spiderfy" when I click on a marker.
My issue is that in my dev console on VS Code, ESLint is still giving me the error 'OverlappingMarkerSpiderfier' is not defined. I don't really know what the issue is since my markers are working as intended when I click on them. Below is a picture showing that OverlappingMarkerWorkers even though there is an error from ESLint:
I want to get rid of the error in case a future error arises because of it. I've searched for solutions, and many people have commented that OverlappingMarkerSpiderfier should be loaded after Google Maps load. I've done that, but the error still persists.
I load my Google Maps asynchronously; below is my .js file that loads the Google Maps and OverlappingMarkerSpiderfier:
import api_keys from './api_keys'
const API_KEY = api_keys.google_maps_api_key;
const CALLBACK_NAME = 'gmapsCallback';
let initialized = !!window.google;
let resolveInitPromise;
let rejectInitPromise;
const initPromise = new Promise((resolve, reject) => {
resolveInitPromise = resolve;
rejectInitPromise = reject;
});
export default function init() {
if (initialized) return initPromise;
initialized = true;
window[CALLBACK_NAME] = () => resolveInitPromise(window.google);
const script = document.createElement('script');
script.async = true;
script.defer = true;
script.src = `https://maps.googleapis.com/maps/api/jskey=${API_KEY}&callback=${CALLBACK_NAME}`;
script.onerror = rejectInitPromise;
document.querySelector('head').appendChild(script);
const spiderfier = document.createElement('script');
spiderfier.defer = true;
spiderfier.src = "https://cdnjs.cloudflare.com/ajax/libs/OverlappingMarkerSpiderfier/1.0.3/oms.min.js";
spiderfier.onerror = rejectInitPromise;
document.querySelector('head').appendChild(spiderfier);
return initPromise;
}
The following is my GoogleMaps component. The OverlappingMarkerSpiderfier implementation is located within "watch":
<template>
<div id="google-map">
</div>
</template>
<script>
import gMaps from '../lib/gMaps.js'
export default {
name: 'GoogleMaps',
props: {
events: Array
},
data() {
return {
map: null,
locations: []
}
},
async mounted() {
try {
const google = await gMaps();
const geocoder = new google.maps.Geocoder();
this.map = new google.maps.Map(this.$el);
geocoder.geocode({ address: 'USA'}, (results, status) => {
if (status !== 'OK' || !results[0]) {
throw new Error(status);
}
this.map.setCenter(results[0].geometry.location);
this.map.fitBounds(results[0].geometry.viewport);
});
} catch (error) {
console.error(error);
}
},
watch: {
async events() { //creates markers for the map; data is from a 3rd party API that is handled by a different component
try {
const google = await gMaps();
var oms = new OverlappingMarkerSpiderfier(this.map, {
markersWontMove: true,
markersWontHide: true,
basicFormatEvents: true
})
for(let i = 0; i < this.events.length; i++) {
let marker = new google.maps.Marker({
position: {
lat: parseInt(this.events[i].latitude, 10),
lng: parseInt(this.events[i].longitude, 10)
},
map: this.map,
title: this.events[i].title
})
let iw = new google.maps.InfoWindow({
content: this.events[i].description || 'No description available.'
});
google.maps.event.addListener(marker, 'spider_click', function() {
iw.open(this.map, marker);
});
oms.addMarker(marker);
}
}
catch(error) {
console.error(error)
}
}
}
}
</script>
<style lang="scss" scoped>
#google-map {
width: auto;
height: 100vh;
}
</style>
try either 1 of these
this.$nexttick(()=>{
code in mounted hook....
})
check if window.google object is loaded and your map reference is available before instantiating OverlappingMarkerSpiderfier.
I was wondering if it is possible to access the data that is within the export default in my javascript file for my Vue component. I am trying to get the contents of the routes array inside the calculateAndDisplayRoute() function.
overview.js
function calculateAndDisplayRoute(directionsService, directionsDisplay) {
var origin, dest;
for (var route in this.routes /*<--HERE*/) {
console.log('www')
if(route.id == this.filter){
console.log('true')
}
}
directionsService.route({
origin: 'Vancouver',
destination: 'Chicago',
travelMode: 'DRIVING'
}, function(response, status) {
if (status === 'OK') {
directionsDisplay.setDirections(response);
} else {
window.alert('Directions request failed due to ' + status);
}
});
}
export default {
name: 'fleet-overview',
data () {
return {
view: '',
routes: [], //<--HERE
users: [],
errorRoute: '',
response: [],
filter: 'searchby',
searchTerm: '',
users: [],
}
},
created: function () {
this.routeView();
},
methods: {
initMap: function(){
this.$nextTick(function(){
var directionsService = new google.maps.DirectionsService;
var directionsDisplay = new google.maps.DirectionsRenderer;
var map = new google.maps.Map(document.getElementById('map'), {
center: {lat: 45.49, lng: -73.61},
zoom: 9
});
directionsDisplay.setMap(map);
var onChangeHandler = function() {
calculateAndDisplayRoute(directionsService, directionsDisplay);
};
document.getElementById('filterselect').addEventListener('change', onChangeHandler);
})
}
//...
}
You could use call to set the context (this) appropriately.
calculateAndDisplayRoute.call(this, directionsService, directionsDisplay);
Or you could rework the function definition to accept a context (you can't call it this, but you could call it context).
function calculateAndDisplayRoute(context, directionsService, directionsDisplay) {
...
for (var route in context.routes /*<--HERE*/) {
console.log('www')
if(route.id == context.filter){
console.log('true')
}
}
...
}
Then in your initMap, you would pass this as the first argument:
calculateAndDisplayRoute(this, directionsService, directionsDisplay);
I use the googleMaps API to get location data from a given address:
function geocodeAddress(address, callback) {
var geocoder = new google.maps.Geocoder();
geocoder.geocode({ address: address }, function(results, status) {
if (status === google.maps.GeocoderStatus.OK) {
if (results.length > 0) {
callback(results[0].geometry.location);
} else {
console.log("No results found");
}
} else {
console.log("Geocoder failed due to: " + status);
}
});
}
function getLatLng(address, callback) {
latLng = [];
geocodeAddress(address, function(position) {
latLng.push(position.lat());
latLng.push(position.lng());
callback(latLng);
});
}
// Search for address provided by user via Google Maps API
setMapToSearchAddress: function() {
var vm = this;
// Set the location in map to the address given
setMapLocationToAddress(this.searchAddress);
getLatLng(this.searchAddress, function(latlng) {
console.log(latlng);
vm.latitude = latlng[0];
vm.longitude = latlng[1];
})
}
But when I send a POST request to my server and print the output of the request being sent, I see, that the longitude is sent as a number, whereas the latitude is sent as a string. But I never converted the data?
function postDataToServer(endpoint, data, callback) {
$.post("v1/".concat(endpoint), function(data, response) {
callback(response);
})
}
createIncident: function() {
var incidentData = {
"incidentReference": "",
"latitude": this.latitude,
"longitude": this.longitude,
"streetName": this.address,
"featureTypeId": 1,
"archived": 0
}
console.log(incidentData);
// POST data to server
postDataToServer("incidents", incidentData, function(response) {
console.log(response);
})
},
Object {incidentReference: "", latitude: "48.15312", longitude:
11.583509999999933, streetName: "Leopoldstraße 8", featureTypeId: 1…}archived: 0featureTypeId: 1incidentReference: ""latitude:
"48.15312"longitude: 11.583509999999933streetName: "Leopoldstraße 8"
Why are these variables treated differently?
I want to find the location name which user select on map.
Currently I am getting latitude and longitude both.
But unable to get the location name.
I am using angularJS and angular-google-maps 2.1.5.
Here is the html.
<ui-gmap-google-map center="map.center" zoom="map.zoom" draggable="true" options="options" events="map.events">
</ui-gmap-google-map>
JS :
$scope.map = {
center: {
latitude: 21.0000,
longitude: 78.0000
},
zoom: 4,
events: {
click: function(mapModel, eventName, originalEventArgs,ok) {
var e = originalEventArgs[0];
objOneDevice.dev_latitude = e.latLng.lat();
objOneDevice.dev_longitude = e.latLng.lng();
$scope.templatedInfoWindow.show = true;
}
}
};
$scope.options = {
scrollwheel: true
};
Anything is appreciated.
Here what I have done using Reverse geocoding.
$scope.map = {
center: {
latitude: 21.0000,
longitude: 78.0000
},
zoom: 4,
events: {
click: function(mapModel, eventName, originalEventArgs,ok) {
var e = originalEventArgs[0];
var geocoder = new google.maps.Geocoder();
var latlng = new google.maps.LatLng(e.latLng.lat(), e.latLng.lng());
geocoder.geocode({ 'latLng': latlng }, function (results, status) {
if (status == google.maps.GeocoderStatus.OK) {
if (results[1]) {
console.log(results[1].formatted_address); // details address
} else {
console.log('Location not found');
}
} else {
console.log('Geocoder failed due to: ' + status);
}
});
}
}
};
Given Latitude/Longitude object you can map location to approximate address using Google Map API.
You can use the Geocoding API for mapping locations to addresses and addresses to locations.
Geocoder.geocode( { 'latLng': latLngObject }, callbackFn);
Here is the link for Google Map API for Geocoding.
Try these links
https://developers.google.com/maps/documentation/javascript/examples/geocoding-reverse?csw=1
Use the Geocoding API for mapping locations to addresses and addresses to locations. http://code.google.com/apis/maps/documentation/javascript/services.html#Geocoding
Geocoder.geocode( { 'latLng': latLngObject }, callback);
http://wbyoko.co/angularjs/angularjs-google-maps-components.html
Hope it helps!!!
var endlocation = {
'center': '52.5606064,2.0312582999999904',
'zoom': 10
};
var start = navigator.geolocation.getCurrentPosition(handle_geolocation_query);
var themap;
var destination = "Wednesbury, WS10 7TB";
$(document).ready(function () {
$('#map').gmap({
'center': endlocation.center,
'zoom': endlocation.zoom,
'disableDefaultUI': true,
'callback': function () {
themap = this;
$('#submit').click(function () {
themap.displayDirections({
'origin': start,
'destination': destination,
'travelMode': google.maps.DirectionsTravelMode.DRIVING,
'unitSystem': google.maps.UnitSystem.IMPERIAL
}, {
'panel': document.getElementById('directions')
},
function (response, status) {
(status === 'OK') ? $('#results').show() : $('#results').hide();
});
return false;
});
}
});
navigator.geolocation.getCurrentPosition(handle_geolocation_query);
});
function handle_geolocation_query(position) {
start = new google.maps.LatLng(lat, lon);
themap.get('map').panTo(start);
}
I cant seem to figure out what I should pass to the handle_geolocation_query to get the map
to give directions from the users location to a fixed point. Thanks in advance for any help and sorry I'm a noob to maps. Heres the html:
<div id="map" style="height:500px;"><div>
If the rest of your code works, this should do the same as the submit, but automatically when the handle_geolocation_query function is run.
function handle_geolocation_query(position) {
start = new google.maps.LatLng(lat, lon);
themap.displayDirections({
'origin': start,
'destination': destination,
'travelMode': google.maps.DirectionsTravelMode.DRIVING,
'unitSystem': google.maps.UnitSystem.IMPERIAL
}, {
'panel': document.getElementById('directions')
},
function (response, status) {
(status === 'OK') ? $('#results').show() : $('#results').hide();
});
});