Add an ImageLayer with ionic-leafletjs - javascript

I want to add an ImageLayer with the Ionic-leafletjs from calendee. I have the map obj. in the angular $scope
Markers, center and so on are working.
This is not working:
$scope.map = {
layers: {
imageOverlay: {
1: {
imageUrl: 'http://www.lib.utexas.edu/maps/historical/newark_nj_1922.jpg',
imageBounds: [[47.062319, 7.614106], [47.062351, 7.614432]]
}
}
}
This is the ionic-leafletjs-map-demo which I used as ground.

Related

Error when trying to set zoom using SceneView

Using esri-loader, when trying to create a SceneView using the following the view is not calculating the zoom correctly and I'm getting the following errors in the console:
[esri.views.3d.support.cameraUtils] #zoomToScale() Cannot compute scale from zoom without a tiling scheme
client-hook-3.js:1 [esri.views.3d.state.ViewStateManager] #zoom= Invalid zoom 3
let map = new WebMap({
portalItem: {
id: MAP_ID_HERE
}
});
let view = new SceneView({
container: "map",
viewingMode: "local",
map: map,
zoom: 3
});
Does anyone happen to know what is causing this? Looking through the documentation for SceneView it seems this should be valid in the constructor.
I think in this particular case, using a web map as the map, you have to wait for the view to load in order to set the zoom level. If not it will not be able to calculate the scale, that is the cause of the error.
This should work,
let view = new SceneView({
container: "map",
map: map,
viewingMode: "local"
});
view.when(function() {
view.zoom = 3;
});
UPDATE: (leave the other code because I think it clarify the problem and the final answer)
Well it seems that is not enough to wait for the view, because the basemap could not load everything. So here you have an alternative that works,
const basemap = Basemap.fromId("dark-gray-vector");
const sceneView = new SceneView({
container: this.$el,
map: new WebMap({
basemap,
}),
center: [US_CENTER.longtitude, US_CENTER.latitude],
viewingMode: "local"
});
basemap.loadAll().then(
() => {
sceneView.goTo({ zoom: 3 });
}
);
In this new solution we actually wait till the basemap loads everything (using loadAll method) an then we set the zoom of the view.
This is the full code in your Map.vue,
<template>
<div />
</template>
<script>
import { loadArcGISModules } from "#deck.gl/arcgis";
const US_CENTER = { longtitude: -98.5795, latitude: 39.8283 };
export default {
name: "Map",
props: {},
mounted() {
loadArcGISModules(
[
"esri/WebMap",
"esri/views/SceneView",
"esri/Basemap",
],
{ css: true }
).then(({ DeckRenderer, modules }) => {
const [WebMap, SceneView, Basemap] = modules;
const basemap = Basemap.fromId("dark-gray-vector");
const sceneView = new SceneView({
container: this.$el,
map: new WebMap({
basemap,
}),
center: [US_CENTER.longtitude, US_CENTER.latitude],
viewingMode: "local"
});
basemap.loadAll().then(
() => {
sceneView.goTo({ zoom: 3 });
}
);
});
},
};
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
div {
width: 100%;
height: 100%;
}
</style>

Using google map in Vue / Laravel

Trying to implement google map into Vue component. But having a hard time. Actually, there is no error. But no map also :) Okay, what I tried so far down below.
In laravel blade I set my api.
<script async defer src="https://maps.googleapis.com/maps/api/js?key={{env('GOOGLE_MAPS_API')}}&callback=initMap"></script>
Then in Vue component;
data() {
return {
mapName: "map",
//some other codes
}
},
mounted() {
this.fetchEstates();
},
methods: {
fetchEstates(page = 1) {
axios.get('/ajax', {
params: {
page
}}).then((response) => {
// console.log(response);
this.estates = response.data.data;
//some other codes....
//some other codes....
},
computed: {
//some other functions in computed...
//
initMap: function(){
var options =
{
zoom : 6,
center : {
lat:34.652500,
lng:135.506302
}
};
var map = new google.maps.Map(document.getElementById(this.mapName), options);
var marker = new google.maps.Marker({
map: map,
icon: 'imgs/marker.png',
url: "/pages/estates.id",
label: {
text: this.estates.price,
color: "#fff",
},
position: {
lat: this.estates.lat,
lng: this.estates.lng
}
});
google.maps.event.addListener(marker, 'click', function () {
window.location.href = this.url;
});
}
<div id="map"></div>
and last marker url id bind is in controller like this,
public function details($id)
{
$estates = allestates::where('id', $id)->first();
return view('pages.details', compact('estates'));
}
Do I missing something in Vue js? Thank you!
From our discussion in the comments, I realise that your issue is because this.estates is still not defined when initMap() is executed. Remember that you are using an asynchronous operation (via axios) to populate this.estates, so it is undefined at runtime. What you can do is:
Keep the map initialisation logic in initMap()
Move all the Google Map marker creation until after the axios promise has been resolved. You can abstract all that into another method, e.g. insertMarkers()
Also, remember that you need to define estates in the app/component data, otherwise it will not be reactive.
Here is an example:
data() {
return {
mapName: "map",
// Create the estate object first, otherwise it will not be reactive
estates: {}
}
},
mounted() {
this.fetchEstates();
this.initMap();
},
methods: {
fetchEstates: function(page = 1) {
axios.get('/ajax', {
params: {
page
}}).then((response) => {
this.estates = response.data.data;
// Once estates have been populated, we can insert markers
this.insertMarkers();
//pagination and stuff...
});
},
// Iniitialize map without creating markers
initMap: function(){
var mapOptions =
{
zoom : 6,
center : {
lat:34.652500,
lng:135.506302
}
};
var map = new google.maps.Map(document.getElementById(this.mapName), mapOptions);
},
// Helper method to insert markers
insertMarkers: function() {
var marker = new google.maps.Marker({
map: map,
icon: 'imgs/marker.png',
url: "/pages/estates.id",
label: {
text: this.estates.price,
color: "#fff",
},
position: {
lat: this.estates.lat,
lng: this.estates.lng
}
});
google.maps.event.addListener(marker, 'click', function () {
window.location.href = this.url;
});
}
},
Update: It also turns out that you have not addressed the issue of the data structure of this.estates. It appears that you are receiving an array from your endpoint instead of objects, so this.estates will return an array, and of course this.estates.lat will be undefined.
If you want to iterate through the entire array, you will have to use this.estates.forEach() to go through each individual estates while adding the marker, i.e.:
data() {
return {
mapName: "map",
// Create the estate object first, otherwise it will not be reactive
estates: {}
}
},
mounted() {
this.fetchEstates();
this.initMap();
},
methods: {
fetchEstates: function(page = 1) {
axios.get('/ajax', {
params: {
page
}}).then((response) => {
this.estates = response.data.data;
// Once estates have been populated, we can insert markers
this.insertMarkers();
//pagination and stuff...
});
},
// Iniitialize map without creating markers
initMap: function(){
var mapOptions =
{
zoom : 6,
center : {
lat:34.652500,
lng:135.506302
}
};
var map = new google.maps.Map(document.getElementById(this.mapName), mapOptions);
},
// Helper method to insert markers
insertMarkers: function() {
// Iterate through each individual estate
// Each estate will create a new marker
this.estates.forEach(estate => {
var marker = new google.maps.Marker({
map: map,
icon: 'imgs/marker.png',
url: "/pages/estates.id",
label: {
text: estate.price,
color: "#fff",
},
position: {
lat: estate.lat,
lng: estate.lng
}
});
google.maps.event.addListener(marker, 'click', function () {
window.location.href = this.url;
});
});
}
},
From what I can see in the screenshot you posted, this.estates is an array of objects? If that's the case you need to iterate through the array using forEach
this.estates.forEach((estate, index) => {
console.log(estate.lat);
//handle each estate object here
});
or use the first item in the array like so this.estates[0].lat, if you're only interested in the first item.

Bing Maps infobox is undefined inside Angular component

I am creating a component in angular/typescript for a bing maps implementation. I went through the process to add an infobox to the map that would initially be not visible to the user. When the user clicks on any of the pushpins on the map the infobox is supposed to display.
However it does not and the property is shown as undefined.
Note: 'DataPoints' contains a list of objects that contains lat long coordinates and an arbitrary ID number.
import { Component, AfterViewInit } from '#angular/core';
import { DataPoint } from '../common/data-point'
import { DataPoints } from '../common/data-points'
#Component({
selector: 'app-simple-bing-map',
templateUrl: './simple-bing-map.component.html',
styleUrls: ['./simple-bing-map.component.css'],
providers: []
})
export class SimpleBingMapComponent implements AfterViewInit {
private map: any;
private infobox: any;
ngAfterViewInit() {
this.getMap();
}
populateMap(){
for(var i in DataPoints){
var pushpin = new Microsoft.Maps.Pushpin(new Microsoft.Maps.Location(DataPoints[i].Lat, DataPoints[i].Long) , null);
pushpin.metadata = {
title: "Test Pushpin",
description: DataPoints[i].ID,
};
//Add a click event handler to the pushpin.
Microsoft.Maps.Events.addHandler(pushpin, 'click', this.displayInfobox);
//place pushpin
this.map.entities.push(pushpin);
}
}
getMap() {
//check if Microsoft is available
if ((window as any).Microsoft && (window as any).Microsoft.Maps) {
//if it is available create map instance
this.map = new Microsoft.Maps.Map(document.getElementById('mapId'), {
credentials: 'Your Bing Maps Key Here',
});
//initialize infobox
this.infobox = new Microsoft.Maps.Infobox(this.map.getCenter(), {
title: 'Pushpins',
description: 'ID Number'
}
);
//hide infobox
this.infobox.setOptions({ visible: false })
//Assign the infobox to a map instance.
this.infobox.setMap(this.map);
this.populateMap();
}
//wait and try again
else {
setTimeout(() => { this.getMap() }, 1000);
}
}
displayInfobox(e) {
//hide any previous infobox
this.infobox.setOptions({ visible: false });
//Make sure the infobox has metadata to display.
if (e.target.metadata) {
//Set the infobox options with the metadata of the pushpin.
this.infobox.setOptions({
location: e.target.getLocation(),
title: e.target.metadata.title,
description: e.target.metadata.description,
visible: true
});
}
}
}
As stated earlier the map loads completely and works as it should. It is just after I enter the 'displayInfobox' method that things act weirdly.
To retain this inside displayInfobox method i would advice you using either bind method like:
Microsoft.Maps.Events.addHandler(pushpin, 'click', this.displayInfobox.bind(this));
or arrow function:
Microsoft.Maps.Events.addHandler(pushpin, 'click', (e) => this.displayInfobox(e));
For other solutions see
https://github.com/Microsoft/TypeScript/wiki/%27this%27-in-TypeScript

How to open map drawn from a leaflet class as a pop up window or in a new tab?

I have the following lines of code that generates a map, as seen it makes use of leaflet class to render it. Which works just fine, except that I additionally require the map to open as a new pop up window, or in a new tab on clicking anywhere on the map.
Code:-
<script src="https://unpkg.com/leaflet#1.0.1/dist/leaflet.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet.draw/0.4.0/leaflet.draw.js"></script>
/* miscellaneous stuff here */
<div class="col-sm-6 col-sm-offset-4">
<leaflet class="showMap" defaults="mappingConfig.defaults" center="mappingConfig.cityCenter" markers="markers" controls="controls"></leaflet>
</div>
How should i go about achieving the same? I've not come across any relevant code examples online, which were helpful to this particular scenario
If, in the template of the modal you have a map with the same id of the map in the main view, and put in a services the map object (to share it between the controllers), you can have same objects in modal and in the view.
angular.module('mymap.services', []).factory('MapServices', function () {
var map ={
center : {
lat: 49,
lng: 34,
zoom: 8
}, defaults : {
zoomControl: false,
attributionControl: true
},
baselayers : {
xyz: {....},
markers:[....]
};
return {
getMap: function () {
return map;
},
}});
Then you can use somethings like:
$scope.$on('leafletDirectiveMarker.map.click', function (event, args) {
$scope.map.center.lat = args.model.lat;
$scope.map.center.lng = args.model.lng;
$scope.valueModal = {};
$scope.valueModal.properties = args.model.properties.properties;
//show modal
$scope.modalPopup.show();
});
Or instead to use markers into the angular-leaflet directive you can create a layer:
leafletData.getMap("map").then(function (map) {
map.invalidateSize();
//resp is the geojson
var geojson = new L.GeoJSON(resp, {
onEachFeature: function (feature, layer) {
layer.on('click', function (e) {
$scope.map.center.lat = feature.geometry.coordinates[1];
$scope.map.center.lng = feature.geometry.coordinates[0];
$scope.feature = feature;
//open a modal
$scope.openLayersModal();
});
}
});
markers.addLayer(geojson);
map.addLayer(markers);
}, function (err) {
console.log('ERROR', err.status);
});
});

Angular-google-maps: How to show Title and Description dynamically on markers

I am using Angular-google-maps, HTML code follows
<ui-gmap-google-map center='mapData.map.center' zoom='mapData.map.zoom'
events="mapEvents">
<ui-gmap-markers models="mapData.map.markers" coords="'self'">
</ui-gmap-markers>
</ui-gmap-google-map>
in JS calling
angular.extend(this, $controller('MapsMixinController',
{$scope:$scope, map:mapData.data[0].map}));
MapsMixinController as follows. Calling this controller from js code. Markers are showing & on click able to mark.
MapsMixinController.js
/**
* Controller providing common behaviour for the other map controllers
*/
angular
.module('app')
.controller('MapsMixinController', ['$scope', 'GeolocationService', 'uiGmapGoogleMapApi', 'map',
function($scope, GeolocationService, GoogleMapApi, map) {
var _this = this;
$scope.mapEvents = {
click: function(mapModel, eventName, originalEventArgs) {
var e = originalEventArgs[0];
if (e.latLng) {
$scope.mapData.map.markers.push({
id: new Date().getTime(),
latitude: e.latLng.lat(),
longitude: e.latLng.lng()
});
// This event is outside angular boundary, hence we need to call $apply here
$scope.$apply();
}
}
};
// Returns a default map based on the position sent as parameter
this.getDefaultMap = function(position) {
return {
markers: [],
center: {
latitude: position.coords.latitude,
longitude: position.coords.longitude
},
zoom: 14
};
};
// Initialize the google maps api and configure the map
GoogleMapApi.then(function() {
GeolocationService().then(function(position) {
$scope.mapData.map = map || _this.getDefaultMap(position);
}, function() {
$scope.error = "Unable to set map data"; // TODO use translate
});
});
}
]);
How can I show title on mouse hover on markers? And on click how to show description on markers?
You can add title property alone with latitude and longtitude property while creating marker data.
/**
* Controller providing common behaviour for the other map controllers
*/
angular
.module('app')
.controller('MapsMixinController', ['$scope', 'GeolocationService', 'uiGmapGoogleMapApi', 'map',
function($scope, GeolocationService, GoogleMapApi, map) {
var _this = this;
$scope.mapEvents = {
click: function(mapModel, eventName, originalEventArgs) {
var e = originalEventArgs[0];
if (e.latLng) {
$scope.mapData.map.markers.push({
id: new Date().getTime(),
latitude: e.latLng.lat(),
longitude: e.latLng.lng(),
title: "Mouse over text"
});
// This event is outside angular boundary, hence we need to call $apply here
$scope.$apply();
}
}
};
// Returns a default map based on the position sent as parameter
this.getDefaultMap = function(position) {
return {
markers: [],
center: {
latitude: position.coords.latitude,
longitude: position.coords.longitude
},
zoom: 14
};
};
// Initialize the google maps api and configure the map
GoogleMapApi.then(function() {
GeolocationService().then(function(position) {
$scope.mapData.map = map || _this.getDefaultMap(position);
}, function() {
$scope.error = "Unable to set map data"; // TODO use translate
});
});
}
]);

Categories

Resources