| Server IP : 89.248.107.232 / Your IP : 216.73.217.70 Web Server : Apache System : Linux host2.kasilh.com 5.14.0-687.36.1.el9_8.x86_64 #1 SMP PREEMPT_DYNAMIC Fri Aug 7 05:40:49 EDT 2026 x86_64 User : seg ( 10005) PHP Version : 7.4.33 Disable Function : opcache_get_status MySQL : OFF | cURL : ON | WGET : OFF | Perl : OFF | Python : OFF | Sudo : OFF | Pkexec : OFF Directory : /var/www/vhosts/seg-sa.es/grupo-seg.com/wp-content/plugins/mapplic-geo/js/ |
Upload File : |
/*
* Mapplic GEO by @sekler
* Version 1.0.3
* https://www.mapplic.com/geo
*/
(function() {
"use strict";
function MapplicGeo(element) {
let self = this;
this.o = {
extent: [-18878672.443631265, -9326678.110352932, 21196344.241947223, 19298333.807917416], // world
center: [23.41, 46.82],
minZoom: 1,
maxZoom: 18,
rotation: false,
rotate: 0,
data: 'data.json',
csv: false,
sidebar: false,
search: true,
searchfields: ['title', 'about'],
sortbytitle: false,
padding: [0, 0, 0, 0],
portrait: 800,
rightSidebar: false,
portraitSidebarAbove: false,
thumbnail: true,
minHeight: 0,
maxHeight: Infinity,
improvedPerformance: false,
hidpi: true,
constrainResolution: false,
renderBuffer: 1000,
attribution: true,
attributionCollapsible: false,
deeplinking: true,
mouseWheel: true,
fullscreen: false,
mouseWheelShift: false,
zoomButtons: false,
resetButton: false,
levelselector: false,
maptilerkey: false,
mapboxtoken: false,
bingmapskey: false,
showFullExtent: true,
globalSample: false,
blurOnZoomOut: true,
resetZoomOnBlur: false,
moreText: false,
hoverTooltip: true,
hoverTooltipFollow: false,
customcss: null
}
this.localize = {
search: 'Search',
more: 'More',
pdir: 'mgeo/'
}
this.wp = false;
this.data = null;
this.init = function(options) {
// overwrite options
Object.assign(this.o, options);
self.el = element;
if (typeof mgeo_localize !== 'undefined') {
Object.assign(this.localize, mgeo_localize);
this.wp = true;
}
self.preloader = document.createElement('div');
self.preloader.classList.add('mgeo-preloader');
self.el.appendChild(self.preloader);
if (self.el.dataset.json) self.o.data = self.el.dataset.json;
if (self.el.dataset.jsonstr) {
this.processData(JSON.parse(self.el.dataset.jsonstr));
self.el.removeAttribute('data-jsonstr');
}
else self.getJSON(self.o.data).then(this.processData);
}
this.processData = function(json) {
self.data = json;
if (json.settings) delete json.settings.data;
Object.assign(self.o, json.settings);
if (self.o.moreText) self.localize.more = self.o.moreText;
if (self.o.improvedPerformance) {
self.o.hidpi = false;
self.o.renderBuffer = 200;
}
// modules
self.dir = new Directory().init();
self.styles = new Styles().init();
self.container = new Container().init();
self.actions = new Actions().init();
if (self.o.sidebar) self.sidebar = new Sidebar().init();
if (self.o.deeplinking) self.deeplinking = new Deeplinking().init();
self.styles.addStyles(json.styles);
self.dir.registerGroups(json.groups);
self.container.addLayers(json.layers);
self.dir.resolveLocations(json.locations);
self.container.addMarkers(json.locations);
self.container.resize();
if (self.o.sidebar) {
if (self.o.search) self.sidebar.addGroups(json.groups);
self.sidebar.addLocations(json.locations);
}
self.getCSV(self.o.csv).then(function(csv) {
self.dir.resolveLocations(csv.data, true);
self.container.addMarkers(csv.data);
if (self.o.sidebar) self.sidebar.addLocations(csv.data);
self.ready();
}).catch(function(error) {
self.ready();
});
}
this.getJSON = function(source) {
return new Promise(function(resolve, reject) {
if (typeof source === 'object') resolve(source);
else if (typeof source === 'string') {
var headers = new Headers();
headers.append('pragma', 'no-cache');
headers.append('cache-control', 'no-cache');
fetch(source, { headers: headers }).then(response => response.json()).then(data => {
resolve(data);
}).catch(function(error) {
resolve({});
});
}
else resolve('invaild json');
});
}
this.getCSV = function(source) {
return new Promise(function(resolve, reject) {
if (typeof Papa === 'undefined') reject('CSV parser missing');
Papa.parse(source, {
header: true,
download: true,
encoding: 'UTF-8',
skipEmptyLines: true,
transformHeader: function(h) { return h.trim().toLowerCase(); },
complete: function(results) { resolve(results); },
error: function(err) { reject(err); }
});
});
}
this.ready = function() {
setTimeout(function() { self.deeplinking?.check(); }, 400);
self.el.dispatchEvent(new CustomEvent('mapready', { detail: self }));
self.preloader.style.opacity = 0;
setTimeout(function() {
self.el.removeChild(self.preloader);
}, 200);
}
// sidebar module
function Sidebar() {
const s = this;
this.el = null;
this.header = null;
this.list = null;
this.toggle = null;
this.tags = null;
this.filters = null;
this.popup = null;
this.init = function() {
this.el = document.createElement('div');
this.el.classList.add('mgeo-sidebar');
if (self.o.rightSidebar) this.el.classList.add('mgeo-sidebar-right');
self.container.addSidebarControl(this.el);
let el = document.createElement('div');
el.classList.add('mgeo-sidebar-dir');
this.el.appendChild(el);
// search markup
if (self.o.search) {
this.header = document.createElement('div');
this.header.classList.add('mgeo-sidebar-header');
el.appendChild(this.header);
const magnifier = self.styles.getIcon('magnifier');
this.header.appendChild(magnifier);
const clear = document.createElement('button');
clear.classList.add('mgeo-filter-clear');
clear.appendChild(self.styles.getIcon('cross'));
this.header.appendChild(clear);
const wrap = document.createElement('div');
this.header.appendChild(wrap);
const input = document.createElement('input');
input.classList.add('mgeo-search-input');
input.setAttribute('type', 'text');
input.setAttribute('spellcheck', 'false');
input.setAttribute('placeholder', self.localize.search);
input.addEventListener('keyup', function(e) {
let change = true;
if (input.value) {
if (self.dir.filters['keyword'] != input.value) self.dir.filters['keyword'] = input.value.replace(/\s+/g, ' ').trim(); // remove redundant spaces
else change = false;
}
else delete self.dir.filters['keyword'];
if (change) {
s.el.classList.toggle('mgeo-sidebar-header-opened', input.value.length < 1);
s.search();
s.refreshCounts();
}
});
input.addEventListener('focus', function() { if (input.value.length < 1) s.el.classList.add('mgeo-sidebar-header-opened'); });
document.addEventListener('click', function(e) { if (s.header != e.target && !s.header.contains(e.target)) s.el.classList.remove('mgeo-sidebar-header-opened'); });
wrap.appendChild(input);
clear.addEventListener('click', function(e) {
e.preventDefault();
input.value = '';
s.tags.innerHTML = '';
self.dir.filters = {};
s.el.classList.remove('mgeo-sidebar-tagsrow');
s.search();
s.refreshCounts();
});
this.toggle = document.createElement('button');
this.toggle.classList.add('mgeo-search-toggle');
this.toggle.appendChild(self.styles.getIcon('filter'));
this.toggle.addEventListener('click', function(e) {
e.preventDefault();
s.el.classList.toggle('mgeo-sidebar-header-opened');
});
this.header.appendChild(this.toggle);
this.tags = document.createElement('div');
this.tags.classList.add('mgeo-sidebar-tags');
this.header.appendChild(this.tags);
this.filters = document.createElement('div');
this.filters.classList.add('mgeo-filters');
this.header.appendChild(this.filters);
const dim = document.createElement('div');
dim.classList.add('mgeo-sidebar-dim');
el.appendChild(dim);
}
else this.el.classList.add('mgeo-sidebar-nosearch');
// list markup
this.list = document.createElement('ul');
this.list.classList.add('mgeo-sidebar-list');
el.appendChild(this.list);
return this;
}
this.search = function() {
self.dir.search(this.list);
if (Object.keys(self.dir.filters).length === 0) self.el.classList.remove('mgeo-filter-active');
else self.el.classList.add('mgeo-filter-active');
}
this.refreshCounts = function() {
if (!this.filters) return false;
const filters = this.filters.querySelectorAll('.mgeo-filter');
for (const filter of filters) {
let count = 0;
const items = s.list.querySelectorAll('.mgeo-dir-item:not(.mgeo-hidden)');
for (const item of items) {
const location = self.dir.l[item.dataset.location];
if (location.res.group == filter.dataset.filter) count++;
}
filter.querySelector('.mgeo-count').innerHTML = '(' + count + ')';
}
}
this.removeLocation = function(id) {
this.list.querySelector('.mgeo-dir-item[data-location="' + id + '"]').remove();
}
this.addLocation = function(location) {
if (String(location.res.hide) != 'true' && !location.res.disabled) {
let item = self.dir.getItem(location);
if (item) this.list.appendChild(item);
if (self.o.sortbytitle) self.dir.sortBy(this.list, 'title');
this.refreshCounts();
}
}
this.addLocations = function(locations) {
for (let id in locations) {
let location = locations[id];
if (String(location.res.hide) != 'true' && !location.res.disabled && location.id != location.sample) {
let item = self.dir.getItem(location);
if (item) this.list.appendChild(item);
}
}
if (self.o.sortbytitle) self.dir.sortBy(this.list, 'title');
this.refreshCounts();
}
this.addTag = function(group) {
const tag = document.createElement('button');
let groups = [];
if (self.dir.filters['group']) groups = self.dir.filters['group'].split(',');
if (!groups.includes(group.id)) {
groups.push(group.id);
self.dir.filters['group'] = groups.toString();
tag.classList.add('mgeo-tag');
tag.dataset.filter = group.id;
tag.innerHTML = group.title;
if (group.color) tag.style.backgroundColor = group.color;
this.tags.appendChild(tag);
tag.appendChild(self.styles.getIcon('cross'));
tag.addEventListener('click', function(e) {
e.preventDefault();
s.removeTag(group.id);
s.el.classList.remove('mgeo-sidebar-header-opened');
});
s.search();
this.el.classList.remove('mgeo-sidebar-header-opened');
this.el.classList.add('mgeo-sidebar-tagsrow');
}
}
this.removeTag = function(id) {
let groups = [];
if (self.dir.filters['group']) {
groups = self.dir.filters['group'].split(',');
let i = groups.indexOf(id);
if (i > -1) {
groups.splice(i, 1)
self.dir.filters['group'] = groups.toString();
}
if (groups.length < 1) delete self.dir.filters['group'];
}
let tag = this.tags.querySelector('button[data-filter="' + id + '"]');
this.tags.removeChild(tag);
s.search();
if (this.tags.querySelectorAll('.mgeo-tag').length < 1) this.el.classList.remove('mgeo-sidebar-tagsrow');
}
this.addGroups = function(groups) {
if (!groups) return false;
if (groups.length > 0) this.toggle.style.display = 'block';
const ul = document.createElement('ul');
let added = false;
groups.forEach(function(group) {
if (group.hide == 'true') return;
const item = document.createElement('li');
item.classList.add('mgeo-group', 'mgeo-filter');
item.dataset.filter = group.id;
const link = document.createElement('a');
link.addEventListener('click', function() { s.addTag(group); });
item.appendChild(link);
const thumb = self.dir.getThumbnail(group.title, group.thumbnail);
link.appendChild(thumb);
if (group.color && thumb) thumb.style.backgroundColor = thumb.style.borderColor = group.color;
const title = document.createElement('h4');
title.classList.add('mgeo-group-title');
title.innerHTML = group.title;
link.appendChild(title);
const count = document.createElement('span');
count.classList.add('mgeo-count');
count.innerHTML = '(0)';
link.appendChild(count);
if (group.about) {
const about = document.createElement('span');
about.classList.add('mgeo-dir-about');
about.innerHTML = group.about;
link.appendChild(about);
}
else title.classList.add('mgeo-title-margin');
ul.appendChild(item);
added = true;
});
if (added) this.el.classList.add('mgeo-sidebar-filterable');
if (this.filters) this.filters.appendChild(ul);
}
this.addPopup = function(location, small = false) {
if (!location) return false;
if (this.popup) this.hidePopup();
this.popup = document.createElement('div');
this.popup.classList.add('mgeo-popup');
this.popup.dataset.location = location.id;
let container = document.createElement('div');
container.classList.add('mgeo-popup-container');
let close = document.createElement('button');
close.classList.add('mgeo-close');
close.appendChild(self.styles.getIcon('cross'));
close.addEventListener('click', function(e) {
e.preventDefault();
s.hidePopup();
return false;
});
container.appendChild(close);
if (location.res.image) {
let ic = document.createElement('div');
ic.classList.add('mgeo-popup-image');
let image = document.createElement('img');
image.src = location.res.image;
ic.appendChild(image);
this.popup.appendChild(ic);
}
if (location.res.title) {
let title = document.createElement('h1');
title.classList.add('mgeo-title');
title.innerHTML = location.res.title;
container.appendChild(title);
}
if (location.res.about) {
let about = document.createElement('h4');
about.classList.add('mgeo-about');
about.innerHTML = location.res.about;
container.appendChild(about);
}
if (location.res.desc) {
let content = document.createElement('div');
content.classList.add('mgeo-popup-content');
content.innerHTML = location.res.desc;
container.appendChild(content);
}
let buttons = document.createElement('div');
buttons.classList.add('mgeo-buttons');
container.appendChild(buttons);
if (location.res.link) {
let more = document.createElement('a');
more.classList.add('mgeo-more');
more.href = location.res.link;
more.innerHTML = self.localize.more;
if (location.res.fill) more.style.backgroundColor = location.res.fill;
buttons.appendChild(more);
}
this.popup.appendChild(container);
this.el.prepend(this.popup);
this.el.classList.add('mgeo-sidebar-popup');
self.el.dispatchEvent(new CustomEvent('popuprender', { detail: s.popup }));
}
this.hidePopup = function() {
if (!this.popup) return false;
this.el.removeChild(this.popup);
this.popup = null;
this.el.classList.remove('mgeo-sidebar-popup');
self.actions.blur();
}
}
// styles module
function Styles() {
const s = this;
this.s = [];
this.icons = {
'cross': {
path: 'M8,0.809L7.191,0L4,3.191L0.809,0L0,0.809L3.191,4L0,7.191L0.809,8L4,4.809L7.191,8L8,7.191L4.809,4L8,0.809z',
viewbox: '0 0 8 8'
},
'magnifier': {
path: 'M15.967,14.803l-4.253-4.285c0.848-1.103,1.364-2.477,1.379-3.971C13.093,2.937,10.156,0,6.546,0S0,2.937,0,6.546c0,3.609,2.937,6.546,6.546,6.546c1.477,0,2.84-0.507,3.94-1.349L14.772,16L15.967,14.803z M1.64,6.579c0-2.706,2.201-4.907,4.907-4.907c2.675,0,4.876,2.203,4.906,4.907c0,2.706-2.2,4.905-4.906,4.905S1.64,9.284,1.64,6.579z',
viewbox: '0 0 16 16'
},
'filter': {
path: 'M1,11h7v2H1V11z M8,3v2h7V3H8z M4,7h2V1H4v2H1v2h3V7z M12,9h-2v6h2v-2h3v-2h-3V9z',
viewbox: '0 0 16 16'
},
'reset': {
path: 'M11,3H5l3-3L11,3z M5,13l6,0.01L8,16L5,13z M3,11L0,8l3-3V11z M13,5l3,3l-3,3V5z M8,5C6.34,5,5,6.34,5,8s1.34,3,3,3s3-1.34,3-3S9.66,5,8,5z M8,10c-1.1,0-2-0.9-2-2s0.9-2,2-2s2,0.9,2,2S9.1,10,8,10z',
viewbox: '0 0 16 16'
},
'compass': {
path: 'M8,4 5,11 8,9 11,11z',
viewbox: '0 0 16 16'
}
}
this.init = function() {
if (self.o.customcss) {
const style = document.createElement('style');
style.innerHTML = self.o.customcss;
document.body.appendChild(style);
}
return this;
}
this.addStyles = function(styles) {
if (!styles) return false;
for (const style of styles) this.s[style.class] = style;
}
this.getIcon = function(name) {
const icon = this.icons[name];
if (!icon) return null;
const ns = 'http://www.w3.org/2000/svg';
const svg = document.createElementNS(ns, 'svg');
svg.classList.add('mgeo-icon', 'mgeo-icon-' + name);
svg.setAttribute('viewBox', icon.viewbox);
const path = document.createElementNS(ns, 'path');
path.setAttribute('d', icon.path);
svg.appendChild(path);
return svg;
}
}
// deeplinking module
function Deeplinking() {
const s = this;
let params;
this.init = function() {
params = new URLSearchParams(window.location.search);
window.onpopstate = function() { s.check(); }
return this;
}
this.set = function(key, value) {
params.set(key, value);
this.update();
}
this.delete = function(key) {
params.delete(key);
this.update();
}
this.check = function() {
params = new URLSearchParams(window.location.search);
const location = self.dir.l[params.get('location')];
if (location) self.actions.focus(location, true);
else self.actions.blur(true);
}
this.update = function() {
window.history.pushState({}, '', `${window.location.pathname}?${params}`);
}
}
// actions module
function Actions() {
this.focused = null;
this.a = {
'': '(Default)',
'none': 'None',
'zoom': 'Zoom',
'tooltip': 'Tooltip',
'sidebar': 'Sidebar popup',
'open-link': 'Open link',
'open-link-new-tab': 'Open link in new tab',
'isolate': 'Isolate layer'
}
this.init = function() { return this; }
this.focus = function(location, deep = false) {
if (!location) return false;
this.blur();
this.focused = location;
self.container.focusLocation(location);
switch (location.res.action) {
case 'none':
return false;
break;
case 'zoom':
self.container.zoomTo(location);
return false;
break;
case 'isolate':
self.container.zoomTo(location); // isolation fires when needed
return false;
break;
case 'open-link':
window.location.href = location.link;
return false;
break;
case 'open-link-new-tab':
window.open(location.link, '_blank');
return false;
break;
case 'sidebar':
if (self.sidebar) {
self.container.addTooltip(location, true);
self.sidebar.addPopup(location);
self.container.zoomTo(location);
break;
}
default:
const tooltip = self.container.addTooltip(location);
self.container.zoomTo(location, tooltip.offsetHeight);
}
if (self.deeplinking && !deep) self.deeplinking.set('location', location.id);
self.el.dispatchEvent(new CustomEvent('locationfocus', { detail: { location: location } }));
}
this.blur = function(deep = false) {
if (!this.focused) return false;
self.container.blurLocation(this.focused);
this.focused = null;
self.container.hideTooltip();
self.sidebar?.hidePopup();
if (self.deeplinking && !deep) self.deeplinking.delete('location');
if (self.o.resetZoomOnBlur) self.container.resetZoom();
self.el.dispatchEvent(new CustomEvent('locationblur'));
}
}
// directory module
function Directory() {
const s = this;
this.filters = {};
this.l = {}; // locations
this.g = {}; // groups
this.def = {};
this.init = function() {
return this;
}
this.replaceVars = function(template, location) {
if (!location.id || !template) return false;
template = template.replace(/\{\{([^}]+)\}\}/g, function (match) {
match = match.slice(2, -2).toLowerCase();
var sub = match.split('.');
if (sub.length > 1) {
var temp = location;
sub.forEach(function(item) {
if (!temp[item]) {
temp = '{{' + match + '}}';
return;
}
temp = temp[item];
});
return temp;
}
else {
if (!location[match]) return '{{' + match + '}}';
return location[match];
}
});
return template;
}
this.search = function(list = null) {
for (const id in s.l) { // loop locations
let location = s.l[id],
matched = true;
for (const attr in s.filters) { // loop filters
let filter = s.filters[attr];
let current = false;
if (attr == 'keyword') { // search
self.o.searchfields.forEach(function(field) { // loop fields
if (location.res[field] && !current) current = !(s.normalizeString(location.res[field]).indexOf(s.normalizeString(filter)) == -1);
});
}
else if (location.res.group && attr == 'group') { // groups
let groups = location.res.group;
if (typeof groups == 'string') groups = groups.split(',');
groups.forEach(function(group) {
if (self.dir.filters['group'].split(',').includes(group)) current = true;
});
}
matched = matched && current;
}
// show/hide list
let item = list.querySelector('.mgeo-dir-item[data-location="' + location.id + '"]');
if (item) {
if (matched) item.classList.remove('mgeo-hidden');
else item.classList.add('mgeo-hidden');
}
// highlight feature
if (Object.keys(s.filters).length === 0) self.container.blurLocation(location);
else if (matched) self.container.highlightLocation(location);
else self.container.disableLocation(location);
}
}
this.getDirectory = function(attribute, pattern, sortby = false) {
const regex = new RegExp(pattern, 'i');
const dir = document.createElement('ul');
for (var id in s.l) {
let location = s.l[id];
if (attribute && pattern && !regex.test(location[attribute])) return true; // skip if no match
if (String(location.hide) != 'true') dir.appendChild(s.getItem(location));
}
if (sortby) this.sortBy(dir, sortby);
return dir;
}
this.getThumbnail = function(name, field) {
let elem = null;
if (field) {
if (field.match(/(http(s)?:\/\/.)?(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)/g)) {
elem = document.createElement('img');
elem.classList.add('mgeo-thumb');
elem.setAttribute('alt', name);
elem.src = field;
}
else {
elem = document.createElement('div');
elem.setAttribute('aria-hidden', true);
elem.classList.add('mgeo-thumb', 'mgeo-thumb-placeholder');
elem.innerHTML = field;
}
}
else {
let text = '';
if (name) {
let words = name.split(' ');
if (words[0]) text += words[0][0];
if (words[1]) text += words[1][0];
}
elem = document.createElement('div');
elem.setAttribute('aria-hidden', true);
elem.classList.add('mgeo-thumb', 'mgeo-thumb-placeholder');
elem.innerHTML = text.toUpperCase();
}
return elem;
}
this.getItem = function(location) {
if (!location.id) location = s.getLocation(location);
if (!location) return false;
const item = document.createElement('li');
item.classList.add('mgeo-dir-item');
item.dataset.location = location.id;
item.dataset.sort = location.title;
const a = document.createElement('a');
item.appendChild(a);
a.addEventListener('click', function(e) {
e.preventDefault();
self.actions.focus(location);
});
if (self.o.thumbnail) {
const thumbEl = self.dir.getThumbnail(location.title, location.res.thumbnail);
a.appendChild(thumbEl);
}
if (location.res.title) {
const titleEl = document.createElement('h4');
titleEl.classList.add('mgeo-dir-title');
titleEl.innerHTML = location.res.title;
a.appendChild(titleEl);
}
if (location.res.about) {
const aboutEl = document.createElement('span');
aboutEl.classList.add('mgeo-dir-about');
aboutEl.innerHTML = location.res.about;
a.appendChild(aboutEl);
}
return item;
}
this.registerGroups = function(groups) {
if (!groups) return false;
groups.forEach(function(group) { if (group.id) s.g[group.id] = group; });
}
this.modifyLocation = function(id = null, obj = null) {
if (!id) { // add
let location = s.resolveLocation(obj);
self.container.addMarker(location);
self.sidebar?.addLocation(location);
}
else if (!obj) { // remove
self.container.removeMarker(s.l[id]);
self.sidebar?.removeLocation(id);
delete s.l[id];
}
else { // modify
if (id == obj.id) { // id same
Object.assign(s.l[id], obj);
s.resolveLocation(s.l[id]);
}
else { // id changed
if (s.l[id].res?.marker) s.l[id].res.marker.setId(obj.id);
s.l[obj.id] = Object.assign(s.l[id], obj);
s.resolveLocation(s.l[obj.id]);
delete s.l[id]
}
self.sidebar?.removeLocation(id);
self.sidebar?.addLocation(s.l[obj.id]);
}
}
this.resolveLocations = function(locations, external = false) {
if (!locations) return false;
locations.forEach(function(location) {
s.resolveLocation(location, external);
});
}
this.resolveLocation = function(location, external = false) {
if (!location.id) return;
if (external) {
location.external = true;
Object.keys(location).forEach(function(attr) { if (location[attr] === '') delete location[attr]; }); // remove empty attributes from external
}
let res = location.res ? Object.assign({}, location.res) : {};
delete location.res;
if (location.sample && s.l[location.sample]) res = Object.assign({}, s.l[location.sample]);
else if (!location.sample && s.l[self.o.globalSample]) res = Object.assign({}, s.l[self.o.globalSample]);
location.res = Object.assign(res, location);
delete location.res.sample;
delete location.res.coord;
location.res.desc = s.replaceVars(location.res.desc, location.res);
if (!location.res.fill && s.g[location.res.group]?.color) location.res.fill = s.g[location.res.group]?.color;
if (!location.res.style && s.g[location.res.group]?.style) location.res.style = s.g[location.res.group]?.style;
location.res.isolated = self.container.l[location.res.layer]?.object.parentLayer || location.res.layer || false;
if (location.res.action == 'isolate') location.res.isolated = location.id;
s.l[location.id] = location;
return s.l[location.id];
}
this.getLocation = function(id) {
return s.l[id];
}
this.normalizeString = function(s) {
if (s) return s.toString().toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g, "");
else return '';
}
this.sort = function(dir, order = 1) {
let items = Array.from(dir.getElementsByClassName('mgeo-dir-item'));
items.sort(function(a, b) {
a = a.dataset.sort;
b = b.dataset.sort;
if (isNaN(a) || isNaN(b)) return (a < b) ? -1 * order : 1 * order;
else return (a - b) * order;
});
items.forEach(function(item) { dir.appendChild(item); });
}
this.sortBy = function(dir, attribute, order = 1) {
let items = Array.from(dir.getElementsByClassName('mgeo-dir-item'));
items.forEach(function(item) {
item.dataset.sort = s.normalizeString(s.l[item.dataset.location][attribute]);
});
this.sort(dir, order);
}
}
// container module
function Container() {
const s = this;
let map;
let globalMarkers;
let tooltip;
let hovertip;
let overlay;
let overControl = false;
let fillZoom;
let ratio = 1;
let sidebarParent;
let hovered = null;
let extentInteraction = null;
let markerDrag = null;
let markerHandle = null;
let zones = {};
let levelSelector;
let fzi = 0; // feature z index
let extf = {}; // external features
let nullStyle = new ol.style.Style(null);
let cache = {
fill: {},
stroke: {},
image: {},
text: {},
style: {}
}
this.controls = null;
this.extent = null;
this.width = 0;
this.height = 0;
this.isolation = false;
this.l = {};
this.layers = {};
this.emptyfeatures = 0;
this.init = function() {
this.el = document.createElement('div');
this.el.classList.add('mgeo-container');
self.el.appendChild(this.el);
const observer = new ResizeObserver(function(entries) {
if (s.width != s.el.offsetWidth || s.height != s.el.offsetHeight) s.resize();
});
observer.observe(this.el);
self.o.extent = this.safeExtent(self.o.extent);
// extent ratio
ratio = ol.extent.getWidth(self.o.extent)/ol.extent.getHeight(self.o.extent);
// controls
this.controls = document.createElement('div');
this.controls.classList.add('mgeo-controls');
const controls = new ol.Collection();
if (self.o.rotation) controls.push(new ol.control.Rotate({ target: this.getControlZone('mgeo-' + self.o.rotation), label: self.styles.getIcon('compass') }));
if (self.o.fullscreen) controls.push(new ol.control.FullScreen({ target: this.getControlZone('mgeo-' + self.o.fullscreen) }));
if (self.o.resetButton) controls.push(new ol.control.Control({ target: this.getControlZone('mgeo-' + self.o.resetButton), element: this.resetControl() }));
if (self.o.zoomButtons) controls.push(new ol.control.Zoom({ target: this.getControlZone('mgeo-' + self.o.zoomButtons) }));
if (self.o.attribution) controls.push(new ol.control.Attribution({ target: this.getControlZone('mgeo-bottom-right'), collapsible: self.o.attributionCollapsible }));
// map
map = new ol.Map({
target: this.el,
controls: controls
});
map.getInteractions().forEach(function(interaction) { if (interaction instanceof ol.interaction.MouseWheelZoom) map.removeInteraction(interaction); });
// mousewheel
if (self.o.mouseWheel) {
let mousewheelcond = ol.events.condition.always;
if (self.o.mouseWheelShift) mousewheelcond = ol.events.condition.shiftKeyOnly;
map.addInteraction(new ol.interaction.MouseWheelZoom({ condition: mousewheelcond }));
}
let oc = this.el.getElementsByClassName('ol-overlaycontainer-stopevent')[0];
oc.appendChild(this.controls);
// if hovertooltip
if (self.o.hoverTooltip) this.hoverTooltip();
// tooltip
overlay = new ol.Overlay({});
map.addOverlay(overlay);
// hovering features
let hovered = null;
map.on('pointermove', function(e) {
const feature = this.forEachFeatureAtPixel(e.pixel, function(feature) { return feature; }, { hitTolerance: 4 });
if (feature && feature.getId() && !markerDrag) {
const location = self.dir.l[feature.getId()];
if (location != hovered) {
if (hovered != self.actions.focused) s.setLocationStyle(hovered, 'base');
if (location != self.actions.focused) s.setLocationStyle(location, 'hover');
hovered = location;
}
}
else if (hovered != self.actions.focused) {
s.setLocationStyle(hovered, 'base');
hovered = null;
}
});
// click event
map.on('singleclick', this.featureClick);
// cursor pointer
map.on('pointermove', function(e) {
const pixel = map.getEventPixel(e.originalEvent);
const hit = map.hasFeatureAtPixel(pixel);
map.getTargetElement().style.cursor = hit ? 'pointer' : '';
});
return this;
}
this.newLayer = function(layer) {
if (!layer.id) return false;
let object;
let style = s.getStyle(layer.style);
if (!style) {
style = new ol.style.Style({
fill: new ol.style.Fill({
color: '#f4f4f4',
}),
stroke: new ol.style.Stroke({
color: '#bbb',
width: 0.5,
})
});
}
switch (layer.type) {
case 'osm':
object = new ol.layer.Tile({ source: new ol.source.OSM({ interpolate: true }) });
break;
case 'markers':
object = new ol.layer.Vector({
updateWhileInteracting: !self.o.improvedPerformance,
updateWhileAnimating: !self.o.improvedPerformance,
className: 'mgeo-markers',
source: new ol.source.Vector({ wrapX: false }),
renderBuffer: self.o.renderBuffer
});
break;
case 'stamen':
object = new ol.layer.Tile({ source: new ol.source.Stamen({ layer: layer.source }) });
break;
case 'image':
let extent = layer.extent ? s.safeExtent(layer.extent) : s.safeExtent(self.o.extent);
object = new ol.layer.Image({
source: new ol.source.ImageStatic({
url: layer.source,
imageExtent: extent
})
});
break;
case 'xyz':
object = new ol.layer.Tile({
source: new ol.source.XYZ({
url: layer.source
})
});
break;
case 'mapbox':
const mapboxStyles = {
'bright': 'mapbox://styles/mapbox/bright-v9',
'streets': 'mapbox://styles/mapbox/streets-v11',
'outdoors': 'mapbox://styles/mapbox/outdoors-v11',
'light': 'mapbox://styles/mapbox/light-v10',
'dark': 'mapbox://styles/mapbox/dark-v10',
}
object = new ol.layer.MapboxVector({
styleUrl: mapboxStyles[layer.source],
accessToken: self.o.mapboxtoken
});
break;
case 'bingmaps':
object = new ol.layer.Tile({
preload: Infinity,
source: new ol.source.BingMaps({
key: self.o.bingmapskey,
hidpi: true,
imagerySet: layer.source
})
});
break;
case 'maptiler':
const maptilerStyles = {
'basic': 'https://api.maptiler.com/maps/basic/{z}/{x}/{y}@2x.png?key=',
'satellite-v2': 'https://api.maptiler.com/tiles/satellite-v2/{z}/{x}/{y}.jpg?key=',
'bright': 'https://api.maptiler.com/maps/bright/{z}/{x}/{y}@2x.png?key=',
'pastel': 'https://api.maptiler.com/maps/pastel/{z}/{x}/{y}@2x.png?key=',
'hybrid': 'https://api.maptiler.com/maps/hybrid/{z}/{x}/{y}@2x.jpg?key=',
'streets': 'https://api.maptiler.com/maps/streets/{z}/{x}/{y}@2x.png?key=',
'topo': 'https://api.maptiler.com/maps/topo/{z}/{x}/{y}@2x.png?key=',
'voyager': 'https://api.maptiler.com/maps/voyager/{z}/{x}/{y}@2x.png?key='
}
object = new ol.layer.Tile({
source: new ol.source.XYZ({
attributions: '<a href="https://www.maptiler.com/copyright/" target="_blank">© MapTiler</a> ' + '<a href="https://www.openstreetmap.org/copyright" target="_blank">© OpenStreetMap contributors</a>',
url: maptilerStyles[layer.source] + self.o.maptilerkey,
tileSize: 512,
tilePixelRatio: 2
})
});
break;
case 'geojson':
case 'topojson':
case 'builtin':
let format;
if (layer.type == 'geojson') format = new ol.format.GeoJSON();
else if (layer.type == 'topojson' || layer.type == 'builtin') format = new ol.format.TopoJSON();
let source = new ol.source.Vector({
attributions: '© <a href="https://www.mapplic.com" target="_blank">Mapplic GEO</a>',
url: (layer.type == 'builtin') ? self.localize.pdir + 'maps/' + layer.source : layer.source,
format: format,
wrapX: false,
overlaps: false
});
source.on('addfeature', function(e) {
let id = e.feature.getId();
if (id) {
extf[id] = e.feature;
let location = self.dir.l[id];
if (location) {
location.res.feature = e.feature;
location.res.offset = [0, 0];
location.res.layer = layer.id;
if (!location.res.layer) location.res.layer = layer.id;
s.setLocationStyle(location);
}
else s.emptyfeatures++;
}
});
object = new ol.layer.Vector({
updateWhileInteracting: !self.o.improvedPerformance,
updateWhileAnimating: !self.o.improvedPerformance,
renderBuffer: self.o.renderBuffer,
source: source,
style: style
});
break;
case 'svg':
object = new ol.layer.Layer({ className: 'different-layer', render: function() { return null; } });
var svgContainer = document.createElement('div');
var xhr = new XMLHttpRequest();
xhr.open('GET', layer.source);
xhr.addEventListener('load', function () {
var svg = xhr.responseXML.documentElement;
svgContainer.ownerDocument.importNode(svg);
svgContainer.appendChild(svg);
svgContainer.style.width = svg.getAttribute('width');
svgContainer.style.height = svg.getAttribute('height');
svgContainer.style.transformOrigin = 'top left';
svgContainer.className = 'mgeo-svg-layer';
var width = parseFloat(svgContainer.style.width);
var height = parseFloat(svgContainer.style.height);
var svgExtent = s.safeExtent(layer.extent);
var svgWidth = ol.extent.getWidth(svgExtent);
var svgCenter = ol.extent.getCenter(svgExtent);
var svgResolution = svgWidth / width;
object.render = function (frameState) {
var scale = svgResolution / frameState.viewState.resolution;
var center = frameState.viewState.center;
var size = frameState.size;
var cssTransform = ol.transform.composeCssTransform(
size[0] / 2,
size[1] / 2,
scale,
scale,
frameState.viewState.rotation,
(svgCenter[0] - center[0]) / svgResolution - width / 2,
(center[1] - svgCenter[1]) / svgResolution - height / 2
);
svgContainer.style.transform = cssTransform;
svgContainer.style.opacity = this.getOpacity();
return svgContainer;
}
object.setExtent(svgExtent);
});
xhr.send();
break;
default:
return false;
}
if (layer.extent) object.setExtent(this.safeExtent(layer.extent));
if (layer.minZoom) object.setMinZoom(layer.minZoom);
if (layer.maxZoom) object.setMaxZoom(layer.maxZoom);
return object;
}
this.addLayers = function(layers) {
this.layers = layers;
if (!layers) layers = [];
if (layers.length < 1) layers.push({'id': 'example-layer', 'title': 'Example Layer', 'type': 'osm'});
if (self.o.levelselector) map.addControl(new ol.control.Control({ target: this.getControlZone('mgeo-' + self.o.levelselector), element: this.levelSelectorControl(layers) }));
for (const layer of layers) {
if (layer.disabled === true) continue;
if (layer.id) s.l[layer.id] = layer;
let object;
if (layer.layers) {
// layer group
const c = new ol.Collection();
for (const sub of layer.layers) {
if (sub.disabled === true) continue;
if (sub.id) s.l[sub.id] = sub;
sub.object = this.newLayer(sub);
sub.object.parentLayer = layer.id;
c.push(sub.object);
}
object = new ol.layer.Group({ layers: c });
}
// single layer
else object = this.newLayer(layer);
if (object) {
map.addLayer(object);
layer.object = object;
if (layer.hide) object.setVisible(false);
if (layer.opacity) object.setOpacity(parseFloat(layer.opacity));
}
}
// global markers
globalMarkers = this.newLayer({ id: 'global', type: 'markers'});
map.addLayer(globalMarkers);
}
this.safeExtent = function(extent) {
if (!extent) return false;
if (Array.isArray(extent)) return extent;
else return extent.split(',').map(Number);
}
this.fit = function(extent, options) {
map.getView().fit(this.safeExtent(extent), options);
}
// drag&drop markers
this.startMarkerDrag = function(id, field = null) {
const location = self.dir.l[id];
if (!location) return false;
let marker = location.res.marker;
if (!marker) marker = this.getMarkerHandle(location);
const c = new ol.Collection();
c.push(marker, true);
if (markerDrag) this.stopMarkerDrag(markerDrag);
markerDrag = new ol.interaction.Modify({ features: c });
map.addInteraction(markerDrag);
markerDrag.on('modifyend', function(e) {
const coord = marker.getGeometry().getCoordinates();
const lonlat = ol.proj.toLonLat(coord);
location.res.coord = coord;
if (field) field.value = lonlat[1].toFixed(6) + ',' + lonlat[0].toFixed(6);
});
}
this.stopMarkerDrag = function() {
if (markerDrag) {
map.removeInteraction(markerDrag);
markerDrag = null;
this.hideMarkerHandle();
}
}
this.getMarkerHandle = function(location) {
if (!markerHandle) {
markerHandle = new ol.Feature({ geometry: new ol.geom.Point(s.resolvedPosition(location)) });
globalMarkers.getSource().addFeature(markerHandle);
}
else {
markerHandle.getGeometry().setCoordinates(s.resolvedPosition(location));
markerHandle.setStyle(null);
}
return markerHandle;
}
this.hideMarkerHandle = function() {
if (!markerHandle) return false;
markerHandle.setStyle(new ol.style.Style({}));
}
// edit extent
this.editExtent = function(field) {
if (!field) this.removeExtentInteraction();
let extent = field.value.split(',').map(Number);
if (extentInteraction) {
map.removeInteraction(extentInteraction);
extentInteraction = null;
this.mapView();
this.resetZoom(0);
}
else {
extentInteraction = s.addExtentInteraction(extent);
this.mapView([-18878672.443631265, -9326678.110352932, 21196344.241947223, 19298333.807917416], 1, 20);
map.getView().fit(self.o.extent, {duration: 600, padding: [50, 50, 50, 50]});
extentInteraction.on('extentchanged', function(e) {
field.value = extentInteraction.getExtent();
});
}
}
this.addExtentInteraction = function(extent) {
const extentStyle = new ol.style.Style({
stroke: new ol.style.Stroke({
color: 'rgba(100, 100, 100, 0.4)',
lineJoin: 'miter',
width: 3
})
});
const extentInteraction = new ol.interaction.Extent({
condition: ol.events.condition.shiftKeyOnly,
boxStyle: extentStyle,
extent: extent
});
map.addInteraction(extentInteraction);
return extentInteraction;
}
this.removeExtentInteraction = function() {
map.removeInteraction(extentInteraction);
extentInteraction = null;
}
this.mapView = function(extent = self.o.extent, minZoom = self.o.minZoom, maxZoom = self.o.maxZoom, zoom = 1, center = null, duration = 0) {
if (extent) this.extent = extent;
if (!center) center = ol.extent.getCenter(extent);
let view = new ol.View({
projection: 'EPSG:3857',
center: center,
enableRotation: self.o.rotation,
rotation: self.o.rotate,
extent: extent,
minZoom: minZoom,
maxZoom: maxZoom,
constrainOnlyCenter: true,
padding: self.o.padding,
showFullExtent: self.o.showFullExtent,
zoom: zoom,
constrainResolution: self.o.constrainResolution
});
map.setView(view);
if (duration >= 0) view.fit(this.extent, {duration: duration});
fillZoom = Math.floor(view.getZoomForResolution(view.getResolutionForExtent(extent)));
view.setMinZoom(fillZoom);
view.on('change:resolution', function(e) {
if (!fillZoom) return false;
if (self.o.blurOnZoomOut && view.getZoom() <= fillZoom) self.actions.blur();
});
map.updateSize();
self.el.style.height = 'auto';
return view;
}
this.resize = function() {
let view = map.getView();
let sw = 0; // width of sidebar
if (self.o.sidebar) {
if (self.el.offsetWidth < self.o.portrait) {
if (self.o.portraitSidebarAbove) self.el.insertBefore(self.sidebar.el, self.el.firstChild);
else self.el.appendChild(self.sidebar.el);
self.el.classList.add('mgeo-portrait');
}
else {
sidebarParent.appendChild(self.sidebar.el);
self.el.classList.remove('mgeo-portrait');
sw = self.sidebar.el.offsetWidth;
}
if (self.o.rightSidebar) {
self.o.padding[1] = sw;
this.controls.style.right = sw + 'px';
}
else {
self.o.padding[3] = sw;
this.controls.style.left = sw + 'px';
}
}
let h = (this.el.offsetWidth - sw) / ratio; // auto height
h = Math.min(Math.max(h, self.o.minHeight), self.o.maxHeight);
self.container.el.style.height = h + 'px';
map.updateSize();
s.width = s.el.offsetWidth;
s.height = s.el.offsetHeight;
this.mapView(s.extent || self.o.extent, self.o.minZoom, self.o.maxZoom, view.getZoom());
}
// controls
this.getControlZone = function(name) {
if (zones[name]) return zones[name];
else {
const el = document.createElement('div');
el.classList.add('mgeo-control-zone', name);
this.controls.appendChild(el);
zones[name] = el;
return zones[name];
}
}
this.addSidebarControl = function(el) {
let sidebar = new ol.control.Control({element: el});
map.addControl(sidebar);
sidebarParent = el.parentElement;
el.addEventListener('mouseenter', function() { overControl = true; });
el.addEventListener('mouseleave', function() { overControl = false; });
}
this.resetControl = function() {
const button = document.createElement('button');
button.addEventListener('click', function(e) {
e.preventDefault();
s.resetIsolation();
s.resetZoom();
self.actions.blur();
self.el.dispatchEvent(new CustomEvent('mapreset'));
});
button.appendChild(self.styles.getIcon('reset'));
const element = document.createElement('div');
element.classList.add('ol-reset', 'ol-unselectable', 'ol-control');
element.appendChild(button);
return element;
}
// levels
this.levelSelectorControl = function(layers) {
levelSelector = document.createElement('div');
levelSelector.classList.add('mgeo-level-switcher', 'ol-unselectable', 'ol-control');
for (const layer of layers) {
if (!layer.disabled && layer.selectable) {
const button = document.createElement('button');
button.setAttribute('data-level', layer.id);
button.innerHTML = layer.title;
if (!layer.hide) button.classList.add('mgeo-selected');
button.addEventListener('click', function(e) {
e.preventDefault();
s.switchLevel(layer.id);
});
levelSelector.appendChild(button);
}
}
return levelSelector;
}
this.switchLevel = function(target) {
// hide
const selected = levelSelector?.getElementsByClassName('mgeo-selected')[0];
if (selected) {
selected.classList.remove('mgeo-selected');
s.l[selected.getAttribute('data-level')].object.setVisible(false);
}
// show
s.l[target].object.setVisible(true);
levelSelector?.querySelector('button[data-level="' + target + '"]')?.classList.add('mgeo-selected');
}
this.resetZoom = function(duration = 600) {
map.getView().fit(this.extent, {duration: duration});
}
this.resolvedPosition = function(location) {
if (!location.res) location.res = {};
if (!location.res.coord) {
if (location.coord) {
let coord = location.coord.split(',');
location.res.coord = ol.proj.fromLonLat([coord[1], coord[0]]);
}
else if (location.res.feature) location.res.coord = ol.extent.getCenter(location.res.feature.getGeometry().getExtent());
else return false;
}
return location.res.coord;
}
this.focusLocation = function(location) {
this.setLocationStyle(location, 'active');
}
this.blurLocation = function(location) {
if (location.res?.feature) location.res.feature.highlight = false;
this.setLocationStyle(location, 'base');
}
this.highlightLocation = function(location) {
if (location.res?.marker) location.res.marker.setStyle(this.getMarkerStyle(location, 'base'));
else if (location.res?.feature && location.res?.style) {
location.res.feature.setStyle(this.getStyle(location.res.style, 'hover', location.res.label));
location.res.feature.highlight = true;
}
}
this.disableLocation = function(location) {
if (location.res?.marker) location.res.marker.setStyle(nullStyle);
else if (location.res?.feature && location.res?.style) {
location.res.feature.setStyle(this.getStyle(location.res.style, 'base', location.res.label))
location.res.feature.highlight = false;
}
}
// layer isolation
this.applyIsolation = function(location) {
if (!location) {
this.resetIsolation(-1);
return false;
}
if (!this.l[location.id]?.object) return false;
if (this.isolation) {
if (this.isolation.tid == location.id) return false;
}
let initial = this.isolation.parent,
pid = this.l[location.res.layer].object.parentLayer || location.res.layer;
this.isolation = {
tid: location.id,
target: this.l[location.id]?.object,
pid: pid,
parent: this.l[pid]?.object
}
if (initial) this.isolation.initial = initial;
else this.isolation.initial = this.isolation.parent;
this.isolation.target.setVisible(true);
this.isolation.parent.setVisible(false);
setTimeout(function() {
let view = map.getView();
s.mapView(s.safeExtent(s.l[location.id].extent) || location.res.feature.getGeometry().getExtent(), s.l[location.id].minZoom, s.l[location.id].maxZoom, view.getZoom(), view.getCenter(), -1);
}, 500);
}
this.resetIsolation = function(duration = 400) {
if (!this.isolation) return false;
this.isolation.target.setVisible(false);
this.isolation.initial.setVisible(true);
this.isolation = false;
let view = map.getView();
this.mapView(self.o.extent, self.o.minZoom, self.o.maxZoom, view.getZoom(), view.getCenter(), duration);
}
// hover tooltip
this.hoverTooltip = function() {
hovertip = document.createElement('div');
hovertip.classList.add('mgeo-tooltip', 'mgeo-hover-tooltip');
this.el.appendChild(hovertip);
const title = document.createElement('h4');
title.classList.add('mgeo-title');
hovertip.appendChild(title);
const overlay = new ol.Overlay({
element: hovertip
});
map.addOverlay(overlay);
map.on('pointermove', function(e) {
const feature = this.forEachFeatureAtPixel(e.pixel, function(feature) { return feature; }, { hitTolerance: 4 });
if (feature && feature.getId() && !overControl) {
const location = self.dir.l[feature.getId()];
if (location && location.res.title && location != self.actions.focused) {
location.res.feature = feature;
let coord = e.coordinate;
if (!self.o.hoverTooltipFollow) coord = s.resolvedPosition(location);
title.innerHTML = location.res.title;
overlay.setPosition(coord);
if (location.res.offset) overlay.setOffset(location.res.offset);
hovertip.style.left = Math.round(-hovertip.offsetWidth/2) + 'px';
hovertip.style.opacity = 1;
}
else hovertip.style.opacity = 0;
}
else hovertip.style.opacity = 0;
});
}
this.featureClick = function(e) {
const feature = this.forEachFeatureAtPixel(e.pixel, function(feature) { return feature; }, { hitTolerance: 4 });
if (feature) {
self.el.dispatchEvent(new CustomEvent('featureclick', { detail: { feature: feature, coord: e.coordinate } }));
let location = self.dir.getLocation(feature.getId());
if (location) {
location.res.feature = feature;
self.actions.focus(location);
}
}
}
this.zoomTo = function(location, pt = 0) {
let duration = 500,
padding = [pt, 0, 0, 0],
geometry = location?.res.feature?.getGeometry(),
extent = false;
if (location.res.layer) {
const i = location.res.isolated || location.res.layer;
const isolate = self.dir.l[i]?.action == 'isolate' ? self.dir.l[i] : false;
this.applyIsolation(isolate);
if (!isolate) this.switchLevel(self.container.l[location.res.layer].object.parentLayer || location.res.layer);
else extent = this.safeExtent(self.container.l[isolate.id].extent);
}
const view = map.getView();
if (geometry && geometry.getType() != 'Point') view.fit(extent || geometry.getExtent(), { duration: duration, padding: padding });
else {
let zoom = (location && location.res.zoom) || Math.min(self.o.maxZoom, Math.round(view.getZoom() * 2));
let respos = s.resolvedPosition(location);
if (zoom >= 0 && respos) {
view.fit(new ol.geom.Point(respos), {
padding: padding,
maxZoom: zoom,
duration: duration
});
}
}
}
// tooltip
this.addTooltip = function(location, small = false) {
if (!location) return false;
if (hovertip) hovertip.style.opacity = 0;
tooltip = document.createElement('div');
tooltip.classList.add('mgeo-tooltip', 'mgeo-popup');
tooltip.dataset.location = location.id;
if (location.res.group) tooltip.dataset.group = location.res.group;
this.el.appendChild(tooltip);
let container = document.createElement('div');
container.classList.add('mgeo-popup-container');
let close = document.createElement('button');
close.classList.add('mgeo-close');
close.appendChild(self.styles.getIcon('cross'));
close.addEventListener('click', function(e) {
e.preventDefault();
self.actions.blur();
return false;
});
container.appendChild(close);
if (location.res.title) {
let title = document.createElement('h4');
title.classList.add('mgeo-title');
title.innerHTML = location.res.title;
container.appendChild(title);
}
if (!small) {
if (location.res.image) {
let ic = document.createElement('div');
ic.classList.add('mgeo-tooltip-image');
let image = document.createElement('img');
image.src = location.res.image;
ic.appendChild(image);
tooltip.prepend(ic);
}
if (location.res.about) {
let about = document.createElement('h5');
about.classList.add('mgeo-about');
about.innerHTML = location.res.about;
container.appendChild(about);
}
if (location.res.desc) {
let content = document.createElement('div');
content.classList.add('mgeo-popup-content');
content.innerHTML = location.res.desc;
container.appendChild(content);
}
let buttons = document.createElement('div');
buttons.classList.add('mgeo-buttons');
container.appendChild(buttons);
if (location.res.link) {
let more = document.createElement('a');
more.classList.add('mgeo-more');
more.href = location.res.link;
more.innerHTML = self.localize.more;
if (location.res.fill) more.style.backgroundColor = location.res.fill;
buttons.appendChild(more);
}
}
else tooltip.classList.add('mgeo-tooltip-small');
tooltip.appendChild(container);
overlay.setElement(tooltip);
overlay.setPosition(s.resolvedPosition(location));
if (location.res.offset) overlay.setOffset(location.res.offset);
tooltip.style.left = Math.round(-tooltip.offsetWidth/2) + 'px';
tooltip.style.opacity = 1;
tooltip.addEventListener('mouseenter', function() { overControl = true; });
tooltip.addEventListener('mouseleave', function() { overControl = false; });
if (!small) self.el.dispatchEvent(new CustomEvent('popuprender', { detail: tooltip }));
return tooltip;
}
this.hideTooltip = function() {
let tooltip = overlay.getElement();
if (!tooltip) return false;
tooltip.style.opacity = 0;
tooltip.style.pointerEvents = 'none';
}
// style cache
this.getFill = function(color) {
if (!color) return undefined;
if (!cache.fill[color]) cache.fill[color] = new ol.style.Fill({ color: color });
return cache.fill[color];
}
this.getStroke = function(color, width = 1) {
if (!color) return undefined;
let key = Array.prototype.join.call(arguments, '-');
if (!cache.stroke[key]) cache.stroke[key] = new ol.style.Stroke({ color: color, width: width });
return cache.stroke[key];
}
this.getText = function(text, fill = '#fff', font = 'bold 10px sans-serif', offsetY = 2) {
if (!text) return undefined;
let key = Array.prototype.join.call(arguments, '-');
if (!cache.text[key]) cache.text[key] = new ol.style.Text({ text: text, fill: this.getFill(fill), font: font, offsetY: offsetY });
return cache.text[key];
}
this.getStyle = function(name, state = 'base', text = false) {
let key = Array.prototype.join.call(arguments, '-');
if (cache.style[key]) return cache.style[key];
else {
const style = self.styles.s[name];
if (!style) return false;
else {
cache.style[key] = new ol.style.Style({
fill: this.getFill(style[state]?.fill || style['base']?.fill),
stroke: this.getStroke(style[state]?.stroke || style['base']?.stroke, style['base'].strokeWidth),
text: this.getText(text, style['base'].textColor || '#888', '11px sans-serif'),
zIndex: fzi++
});
return cache.style[key];
}
}
}
this.setLocationStyle = function(location, state = 'base') {
if (!location) return false;
if (location.res?.marker) location.res.marker.setStyle(this.getMarkerStyle(location, state));
else if (location.res?.feature) {
if (location.res.style) {
const text = location.res.type == 'text' ? location.res.label || location.res.title : false;
if (location.res.feature.highlight && state == 'base') state = 'hover';
location.res.feature.setStyle(this.getStyle(location.res.style, state, text));
}
else if (location.res.fill) location.res.feature.setStyle(new ol.style.Style({ fill: this.getFill(location.res.fill), stroke: this.getStroke('#aaa', 1) }));
}
}
this.markers = {
'pin': {'src': self.localize.pdir + '/markers/pin.svg', 'size': [21, 28], 'anchor': [0.5, 1], 'textY': -16 },
'pin1': {'src': self.localize.pdir + '/markers/pin1.svg', 'size': [20, 26], 'anchor': [0.5, 1], 'textY': -16 },
'pin2': {'src': self.localize.pdir + '/markers/pin2.svg', 'size': [22, 26], 'anchor': [0.5, 1], 'textY': -14 },
'pin3': {'src': self.localize.pdir + '/markers/pin3.svg', 'size': [16, 24], 'anchor': [0.5, 1], 'textY': -14 },
'round1': {'src': self.localize.pdir + '/markers/round1.svg', 'size': [20, 20], 'anchor': [0.5, 0.5], 'textY': 2 },
}
this.getMarkerStyle = function(location, state = 'base') {
let style,
base = self.styles.s[location.res.style]?.['base'],
fill = self.styles.s[location.res.style]?.[state]?.fill || base?.fill || location.res?.fill,
textColor = base?.textColor;
const m = this.markers[location.res.type];
const type = m ? 'marker' : location.res.type;
switch (type) {
case 'hidden':
return false;
case 'marker':
location.res.offset = [0, -m.size[1] * m.anchor[1] * Math.min(location.res.scale || 1, 1)];
style = new ol.style.Style({
image: new ol.style.Icon({
anchor: m.anchor,
src: m.src,
scale: Math.min(location.res.scale || 1, 1),
color: fill
}),
text: this.getText(location.res.label, textColor, 'bold 10px sans-serif', m.textY)
});
break;
case 'text':
style = new ol.style.Style({ text: this.getText(location.res?.label, textColor, 'bold 12px sans-serif') });
break;
case 'thumb':
if (!location.res.thumbnail) break;
location.res.offset = [0, -20];
style = new ol.style.Style({
image: new ol.style.Icon({
src: location.res.thumbnail,
scale: location.res.scale || 1,
color: fill
})
});
break;
case 'square':
location.res.offset = [0, -(location.res.scale * 10 || 10)];
style = new ol.style.Style({
image: new ol.style.RegularShape({
fill: this.getFill(fill),
stroke: this.getStroke(self.styles.s[location.res.style]?.['base'].stroke || null, self.styles.s[location.res.style]?.['base'].strokeWidth),
radius: -location.res.offset[1] / 0.785,
angle: 0.785,
points: 4
}),
text: this.getText(location.res?.label, textColor)
});
break;
case 'dot-l':
case 'dot-r':
location.res.offset = [0, -(location.res.scale * 5 || 4)];
let text = this.getText(location.res?.label, textColor || '#000', '12px sans-serif', 1);
if (text) {
if (type == 'dot-r') {
text.setTextAlign('right');
text.setOffsetX(location.res.offset[1]-4);
}
else {
text.setTextAlign('left');
text.setOffsetX(-location.res.offset[1]+4);
}
text.setBackgroundFill(this.getFill('rgba(255,255,255,0.01)'));
}
style = new ol.style.Style({
image: new ol.style.Circle({
fill: this.getFill(fill),
stroke: this.getStroke(self.styles.s[location.res.style]?.['base'].stroke || null, self.styles.s[location.res.style]?.['base'].strokeWidth),
radius: -location.res.offset[1]
}),
text: text
});
break;
case 'circle':
default:
location.res.offset = [0, -(location.res.scale * 10 || 10)];
style = new ol.style.Style({
image: new ol.style.Circle({
fill: this.getFill(fill),
stroke: this.getStroke(self.styles.s[location.res.style]?.['base'].stroke || null, self.styles.s[location.res.style]?.['base'].strokeWidth),
radius: -location.res.offset[1]
}),
text: this.getText(location.res?.label, textColor)
});
}
if (state == 'active') style.setZIndex(fzi++);
return style;
}
// markers
this.addMarkers = function(locations) {
if (!locations) return false;
locations.forEach(function(location) { if (!location.res.disabled && location.id != location.sample) self.container.addMarker(location); });
}
this.addMarker = function(location) {
const res = s.resolvedPosition(location);
if (!location || !location.res.type || location.res.type == 'text') return false;
const f = new ol.Feature({ geometry: new ol.geom.Point(res) });
let style = this.getMarkerStyle(location);
if (!style) return false;
style.setZIndex(fzi++);
f.setStyle(style);
f.setId(location.id);
location.res.marker = f;
const layer = s.l[location?.res?.layer]?.object?.getSource() || globalMarkers.getSource();
layer.addFeature(f);
}
this.removeMarker = function(location) {
const layer = s.l[location?.res?.layer]?.object?.getSource() || globalMarkers.getSource();
layer.removeFeature(location.res.marker)
}
this.getCoord = function(coord = map.getView().getCenter()) {
const pair = ol.proj.toLonLat(coord);
return pair[1].toFixed(6) + ',' + pair[0].toFixed(6);
}
}
}
document.querySelectorAll('.mgeo-map').forEach(function(el) {
let geo = new MapplicGeo(el);
geo.init();
});
})();