Mapbox map already initialized - javascript

I have an Angular service function to build a mapbox map like so:
app.service("MapService", [function(){
//mapbox vars
var map = {
minZoom: 11,
id: "xxxxxxxx",
token: "xxxxxxxx"
};
//build map
this.buildMap = function(lat, lon, zoom){
//map bounds
var southWest = L.latLng(54.04407014753034, -0.745697021484375),
northEast = L.latLng(53.45698455620496, -2.355194091796875),
bounds = L.latLngBounds(southWest, northEast);
//build map object
L.mapbox.accessToken = map.token;
map.obj = L.mapbox.map("map", map.id, {
maxBounds: bounds,
zoomControl: false,
minZoom: map.minZoom,
attributionControl: false
}).setView([lat, lon], zoom, {
pan: { animate: true },
zoom: { animate: true }
});
}
}]);
This simply populates a div:
<div id="map"></div>
When I go to a new Angular view and call this function again (to populate a new div with id map with the map) it gives me the error:
Map container is already initialized
How do I solve this problem?

You have to destroy the map before reinitializing it. Use the following
if(map.obj != undefined) map.obj.remove();
before
map.obj = L.mapbox.map("map", map.id, {

Using a directive is much more suitable for this kind of purpose, you won't run into stuff like this. In the following directive i'm using Leaflet, but it's just the same as using Mapbox (Mapbox is an extended version of Leaflet):
angular.module('app').directive('leaflet', [
function () {
return {
restrict: 'EA',
replace: true,
template: '<div></div>',
link: function (scope, element, attributes) {
scope.$emit('leaflet-ready', new L.Map(element[0]));
}
};
}
]);
Use it in your view:
<leaflet></leaflet>
Controller:
angular.module('app').controller('map1Controller', function($scope) {
$scope.$on('leaflet-ready', function (e, leaflet) {
// leaflet var contains map instance, do stuff
})
});
Here's an example of the concept: http://plnkr.co/edit/SFgGhVUtBOqsIwYuwNuv?p=preview

Related

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"
});
});
});

How to do on page load of dynamic pages with JavaScript and Rails?

