I've searched high and low for proper documentation on how to configure Azure Maps with Angular and haven't found anything. How do I do this?
(Please look to the comments for my self-answered question)
As documentation for configuring Azure Maps with Angular does not exist, this post will accomplish that instead. By the end of this post, you should have a working Angular version of Azure Maps with map markers. Before adding any code, please follow the steps from the Microsoft website to set up your Azure Map keys: https://learn.microsoft.com/en-us/azure/azure-maps/
The first step to create your Azure Maps component is to create a new Angular component and add the following to your .html file:
<div id="azure-map"></div>
The id can be used for styling your component in the .scss file.
Next, we will work on the .ts file. First, let's set up the map. We'll add the following class variables for the map and coordinates:
map: any;
defaultLat: number = 47.608013; // Seattle coordinates
defaultLng: number = -122.335167;
and this output to emit coordinates to the map's parent component:
#Output() outputCoordinates: EventEmitter<number[]> = new EventEmitter<number[]>();
Now we will make a function called InitMap() and add this code snippet inside to initialize the base map and its properties:
this.map = new atlas.Map('azure-map', {
center: [this.defaultLng, this.defaultLat],
zoom: 12,
language: 'en-US',
showLogo: true,
showFeedbackLink: false,
dragRotateInteraction: false,
authOptions: {
authType: AuthenticationType.subscriptionKey,
subscriptionKey: 'YOUR_SUBSCRIPTION_KEY_HERE'
}
});
Next, we will add this code snippet inside InitMap() to register the map click hander and zoom controls:
this.map.events.add('ready', () => {
// Register the map click handler
this.map.events.add('click', (e) => {
this.outputCoordinates.emit([e.position[0], e.position[1]]); // 0 = longitude, 1 = latitude
});
//Construct a zoom control and add it to the map.
this.map.controls.add(new atlas.control.ZoomControl({
style: ControlStyle.auto,
zoomDelta: 1
}), {position: ControlPosition.BottomLeft});
});
We must also call the InitMap() function inside of ngOnInit().
The next step is to create the functionality to allow the user to drop and move pins on the map. This function will erase the current marker on the map, set the new marker's coordinates, initialize the marker drag handler, and set the boundaries of the map to track the newly placed pin marker. To handle all these operations, we will add this class variable:
markersReference: Marker[] = [];
and this function:
setMarkers(markers: Marker[]) {
if (markers && markers.length > 0) {
this.markersReference = markers;
this.map.markers.clear();
let boundsPositions: Array<{lng: number, lat:number}> = [];
for (let marker of markers) {
if (marker.latitude && marker.longitude) {
let htmlMarker = new atlas.HtmlMarker({
draggable: true,
position: [marker.longitude, marker.latitude] // longitude first
});
// Register the marker drag handler
this.map.events.add('dragend', htmlMarker, (e) => {
var pos = htmlMarker.getOptions().position;
this.outputCoordinates.emit([pos[0], pos[1]]); // 0 = longitude, 1 = latitude
});
boundsPositions.push({lng: marker.longitude, lat: marker.latitude}) // lat, lng
this.map.markers.add(htmlMarker);
}
}
this.map.setCamera({padding: {top: 20, bottom: 20, left: 20, right: 20}, maxZoom: 16,
bounds: atlas.data.BoundingBox.fromLatLngs(boundsPositions)});
}
Now we will add a function that allows us to center the map focus onto the dropped pin:
centerMapWithCoords(lon: number, lat: number) {
this.map.setCamera({zoom: 12, maxZoom: 16, center: [lon, lat]});
}
Lastly, in order to pick up changes that the user makes to the map, we will subscribe to the map subject and its markers. Add these inputs alongside your class variables:
#Input() markerDataSubject: Subject<Marker[]> = new Subject<Marker[]>();
#Input() centerMapSubject: Subject<{lng: number, lat: number}> = new Subject<{lng: number, lat: number}>();
Next, add these subscriptions to your ngOnInit():
this.subscriptions.push((this.centerMapSubject).asObservable().subscribe((coords) =>
this.centerMapWithCoords(coords.lng, coords.lat)));
this.subscriptions.push((this.markerDataSubject).asObservable().subscribe((markers) =>
this.setMarkers(markers)));
And unsubscribe when the component is closed:
ngOnDestroy() {
for (const s of this.subscriptions) {
s.unsubscribe();
}
}
Overall, the class in your .ts file should look similar to the following:
export class AzureMapComponent implements OnInit {
#Input() markerDataSubject: Subject<Marker[]> = new Subject<Marker[]>();
#Input() centerMapSubject: Subject<{lng: number, lat: number}> = new Subject<{lng: number, lat: number}>();
#Output() outputCoordinates: EventEmitter<number[]> = new EventEmitter<number[]>();
subscriptions: Subscription[] = [];
map: any;
markersReference: Marker[] = [];
defaultLat: number = 47.608013; // Seattle coordinates
defaultLng: number = -122.335167;
ngOnInit() {
this.InitMap();
this.subscriptions.push((this.centerMapSubject).asObservable().subscribe((coords) =>
this.centerMapWithCoords(coords.lng, coords.lat)));
this.subscriptions.push((this.markerDataSubject).asObservable().subscribe((markers) =>
this.setMarkers(markers)));
}
//Create an instance of the map control and set some options.
InitMap() {
this.map = new atlas.Map('azure-map', {
center: [this.defaultLng, this.defaultLat],
zoom: 12,
language: 'en-US',
showLogo: true,
showFeedbackLink: false,
dragRotateInteraction: false,
authOptions: {
authType: AuthenticationType.subscriptionKey,
subscriptionKey: 'YOUR_SUBSCRIPTION_KEY_HERE'
}
});
this.map.events.add('ready', () => {
// Register the map click handler
this.map.events.add('click', (e) => {
this.outputCoordinates.emit([e.position[0], e.position[1]]); // 0 = longitude, 1 = latitude
});
//Construct a zoom control and add it to the map.
this.map.controls.add(new atlas.control.ZoomControl({
style: ControlStyle.auto,
zoomDelta: 1
}), {position: ControlPosition.BottomLeft});
});
}
setMarkers(markers: Marker[]) {
if (markers && markers.length > 0) {
this.markersReference = markers;
this.map.markers.clear();
let boundsPositions: Array<{lng: number, lat:number}> = [];
for (let marker of markers) {
if (marker.latitude && marker.longitude) {
let htmlMarker = new atlas.HtmlMarker({
draggable: true,
position: [marker.longitude, marker.latitude] // longitude first
});
// Register the marker drag handler
this.map.events.add('dragend', htmlMarker, (e) => {
var pos = htmlMarker.getOptions().position;
this.outputCoordinates.emit([pos[0], pos[1]]); // 0 = longitude, 1 = latitude
});
boundsPositions.push({lng: marker.longitude, lat: marker.latitude}) // lat, lng
this.map.markers.add(htmlMarker);
}
}
this.map.setCamera({padding: {top: 20, bottom: 20, left: 20, right: 20}, maxZoom: 16,
bounds: atlas.data.BoundingBox.fromLatLngs(boundsPositions)});
}
}
centerMapWithCoords(lon: number, lat: number) {
this.map.setCamera({zoom: 12, maxZoom: 16, center: [lon, lat]});
}
ngOnDestroy() {
for (const s of this.subscriptions) {
s.unsubscribe();
}
}
}
Now that your Azure Maps component is complete, all you have to do is call an instance of your component within the .html of whatever view you'd like to place it in and coordinate the required inputs and output:
<app-azure-map
[markerDataSubject]="locationMarkerSubject"
[centerMapSubject]="centerMapSubject"
(outputCoordinates)="updateCoordinates($event)">
</app-azure-map>
The input subjects on your parent component should look something like this:
locationMarkerSubject: Subject<Marker[]> = new Subject<Marker[]>();
centerMapSubject: Subject<{lng: number, lat: number}> = new Subject<{lng: number, lat: number}>();
And your updateCoordinates() function will handle the marker data sent back from user input upon clicking the map.
Related
Using react hooks and google maps API I have a code that puts a marker on the map but I don't know add show directions in this code. I searched but couldn't find a map example that includes both and also there is no definite google maps documentation for react hooks. I believe many other people are wondering this too, adding a marker and directions same map in react. I don't want to ask nonspecific questions but I have to. Thank you in advance
My goal is that the map component shows the location as a marker but I have directions too.
Only pins marker working code HERE
Here is my map component I've tried to implement directions but not worked.
MapTest.js
import { Fragment, useEffect, useState, useRef } from 'react';
import { useGoogleMaps } from 'react-hook-google-maps';
import {
withGoogleMap,
withScriptjs,
GoogleMap,
DirectionsRenderer,
} from 'react-google-maps';
const directions = [
{
lat: 35,
lng: -100,
},
{
lat: 36,
lng: -100,
},
];
const MapTest = () => {
const prevMarkersRef = useRef([]);
const [directions, setDirections] = useState('');
// incoming location to set
let point = {
lat: 34,
lng: -100,
};
// Map options
const { ref, map, google } = useGoogleMaps(
'YOUR API KEY',
{
zoom: 8,
center: point,
},
<DirectionsRenderer directions={directions} />
);
// directions
if (map) {
const directionsService = new google.maps.DirectionsService();
const origin = {
lat: 35,
lng: -100,
};
const destination = origin;
directionsService.route(
{
origin: origin,
destination: point,
travelMode: google.maps.TravelMode.DRIVING,
waypoints: directions,
},
(result, status) => {
if (status === google.maps.DirectionsStatus.OK) {
console.log(result);
setDirections(result);
} else {
console.error(`error fetching directions ${result}`);
}
}
);
}
useEffect(() => {
if (map) {
// ADD MARKER
const m = addMarker();
clearMarkers(prevMarkersRef.current); //clear prev markers
prevMarkersRef.current.push(m);
map.setCenter(point);
}
}, [point]);
// SIDE FUNCTIONS
function addMarker() {
return new window.google.maps.Marker({
position: point,
map: map,
});
}
function clearMarkers(markers) {
for (let m of markers) {
m.setMap(null);
}
}
return (
<div>
<div
ref={ref}
style={{ width: 400, height: 300 }}
/>
</div>
);
};
export default MapTest;
You can use the google.maps.DirectionsService() to call the Google Maps Directions API and google.maps.DirectionsRenderer() to display the directions to the map. You can instantiate them inside of your useEffect, bind the DirectionsRenderer to your map using setMap() then pass them in the function calcRoute(directionsService, directionsRenderer)` that will call the DirectionService:
useEffect(() => {
if (map) {
// ADD MARKER
const m = addMarker();
clearMarkers(prevMarkersRef.current); //clear prev markers
prevMarkersRef.current.push(m);
map.setCenter(point);
let directionsService = new google.maps.DirectionsService();
let directionsRenderer = new google.maps.DirectionsRenderer();
directionsRenderer.setMap(map);
calcRoute(directionsService, directionsRenderer);
}
}, [point]);
For the calcRoute function, build the directions request by setting your origin, destination, mode and other parameters. Then pass them to the directionService.route to send the request. If the DirectionService result status is OK, show your result via the DirectionsRenderer:
function calcRoute(directionsService, directionsRenderer) {
let request = {
origin: point,
destination: dest,
travelMode: "DRIVING"
};
directionsService.route(request, function(result, status) {
if (status == "OK") {
directionsRenderer.setDirections(result);
}
});
}
Here is a sample working code.
I'm currently using PlacesService library from Google Maps API. I want to render suggestions for New York City. I added strict_bounds for this matter but I still get results way out of New York as shown in the image.
For the most part it works, but I get many out of bounds suggestions.
/** #private {?this._google.maps.Map} The google map object. */
this._map = new this._google.maps.Map(this._mapEl, {
zoom: 11,
center: this._mapPosition
});
/** #private {this._google.maps.places.Autocomplete}Autocomplete instance */
this._service = new this._google.maps.places.AutocompleteService();
/** #private {this._google.maps.places.PlaceService}PlaceService instance */
this._placeService = new this._google.maps.places.PlacesService(this._map);
Here is the instance in the constructor.
// Attach handler for the autocomplete search box. This updates the map
// position and re-sorts locations around that position.
this._searchEl.addEventListener('keyup', (event) => {
if(event.target.value) {
this._service.getPlacePredictions({
input: event.target.value,
offset: 3,
strictBounds: true,
types: ['geocode'],
bounds: this._map.getBounds()
}, (predictions) => {
if(predictions) {
let results = predictions.map(e => [e['description']]);
event.target.missplete = new MissPlete({
input: event.target,
options: results,
className: 'c-autocomplete'
})
event.target.missplete.select = () => {
let msplt = event.target.missplete;
if (msplt.highlightedIndex !== -1) {
msplt.input.value = msplt
.scoredOptions[msplt.highlightedIndex].displayValue;
msplt.removeDropdown();
console.dir('we did it');
}
};
event.target.predictions = predictions;
console.log(event.target.predictions);
}
});
}
});
There is a method displayPlacesOnMap that gets an array of Google place objects. Here we get the place_id in order to make use of PlaceService library.
displayPlacesOnMap(mapItems) {
if(mapItems) {
mapItems.forEach(place => {
let request = {
placeId: place.place_id,
fields: ['name', 'formatted_address', 'place_id', 'geometry']
}
const officeMap = this;
this._placeService.getDetails(request, function(place, status) {
if (status === 'OK') {
officeMap._mapPosition = place.geometry.location;
officeMap._map.panTo(officeMap._mapPosition);
officeMap.sortByDistance().clearLocations().updateUrl().updateList()
.updateUrl();
$(officeMap._searchEl).blur();
}
})
})
}
};
I am working on an ionic project in which i am using 3d map.
In this project I have created an marker on the map, on this marker I want to have the click event. following is my code
//import Wrld from 'wrld.js';
import * as eeGeo from '../../assets/js/eegeo';
import 'rxjs/add/operator/map';
ionViewDidLoad() {
this.map = eeGeo.map("map", "57c2476b17a6a45fa03e4b8d042235d1", {
center: [37.7858, -122.401],
zoom: 16,
indoorsEnabled: true,
displayEntranceMarkers: true
});
}
selectitem(item)
{
this.searchbar = false;
this.cardhaed = item.title;
this.floor = item.floor
console.log(this.floor);
this.lat = item.latitude;
this.lng = item.longitude;
this.addmarker(item);
}
addmarker(item)
{
this.marker = eeGeo.marker([this.lat, this.lng], {
title: item.title,
indoorsEnabled: true,
indoorMapId: item.indoorid,
displayEntranceMarkers: false,
indoorMapFloorId: item.id
}).addTo(this.map);
this.marker.on('click', function(e) {
alert(e.latlng);
});
But in this the on event is not working.
Any help ???
I can’t access the values lat , lng from data() in maps() method.
my vue.js component
code link : https://gist.github.com/melvin2016/c8082e27b9c50964dcc742ecff853080
console image of lat,lng
enter image description here
<script>
import Vue from 'vue';
import navbarSec from './navbarSec.vue';
export default {
data(){
return{
lat: '',
lng: '',
mapState: window.mapState,
from:'',
to:'',
placesFrom:[],
placesTo:[]
};
},
components:{
'navbar':navbarSec
},
created(){
var token = this.$auth.getToken();
this.$http.post('http://localhost:3000/book',{},{headers: {'auth':token}}).then(function(data){
this.session = true;
})
.catch(function(data){
this.session = false;
this.$auth.destroyToken();
Materialize.toast(data.body.message, 6000,'rounded');
this.$router.push('/login');
});
if(navigator.geolocation){
navigator.geolocation.getCurrentPosition((data)=>{
this.lat = data.coords.latitude;
this.lng = data.coords.longitude;
this.from=data.coords.latitude+' , '+data.coords.longitude;
});
}else{
Materialize.toast("Cannot Get Your Current Location !", 6000,'rounded');
}
},
mounted(){
if (this.mapState.initMap) {// map is already ready
var val = this.mapState.initMap;
console.log(val);
this.maps();
}
},
watch: {
// we watch the state for changes in case the map was not ready when this
// component is first rendered
// the watch will trigger when `initMap` will turn from `false` to `true`
'mapState.initMap'(value){
if(value){
this.maps();
}
},
from : function(val){
if(val){
var autoComplete = new google.maps.places.AutocompleteService();
autoComplete.getPlacePredictions({input:this.from},data=>{
this.placesFrom=data;
});
}
},
to:function(val){
if(val){
var autoComplete = new google.maps.places.AutocompleteService();
autoComplete.getPlacePredictions({input:this.to},data=>{
this.placesTo=data;
});
}
}
},
methods:{
maps(){
var vm = this;
var lati = vm.lat;
var lngi = vm.lng;
console.log(lati+' '+lngi);
var map;
var latlng = {lat: lati, lng:lngi };
console.log(latlng);
this.$nextTick(function(){
console.log('tickkkk');
map = new google.maps.Map(document.getElementById('maplo'), {
zoom: 15,
center: latlng
});
var marker = new google.maps.Marker({
position: latlng,
map: map
});
});
}
}
}
</script>
This is happening because you're calling maps() in the mounted at which point, the navigator.geolocation.getCurrentPosition((data) => {}) code hasn't resolved. With that in mind, call this.maps() within the getCurrentPosition method i.e:
navigator.geolocation.getCurrentPosition((data)=>{
this.lat = data.coords.latitude;
this.lng = data.coords.longitude;
this.from=data.coords.latitude+' , '+data.coords.longitude;
this.maps()
});
I've not looked in detail but you might be able to change the bits within the maps() method to remove the nextTick stuff when you do this as you'll be calling it a lot later in the cycle at which point everything will have been rendered.
So I have two components, first of them is just form which I use for adding latitude and longitude to service for markers.
My main component is the map where I want to add markers.
My first problem is that I initialize map and get data in ngOnInit() so I can't dynamically add new data because the component doesn't reload and OnInit don't run again. My second problem is that I don't know how to add new markers when I initialize my map in ngOnInit.
That is my main component:
export class AppComponent implements OnInit {
parties: Party[] = [];
party_location: Location[] = [];
constructor(private partyService: PartyService) { }
ngOnInit() {
map = new google.maps.Map(document.getElementById('map'), {
center: { lat: -25.363, lng: 131.044 },
scrollwheel: true,
zoom: 16
});
this.parties = this.partyService.getData(); //get my data
for (let item of this.parties) { //try to add markers
let marker = new google.maps.Marker({
map: map,
position: { lat: item.lan1, lng: item.lan2 },
title: item.title
});
}
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(function(position) {
let pos = {
lat: position.coords.latitude,
lng: position.coords.longitude
};
infoWindow.setPosition(pos);
infoWindow.setContent('This is you.');
infoWindow.open(map);
map.setCenter(pos);
});
}
let infoWindow = new google.maps.InfoWindow;
}
}
May be you have some advice how to change my code??