Cartodb show route on click - javascript

I have created a map with cartodb which shows the train connection between cities. For this I have created two databases: The first one (cities) contains the information about the cities, route, order. Eg city: hamburg route: 1 order:1, city:lübeck route:1 order:2. In the second database (verbindungen) I have made a line between cities with the same route number. In my map I have added each database as a layer.
When I load the map only the cities are shown. Now I want to be able to click on one city and only the connection from/to this city are supposed to be shown.
This is my code so far:
var sql = new cartodb.SQL({ user: 'docu', format: 'geojson' });
function showFeature(route) {
sql.execute("select*from verbindungen where route = {{route}}", {route: route} ).done(function(geojson) {
if (route) {
map.removeLayer(route);
}
route = L.geoJson(geojson, {
style: {
color: "#000",
opacity: 0.65
}
}).addTo(map);
});
}
cartodb.createLayer(map, layerUrl)
.addTo(map)
.on('done', function(layer) {
layer.setInteraction(true);
var sublayerVerbindungen = layer.getSubLayer(1);
sublayerVerbindungen.hide()
var sublayerCities = layer.getSubLayer(0);
sublayerCities.show();
sublayerTerminals.on('featureClick', function(e, latlng, pos, data) {
showFeature(data.route)
});
})
.on('error', function() {
//log the error
});
I have tried to use this example: http://bl.ocks.org/javisantana/d20063afd2c96a733002

I have solved my problem:
cartodb.createLayer(map, layerUrl,
{https: true,
legends: false,
cartodb_logo:false,
layerIndex: 1
})
.addTo(map)
.on('done', function(layer) {
var sublayerVerbindungen = layer.getSubLayer(1);
sublayerVerbindungen.hide();
sublayerVerbindungen.setInteractivity('cartodb_id, a_route, the_geom, informationen_zur_route');
sublayerVerbindungen.setInteraction(true);
var sublayerCities = layer.getSubLayer(0);
sublayerCities.show();
sublayerCities.setInteractivity('cartodb_id, a_route, the_geom');
sublayerCities.setInteraction(true);
sublayerCities.on('featureClick', function(e, latlng, pos, data) {
console.log(data.a_route);
var newSql = "SELECT * FROM verbindungen WHERE a_route=" + data.a_route;
console.log(newSql);
sublayerVerbindungen.setSQL(newSql);
sublayerVerbindungen.show();
});
})
.on('error', function() {
//log the error
});

Related

center map if id #clicked in the looped

I have a two components. first one is for the articles content that I am looping. And second one is google map for the articles addresses... They are connected in store.js. No problem with that. And I am trying to center the map to if any articles's id clicked in component one. But having some problems about how to implement clicked event to google map component's listener?
What I thought, using event bus for the situation. And send which "id" clicked.
UPDATED
articles component
<table>
<tbody>
<tr :key="item.id" v-for="item in articles" #clicked="changeCenter(item.id)">
<th>
<img :src="item.image" alt="">
</th>
<td>
<p>{{item.name}}</p>
<p>{{item.address.substr(0,40)}}...</p>
</td>
<td>
<router-link :to="{ name: 'detail', params: {id:item.id} }"><i class="fas fa-chevron-right"></i></router-link>
</td>
</tr>
</tbody>
</table>
And add the method.
methods: {
changeCenter(id){
eventBus.$emit("centerMap", id);
},
},
and on the other side google map component is below.
in created() hook I am catching the id that clicked. But I can't access the value inside the buildMarkers() function. I mean I can console.log(this.centerid) in created() hook there is no problem. I can see the data that I am passing. But when the time come to put the this.centerid in the buildMarkers() or any other methods, centerid is start to being undefined
created(){
eventBus.$on("centerMap", (id) => {
this.centerid = id;
})
},
and rest is a a bit mistery to me. google map code is right below.
data(){
return {
markers:[],
infoWindows: [],
centerid: "",
};
},
mounted(){
this.initMap();
this.clearMarkers();
this.buildMarkers();
},
created(){
eventBus.$on("centerMap", (id) => {
this.centerid = id;
})
},
watch: {
articles(){
this.clearMarkers();
this.buildMarkers();
}
},
methods: {
initMap(){
const japan = {
lat: 35.652832,
lng: 139.839478
}
this.map = new google.maps.Map(document.getElementById("map"), {
zoom: 7,
// maxZoom: 10,
center: {lat: japan.lat, lng: japan.lng}
})
},
buildMarkers(){
var map = this.map;
this.markers = [];
for(var i = 0; i < this.articles.length; i++){
// some codes...
// some codes...
var marker = new MarkerWithLabel({
icon: {
path: google.maps.SymbolPath.CIRCLE,
scale: 0
},
position: position,
map: this.map,
labelClass: "markerClass",
labelAnchor: new google.maps.Point(30, 30),
labelContent: label,
});
this.markers.push(marker);
if (this.articles[i].id === this.centerid) {
google.maps.event.addListener(marker, function () {
infowindow.open(map, this);
map.setCenter(this.getPosition());
map.setZoom(10);
});
}
}
},
clearMarkers(){
for( var i = 0; i < this.markers.length; i++ ){
this.markers[i].setMap( null );
}
},
},
The problem is at this part. I am not sure how to handle this. Like I said before. I can't access the centeridto 's value.
if (this.articles[i].id === this.centerid) {
google.maps.event.addListener(marker, function () {
infowindow.open(map, this);
map.setCenter(this.getPosition());
map.setZoom(10);
});
}