I'm trying to set up an on page load function for dynamic pages (shows) that will only display certain variables that meet the requirements. For example if someone clicks on West Coast the map will only display California, Washington, and Idaho. If they click on Great Plains the map shows Iowa, Nebraska, Kansas. And so on.
Right now I have it set up, in my region.js file:
app.regions = () => {
function init() {
startGoogleMap();
}
let startGoogleMap = () => {
let map = new google.maps.Map(document.getElementById("region-banner"), {
zoom: 7,
// The latitude and longitude to center the map (always required)
disableDefaultUI: true,
gestureHandling: "cooperative",
styles: mapStyle
});
const cali = new google.maps.Marker({
position: new google.maps.LatLng(x, y),
map: map,
icon: '/icon.jpg',
title: 'California',
region: 'West Coast'
});
return init();
};
Then in my regions I actually have a list of regions with the following in the table:
create_table "regions", force: :cascade do |t|
t.string "name"
t.string "url_name"
end
So in a nutshell I need to compare a variable against the region name on page load.
UPDATE:
This is what is currently in my show:
<div class="container-fluid">
<div class="row">
<div class="banner" id="region-banner">
</div>
</div>
</div>
<script>document.addEventListener('DOMContentLoaded', app.regions);</script>
EDIT:
I applied the following in my show > script:
let regionContainer = document.getElementById('region-banner');
regionContainer.dataset.region
Then changed my show to:
<div class="banner" id"region-banner"><%= #region.name %></div>
Nothing happened. No errors though.
One way to achieve this is to use data attributes on the container in which your map is loaded (although you could really use any element).
Assuming you have a variable #region for the current region in your show view, and assuming you're going to load the map in a div with a "map" id, you could do something like this in the markup:
<div id="map" data-region="<%= #region.name %>"></div>
And then you could retrieve that attribute in your JS file with something like this:
var mapContainer = document.getElementById('map');
mapContainer.dataset.region // Would be the result of #region.name
So the fix for future reference I did with both JS and Rails.
In JS
const markers = {
"west/coast": [{
position: [x,y],
title: 'California'
}]};
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();
markers[regionName].forEach(({ position, ...opts }) => {
const marker = new google.maps.Marker({
...opts,
position: new google.maps.LatLng(...position),
map
});
bounds.extend(marker.position);
});
Then in the show:
<div class="banner" id="region-banner" data-region-name=<%=#region.name.downcase%></div>

Hide full screen control of google map charts

Hello I am using google map charts available in google visualization chartsGoogle Map Chart. But there is no way we can hide the full screen control symbol appears in top-right position. Available options can be seen here. I have also tried to use fullscreenControl: false option given on official google map api documentation fullscreenControl but that did not work for me. Please suggest how to disable this options on mobile/ionic apps.
this.element = this.el.nativeElement;
let mapOptions = {
showTooltip: true,
tooltip: { isHtml: true },
mapType: 'satellite',
useMapTypeControl: true,
fullscreenControl: false
};
let data = [
['Lat', 'Long', 'Name'],
[37.4232, -122.0853, 'Work'],
[37.4289, -122.1697, 'University'],
[37.6153, -122.3900, 'Airport'],
[37.4422, -122.1731, 'Shopping']
];
dataTable = google.visualization.arrayToDataTable( data );
this.map = new google.visualization.Map(this.element);
this.map.draw( dataTable, mapOptions );
Thanks in advance
Below code working for me in Ionic app, You can try this:
declare var google; // declare this var above #Component({}) with your other imports
directionsDisplay = new google.maps.DirectionsRenderer;
#ViewChild('map') mapElement: ElementRef;
map: any;
this.map = new google.maps.Map(this.mapElement.nativeElement, {
zoom: 15,
center: this.maplocation, // this.maplocation={lat:'',long:''} is my Lat,Long values
fullscreenControl: false
});
this.directionsDisplay.setMap(this.map);
new google.maps.Marker({
position: this.maplocation,
map: this.map
});
In HTML :
<ion-content>
<div #map id="map"></div>
</ion-content>

Angular Maps DrawManager instance in Custom Control

I having the diffuculty to create a custom control in order to change drawMode of the map in the Angular Google Maps library.
My markup is this
<ui-gmap-google-map id="map" center="map.center" pan="map.pan" zoom="map.zoom" draggable="true" refresh="map.refresh" options="map.options" events="map.events" bounds="map.bounds" dorebuildall="true">
<ui-gmap-map-control template="js/app/templates/mapToolbar.tpl.html" position="top-right" controller="mapWidgetCtrl"></ui-gmap-map-control>
<ui-gmap-polygons models="map.polygons" clickable="true" draggable="true" editable="true" dorebuildall="true"></ui-gmap-polygons>
<ui-gmap-markers models="mapMarkers" coords="'self'" icon="'icon'" events="clickEventsObject"></ui-gmap-markers>
<ui-gmap-drawing-manager options="drawingManagerOptions" control="drawingManagerControl" events="drawEventHandler"></ui-gmap-drawing-manager>
</ui-gmap-google-map>
And my controllers are these (writing most relevant parts of code)
app.controller('mapSearchCtrl', ["$scope", "$http", function ($scope, $http) {
$scope.map = {
center: {
latitude: 40,
longitude: 20
},
zoom: 9,
bounds: {},
polygons: {},
options: {
panControl: false,
zoomControl: true,
zoomControlOptions: {
position: google.maps.ControlPosition.RIGHT_BOTTOM
},
mapTypeControl: false,
disableDefaultUI: true
}
};
$scope.drawingManagerOptions = {
drawingMode: null,
drawingControl: true,
drawingControlOptions: {
position: google.maps.ControlPosition.TOP_CENTER,
drawingModes: [
google.maps.drawing.OverlayType.POLYGON,
]
},
polygonOptions: {
strokeWeight: 3,
editable: true
}
};
$scope.drawingManagerControl = {};
$scope.options = {};
$scope.mapMarkers = [];
$scope.clickEventsObject = {
mouseover: markerMouseOver,
mouseout: markerMouseOut
};
$scope.drawEventHandler = {
polygoncomplete: polygonComplete
};
function polygonComplete(drawingManager, eventName, scope, args) {
//$scope.drawingManagerControl.getDrawingManager().setDrawingMode(null); works here
//code...
});
[etc]..
And the other controller for the template with custom controls is:
app.controller('mapWidgetCtrl', ['$scope', function ($scope) {
$scope.toogleMap = function() {
console.log('Change map view via drawingManager');
};
}]);
I am having a difficutly into changing map view manually in both controllers (can't pass it somehow from one to another either). It seems I can't find a way to get the instance of drawManager outside of anything except polygonComplete function.
Any advice would be helpful, thank you.
You could introduce a service to share Drawing Manager Control across controllers:
app.service('sharedMapProperties', function () {
var drawingManagerControl = {};
return {
setDrawingManagerControl: function (value) {
drawingManagerControl = value;
},
getDrawingManagerControl: function () {
return drawingManagerControl;
}
}
});
Now you could save the control (drawingManagerControl variable) once the map is initialized:
app.controller('mapSearchCtrl', function ($scope, uiGmapIsReady, sharedMapProperties) {
//the remaining code is omitted..
$scope.drawingManagerControl = {};
sharedMapProperties.setDrawingManagerControl($scope.drawingManagerControl);
});
and then get control once the button clicked:
app.controller('mapWidgetCtrl', ['$scope', 'sharedMapProperties', function ($scope, sharedMapProperties) {
$scope.toogleMap = function () {
var control = sharedMapProperties.getDrawingManagerControl();
var drawingManager = control.getDrawingManager();
drawingManager.setDrawingMode(google.maps.drawing.OverlayType.CIRCLE);
console.log('Change map view via drawingManager');
};
}]);
Working example

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*);

Categories