var position = [48.210033, 16.363449];
var venues = [];
// the html div element "mydiv" saves the response from foursquare
// if no request is made, just show the map.
if(document.getElementById("mydiv").innerText.length > 2 ) {
var geocode = JSON.parse(document.getElementById("mydiv").innerText);
var geoposition = geocode.response.geocode.feature.geometry.center;
position = [geoposition.lat, geoposition.lng];
venues = geocode.response.venues;
}
var mymap = L.map(document.getElementById("mapid"), {minZoom: 4,
maxZoom: 20}).setView(position, 13);
//mapbox own key
L.tileLayer('https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token=pk.eyJ1IjoibWFzdGVyY29ycCIsImEiOiJjamkxdHlpM3IwejY3M3VybjFhdHV4OWZrIn0.K9IWyiRWAu9zEwNalyYpog', {
maxZoom: 18,
attribution: 'Map data © <a href="https://www.openstreetmap.org/">OpenStreetMap</a> contributors, ' +
'<a href="https://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>, ' +
'Imagery © <a href="https://www.mapbox.com/">Mapbox</a>',
id: 'mapbox.streets'
}).addTo(mymap);
// helper var for marker deletion later
var marker;
start();
// if the map is moved or zoomed in or out, delete markers and create everything new
mymap.on('moveend', function(e) {
markerDelAgain();
start();
});
/**Initialize all found elements and create the markers with corrected order and no overlap */
function start() {
marker = new Array();
var elements = [];
for (var i = 0; i < venues.length; i++) {
pixelpos = mymap.project([venues[i].location.lat, venues[i].location.lng]);
xcenter = pixelpos.x;
ycenter = pixelpos.y;
width = 150;
height = 100;
xrank = xcenter;
yrank = ycenter;
name = venues[i].name;
adress = venues[i].location.formattedAddress;
var element = {
x: xcenter - width/2, y: ycenter - height/2, width: width, height: height,
index: i + 1, xcenter: xcenter, ycenter: ycenter,
xrank: xrank, yrank: yrank, name:name, adress: adress
};
elements.push(element)
}
// elements sorted by index
elements.sort(function (a, b) {
return a.index - b.index
});
var overlapRect = detectOverlap(elements);
// while there are elements overlapping, remove the overlap and repair their order
while (overlapRect != 0) {
while (overlapRect.length > 0) {
var overLap = overlapRect.pop();
removeOverlap(overLap[0], overLap[1]);
}
elements.sort(function (a, b) {
return (a.xcenter - b.xcenter)});
// repair order for x direction
elements = orderRepair(elements, 0);
elements.sort(function (a, b) {
return (a.ycenter - b.ycenter)});
// repair order for y direction
elements = orderRepair(elements, 1);
overlapRect = detectOverlap(elements);
}
// sort elements by index and create the markers for the map
elements.sort(function (a, b) {
return a.index - b.index
});
for(var i= 0; i < elements.length; i++){
var latlng = mymap.unproject([elements[i].x, elements[i].y]);
marker.push(L.marker(new L.LatLng(latlng.lat, latlng.lng), {icon:createLabelIcon("textLabelclass", elements[i])}));
}
// add all markers to an array => easy to delete after
for(var index = 0; index < marker.length; index++){
marker[index].addTo(mymap);
}
}
/**
* removes overlap of 2 elements in the shortest overlap direction
* @param {element} rect1 and rect2 are 2 elements
* @param {element} rect1 and rect2 are 2 elements
*/
function removeOverlap(rect1, rect2){
var xoverlap;
var yoverlap;
var threshold = 2;
if (rect1.xrank < rect2.xrank){
xoverlap = (rect1.x + rect1.width) - rect2.x;
}
else if (rect1.xrank > rect2.xrank){
xoverlap = (rect2.x + rect2.width) - rect1.x;
} else{
xoverlap = Number.MAX_SAFE_INTEGER;
}
if (rect1.yrank < rect2.yrank){
yoverlap = (rect1.y + rect1.height) - rect2.y;
}
else if (rect1.yrank > rect2.yrank){
yoverlap = (rect2.y + rect2.height) - rect1.y;
} else{
yoverlap = Number.MAX_SAFE_INTEGER;
}
if (xoverlap < threshold){
xoverlap = threshold;
}
if(yoverlap < threshold){
yoverlap = threshold;
}
if(xoverlap <= yoverlap) {
if(rect1.xrank < rect2.xrank) {
rect1.x = rect1.x - xoverlap / 2;
rect2.x = rect2.x + xoverlap / 2 ;
}else if(rect1.xrank > rect2.xrank){
rect1.x = rect1.x + xoverlap / 2 ;
rect2.x = rect2.x - xoverlap / 2 ;
}
rect1.xcenter = calcCenter(rect1.x, rect1.width);
rect2.xcenter = calcCenter(rect2.x, rect2.width);
}
else if ( xoverlap > yoverlap){
if(rect1.yrank < rect2.yrank) {
rect1.y = rect1.y - yoverlap / 2 ;
rect2.y = rect2.y + yoverlap / 2 ;
}else if(rect1.yrank > rect2.yrank) {
rect1.y = rect1.y + yoverlap / 2 ;
rect2.y = rect2.y - yoverlap / 2 ;
}
rect1.ycenter = calcCenter(rect1.y, rect1.height);
rect2.ycenter = calcCenter(rect2.y, rect2.height);
}
}
/**
* Tests if 2 rectangles are overlapping, returns true or false
* @param {element} rect1 and rect2 are 2 elements
* @param {element} rect1 and rect2 are 2 elements
* @return {boolean} true if overlapping, false else
*/
function isOverlapping(rect1, rect2) {
if(rect1.x > (rect2.x + rect2.width) ||
(rect1.x + rect1.width) < rect2.x ||
rect1.y > (rect2.y + rect2.height) ||
(rect1.y + rect1.height) < rect2.y ) {
return false;
} else{
return true;
}
}
/**
* detects overlapping elements and returns a list with overlapping arrays.
*
* @param {Object} elements - list of elements
* @return {Array} returns multidimensional array of overlapping elements.
*/
function detectOverlap(elements){
var overlapRect = [];
for(var i = 0; i < elements.length -1 ; i ++) {
for (var k = i + 1; k < elements.length; k++) {
if (isOverlapping(elements[i], elements[k])) {
overlapRect.push([elements[i], elements[k]]);
}
}
}
return overlapRect;
}
/**
* Modified version of MERGE-SORT. removeOverlap can introduce orthogonal order errors.
* orderRepair pushes elements with a wrong orthogonal order on the same spot. ( seperate for x and y direction )
* After that, removeOverlap can be applied again to the new overlapping elements.
*
* @param {Object} elements - list of elements
* @param {boolean} dimension - 0 for x dimension, 1 for y dimension
* @return {Array<Object>} returns Array of elements with new overlaps
*/
function orderRepair(elements, dimension){
if( elements.length == 1){
return elements;
}
var mid = Math.floor(elements.length/2);
var leftlist = elements.slice(0, mid);
var rightlist = elements.slice(mid);
return merge(orderRepair(leftlist, dimension),orderRepair(rightlist,dimension), dimension);
}
/**
* merges lists of elements together
*
* @param {Object} left - list of elements
* @param {Object] right - list of elements
* @param {boolean} dimension - 0 for x dimension, 1 for y dimension
* @return {Array<Object>} returns Array of elements with new overlaps
*/
function merge(left, right, dimension){
var result = [];
var indexleft = 0;
var indexright = 0;
while(indexleft < left.length && indexright < right.length && dimension == 0){
if( left[indexleft].xrank < right[indexright].xrank){
result.push(left[indexleft]);
indexleft++;
}else {
var group = [];
for(var index = indexleft; index < left.length; index++) {
group.push(left[index]);
}
for(var index = indexright; index < right.length; index++) {
if(left[indexleft].xrank > right[index].xrank){
group.push(right[index]);
break;
}else if(left[indexleft].xrank == right[index].xrank){
group.push(right[index]);
}}
var xavg = 0;
for(var index = 0; index < group.length; index++){
xavg = xavg + group[index].xcenter;
}
xavg = xavg / group.length;
for(var index = 0; index < group.length; index++){
group[index].xcenter = xavg;
group[index].x = calcBorder(xavg, group[index].width);
}
result.push(right[indexright]);
indexright++;
}
}
while(indexleft < left.length && indexright < right.length && dimension == 1){
if( left[indexleft].yrank < right[indexright].yrank){
result.push(left[indexleft]);
indexleft++;
} else {
var group = [];
for(var index = indexleft; index < left.length; index++) {
group.push(left[index]);
}
for(var index = indexright; index < right.length; index++) {
if(left[indexleft].yrank > right[index].yrank){
group.push(right[index]);
break;
}
else if(left[indexleft].yrank == right[index].yrank){
group.push(right[index]);
}}
var yavg = 0;
for(var index = 0; index < group.length; index ++){
yavg = yavg + group[index].ycenter;
}
yavg = yavg / group.length;
for(var index = 0; index < group.length; index ++){
group[index].ycenter = yavg;
group[index].y = calcBorder(yavg, group[index].height);
}
result.push(right[indexright]);
indexright++;
}
}
return result.concat(left.slice(indexleft)).concat(right.slice(indexright));
}
/**
* calculate xcenter or ycenter pixel from pixel and height/width
*
* @param {float} center - xcenter or ycenter pixel from element
* @param {float} length - width or height from element
* @return {float} returns centerpixel
*/
function calcCenter(position, length){
return position + length/2;
}
/**
* calculate x or y pixel from center and height/width
*
* @param {float} center - xcenter or ycenter pixel from element
* @param {float} length - width or height from element
* @return {float} returns borderpixel
*/
function calcBorder(center, length){
return center - length/2;
}
/**
* Removes all Markers from the map. Needed for different Zoom levels.
*/
function markerDelAgain() {
for(var i=0;i<marker.length;i++) {
mymap.removeLayer(marker[i]);
}
}
/**
* Create the Location Labels as custom div markers instead of an image.
* height and width is hardcoded into the div.
* @param {string} labelClass - custom classname for divIcon
* @param {Object} element - custom element with location-name and location-adress
* @return {divIcon} returns icon
*/
function createLabelIcon(labelClass, element){
return L.divIcon({
className: labelClass,
html: "<div style='color:#AACCFF; text-align: center; vertical-align: middle; border-radius: 10px; " +
" word-wrap: break-word; display: inline-block; height:100px; width:150px; background-color:#222233'>"+
"<b>"+element.name+"</b>" + "<br>".concat(element.adress[0], "<br>", element.adress[1], "<br>",
element.adress[2])+"</div>"
})
}