VueJS: Google Maps loads before data is ready - how to make it wait? (Nuxt)

This is my first VueJS project and I've got vue2-google-maps up and running but I've come across an issue when I attempt to connect the map markers to my site's JSON feed (using the Wordpress REST API), the Lat and Lng values are returning undefined or NaN.
On further investigation (thanks to #QuỳnhNguyễn below) it seems like the Google Maps instance is being run before the data is ready. I have tried watching for the feed to be loaded before initialising the map, but it doesn't seem to work.
The marker locations are pulled in from the WordPress REST API using JSON and exist in an array (locations). The array is present and populated in Vue Dev Tools (51 records), but when checking on mounted, the array is empty. The data is pulled in at the created stage, so I don't know why it wouldn't be ready by the mounted stage.
The code in question is as below...
Template:
<template>
<gmap-map v-if="feedLoaded" ref="map" :center="center" :zoom="zoom" :map-type-id="mapTypeId" :options="options">
<gmap-marker
:key="index" v-for="(m, index) in locations"
:position="{ lat: parseFloat(m.place_latitude), lng: parseFloat(m.place_longitude) }"
#click="toggleInfoWindow(m,index)"
:icon="mapIconDestination">
</gmap-marker>
<gmap-info-window></gmap-info-window>
</gmap-map>
</template>
Script
<script>
const axios = require('axios');
const feedURL = "API_REF";
export default {
props: {
centerRef: {
type: Object,
default: function() {
return { lat: -20.646378400026226, lng: 116.80669825605469 }
}
},
zoomVal: {
type: Number,
default: function() {
return 11
}
}
},
data: function() {
return {
feedLoaded: false,
zoom: this.zoomVal,
center: this.centerRef,
options: {
mapTypeControl: false,
streetViewControl: false,
},
mapTypeId: 'styledMapType',
mapIconDestination: '/images/map-pin_destination.png',
mapIconActivity: '/images/map-pin_activity.png',
mapIconAccommodation: '/images/map-pin_accommodation.png',
mapIconEvent: '/images/map-pin_event.png',
mapIconBusiness: '/images/map-pin_business.png',
locations: [],
markers: []
}
},
created: function() {
this.getData();
},
mounted: function() {
this.$nextTick(() => {
this.$refs.karrathaMap.$mapPromise.then((map) => {
var styledMapType = new google.maps.StyledMapType(
[...MAP_STYLE SETTINGS...]
)
map.mapTypes.set('styled_map', styledMapType);
map.setMapTypeId('styled_map');
})
});
},
watch: {
feedLoaded: function() {
if (this.feedLoaded == true) {
console.log(JSON.stringify(this.locations))
}
}
},
methods: {
getData() {
const url = feedURL;
axios
.get(url)
.then((response) => {this.locations = response.data;})
.then(this.feedLoaded = true)
.catch( error => { console.log(error); }
);
}
}
}
</script>
It turns out the issue was dirty data.
The JSON response was including locations that were not supposed to be included on the map, so it failed every time it came across an entry that didn't include the ACF fields, despite me setting the feed to only return data where include on map was true.
I have solved the issue by handling the data once the feed is loaded and creating a new array (markers) from it using the valid data, then using this, rather than the original (locations) array to place the markers on the map.
Cleaning the data:
watch: {
feedLoaded: function() {
if (this.feedLoaded == true) {
var LocationList = this.locations;
for (var i = 0; i < LocationList.length; i++) {
var includeOnMap = LocationList[i].acf['place_include-on-map'];
if (includeOnMap === true) {
var placeName = LocationList[i].title.rendered;
var placeDescription = LocationList[i].acf['place_short-description'];
var placeLatitude = LocationList[i].acf['place_latitude'];
var placeLongitude = LocationList[i].acf['place_longitude'];
var placeIcon = this.mapIconDestination;
this.markers.push({ name: placeName, lat: placeLatitude, lng: placeLongitude, icon: placeIcon });
}
}
}
}
}
Then, the gmap component:
<gmap-map ref="karrathaMap" :center="center" :zoom="zoom" :map-type-id="mapTypeId" :options="options">
<gmap-marker v-if="feedLoaded == true" :key="index" v-for="(m, index) in markers" :position="{ lat: parseFloat(m.lat), lng: parseFloat(m.lng) }" #click="toggleInfoWindow(m,index)" :icon="m.icon"></gmap-marker>
<gmap-info-window></gmap-info-window>
</gmap-map>
Thank you everybody who contributed to helping me get to the bottom of the issue. I will now spend some time rethinking how the data is structured.
It appears to be related with data format. According to vue-devtools from provided screenshot your data is returned from WordPress REST API in the following format:
[
{
"acf": {
"place_latitude": "-22.695754",
"place_longitude": "118.269081",
"place_short-description": "Karijini National Park"
},
"id": 12,
"parent": 10,
"title": {
"rendered": "Karijini National Park"
}
},
...
]
Having how locations array is getting initialized (getData method), position property could be passed like this:
<gmap-marker
:key="index"
v-for="(m, index) in locations"
:position="{ lat: parseFloat(m.acf.place_latitude), lng: parseFloat(m.acf.place_longitude) }"
></gmap-marker>
Here is a demo
vuejs supports v-if directive on elements. I recommend you try as following code snippet.
<template>
<div id="map" v-if="loaded">
<gmap-map ref="map" :center="center" :zoom="zoom" :map-type-id="mapTypeId" :options="options">
<gmap-marker
:key="index" v-for="(m, index) in locations"
:position="{ lat: parseFloat(m.place_latitude), lng: parseFloat(m.place_longitude) }"
#click="toggleInfoWindow(m,index)"
:icon="mapIconDestination">
</gmap-marker>
<gmap-info-window></gmap-info-window>
</gmap-map>
</div>
</template>
<script>
export default {
data() {
return {
loaded: false
}
},
beforeMount: function () {
const url = feedURL;
axios
.get(url)
.then((response) => {
this.locations = response.data;
//activate element after api call response recieved
this.loaded = true
})
.catch(error => {
console.log(error);
}
);
}
}
</script>

How to apply JSON responseText to Google Maps in Ruby on Rails?

So in my Rails app I am building out a region show page with multiple locations. Users are able to insert new locations and map markers need to be dynamically placed based on the latitude and longitude that they enter. In my Region show page I have the following:
<div class="container-fluid">
<div class="row">
<div class="banner" id="region-banner" data-region-name="<%=#region.name.downcase%>">
<script>document.addEventListener('DOMContentLoaded',app.regions);</script>
Then in my region.js file I have the following:
import { tns } from 'tiny-slider/src/tiny-slider.module';
import { mapStyle } from './styles/mapStyle';
app.regions = () => {
function init() {
startGoogleMap();
}
let startGoogleMap = () => {
let map = new google.maps.Map(document.getElementById('region-banner'), {
zoom: 3,
minZoom: 3,
disableDefaultUI: true,
gestureHandling: 'cooperative',
styles: mapStyle
});
var mapElement = document.getElementById('region-banner');
const regionName = mapElement.getAttribute('data-region-name');
let bounds = new google.maps.LatLngBounds();
let promise = $.getJSON('/locations.json');
promise.done(function(data) {
return $.each(data, function() {
return new google.maps.Marker({
position: {
lat: parseFloat(data.lat),
lng: parseFloat(data.lng) },
map: map,
icon: "/marker.png"
});
});
});
console.log(promise);
map.fitBounds(bounds);
};
return init();
};
Then in my controller I have:
def show
#locations = Location.all
respond_to do |format|
format.json { render json: #locations }
format.html
end
end
So no real errors are applying however...nothing appears. The console shows the responseText:“[{“id”: 5, “name”: “Chicago”, “abbreviation”: “CHI”, “lat”: “44.222”, “lng”: “-22.111”}, {“id”: 6, “name”: “Frankfort”, “abbreviation”: “FKT”, “lat”: “41.3232”, “lng”: “-19.5221”} ]”. Which really on this page I should only need/use the first.
Shouldn't it also be applying the markers at this stage since I put in the position?
this is the code responsible for setting your Markers.
I would focus on understanding why promise.done() does not run create the new google.maps.Marker() for each row in your (data) from your response.
Maybe the problem is connected to the /marker.png
promise.done(function(data) {
return $.each(data, function() {
return new google.maps.Marker({
position: {
lat: parseFloat(data.lat),
lng: parseFloat(data.lng) },
map: map,
icon: "/marker.png"
});
});
});

When clicking cluster, how to show Infowindow which has markers's values?

I have been trying to use google map with angularJS.
I have learned how to use it through https://angular-ui.github.io/angular-google-maps/#!/.
Everything goes well.
For each marker, I can show InfoWindow which has an element information of myList.
But I have got stuck in InfoWindow with cluster.
When cluster is clicked, I want to show the information list of markers in cluster.
Even I can't show simple InforWindow when clicking the cluster.
Below sources are my code.
Please tell me if it is not enough to solve my problem.
Please tell me what is wrong and how to solve this.
Have a nice day.
* javascript
$scope.map.map = {
center: { latitude: $scope.map.myList[0].lat, longitude: $scope.map.myList[0].lng },
zoom: 17,
events : {
tilesloaded: function (map) {
$scope.$apply(function () {
google.maps.event.addDomListener(window, 'resize', function() {
var lat = $scope.map.myList[$scope.map.markerPosition-1].lat;
var lng = $scope.map.myList[$scope.map.markerPosition-1].lng;
var center = new google.maps.LatLng(lat, lng);
map.setCenter(center);
});
});
}
},
markersEvents: {
click: function(marker, eventName, model) {
model.show = !model.show;
return;
}
},
clusterOptions : { // options of cluster
gridSize: 40,
ignoreHidden: true,
zoomOnClick : false,
averageCenter : true,
styles: [
{
height: 53,
url: "http://google-maps-utility-library-v3.googlecode.com/svn/trunk/markerclusterer/images/m3.png",
width: 53,
textColor : 'white'
}
]
},
clusterEvent: { // when cluster's clicked
click: function(map, markers) {
var contentString = 'ABCD';
var infowindow = new google.maps.InfoWindow({
content: contentString
});
infowindow.open(map, markers);
return;
}
}
};
$scope.map.options = {
streetViewControl : false
};
$scope.map.markers = [];
* html
<ui-gmap-google-map center='map.map.center' zoom="map.map.zoom" options="map.options" events="map.map.events">
<ui-gmap-markers models="map.markers" coords="'self'" icon="a" events="map.map.markersEvents" options="'options'"
doCluster="true" clusterOptions="map.map.clusterOptions" clusterEvents="map.map.clusterEvent">
<ui-gmap-windows show="show">
<div ng-non-bindable>{{id}}</div>
</ui-gmap-windows>
</ui-gmap-markers>
Answer myself.
var infoWindowOptions = {
content: "asdfasdf"
};
var infowindow = new google.maps.InfoWindow(infoWindowOptions);
infowindow.open(map.map_, *marker*);

Gmap3 Clear Directions

I need to recalculate the directions, when another marker is clicked or when my origin marker is dragged to another location.
at the moment, I am inserting a marker when a user inserts his/her address then when the users clicks on any existing marker it calculates the directions. Unfortunately it doesn't clear the previous directions.
Any Help at all will be greatly appreciated.
Here's the code:
jQuery(document).ready(function() {
jQuery.getJSON('./index.php', {
option: "com_locate",
view: "branches",
tmpl: "component",
format: "json",
},
function(json){
jQuery(function(){
jQuery("#googleMap").gmap3({
map:{
options: {
center:[-29.8191,25.3499],
zoom: 5,
mapTypeId: google.maps.MapTypeId.ROADMAP,
mapTypeControl: false,
streetViewControl: false
}
},
marker: {
values: json,
options: {
icon: new google.maps.MarkerImage("http://maps.gstatic.com/mapfiles/icon_green.png")
},
events:{
mouseover: function(marker, event, context){
jQuery(this).gmap3(
{clear:"overlay"},
{
overlay:{
id: "tooltip",
latLng: marker.getPosition(),
options:{
content: "<div class='infobulle"+(context.data.drive ? " drive" : "")+"'>" +
"<div class='bg'></div>" +
"<div class='text'>" + context.data.city + " (" + context.data.telephone + ")</div>" +
"</div>" +
"<div class='arrow'></div>",
offset: {
x:-46,
y:-73
}
}
}
});
},
mouseout: function(){
jQuery(this).gmap3({clear:"overlay"});
},
click: function(marker, event, context){
markerSelected(context.id);
}
}
}
});
///////////////
jQuery('#test-ok').click(function(){
var addr = jQuery('#test-address').val();
if ( !addr || !addr.length ) return;
jQuery("#googleMap").gmap3({
getlatlng:{
address: addr,
callback: function(results){
if ( !results ) return;
jQuery("#googleMap").gmap3({
clear:{id:"user"}
},
{
marker:{
latLng:results[0].geometry.location,
id:"user",
name:"user",
options:{
draggable: true
}
},
map:{
center: true,
zoom: 5
}
});
}
}
});
});
jQuery('#test-address').keypress(function(e){
if (e.keyCode == 13){
jQuery('#test-ok').click();
}
});
///////////////
///////////////
function markerSelected(id){
var marker = jQuery('#googleMap').gmap3({get:id});
var usermarker = jQuery('#googleMap').gmap3({get:"user"});
jQuery("#googleMap").gmap3({
getroute:{
options:{
origin:[usermarker.getPosition().lat(),usermarker.getPosition().lng()],
destination:[marker.getPosition().lat(),marker.getPosition().lng()],
travelMode: google.maps.DirectionsTravelMode.DRIVING
},
callback: function(results){
if (!results) return;
jQuery(this).gmap3({
map:{
options:{
}
},
directionsrenderer:{
container: jQuery(document.createElement("div")).addClass("googlemap").insertAfter(jQuery("#googleMap")),
options:{
directions:results
}
}
});
}
}
});
}
});
});
});
The code you're using creates a new DOM element each time you do a directions request, without removing any existing such elements or replacing content in any existing elements. The pertinent part of your code is this:
directionsrenderer:{
container: jQuery(document.createElement("div")).addClass("googlemap").insertAfter(jQuery("#googleMap")),
// The above creates a new DOM element every time markerSelected() is called!
options:{
directions:results
}
}
You want to create that only once. If you want, you can do it directly in the HTML markup.
Use the below as a replacement for your getroute callback function. I've plugged in a unique ID for the container element and left the "googlemap" class intact in case it's needed for CSS or other sections of code. Since you specifically want only one set of directions to be visible, though, let's select your container by ID.
callback: function(results){
if (!results) return;
if (!jQuery("#dircontainer").length>0) {
jQuery("<div id='dircontainer' class='googlemap'></div>").insertAfter("#googleMap");
} // Creates your directions container if it doesn't already exist.
else {
jQuery("#dircontainer").html("");
} /* I'm clearing the existing contents of the container in case gmap3 doesn't
automatically clear it when the new directions are inserted.
You can experiment with removing this else statement. */
jQuery(this).gmap3({
map:{
options:{
}
},
directionsrenderer:{
container: jQuery("#dircontainer"),
options:{
directions:results
}
}
});
}
I'm making some assumptions here about the way the gmap3 plugin works; I've worked with jQuery and the Google Maps JS API, but not with this plugin.

Categories