Clickable location labels on Google Map API [duplicate] - javascript

This question already has an answer here:
Google Maps API V3: How to get region border coordinates (polyline) data? [duplicate]
(1 answer)
Closed 6 years ago.
Now on google.com/maps page when I click on the name/label of a country or city, this area are highlighted and opens left popup window with information.
Is it possible to realize the same thing with the Google Maps API?
To highlight label on mouse over, to highlight boundaries of country, state or city on click and to get selected by user label/name in js.
I looked Google Maps API JavaScript documentation but similar functionality not found.
Thanks!
Sorry for imprecise question.
Is there any way to set clickable country / city names on map with API, like on google.com/maps page?

Recommend a 'barebones' GMap class. As it happens I am just re-visiting this myself so try this code (it might need some changes)
/**
* 'GMap'
* extends googlemaps to allow simpler coding in other apps, should be loaded after the main google maps
* a new instance of a google map is contructed calling this with identical arguments to the standard class and then returned
* the map name must be unique and reserved as a global by the client code, eg var pagemap outside of function, so the external callbacks
* can use this name to run a function against, eg pagemap.queryMove_do
*
*/
function GMap(mapId, options) {
this.name = null;
this.map = null;
this.bounds = null; //view point boundary
this.gui_reg;
this.mapoptions;
this.calledbackonload;
this.polygons = {};
this.lngoffset = {'direction': '', referenceelem: ''};
this.infowindows = {};
this.infowindowindex = 0;
GMap.prototype.__construct = function (mapId, options) {
var mapbuffer = new Data_buffer(); //this has to be vague as 3rd party api data is not always the same format, simply return data to the logged map interface objects
if (typeof window.MAP_INTERFACE_CTRL === 'undefined') {//if not created, global constant to co-ordinate ajax requests with map objects and 3rd party API, and allow funneling back to local objects
window.MAP_INTERFACE_CTRL = new Data_buffer(); //this has to be vague as 3rd party api data is not always the same format, simply return data to the logged map interface objects
}
options.panControlOptions = model.array_defaults(
options.ctrlcfg,
{
position: google.maps.ControlPosition.RIGHT_BOTTOM
}
);
options.zoomControlOptions = model.array_defaults(
options.ctrlcfg,
{
position: google.maps.ControlPosition.RIGHT_CENTER
}
);
this.mapoptions = model.array_defaults(
options,
{
center: new google.maps.LatLng(54.5, -7),
zoom: 6,
minzoom: 15,
maxzoom: 21
}
);
this.gui_reg = {location: {address: new Array()}};
this.name = mapId;
this.map = new google.maps.Map(document.getElementById(mapId), this.mapoptions);
var selfobj = this;
google.maps.event.addListener(//processes client registration for registered events
this.map,
'dragend', //center changed causes too many requests
function () {
selfobj.update_client_go("location");
}
);
//create invisible scratchpad
if ($('#map_pool').length == 0) {
$('body').append("<div id='map_pool'></div>");
$('#map_pool').hide();
}
/*
callback_obj = this;
google.maps.event.addListener(this.map, 'tilesloaded', function(evt) {
callback_obj.map.setZoom(9);
});*/
}
/*****************************************************************/
/* METHODS FOR BOTH GMAP AND CLIENT */
/*****************************************************************/
/*
* defines offset of mapcentre away from an element such as custom controls
* which overlay a large amount of map
* positive or negative according to offset direction where the element might be on the
* left or the right
*/
GMap.prototype.set_lng_offset = function (elemid, dir) {
var direction = 'right';
if (dir == 'left') {
direction = dir;
}
this.lngoffset = {direction: direction, referenceelem: elemid};
}
/*
* returns lng offset according to definitions and current map situation
*/
GMap.prototype.calc_lng_offset = function () {
//get lng width of map
var eastlng = this.map.getBounds().getNorthEast().lng();
var westlng = this.map.getBounds().getSouthWest().lng();
var lngspan;
if (westlng <= 0 && eastlng >= 0) {
lngspan = Math.abs(westlng) + eastlng;
} else if (westlng >= 0 && eastlng <= 0) {
lngspan = (180 - westlng) + (180 - Math.abs(eastlng));
} else if (westlng < 0 && eastlng < 0) {
lngspan = Math.abs(eastlng) + westlng;
} else {
lngspan = eastlng - westlng;
}
//get px width of map and referenced element
var mapwidth = $("#" + this.name).width();
var offsetpx = $("#" + this.lngoffset.referenceelem).width();
//convert this px to lng offset for map by multiplying by lngpx
var lngperpx = lngspan / mapwidth; //calc lng/px as lngpx
//calc the offset lng
var offsetlng = offsetpx * lngperpx;
if (this.lngoffset.direction == 'left') {
var offsetlng = 0 - offsetlng;
}
return offsetlng;
}
/*
* returns an object with north south east west and centre coordinates of the current view
* args.precision states number of figures after decimal returned - if not give a full result is returned.
* #returns {float north,float east,float south,float west,float lat,float lng}
*/
GMap.prototype.get_viewport_coords = function (args) {
if (args == undefined) {
args = {};
}
var coords = {};
coords.north = this.map.getBounds().getNorthEast().lat();
coords.east = this.map.getBounds().getNorthEast().lng();
coords.south = this.map.getBounds().getSouthWest().lat();
coords.west = this.map.getBounds().getSouthWest().lng();
coords.lat = this.map.getCenter().lat();
coords.lng = this.map.getCenter().lng();
if (args.precision != undefined) {
coords.north = coords.north.toFixed(args.precision);
coords.east = coords.east.toFixed(args.precision);
coords.south = coords.south.toFixed(args.precision);
coords.west = coords.west.toFixed(args.precision);
}
return coords;
}
/*
* 'map_query'
* takes a text query and queries to google for info
*
* required due to APIs not being the same, a uuid is registered globally and a global process used to redirect
* because of this the global process must identify a map object by UUID and only expect a single argument as an object literal (JSON)
* this is then passed back to the object that initiated the ajax or UUID request
*
* #param OBJECT query_cfg to send to gmap geocode
* #param string query_cfg.type states what type of query and what action to take, allowing multiple use for this method
* #param string query_cfg.query_data a string containing address data such as '<postcode>, <street>, <property>'
* #param string query_cfg.callback to return the data to
*/
GMap.prototype.geo_query = function (query_cfg) {
//package pre query data and set defaults if not set
query_cfg = model.array_defaults(
query_cfg,
{
clientid: this.name,
callback: 'update_client_do'//client code can set its own callback,else set callback to this
}
);
var geo_queryid = MAP_INTERFACE_CTRL.callback_push(query_cfg);
switch (query_cfg.type) {
case "location"://returns location data at given lat lng coords
var gcoder = new google.maps.Geocoder();
gcoder.geocode({'location': {lat: query_cfg.lat, lng: query_cfg.lng}}, function (results, status) {
var server_response = {results: results, status: status};
eval("MAP_INTERFACE_CTRL.callback_pop('" + geo_queryid + "',server_response)");
});
break;
case "address_latlng":
var gcoder = new google.maps.Geocoder();
gcoder.geocode({'address': query_cfg.query_data}, function (results, status) {
var server_response = {results: results, status: status};
eval("MAP_INTERFACE_CTRL.callback_pop('" + geo_queryid + "',server_response)");
});
break;
case "sv_pano_latlng"://gets streetview pano for given lat lng
var sv_service = new google.maps.StreetViewService();
sv_service.getPanoramaByLocation(query_cfg.latlng, query_cfg.radius, function (results, status) {
var server_response = {results: results, status: status};
eval("MAP_INTERFACE_CTRL.callback_pop('" + geo_queryid + "',server_response)");
});
break;
case "viewport_range"://returns lat lng for viewport and centre to callback
var gcoder = new google.maps.Geocoder();
var bounds = this.bounds;
var center = this.map.getCenter();
gcoder.geocode({address: change_request}, function (results, status) {
callback_obj.viewport_range_do(results, status)
});
break;
}
}
/*
* 'formatted_geo_result'
* interprets geocoded results to a standard uniform independent of map api
* #param OBJECT geo_result data returned (in this map interface from google)
*/
GMap.prototype.formatted_geo_result = function (geodata) {
var formatted = {};
var itemdefaults = {
address: '',
lat: null,
lng: null
}
for (var i in geodata.results) {
var item = {
address: geodata.results[i].formatted_address,
lat: geodata.results[i].geometry.location.lat(),
lng: geodata.results[i].geometry.location.lng(),
}
formatted[i] = model.array_defaults(item, itemdefaults);
}
return formatted;
}
/*
* 'map_change'
* takes a request and processes and changes map accordingly
*
* callerobj is optional, if not given then callback will be back to this map object
* if a callerobj is given this must be to an object which can accept it
* (if callerobj is a top level function it needs to pass the object as keyword window - not 'window')
* The callback method is named as query type with _do appended eg address_move_do
* Some query types require the caller object to handle the query and DO NOT have a method defined in the map object
* If this map object DOES have the callback but a callback_obj is given then it will override this map object
*
* #param string change_request free text to send to gmap object
* #param string change_type states what type of query and what action to take, allowing multiple use for this method
* #param string callback function to run on change completion.
*/
GMap.prototype.map_change = function (change_type, change_data, callback) {
switch (change_type) {
case "lat_lng":
this.map.panTo({lat: change_data.latlng.lat, lng: change_data.latlng.lng + this.calc_lng_offset()});
break;
case "address"://to address_latlng as translation for this map object
this.geo_query({type: "address_latlng", clientid: this.name, callback: "map_change_do", change_type: change_type, query_data: change_data});
break;
}
if (callback != undefined) {
this.loadedCallback(callback);
}
}
/*
* 'map_change_do'
* processes any ajax return required by map_change
*
* #param string change_request free text to send to gmap object
* #param string change_type states what type of query and what action to take, allowing multiple use for this method
*/
GMap.prototype.map_change_do = function (pre_data, callback_data) {
switch (pre_data.change_type) {
case "address":
this.map_change("lat_lng", {latlng: {lat: callback_data.results[0].geometry.location.lat(), lng: callback_data.results[0].geometry.location.lng()}}); //callback_data.results[0].geometry.location.lat()
break;
}
}
/*
* permanently deletes all objects such as map markers (pins) etc
*/
GMap.prototype.clearmapobjects = function () {
this.map.clearMarkers();
}
GMap.prototype.panotestA = function () {
var locquery = "framlingham tech centre";
this.map_change("address", locquery);
this.map.setZoom(20);
this.geo_query("location", locquery)
this.geo_query({type: "address_latlng", clientid: this.name, callback: "panotestB", query_data: locquery});
}
GMap.prototype.panotestB = function (pre_data, callback_data) {
var pin = this.add_pin(callback_data.results[0].geometry.location.lat(), callback_data.results[0].geometry.location.lng(), {infopop: {content: 'hello', streetview: true}});
}
/*
* 'add_pin'
* adds a pin to a map
*
* #param float lat is latitude position
* #param float lng is longitude position
* #param object info_args other pin constructs such as the info displayed while clicking it
* #return object marker
*/
GMap.prototype.add_pin = function (lat, lng, info_args) {
info_args = model.array_defaults(
info_args,
{
width: 16,
height: 16,
animation: google.maps.Animation.DROP,
icon: {
url: "http://" + ROOTUC + "//css/images/icons/ROYOdot_a.gif"
},
infopop: null
}
);
/*
var icon = {
url: info_args.icon.url,
size: new google.maps.Size(20, 20),
origin: new google.maps.Point(0, 0),
anchor: new google.maps.Point(0, 0)
};
*/
var iconImage = new google.maps.MarkerImage(
info_args.icon.url, // url to image inc http://
null, // desired size
null, // offset within the scaled sprite
null, // anchor point is half of the desired size
new google.maps.Size(info_args.width, info_args.height) // required size
);
var pin = new google.maps.Marker({
position: new google.maps.LatLng(lat, lng),
map: this.map,
title: info_args.label,
icon: iconImage,
optimized: false, //to allow for animations
animation: info_args.animation,
});
if (info_args.infopop != null) {
/*content is forced into div with black font as google default
* is white text on white background ?!?!?!
*/
info_args.infopop = model.array_defaults(
info_args.infopop,
{
fontcolor: '#000000',
infowidth: '40em',
streetview: false
});
//standard google defs
var infowindow = new google.maps.InfoWindow();
//extra seperate data ref for client apps
infowindow.metadata = {
parent: this,
parentid: this.infowindowindex,
};
this.infowindows[this.infowindowindex] = {
infowidth: info_args.infopop.infowidth,
content: info_args.infopop.content,
fontcolor: info_args.infopop.fontcolor,
latlng: {lat: lat, lng: lng},
streetview: info_args.infopop.streetview
};
this.infowindowindex++;
//build for pin click
google.maps.event.addListener(pin, 'click', function () {
var infodata = infowindow.metadata.parent.infowindows[infowindow.metadata.parentid];
//build content
var infocontent = "<div id='" + infowindow.metadata.parent.name + "infowindow" + (infowindow.metadata.parentid) + "' style='color:" + infodata.fontcolor + ";width:" + infodata.infowidth + "'>";
infocontent += infodata.content;
if (infodata.streetview) {
infocontent += "<div id='" + infowindow.metadata.parent.name + "infowindowsvframe" + (infowindow.metadata.parentid) + "' class='infostreetviewframe' >";
infocontent += ". . . loading view</div>";
}
infocontent += "</div>";
infowindow.setContent(infocontent);
infowindow.open(this.map, pin);
if (infodata.streetview) {
var service = new google.maps.StreetViewService();
infowindow.metadata.parent.geo_query({type: "sv_pano_latlng", latlng: infodata.latlng, radius: 50, clientid: infowindow.metadata.parent.name, streetviewframe: infowindow.metadata.parent.name + "infowindowsvframe" + (infowindow.metadata.parentid), callback: "add_streetview_do"});
}
});
}
}
/*
* 'add_streetview'
* for flexibility addition of a streetview into a named dom element id
*
* #param object args
* #param.args string elemid the element to put the streetview into
*
*/
GMap.prototype.add_streetview_do = function (predata, callbackdata) {
var svelement = document.getElementById(predata.streetviewframe);
if (callbackdata.status == google.maps.StreetViewStatus.OK) {
var targetPOVlocation = predata.latlng
var svLatLng = {lat: callbackdata.results.location.latLng.lat(), lng: callbackdata.results.location.latLng.lng()};
// var svLatLng = new google.maps.LatLng(callbackdata.results.location.latLng.lat(), callbackdata.results.location.latLng.lng());
//var svLatLng = callbackdata.results.location.latLng;
var povyaw = this.getBearing(svLatLng, targetPOVlocation);
var sv = new google.maps.StreetViewPanorama(svelement);
var svoptions = {
position: svLatLng,
addressControl: false,
linksControl: false,
panControl: false,
zoomControlOptions: {
style: google.maps.ZoomControlStyle.SMALL
},
pov: {
heading: povyaw,
pitch: 10,
zoom: 1
},
enableCloseButton: false,
visible: true
};
sv.setOptions(svoptions);
} else {
svelement.innerHTML = "no streetview available";
}
}
/*
* 'getBearing'
* calcs bearing from two latlngs
*
*/
GMap.prototype.getBearing = function (fromLatLng, targetLatLng) {
var DEGREE_PER_RADIAN = 57.2957795;
var RADIAN_PER_DEGREE = 0.017453;
var dlat = targetLatLng.lat - fromLatLng.lat;
var dlng = targetLatLng.lng - fromLatLng.lng;
// We multiply dlng with cos(endLat), since the two points are very closeby,
// so we assume their cos values are approximately equal.
var bearing = Math.atan2(dlng * Math.cos(fromLatLng.lat * RADIAN_PER_DEGREE), dlat)
* DEGREE_PER_RADIAN;
if (bearing >= 360) {
bearing -= 360;
} else if (bearing < 0) {
bearing += 360;
}
return bearing;
}
/*
* 'add_polygon'
* adds a polygon to a map using an array of lat lng coords
* these are pushed in the order they appear in the locationarray
*
* #param array locationarray - which event/change type triggers update of this element
*
*/
GMap.prototype.add_polygon = function (args) {
//construct and store polygon
var polyobj = model.array_defaults(args, {name: "polygon", polygon: null, infohtml: null, box: null, focuson: false});
//remove any same named polygon
if (this.polygons[args.name] != undefined) {
this.polygons[args.name].polygon.setMap(null);
this.polygons[args.name] = null;
}
var newpolygon = new Array();
polyobj.box = new google.maps.LatLngBounds();
for (var l in args.polygon) {
var point = new google.maps.LatLng(args.polygon[l].lat, args.polygon[l].lng);
newpolygon.push(point);
polyobj.box.extend(point);
}
polyobj.polygon = new google.maps.Polygon(
{
map: this.map,
paths: newpolygon,
strokeColor: '#00ff00',
strokeOpacity: 0.75,
strokeWeight: 2,
fillColor: '#00ff00',
fillOpacity: 0.15
}
);
this.polygons[args.name] = polyobj;
//set required changes to map
if (args.infohtml != null) {
// content is forced into div with black font as google default is white text on white background ?!?!?!
polyobj.info = new google.maps.InfoWindow(
{
content: "<div style='color:#000000'>" + args.infohtml + "</div>"
});
var selfobj = this;
google.maps.event.addListener(this.polygons[args.name].polygon, 'click', function (event) {
var point = event.latLng;
selfobj.polygons[args.name].info.setPosition(point);
selfobj.polygons[args.name].info.open(selfobj.map);
});
}
if (polyobj.focuson) {
this.map.fitBounds(polyobj.box);
}
}
/*****************************************************************/
/* methods for CLIENT --> GMAP change */
/*********************************************************S ********/
/*
* allows gui to register elements so when changes / events occur in the map gui, its
* parent gui can be updated.
* #param string change_type - which event/change type triggers update of this element
* #param string change_return - data required to be returned eg latlng or address etc
* #param string callback - the page element identifier to update
*/
GMap.prototype.register_elem = function (change_type, event_return_cfg) {
switch (change_type) {
case "location":
switch (event_return_cfg.return_type) {
case "address":
this.gui_reg.location.address.push(event_return_cfg);
break;
}
break;
}
}
/*****************************************************************/
/* methods for GMAP --> CLIENT change */
/*****************************************************************/
/*
* checks registered change type to send back to client
* #param string change_type - which event/change type triggers update of this element
* #param string data_request - data required to be returned
* #param string regelem - the parent gui identifier to update5
* #param object attr - which attribute to update eg for input, its value, for div its html
*/
GMap.prototype.update_client_go = function (change_type, data_request) {
switch (change_type) {
case "location":
var loc = this.get_viewport_coords();
this.geo_query({type: "location", lat: loc.lat, lng: loc.lng});
}
}
/*
* actions client_update
* #param string change_type - which event/change type triggers update of this element
* #param string data_request - data required to be returned
* #param string regelem - the parent gui identifier to update5
* #param object attr - which attribute to update eg for input, its value, for div its html
*/
GMap.prototype.update_client_do = function (pre_data, callback_data) {
if (callback_data.status != "ZERO_RESULTS") {
switch (pre_data.type) {
case "location":
var location_data_registrars = this.gui_reg.location;
for (var loc_type_list in location_data_registrars) {
switch (loc_type_list) {
case "address":
var address = callback_data.results[0].formatted_address;
for (var r in location_data_registrars[loc_type_list]) {
var event_return = location_data_registrars[loc_type_list][r];
if (event_return != undefined) {
event_return.address = this.address_parse("postcode", address);
if (event_return.callback != undefined) {
eval(event_return.callback + "(event_return)");
}
}
}
break;
}
}
break;
}
}
}
/*****************************************************************/
/* PRIVATE METHODS */
/*****************************************************************/
GMap.prototype.address_parse = function (required_part, address) {
var result = "";
address = address.split(", ");
var country = address[address.length - 1];
switch (country) {
case "UK":
switch (required_part) {
case "postcode":
result = address[address.length - 2];
result = result.substr(result.indexOf(" ") + 1);
break;
}
break;
}
return result;
}
GMap.prototype.loadedCallback = function (callback) {
this.calledbackonload = false;
google.maps.event.addListenerOnce(this.map, 'tilesloaded', function () {
google.maps.event.clearListeners(this.map, 'idle');
eval(callback + '()');
});
google.maps.event.addListenerOnce(this.map, 'idle', function () {
eval(callback + '()'); //add idle as a catch all
});
/*
google.maps.event.addListenerOnce(this.map, 'idle', function() {
eval(callback + '()')
});
*/
}
/*
* zooms map to next level from current depending on dir being '+' or '-'
*/
GMap.prototype.incrementZoom = function (dir, callback) {
var newZoom = this.map.getZoom();
if (dir == '+') {
newZoom++;
}
if (dir == '-') {
newZoom--;
}
this.map.setZoom(newZoom);
if (callback != undefined) {
this.loadedCallback(callback);
}
}
/*
* zooms map to fully zoomed in
*/
GMap.prototype.zoomMax = function (callback) {
this.map.setZoom(this.mapoptions.maxzoom);
if (callback != undefined) {
this.loadedCallback(callback);
}
}
/*
* zooms map to fully zoomed out
*/
GMap.prototype.zoomMin = function (callback) {
this.map.setZoom(this.mapoptions.minzoom);
if (callback != undefined) {
this.loadedCallback(callback);
}
}
/*
* zooms map to contain the given box as per gmaps southwest / northeast corner specs
*/
GMap.prototype.boxZoom = function (latfrom, latto, lngfrom, lngto) {
var bottomleft = {lat: latfrom, lng: lngfrom};
var topright = {lat: latto, lng: lngto};
var box = new google.maps.LatLngBounds(bottomleft, topright);
this.map.fitBounds(box);
}
/*
* forces zoom in to min max params and returns true if it has been constrained
* additional callback if required for when zoom to constraints has finished
*/
GMap.prototype.constrainzoom = function (callback) {
if (this.map.getZoom() > this.mapoptions.maxzoom) {
this.map.setZoom(this.mapoptions.maxzoom);
} else if (this.map.getZoom() < this.mapoptions.minzoom) {
this.map.setZoom(this.mapoptions.minzoom);
} else {
return false;
}
if (callback != undefined) {
this.loadedCallback(callback);
}
return true;
}
/*add extras to generic map object*/
google.maps.Map.prototype.clearMarkers = function () {
for (var i = 0; i < this.markers.length; i++) {
this.markers[i].setMap(null);
}
this.markers = new Array();
};
/*
* places an existing div by id as a map control
*/
GMap.prototype.addcontrol = function (divid, posn) {
if (posn == undefined) {
posn = google.maps.ControlPosition.TOP_LEFT;
}
this.map.controls[posn].push(document.getElementById(divid));
}
/* translate normal functions to internal map object */
/* trigger setup */
this.__construct(mapId, options);
}

Related

Mapbox gl js url parameter

We have configured mapbox gl js successfully. We have a working map with locations / stores that are clickable which show the mapbox map popup, giving the user some details e.g., contact information, opening hours etc...
We are wanting to have parameter within a URL to easily access a specific listing (store/location) for example:
https://example.com/store-locator?store=1, by accessing the "store locator" with the ID 1 in the URL we would like the map to focus in on that location and have the mapbox popup show - matching location e.g., 1. For this case, let's suppose the JSON property "LocationX" is to be given an ID which is 1
I tried with the below code, but was not successful
const queryString = window.location.search;
const urlParams = new URLSearchParams(queryString);
const storeID = urlParams.get('branch')
console.log(storeID);
$(document).ready(function() {
if (storeID == "1") {
$("#link-0").click();
};
})
#Link-0 is the id for one of the locations/listings.
<div id="listing-0" class="item active">Broxburn<div>
Mapbox GL JS
/**
* Add the map to the page
*/
const map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/light-v10',
center: [-1.5466858011960802, 53.478230478528126],
zoom: 5,
scrollZoom: true
});
// Add geolocate control to the map.
map.addControl(
new mapboxgl.GeolocateControl({
positionOptions: {
enableHighAccuracy: true
},
// When active the map will receive updates to the device's location as it changes.
trackUserLocation: true,
// Draw an arrow next to the location dot to indicate which direction the device is heading.
showUserHeading: true
})
);
const stores = {
'type': 'FeatureCollection',
'features': [
{
'type': 'Feature',
'geometry': {
'type': 'Point',
'coordinates': [-3.4524379167753025, 55.942792181678804]
},
'properties': {
'refName': 'Broxburn',
'phoneFormatted': 'xxxxxxxxxxxxxxxxxx',
'phone': 'xxxxxxxxxxxxxxxxxx',
'phone2': 'xxxxxxxxxxxxxxxxxx',
'address': 'xxxxxxxxxxxxxxxxxx',
'city': 'xxxxxxxxxxxxxxxxxx',
'country': 'xxxxxxxxxxxxxxxxxx',
'postalCode': 'xxx xxx',
'email': 'xxxxxxxxxxxxxxxxxx',
'service1': 'xxxxxxx',
'service2': 'xxxxxxx',
'weekday': 'xxxxxxx - xxxxxxx',
'weekend': 'xxxxxxx - xxxxxxx',
'sunday': 'Closed'
}
},
{
'type': 'Feature',
'geometry': {
'type': 'Point',
'coordinates': [-2.9544548591328614, 54.92245269446434]
},
'properties': {
'refName': 'Carlisle',
'phoneFormatted': 'xxxxxxxxxxxxxxxxxx',
'phone': 'xxxxxxxxxxxxxxxxxx',
'phone2': 'xxxxxxxxxxxxxxxxxx',
'address': 'xxxxxxxxxxxxxxxxxx',
'city': 'xxxxxxxxxxxxxxxxxx',
'country': 'xxxxxxxxxxxxxxxxxx',
'postalCode': 'xxx xxx',
'email': 'xxxxxxxxxxxxxxxxxx',
'service1': 'xxxxxxx',
'service2': 'xxxxxxx',
'weekday': 'xxxxxxx - xxxxxxx',
'weekend': 'xxxxxxx - xxxxxxx',
'sunday': 'Closed'
}
}
}
]
};
/**
* Assign a unique id to each store. You'll use this `id`
* later to associate each point on the map with a listing
* in the sidebar.
*/
stores.features.forEach((store, i) => {
store.properties.id = i;
});
/**
* Wait until the map loads to make changes to the map.
*/
map.on('load', () => {
/**
* This is where your '.addLayer()' used to be, instead
* add only the source without styling a layer
*/
map.addSource('places', {
'type': 'geojson',
'data': stores
});
/**
* Create a new MapboxGeocoder instance.
*/
const geocoder = new MapboxGeocoder({
accessToken: mapboxgl.accessToken,
mapboxgl: mapboxgl,
marker: true,
});
/**
* Add all the things to the page:
* - The location listings on the side of the page
* - The search box (MapboxGeocoder) onto the map
* - The markers onto the map
*/
buildLocationList(stores);
map.addControl(geocoder, 'top-left');
addMarkers();
/**
* Listen for when a geocoder result is returned. When one is returned:
* - Calculate distances
* - Sort stores by distance
* - Rebuild the listings
* - Adjust the map camera
* - Open a popup for the closest store
* - Highlight the listing for the closest store.
*/
geocoder.on('result', (event) => {
/* Get the coordinate of the search result */
const searchResult = event.result.geometry;
/**
* Calculate distances:
* For each store, use turf.disance to calculate the distance
* in miles between the searchResult and the store. Assign the
* calculated value to a property called `distance`.
*/
const options = { units: 'miles' };
for (const store of stores.features) {
store.properties.distance = turf.distance(
searchResult,
store.geometry,
options
);
}
/**
* Sort stores by distance from closest to the `searchResult`
* to furthest.
*/
stores.features.sort((a, b) => {
if (a.properties.distance > b.properties.distance) {
return 1;
}
if (a.properties.distance < b.properties.distance) {
return -1;
}
return 0; // a must be equal to b
});
/**
* Rebuild the listings:
* Remove the existing listings and build the location
* list again using the newly sorted stores.
*/
const listings = document.getElementById('listings');
while (listings.firstChild) {
listings.removeChild(listings.firstChild);
}
buildLocationList(stores);
/* Open a popup for the closest store. */
createPopUp(stores.features[0]);
/** Highlight the listing for the closest store. */
const activeListing = document.getElementById(
`listing-${stores.features[0].properties.id}`
);
activeListing.classList.add('active');
/**
* Adjust the map camera:
* Get a bbox that contains both the geocoder result and
* the closest store. Fit the bounds to that bbox.
*/
const bbox = getBbox(stores, 0, searchResult);
map.fitBounds(bbox, {
padding: 100
});
});
});
/**
* Using the coordinates (lng, lat) for
* (1) the search result and
* (2) the closest store
* construct a bbox that will contain both points
*/
function getBbox(sortedStores, storeIdentifier, searchResult) {
const lats = [
sortedStores.features[storeIdentifier].geometry.coordinates[1],
searchResult.coordinates[1]
];
const lons = [
sortedStores.features[storeIdentifier].geometry.coordinates[0],
searchResult.coordinates[0]
];
const sortedLons = lons.sort((a, b) => {
if (a > b) {
return 1;
}
if (a.distance < b.distance) {
return -1;
}
return 0;
});
const sortedLats = lats.sort((a, b) => {
if (a > b) {
return 1;
}
if (a.distance < b.distance) {
return -1;
}
return 0;
});
return [
[sortedLons[0], sortedLats[0]],
[sortedLons[1], sortedLats[1]]
];
}
/**
* Add a marker to the map for every store listing.
**/
function addMarkers() {
/* For each feature in the GeoJSON object above: */
for (const marker of stores.features) {
/* Create a div element for the marker. */
const el = document.createElement('div');
/* Assign a unique `id` to the marker. */
el.id = `marker-${marker.properties.id}`;
/* Assign the `marker` class to each marker for styling. */
el.className = 'marker';
/**
* Create a marker using the div element
* defined above and add it to the map.
**/
new mapboxgl.Marker(el, { offset: [0, -23] })
.setLngLat(marker.geometry.coordinates)
.addTo(map);
/**
* Listen to the element and when it is clicked, do three things:
* 1. Fly to the point
* 2. Close all other popups and display popup for clicked store
* 3. Highlight listing in sidebar (and remove highlight for all other listings)
**/
el.addEventListener('click', (e) => {
flyToStore(marker);
createPopUp(marker);
const activeItem = document.getElementsByClassName('active');
e.stopPropagation();
if (activeItem[0]) {
activeItem[0].classList.remove('active');
}
const listing = document.getElementById(
`listing-${marker.properties.id}`
);
listing.classList.add('active');
});
}
}
/**
* Add a listing for each store to the sidebar.
**/
function buildLocationList(stores) {
for (const store of stores.features) {
/* Add a new listing section to the sidebar. */
const listings = document.getElementById('listings');
const listing = listings.appendChild(document.createElement('div'));
/* Assign a unique `id` to the listing. */
listing.id = `listing-${store.properties.id}`;
/* Assign the `item` class to each listing for styling. */
listing.className = 'item';
/* Add the link to the individual listing created above. */
const link = listing.appendChild(document.createElement('a'));
link.href = '#/';
link.className = 'title';
link.id = `link-${store.properties.id}`;
link.innerHTML = `${store.properties.refName}`;
/* Add details to the individual listing. */
const details = listing.appendChild(document.createElement('div'));
details.innerHTML = details.innerHTML = `${store.properties.address}, ${store.properties.city}`;
if (store.properties.phone) {
details.innerHTML += ` ยท ${store.properties.phoneFormatted}`;
}
if (store.properties.service1) {
details.innerHTML += `<br><i class="fa-solid fa-truck-front"></i>`;
if (store.properties.service2) { details.innerHTML +=`<i data-bs-toggle="tooltip" data-bs-placement="bottom" title="Tooltip on bottom" class="fa-solid fa-spray-can" style="padding-left: 15px;"></i>`;
if (store.properties.service3) { details.innerHTML +=`<i class="fa-solid fa-helmet-safety" style="padding-left: 15px;"></i>`;
}
}
}
/**
* Listen to the element and when it is clicked, do four things:
* 1. Update the `currentFeature` to the store associated with the clicked link
* 2. Fly to the point
* 3. Close all other popups and display popup for clicked store
* 4. Highlight listing in sidebar (and remove highlight for all other listings)
**/
link.addEventListener('click', function () {
for (const feature of stores.features) {
if (this.id === `link-${feature.properties.id}`) {
flyToStore(feature);
createPopUp(feature);
}
}
const activeItem = document.getElementsByClassName('active');
if (activeItem[0]) {
activeItem[0].classList.remove('active');
}
this.parentNode.classList.add('active');
});
}
}
/**
* Use Mapbox GL JS's `flyTo` to move the camera smoothly
* a given center point.
**/
function flyToStore(currentFeature) {
map.flyTo({
center: currentFeature.geometry.coordinates,
zoom: 15
});
}
/**
* Create a Mapbox GL JS `Popup`.
**/
function createPopUp(currentFeature) {
const popUps = document.getElementsByClassName('mapboxgl-popup');
if (popUps[0]) popUps[0].remove();
const popup = new mapboxgl.Popup({ closeOnClick: false })
.setLngLat(currentFeature.geometry.coordinates)
.setHTML(
`<h3>Company Name - ${currentFeature.properties.refName}</h3>
<h4>${currentFeature.properties.address}, ${currentFeature.properties.city}, ${currentFeature.properties.postalCode}</h4>
<h4><i style="margin-right:5px" class="fa-solid fa-clock"></i>Mon-Fri:<span style="margin-right:19px"></span> ${currentFeature.properties.weekday}<br><i style="margin-right:17px" class=""></i>Saturday: <span style="margin-right:10px"></span> ${currentFeature.properties.weekend}<br><i style="margin-right:17px" class=""></i>Sunday: <span style="margin-right:20px"></span> ${currentFeature.properties.sunday}</h4>
<h4><i style="margin-right:5px" class="fa-solid fa-phone"></i>${currentFeature.properties.phone} <br></h4>
<div style="padding:0;" id="alt-phone2" class="alt-phone2"> <h4 style="overflow-wrap: break-word;"><i style="margin-right:5px" class="fa-solid fa-phone"></i>${currentFeature.properties.phone2}</h4></div>
<div style="padding:0;" id="alt-phone2" class="alt-phone3"> <h4 style="overflow-wrap: break-word;"><i style="margin-right:5px" class="fa-solid fa-phone"></i>${currentFeature.properties.phone3}</h4></div>
<h4 style="overflow-wrap: break-word;"><i style="margin-right:5px" class="fa-solid fa-envelope"></i>${currentFeature.properties.email}</h4>`
)
.addTo(map);
if (currentFeature.properties.phone2 === undefined) {
document.getElementsByClassName("alt-phone2")[0].style.display = "none";
}
if (currentFeature.properties.phone3 === undefined) {
document.getElementsByClassName("alt-phone3")[0].style.display = "none";
}
}
I think that your code looks good, but there is likely a race condition between the $(document).ready(...) and the map.on('load', ...) events. The first one fires before the second one. That means, your JavaScript that "clicks" on a link tries to click on a link that doesn't yet exist in the DOM. If this is the case, the solution is to move the "clicking" code after the code that creates the links.
i.e. create this function:
function clickSelectedStore() {
const queryString = window.location.search;
const urlParams = new URLSearchParams(queryString);
const storeID = urlParams.get('branch')
console.log(storeID);
if (storeID) {
$('#link-' + storeID).click();
}
}
and call it at the end of the map.on('load', ...) event (after the buildLocationList(...) function has already been called.
If that doesn't help, you should create a Minimal Reproducible Example so that others can see your code in action.

Bing Maps v8 API - Zoom Level to show route and pushpin location

I am using the Bing Maps v8 API. I have a map that shows a driving route, and a pushpin of another location. I need to get the map to zoom out to show both the waypoint locations and the pushpin location. Right now, I can get the map to zoom to the waypoints of the pushpin location.
I am not sure how to get both to show on the map zoom. I know that I need to use the LocationRect class.
var searchManager;
var startingPoint = document.getElementById('startPoint').value;
var address = $("#addressLine").val();
var loc;
var patAddLoc;
var waypointLoc;
var pinsLocs = [];
// GET ROUTE BASED ON DIRECTION
function GetMap() {
var map = new Microsoft.Maps.Map(document.getElementById('myMap'), {
//BING API KEY VIA AZURE
credentials: 'X',
zoom: 10,
});
// geocode patient address
geocodeQuery(address);
// look for patient address
function geocodeQuery(query) {
//If search manager is not defined, load the search module.
if (!searchManager) {
//Create an instance of the search manager and call the geocodeQuery function again.
Microsoft.Maps.loadModule('Microsoft.Maps.Search', function () {
searchManager = new Microsoft.Maps.Search.SearchManager(map);
geocodeQuery(query);
});
} else {
var searchRequest = {
where: query,
callback: function (r) {
//Add the first result to the map and zoom into it.
if (r && r.results && r.results.length > 0) {
var pin = new Microsoft.Maps.Pushpin(r.results[0].location);
// get patient address location
patAddLoc = r.results[0].location;
map.entities.push(pin);
// add patient location to pushpin array
pinsLocs.push(patAddLoc);
// view is set with route location
//map.setView({ bounds: r.results[0].bestView });
}
// directions manager
// addWaypoint
Microsoft.Maps.loadModule('Microsoft.Maps.Directions', function addWaypoint() {
var directionsManager = new Microsoft.Maps.Directions.DirectionsManager(map);
// if less than 2 point on the map
if (directionsManager.getAllWaypoints().length < 2) {
directionsManager.clearAll();
var startWaypoint = new Microsoft.Maps.Directions.Waypoint({ address: document.getElementById('startPoint').value });
directionsManager.addWaypoint(startWaypoint);
var endWaypoint = new Microsoft.Maps.Directions.Waypoint({ address: document.getElementById('endPoint').value });
directionsManager.addWaypoint(endWaypoint);
}
// Insert a waypoint
if (document.getElementById('wayPoints').value !== null) {
var addressList = JSON.parse(document.getElementById('wayPoints').value);
var arrayLength = addressList.length;
// insert waypoints from schedule
for (var i = 0; i < arrayLength; i++) {
//alert(addressList[i]);
var hvaddress = addressList[i];
var newWP = new Microsoft.Maps.Directions.Waypoint({ address: hvaddress });
directionsManager.addWaypoint(newWP, 1);
}
}
// Set the element in which the itinerary will be rendered - set autoupdatemapview to false to override autozoom to route
directionsManager.setRenderOptions({ itineraryContainer: document.getElementById('printoutPanel'), autoUpdateMapView: false });
directionsManager.calculateDirections();
var allWaypoints = directionsManager.getAllWaypoints();
// add way point to pinsLocs array
for (var i = 0; i < allWaypoints.length; i++) {
// returns nulls
alert(allWaypoints);
loc = allWaypoints[i].getLocation();
var showMeLoc = loc;
// showMeLoc = undefined.....?
alert(showMeLoc);
pinsLocs.push(loc);
}
// only the address search location is added to the array, waypoint locations are null
alert(pinsLocs);
//Create a LocationRect from array of locations and set the map view.
map.setView({
bounds: Microsoft.Maps.LocationRect.fromLocations(pinsLocs),
padding: 100 //Add a padding to buffer map to account for pushpin pixel dimensions
});
});
},
errorCallback: function (e) {
//If there is an error, alert the user about it.
alert("No results found.");
}
};
//Make the geocode request.
searchManager.geocode(searchRequest);
}
}
}
UPDATE - HERE is the working code for those who have the same question!
var searchManager;
var startingPoint = document.getElementById('startPoint').value;
var address = $("#addressLine").val();
var patAddLoc;
// GET ROUTE BASED ON DIRECTION
function GetMap() {
var map = new Microsoft.Maps.Map(document.getElementById('myMap'), {
//BING API KEY VIA AZURE
credentials: 'XXXXX',
zoom: 10,
});
// geocode patient address
geocodeQuery(address);
// look for patient address
function geocodeQuery(query) {
//If search manager is not defined, load the search module.
if (!searchManager) {
//Create an instance of the search manager and call the geocodeQuery function again.
Microsoft.Maps.loadModule('Microsoft.Maps.Search', function () {
searchManager = new Microsoft.Maps.Search.SearchManager(map);
geocodeQuery(query);
});
} else {
var searchRequest = {
where: query,
callback: function (r) {
//Add the first result to the map and zoom into it.
if (r && r.results && r.results.length > 0) {
locs = [];
var pin = new Microsoft.Maps.Pushpin(r.results[0].location);
// get patient address location
patAddLoc = r.results[0].location;
map.entities.push(pin);
// add patient location to pushpin array
locs.push(r.results[0].location);
// view is set with route location
//map.setView({ bounds: r.results[0].bestView });
}
// directions manager
// addWaypoint
Microsoft.Maps.loadModule('Microsoft.Maps.Directions', function addWaypoint() {
var directionsManager = new Microsoft.Maps.Directions.DirectionsManager(map);
// if less than 2 point on the map
if (directionsManager.getAllWaypoints().length < 2) {
directionsManager.clearAll();
var startWaypoint = new Microsoft.Maps.Directions.Waypoint({ address: document.getElementById('startPoint').value });
directionsManager.addWaypoint(startWaypoint);
var endWaypoint = new Microsoft.Maps.Directions.Waypoint({ address: document.getElementById('endPoint').value });
directionsManager.addWaypoint(endWaypoint);
}
// Insert a waypoint
if (document.getElementById('wayPoints').value !== null) {
var addressList = JSON.parse(document.getElementById('wayPoints').value);
var arrayLength = addressList.length;
// insert waypoints from schedule
for (var i = 0; i < arrayLength; i++) {
//alert(addressList[i]);
var hvaddress = addressList[i];
var newWP = new Microsoft.Maps.Directions.Waypoint({ address: hvaddress });
directionsManager.addWaypoint(newWP, 1);
}
}
// Set the element in which the itinerary will be rendered - set autoupdatemapview to false to override autozoom to route
directionsManager.setRenderOptions({ itineraryContainer: document.getElementById('printoutPanel'), autoUpdateMapView: false });
//Add event handlers to directions manager.
Microsoft.Maps.Events.addHandler(directionsManager, 'directionsError', directionsError);
Microsoft.Maps.Events.addHandler(directionsManager, 'directionsUpdated', directionsUpdated);
// Calculate directions, which displays a route on the map
directionsManager.calculateDirections();
// get route boundaries
function directionsUpdated(e) {
//Get the current route index.
var routeIdx = directionsManager.getRequestOptions().routeIndex;
// get northeast bounding box corner
var bBoxNE = e.routeSummary[routeIdx].northEast;
locs.push(bBoxNE);
// get southwest bounding box corner
var bBoxSW = e.routeSummary[routeIdx].southWest;
locs.push(bBoxSW);
//SET MAP VIEW
//Create a LocationRect from array of locations and set the map view.
map.setView({
bounds: Microsoft.Maps.LocationRect.fromLocations(locs),
padding: 50 //Add a padding to buffer map to account for pushpin pixel dimensions
});
}
function directionsError(e) {
alert('Error: ' + e.message + '\r\nResponse Code: ' + e.responseCode)
}
});
},
errorCallback: function (e) {
//If there is an error, alert the user about it.
alert("No results found.");
}
};
//Make the geocode request.
searchManager.geocode(searchRequest);
}
}
}
If I understand correctly, you want a view that the entirety of the route and the extra location are visible?
How about using directionsManager.getCurrentRoute().routePath to get all the locations on the route, and then using LocationRect.fromLocations(), which can take in an array of locations - in your case all locations of above plus the additional one.
Note that calculateDirections and geocode and asynchronous, so you may want to handle the dependency on patAddLoc (e.g. move the directions code into the geocode callback).
Reference:
https://msdn.microsoft.com/en-us/library/mt750375.aspx
https://msdn.microsoft.com/en-us/library/mt750645.aspx
https://msdn.microsoft.com/en-us/library/mt712644.aspx

Google Maps Polygons self intersecting detection

I'm trying to implement a polygon self intersection algorithm from Google Maps API V3 polygons.
The goal is just to detect if yes or no, a simple polygon drawn by the user is self crossing.
I have found this very interesting link, but it assumes that coordinates of the polygon's vertices are given on geoJSON format. However, this isn't my case ; I'm only able to retrieve polygons coordinates using polygon.getPath() into a polygoncomplete event.
This is how i retrieve the coordinates :
google.maps.event.addDomListener(drawingManager, 'polygoncomplete', function(polygon)
{
var polygonBounds = polygon.getPath();
var coordinates = [];
for(var i = 0 ; i < polygonBounds.length ; i++)
{
vertice = {
"Latitude" : polygonBounds.getAt(i).lat(),
"Longitude" : polygonBounds.getAt(i).lng()
}
coordinates.push(vertice );
}
}
How can I transform these coordinates, given by polygon.getpath() into geoJSON format ?
Is there any better way to detect if a Google Maps polygon is self-intersecting ? If so, could you please share some code sample and not just a mathematical explaination ?
PS : I've seen this link but without any code sample, I'm a little bit lost.
You don't need to convert them to GeoJSON to use the jsts library, you need to convert them from google.maps.LatLng objects to jsts.geom.Coordinates. Instead of using this:
var geoJSON2JTS = function(boundaries) {
var coordinates = [];
for (var i = 0; i < boundaries.length; i++) {
coordinates.push(new jsts.geom.Coordinate(
boundaries[i][1], boundaries[i][0]));
}
return coordinates;
};
Use this, which will convert coordinates in a google.maps.Polygon path to the JTS format:
var googleMaps2JTS = function(boundaries) {
var coordinates = [];
for (var i = 0; i < boundaries.getLength(); i++) {
coordinates.push(new jsts.geom.Coordinate(
boundaries.getAt(i).lat(), boundaries.getAt(i).lng()));
}
return coordinates;
};
then change "findSelfIntersects" like this:
/**
* findSelfIntersects
*
* Detect self-intersections in a polygon.
*
* #param {object} google.maps.Polygon path co-ordinates.
* #return {array} array of points of intersections.
*/
var findSelfIntersects = function(googlePolygonPath) {
var coordinates = googleMaps2JTS(googlePolygonPath);
var geometryFactory = new jsts.geom.GeometryFactory();
var shell = geometryFactory.createLinearRing(coordinates);
var jstsPolygon = geometryFactory.createPolygon(shell);
// if the geometry is aleady a simple linear ring, do not
// try to find self intersection points.
var validator = new jsts.operation.IsSimpleOp(jstsPolygon);
if (validator.isSimpleLinearGeometry(jstsPolygon)) {
return;
}
var res = [];
var graph = new jsts.geomgraph.GeometryGraph(0, jstsPolygon);
var cat = new jsts.operation.valid.ConsistentAreaTester(graph);
var r = cat.isNodeConsistentArea();
if (!r) {
var pt = cat.getInvalidPoint();
res.push([pt.x, pt.y]);
}
return res;
};
proof of concept fiddle (credit to HoffZ)
code snippet:
var mapOptions = {
zoom: 16,
center: new google.maps.LatLng(62.1482, 6.0696)
};
var drawingManager = new google.maps.drawing.DrawingManager({
drawingControl: false,
polygonOptions: {
editable: true
}
});
var googleMaps2JTS = function(boundaries) {
var coordinates = [];
for (var i = 0; i < boundaries.getLength(); i++) {
coordinates.push(new jsts.geom.Coordinate(
boundaries.getAt(i).lat(), boundaries.getAt(i).lng()));
}
coordinates.push(coordinates[0]);
console.log(coordinates);
return coordinates;
};
/**
* findSelfIntersects
*
* Detect self-intersections in a polygon.
*
* #param {object} google.maps.Polygon path co-ordinates.
* #return {array} array of points of intersections.
*/
var findSelfIntersects = function(googlePolygonPath) {
var coordinates = googleMaps2JTS(googlePolygonPath);
var geometryFactory = new jsts.geom.GeometryFactory();
var shell = geometryFactory.createLinearRing(coordinates);
var jstsPolygon = geometryFactory.createPolygon(shell);
// if the geometry is aleady a simple linear ring, do not
// try to find self intersection points.
var validator = new jsts.operation.IsSimpleOp(jstsPolygon);
if (validator.isSimpleLinearGeometry(jstsPolygon)) {
return;
}
var res = [];
var graph = new jsts.geomgraph.GeometryGraph(0, jstsPolygon);
var cat = new jsts.operation.valid.ConsistentAreaTester(graph);
var r = cat.isNodeConsistentArea();
if (!r) {
var pt = cat.getInvalidPoint();
res.push([pt.x, pt.y]);
}
return res;
};
var map = new google.maps.Map(document.getElementById("map"), mapOptions);
drawingManager.setDrawingMode(google.maps.drawing.OverlayType.POLYGON);
drawingManager.setMap(map);
google.maps.event.addListener(drawingManager, 'polygoncomplete', function(polygon) {
//var polyPath = event.overlay.getPath();
var intersects = findSelfIntersects(polygon.getPath());
console.log(intersects);
if (intersects && intersects.length) {
alert('Polygon intersects itself');
} else {
alert('Polygon does not intersect itself');
}
});
#map {
width: 500px;
height: 400px;
}
<script src="https://maps.google.com/maps/api/js?libraries=drawing&key=AIzaSyCkUOdZ5y7hMm0yrcCQoCvLwzdM6M8s5qk"></script>
<script src="https://cdn.rawgit.com/bjornharrtell/jsts/gh-pages/1.4.0/jsts.min.js"></script>
<p>
Draw a polygon on the map
</p>
<div id="map">
</div>

OL3: GetFeature from Layers by Coordinate

I want to get the Feature of a Layer by coordinate.
Furthermore I want to open this feature in a popup, which I have solved so far by an onclick event. But I want to realize by giving the coordinates of a feature and opening the popup of the featue.
I have a layer with the map and a layer with the features:
if (trackMap != null) {
for (var i = 0; i < trackMap.length; i++) {
var trackInfo = trackMap[i];
lat = parseFloat(trackInfo.lat);
lon = parseFloat(trackInfo.lon);
var layergpx = new ol.layer.Vector({
source: new ol.source.Vector({
parser: new ol.parser.GPX(),
url: '${contextRoot}/gps/gpx2' + trackInfo.url
})
});
layers.push(layergpx);
}
}
I want to get the feature of this layer in another Javascript function.
How I open a pop up by clicking on the map:
/**
* The Click Event to show the data
*/
var element = document.getElementById('popup');
var popup = new ol.Overlay({
element: element,
positioning: ol.OverlayPositioning.BOTTOM_CENTER,
stopEvent: false
});
map.addOverlay(popup);
map.on('singleclick', function(evt) {
map.getFeatures({
pixel: evt.getPixel(),
layers: vectorLayers,
success: function(layerFeatures) {
var feature = layerFeatures[0][0];
if (feature) {
var geometry = feature.getGeometry();
var coord = geometry.getCoordinates();
popup.setPosition(coord);
$(element).popover({
'placement': 'top',
'html': true,
'content': feature.get('desc')
});
$(element).popover('show');
} else {
$(element).popover('destroy');
}
}
});
});
But I want this feature not to be opened by clicking on it on the map, but by entering a coordinate in a textfield and the map opens this pop up, like in the onclick event.
Take a look at this example to see if it helps you:
http://openlayers.org/en/latest/examples/kml.html
var displayFeatureInfo = function(pixel) {
map.getFeatures({
pixel: pixel,
layers: [vector],
success: function(featuresByLayer) {
var features = featuresByLayer[0];
var info = [];
for (var i = 0, ii = features.length; i < ii; ++i) {
info.push(features[i].get('name'));
}
document.getElementById('info').innerHTML = info.join(', ') || '&nbsp';
}
});
map.getFeatures() has this success callback where it delivers the features of the layers specified in layers: [vector]. Customize it at will to get what you need.
=== Update ===
In the OpenLayers 3's Map object you have a function: getPixelFromCoordinate
/**
* #param {ol.Coordinate} coordinate Coordinate.
* #return {ol.Pixel} Pixel.
*/
ol.Map.prototype.getPixelFromCoordinate = function(coordinate) {
var frameState = this.frameState_;
if (goog.isNull(frameState)) {
return null;
} else {
var vec2 = coordinate.slice(0, 2);
return ol.vec.Mat4.multVec2(frameState.coordinateToPixelMatrix, vec2, vec2);
}
};

Show a moving marker on the map

I am trying to make a marker move(not disappear and appear again) on the map as a vehicle moves on the road.
I have two values of latLng and I want to move the marker between the two till the next point is sent by the vehicle. And then repeat the process again.
What I tried:[This is not a very efficient way, I know]
My thought was to implement the above using the technique in points below:
1) Draw a line between the two.
2) Get the latLng of each point on 1/10th fraction of the polyline.
3) Mark the 10 points on the map along with the polyline.
Here is my Code:
var isOpen = false;
var deviceID;
var accountID;
var displayNameOfVehicle;
var maps = {};
var lt_markers = {};
var lt_polyLine = {};
function drawMap(jsonData, mapObj, device, deleteMarker) {
var oldposition = null;
var oldimage = null;
var arrayOflatLng = [];
var lat = jsonData[0].latitude;
var lng = jsonData[0].longitude;
//alert(jsonData[0].imagePath);
var myLatLng = new google.maps.LatLng(lat, lng);
if (deleteMarker == true) {
if (lt_markers["marker" + device] != null) {
oldimage = lt_markers["marker" + device].getIcon().url;
oldposition = lt_markers["marker" + device].getPosition();
lt_markers["marker" + device].setMap(null);
lt_markers["marker" + device] = null;
}
else {
console.log('marker is null');
oldimage = new google.maps.MarkerImage(jsonData[0].imagePath,
null,
null,
new google.maps.Point(5, 17), //(15,27),
new google.maps.Size(30, 30));
oldposition = myLatLng;
}
}
var image = new google.maps.MarkerImage(jsonData[0].imagePath,
null,
null,
new google.maps.Point(5, 17), //(15,27),
new google.maps.Size(30, 30));
lt_markers["marker" + device] = new google.maps.Marker({
position: myLatLng,
icon: image,
title: jsonData[0].address
});
if (oldposition == myLatLng) {
alert('it is same');
lt_markers["marker" + device].setMap(mapObj);
mapObj.panTo(myLatLng);
}
else {
alert('it is not same');
var markMarker = null;
var i = 10;
for (i = 10; i <= 100; i + 10) {
//-------
// setTimeout(function() {
if (markMarker != null) {
markMarker.setMap(null);
markMarker = null;
}
alert('inside the loop');
var intermediatelatlng = mercatorInterpolate(mapObj, oldposition, myLatLng, i / 100);
alert('Intermediate Latlng is :' + intermediatelatlng);
arrayOflatLng.push(intermediatelatlng);
var flightPath = new google.maps.Polyline({
path: arrayOflatLng,
strokeColor: "#FFFFFF",
strokeOpacity: 1.0,
strokeWeight: 1
});
flightPath.setMap(mapObj);
if (i != 100) {
markMarker = new google.maps.Marker({
position: intermediatelatlng,
icon: image,
title: jsonData[0].address,
map: mapObj
});
}
else {
markMarker = new google.maps.Marker({
position: intermediatelatlng,
icon: oldimage,
title: jsonData[0].address,
map: mapObj
});
}
mapObj.panTo(intermediatelatlng);
//--------
// }, 1000);
}
}
}
function mercatorInterpolate(map, latLngFrom, latLngTo, fraction) {
// Get projected points
var projection = map.getProjection();
var pointFrom = projection.fromLatLngToPoint(latLngFrom);
var pointTo = projection.fromLatLngToPoint(latLngTo);
// Adjust for lines that cross the 180 meridian
if (Math.abs(pointTo.x - pointFrom.x) > 128) {
if (pointTo.x > pointFrom.x)
pointTo.x -= 256;
else
pointTo.x += 256;
}
// Calculate point between
var x = pointFrom.x + (pointTo.x - pointFrom.x) * fraction;
var y = pointFrom.y + (pointTo.y - pointFrom.y) * fraction;
var pointBetween = new google.maps.Point(x, y);
// Project back to lat/lng
var latLngBetween = projection.fromPointToLatLng(pointBetween);
return latLngBetween;
}
Problems Faced:
1) The marker is not showing up on the map because the process of plotting and removal of marker is so fast that the marker is not visisble on screen. I've tried setTimeOut, and It does not help at all.
2) if I alow the browser to run this code for more than 5 minutes, the browser crashes.
Note: The Above function is called every 10 seconds using setInterval.
What Can be a better solution? Please Help..
For the marker to move relatively smoothly, you need to
Update more than every 1/10 fraction of the polyline (at least every few pixels)
Call the update method more frequently
Don't delete and re-add the marker
For example, something like:
var counter = 0;
interval = window.setInterval(function() {
counter++;
// just pretend you were doing a real calculation of
// new position along the complex path
var pos = new google.maps.LatLng(35, -110 + counter / 100);
marker.setPosition(pos);
if (counter >= 1000) {
window.clearInterval(interval);
}
}, 10);
I made a simple example at http://jsfiddle.net/bmSbU/2/ which shows a marker moving along a straight path. If this is what you want, most of your code above regarding where along the line you are can be reused (or check out http://broady.github.io/maps-examples/points-along-line/along-directions.html )
You can use marker-animate-unobtrusive library to make markers
smoothly transition from one location to another (instead of reappearing).
You could initialize your marker like that:
var marker = new SlidingMarker({
//your original marker options
});
Just call marker.setPosition() each time new vehicle's coordinate arrive.
P.S. I'm the author of the library.
Why not keep the existing Marker/ MarkerImage and call setPosition() to move it, either on a timer or as the position changes?
Deleting it & recreating it is what causes it to flash/ flicker and eventually crash. If you keep the same instance but just move it, you should do much better.
See: Marker.setPosition()
https://developers.google.com/maps/documentation/javascript/reference#Marker

Categories

Resources