'use strict'; function _typeof(obj) { '@babel/helpers - typeof'; if (typeof Symbol === 'function' && typeof Symbol.iterator === 'symbol') { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === 'function' && obj.constructor === Symbol && obj !== Symbol.prototype ? 'symbol' : typeof obj; }; } return _typeof(obj); } /** * Geocluster function. Original: * https://github.com/yetzt/node-geocluster * and adapted to work with iMaps */ function geocluster(elements, bias, defaults, tooltipTemplate) { bias = parseFloat(bias); if (!(this instanceof geocluster)) return new geocluster(elements, bias, defaults, tooltipTemplate); return this._cluster(elements, bias, defaults, tooltipTemplate); } // degrees to radians geocluster.prototype._toRad = function (number) { return (number * Math.PI) / 180; }; // geodetic distance approximation geocluster.prototype._dist = function (lat1, lon1, lat2, lon2) { var dlat = this._toRad(lat2 - lat1); var dlon = this._toRad(lon2 - lon1); var a = Math.sin(dlat / 2) * Math.sin(dlat / 2) + Math.sin(dlon / 2) * Math.sin(dlon / 2) * Math.cos(this._toRad(lat1)) * Math.cos(this._toRad(lat2)); return Math.round(2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)) * 6371 * 100) / 100; }; geocluster.prototype._centroid = function (set) { var cetroidObj = Object.values(set).reduce( function (accumulator, currentValue) { return { latitude: accumulator.latitude + currentValue.latitude, longitude: accumulator.longitude + currentValue.longitude, }; }, { latitude: 0, longitude: 0, } ); cetroidObj = Object.values(cetroidObj).map(function (e) { return e / Object.keys(set).length; }); return cetroidObj; }; geocluster.prototype._clean = function (data) { return data.map(function (cluster) { return [cluster.centroid, cluster.elements]; }); }; geocluster.prototype._cluster = function (elements, bias, defaults, tooltipTemplate) { var self = this, cluster_map_collection = []; var tempMarker; // set bias to 1 on default if (typeof bias !== 'number' || isNaN(bias)) bias = 1; var tot_diff = 0; var diffs = []; var diff; // calculate sum of differences for (var i = 1; i < elements.length; i++) { diff = self._dist( elements[i].latitude, elements[i].longitude, elements[i - 1].latitude, elements[i - 1].longitude ); tot_diff += diff; diffs.push(diff); } // calculate mean diff var mean_diff = tot_diff / diffs.length; var diff_variance = 0; // calculate variance total diffs.forEach(function (diff) { diff_variance += Math.pow(diff - mean_diff, 2); }); // derive threshold from stdev and bias - modified to allow bias to be more decisive var diff_stdev = Math.sqrt(diff_variance / diffs.length); var threshold = 10000 * bias; var cluster_map = []; // generate random initial cluster map cluster_map.push({ centroid: elements[Math.floor(0.5 * elements.length)], elements: [], fill: defaults.fill, hover: defaults.hover, radius: defaults.radius, }); // loop elements and distribute them to clusters var changing = true; while (changing === true) { var new_cluster = false; var cluster_changed = false; // iterate over elements elements.forEach(function (e, ei) { var closest_dist = Infinity; var closest_cluster = null; // find closest cluster cluster_map.forEach(function (cluster, ci) { // distance to cluster var dist = self._dist( e.latitude, e.longitude, cluster_map[ci].centroid.latitude, cluster_map[ci].centroid.longitude ); if (dist < closest_dist) { closest_dist = dist; closest_cluster = ci; } }); // is the closest distance smaller than the stddev of elements? if (closest_dist < threshold || closest_dist === 0) { // put element into existing cluster cluster_map[closest_cluster].elements.push(e); } else { // create a new cluster with this element cluster_map.push({ centroid: e, elements: [e], }); new_cluster = true; } }); // delete empty clusters from cluster_map cluster_map = cluster_map.filter(function (cluster) { return cluster.elements.length > 0; }); // calculate the clusters centroids and check for change cluster_map.forEach(function (cluster, ci) { var centroid = self._centroid(cluster.elements); changing = false; if ( centroid.latitude !== cluster.centroid.latitude || centroid.longitude !== cluster.centroid.longitude ) { cluster_map[ci].centroid = centroid; cluster_changed = true; } }); // loop cycle if clusters have changed if (!cluster_changed && !new_cluster) { changing = false; } else { // remove all elements from clusters and run again if (changing) cluster_map = cluster_map.map(function (cluster) { cluster.elements = []; return cluster; }); } } cluster_map = cluster_map.map(function (cluster) { if (cluster.elements.length === 1) { cluster_map_collection.push(cluster.elements[0]); } else { tempMarker = { id: '', label: cluster.elements.length, name: '', value: cluster.elements.length, cluster: true, latitude: cluster.centroid[0], longitude: cluster.centroid[1], elements: cluster.elements, content: '', fill: defaults.fill, hover: defaults.hover, radius: defaults.radius, action: '', }; // in case we have a custom tooltipTemplate if (tooltipTemplate) { tempMarker.tooltipTemplate = tooltipTemplate; } cluster_map_collection.push(tempMarker); } }); // compress result return cluster_map_collection; }; /* ROUTER */ var iMapsRouter = {}; iMapsRouter.getGeoFiles = function (regionClicked) { var regionID = regionClicked.id.toString(), regionName = regionClicked.name, urlappend, varappend, geoFiles = {}, continents = [ 'southAmerica', 'northAmerica', 'europe', 'middleEast', 'asia', 'oceania', 'africa', 'antarctica', ]; // continents if (continents.includes(regionID)) { urlappend = 'region/world/'; varappend = '_region_world_'; } // us maps else if (regionID.includes('US-')) { urlappend = 'region/usa/'; varappend = '_region_usa_'; regionID = regionID.substr(-2).toLowerCase(); } else { urlappend = ''; varappend = '_'; regionID = regionName.toLowerCase().replace('united states', 'usa'); } geoFiles.src = 'https://cdn.amcharts.com/lib/4/geodata/' + urlappend + regionID + 'Low.js'; geoFiles.map = 'am4geodata' + varappend + regionID + 'Low'; geoFiles.title = regionName; return geoFiles; }; iMapsRouter.getAllSrc = function () { var sources = {}, paths = { main: '../../vendor/geodata', usa: '../../vendor/geodata/region/usa', world: '../../vendor/geodata/region/world', }; var fs = require('fs'); Object.keys(paths).forEach(function (item) { sources[item] = []; fs.readdir(paths[item], function (err, files) { files.forEach(function (file) { sources[item].push(file); }); }); }); return sources; }; /** * get map am4charts variable name based on name value */ iMapsRouter.getVarByName = function (variable) { if (typeof variable === 'undefined') { return 'am4geodata_worldLow'; } else if (variable.includes('custom')) { return variable; } variable = variable.replace(/\//g, '_'); return 'am4geodata_' + variable; }; iMapsRouter.getCleanMapName = function (mapName, id) { if (typeof mapName === 'undefined') { return false; } mapName = mapName.replace('Low', ''); mapName = mapName.replace('High', ''); if (mapName === 'custom') { mapName += id; } return mapName; }; iMapsRouter.iso2cleanName = function (iso, mapID) { var countries = iMapsRouter.getCountries(); var continents = [ 'africa', 'antarctiva', 'asia', 'europe', 'middleEast', 'northAmerica', 'oceania', 'southAmerica', 'centralAmerica', ]; var tempIso; var series = iMapsManager.maps[mapID].seriesIndex; var match = false; console.log('ISO: ' + iso); // exceptions if ('VA' === iso) { return 'vatican'; } else if ('US' === iso) { return 'usa'; } else if (iso.includes('CA-')) { return 'region/canada/' + iso.replace('CA-', '').toLowerCase(); } else if (iso.includes('MX-')) { return 'region/mexico/' + iso.replace('MX-', '').toLowerCase(); } else if (continents.includes(iso)) { return 'region/world/' + iso; } else if (iso.includes('US-')) { tempIso = [ 'region/usa/' + iso.replace('US-', '').toLowerCase(), 'region/usa/congressional/' + iso.replace('US-', '').toLowerCase(), 'region/usa/congressional2022/' + iso.replace('US-', '').toLowerCase(), ]; } else if ('GB' === iso) { tempIso = ['uk', 'ukCountries', 'ukCounties']; } else if ('BA' === iso) { tempIso = ['bosniaHerzegovinaCantons', 'bosniaHerzegovina']; } else if ('IL' === iso) { tempIso = ['israel', 'israelPalestine']; } else if ('SI' === iso) { tempIso = ['sloveniaRegions', 'slovenia']; } else if ('IN' === iso) { tempIso = ['india2019', 'india', 'india2020']; } else if ('AE' === iso) { tempIso = ['unitedArabEmirates', 'uae']; } else if ('FR' === iso) { tempIso = ['franceDepartments', 'france']; } else if ('MG' === iso) { tempIso = ['madagascarProvince', 'madagascarRegion']; } else if ('PT' === iso) { tempIso = ['portugal', 'portugalRegions']; } else if ('RS' === iso) { tempIso = ['serbia', 'serbiaNoKosovo']; } else if ('CD' === iso) { tempIso = ['congoDR']; } else if ('CG' === iso) { tempIso = ['congo']; } else if ('CZ' === iso) { tempIso = ['czechRepublic', 'czechia']; } else if ('MM' === iso) { tempIso = ['myanmar']; } // the rest // this block needs reviewing.. if/else are a mess if (Array.isArray(tempIso)) { tempIso.forEach(function (item, index) { if (series.hasOwnProperty(item)) { match = item; } }); if (match) { return match; } else { if (countries.hasOwnProperty(iso)) { return iMapsRouter.camelize(countries[iso]); } } } else { if (countries.hasOwnProperty(iso)) { return iMapsRouter.camelize(countries[iso]); } } return false; }; iMapsRouter.getCountries = function () { var rawjson = '{"AF":"Afghanistan","AX":"\xC5land Islands","AL":"Albania","DZ":"Algeria","AS":"American Samoa","AD":"Andorra","AO":"Angola","AI":"Anguilla","AQ":"Antarctica","AG":"Antigua & Barbuda","AR":"Argentina","AM":"Armenia","AW":"Aruba","AC":"Ascension Island","AU":"Australia","AT":"Austria","AZ":"Azerbaijan","BS":"Bahamas","BH":"Bahrain","BD":"Bangladesh","BB":"Barbados","BY":"Belarus","BE":"Belgium","BZ":"Belize","BJ":"Benin","BM":"Bermuda","BT":"Bhutan","BO":"Bolivia","BA":"Bosnia & Herzegovina","BW":"Botswana","BR":"Brazil","IO":"British Indian Ocean Territory","VG":"British Virgin Islands","BN":"Brunei","BG":"Bulgaria","BF":"Burkina Faso","BI":"Burundi","KH":"Cambodia","CM":"Cameroon","CA":"Canada","IC":"Canary Islands","CV":"Cape Verde","BQ":"Caribbean Netherlands","KY":"Cayman Islands","CF":"Central African Republic","EA":"Ceuta & Melilla","TD":"Chad","CL":"Chile","CN":"China","CX":"Christmas Island","CC":"Cocos (Keeling) Islands","CO":"Colombia","KM":"Comoros","CG":"Congo - Brazzaville","CD":"Congo - Kinshasa","CK":"Cook Islands","CR":"Costa Rica","CI":"C\xF4te d\u2019Ivoire","HR":"Croatia","CU":"Cuba","CW":"Cura\xE7ao","CY":"Cyprus","CZ":"Czechia","DK":"Denmark","DG":"Diego Garcia","DJ":"Djibouti","DM":"Dominica","DO":"Dominican Republic","EC":"Ecuador","EG":"Egypt","SV":"El Salvador","GQ":"Equatorial Guinea","ER":"Eritrea","EE":"Estonia","SZ":"Eswatini","ET":"Ethiopia","FK":"Falkland Islands","FO":"Faroe Islands","FJ":"Fiji","FI":"Finland","FR":"France","GF":"French Guiana","PF":"French Polynesia","TF":"French Southern Territories","GA":"Gabon","GM":"Gambia","GE":"Georgia","DE":"Germany","GH":"Ghana","GI":"Gibraltar","GR":"Greece","GL":"Greenland","GD":"Grenada","GP":"Guadeloupe","GU":"Guam","GT":"Guatemala","GG":"Guernsey","GN":"Guinea","GW":"Guinea-Bissau","GY":"Guyana","HT":"Haiti","HN":"Honduras","HK":"Hong Kong SAR China","HU":"Hungary","IS":"Iceland","IN":"India","ID":"Indonesia","IR":"Iran","IQ":"Iraq","IE":"Ireland","IM":"Isle of Man","IL":"Israel","IT":"Italy","JM":"Jamaica","JP":"Japan","JE":"Jersey","JO":"Jordan","KZ":"Kazakhstan","KE":"Kenya","KI":"Kiribati","XK":"Kosovo","KW":"Kuwait","KG":"Kyrgyzstan","LA":"Laos","LV":"Latvia","LB":"Lebanon","LS":"Lesotho","LR":"Liberia","LY":"Libya","LI":"Liechtenstein","LT":"Lithuania","LU":"Luxembourg","MO":"Macao SAR China","MG":"Madagascar","MW":"Malawi","MY":"Malaysia","MV":"Maldives","ML":"Mali","MT":"Malta","MH":"Marshall Islands","MQ":"Martinique","MR":"Mauritania","MU":"Mauritius","YT":"Mayotte","MX":"Mexico","FM":"Micronesia","MD":"Moldova","MC":"Monaco","MN":"Mongolia","ME":"Montenegro","MS":"Montserrat","MA":"Morocco","MZ":"Mozambique","MM":"Myanmar (Burma)","NA":"Namibia","NR":"Nauru","NP":"Nepal","NL":"Netherlands","NC":"New Caledonia","NZ":"New Zealand","NI":"Nicaragua","NE":"Niger","NG":"Nigeria","NU":"Niue","NF":"Norfolk Island","KP":"North Korea","MK":"North Macedonia","MP":"Northern Mariana Islands","NO":"Norway","OM":"Oman","PK":"Pakistan","PW":"Palau","PS":"Palestinian Territories","PA":"Panama","PG":"Papua New Guinea","PY":"Paraguay","PE":"Peru","PH":"Philippines","PN":"Pitcairn Islands","PL":"Poland","PT":"Portugal","XA":"Pseudo-Accents","XB":"Pseudo-Bidi","PR":"Puerto Rico","QA":"Qatar","RE":"R\xE9union","RO":"Romania","RU":"Russia","RW":"Rwanda","WS":"Samoa","SM":"San Marino","ST":"S\xE3o Tom\xE9 & Pr\xEDncipe","SA":"Saudi Arabia","SN":"Senegal","RS":"Serbia","SC":"Seychelles","SL":"Sierra Leone","SG":"Singapore","SX":"Sint Maarten","SK":"Slovakia","SI":"Slovenia","SB":"Solomon Islands","SO":"Somalia","ZA":"South Africa","GS":"South Georgia & South Sandwich Islands","KR":"South Korea","SS":"South Sudan","ES":"Spain","LK":"Sri Lanka","BL":"St Barth\xE9lemy","SH":"St Helena","KN":"St Kitts & Nevis","LC":"St Lucia","MF":"St Martin","PM":"St Pierre & Miquelon","VC":"St Vincent & Grenadines","SD":"Sudan","SR":"Suriname","SJ":"Svalbard & Jan Mayen","SE":"Sweden","CH":"Switzerland","SY":"Syria","TW":"Taiwan","TJ":"Tajikistan","TZ":"Tanzania","TH":"Thailand","TL":"Timor-Leste","TG":"Togo","TK":"Tokelau","TO":"Tonga","TT":"Trinidad & Tobago","TA":"Tristan da Cunha","TN":"Tunisia","TR":"Turkey","TM":"Turkmenistan","TC":"Turks & Caicos Islands","TV":"Tuvalu","UG":"Uganda","UA":"Ukraine","AE":"United Arab Emirates","GB":"United Kingdom","US":"United States","UY":"Uruguay","UM":"US Outlying Islands","VI":"US Virgin Islands","UZ":"Uzbekistan","VU":"Vanuatu","VA":"Vatican City","VE":"Venezuela","VN":"Vietnam","WF":"Wallis & Futuna","EH":"Western Sahara","YE":"Yemen","ZM":"Zambia","ZW":"Zimbabwe"}'; return JSON.parse(rawjson); }; iMapsRouter.camelize = function (str) { return str .replace(/(?:^\w|[A-Z]|\b\w)/g, function (word, index) { return index == 0 ? word.toLowerCase() : word.toUpperCase(); }) .replace(/\s+/g, ''); }; /** * File that contains functions to prepare raw data that arrives in a javascript variable. * 1) Convert color strings to color objects * */ var iMapsModel = {}; iMapsModel.prepareData = function (fullData) { if (!fullData) { return fullData; } if (!Array.isArray(fullData)) { return fullData; } fullData.forEach(function (data, index) { fullData[index] = iMapsModel.iterateData(data); if (Array.isArray(fullData[index].overlay) && fullData[index].overlay.length > 0) { fullData[index].overlay.forEach(function (odata, ind) { // if the overlay map is the same as base map, only include the active regions if ( fullData[index].overlay[ind].map === fullData[index].map && !iMapsManager.bool(fullData[index].allowEmpty) ) { fullData[index].overlay[ind].include = iMapsModel.prepareOverlayInclude(odata); } fullData[index].overlay[ind] = iMapsModel.iterateData(odata); }); } }); return fullData; }; iMapsModel.prepareOverlayInclude = function (data) { var includes = data.includes || ''; if (data.regions) { data.regions.forEach(function (region, index) { includes += ',' + region.id; }); } return includes; }; iMapsModel.iterateData = function (data) { // check if there's a custom callback function to prepare the data if (typeof igm_custom_filter === 'function') { data = igm_custom_filter(data); } // check if there's a map specific custom callback function var mapFunction = 'igm_custom_filter_' + data.id; if (typeof window[mapFunction] === 'function') { data = window[mapFunction](data); } // create color objects data = iMapsModel.prepareColor(data); // convert exclude and includes to array data = iMapsModel.prepareExcludeIncludes(data); // convert coordinates to int data = iMapsModel.coordinatesToInt(data); // prepare entries that use defaults data = iMapsModel.prepareEntriesData(data); // prepare grouped regions data = iMapsModel.prepareGroupedRegions(data); //extract image source from imageMarkers data = iMapsModel.prepareImageFields(data); //prepare lines multiGeoLine format data = iMapsModel.prepareMultiGeoLine(data); return data; }; iMapsModel.prepareMultiGeoLine = function (data) { if (Array.isArray(data.lines) && data.lines.length) { //make sure we only get entries with geoline data data.lines = data.lines.filter(function (line) { if (Array.isArray(line.multiGeoLine) && line.multiGeoLine.length) { return line; } }); data.lines.map(function (line) { // notice the arrow option needs to be reversed, because it controls the arrow.disabled option when rendered line.arrow = typeof line.arrow !== 'undefined' ? iMapsManager.bool(line.arrow) : false; line.arrowDisabled = !line.arrow; line.multiGeoLine.map(function (geoLine) { Object.assign(geoLine, geoLine.coordinates); delete geoLine.coordinates; }); }); } return data; }; iMapsModel.prepareImageFields = function (data) { // image markers if (Array.isArray(data.imageMarkers) && Array.isArray(data.imageMarkers)) { data.imageMarkers.map(function (marker) { if ( typeof marker.href === 'undefined' && marker.image && (Array.isArray(marker.image) || typeof marker.image === 'object') ) { marker.href = marker.image.url; } return marker; }); } return data; }; iMapsModel.prepareGroupedRegions = function (data) { var regions, tempRegion, group, groupIds, groupName; // regions if (Array.isArray(data.regions) && data.regions.length) { if (typeof data.regionGroups === 'undefined') { data.regionGroups = []; } // if the option to group entries is enabled, cosider them as a group entry if (iMapsManager.bool(data.regionsGroupHover)) { data.regionGroups.push(data.regions); return data; } data.regions.map(function (region, index) { if (region.id && String(region.id).includes(',')) { group = []; groupIds = []; regions = region.id.split(','); groupName = region.name; regions.forEach(function (reg, ix) { tempRegion = Object.assign({}, region); tempRegion.id = reg.trim(); // keep some original values tempRegion.originalID = region.id; tempRegion.groupName = groupName; // delete the group name, to keep region name delete tempRegion.name; delete region.name; // check if numeric if (!isNaN(tempRegion.id)) { // tempRegion.id = parseFloat( tempRegion.id ); } // check if not repeated if (!groupIds.includes(tempRegion.id)) { groupIds.push(tempRegion.id); group.push(tempRegion); } }); // add group to main data object // finally add it to the main data object data.regionGroups.push(group); // remove this region from the default regions data set data.regions.splice(index, -1); } else { return region; } }); } return data; }; iMapsModel.prepareTooltip = function (tooltipContent, options) { var maxWidth, isMSIE, images, tooltipNode, range, tempDiv; if (typeof options === 'undefined' || typeof tooltipContent === 'undefined') { return tooltipContent; } maxWidth = typeof options.maxWidth !== 'undefined' && options.maxWidth !== '' ? parseInt(options.maxWidth) : false; isMSIE = iMapsModel.isMSIE(); // remove html tags for IE if (isMSIE) { return iMapsModel.removeHTMLtags(tooltipContent); } // check for images if (tooltipContent && tooltipContent.includes(' 0) { images.forEach(function (img) { if (img.width !== 0 && img.style.width === '') { img.style.width = img.width + 'px'; } if (img.height !== 0 && img.style.height === '') { img.style.height = img.height + 'px'; } }); tempDiv = document.createElement('div'); tempDiv.appendChild(tooltipNode.cloneNode(true)); tooltipContent = tempDiv.innerHTML; } } // tooltip if (maxWidth && tooltipContent !== '' && !isMSIE) { tooltipContent = '
' + tooltipContent + '
'; } return tooltipContent; }; iMapsModel.isMSIE = function () { return window.navigator.userAgent.match(/(MSIE|Trident)/); }; iMapsModel.removeHTMLtags = function (str) { if (str === null || str === '') return ''; else str = str.toString(); return str.replace(/<[^>]*>/g, ''); }; iMapsModel.prepareEntriesData = function (data) { if (data.heatMapMarkers && data.heatMapMarkers.enabled === '1') { data.heatMapMarkers.minRadius = parseInt(data.heatMapMarkers.minRadius); data.heatMapMarkers.maxRadius = parseInt(data.heatMapMarkers.maxRadius); } var mapID = data.id; // regions if (Array.isArray(data.regions) && data.regions.length) { data.regions.map(function (region) { if (typeof region.useDefaults === 'undefined' || region.useDefaults === '1') { Object.assign(region, data.regionDefaults); } if (typeof region.action !== 'undefined' && region.action === 'default') { region.action = data.regionDefaults.action; } if ( typeof data.regionDefaults.triggerClickOnHover !== 'undefined' && data.regionDefaults.triggerClickOnHover === '1' ) { region.triggerClickOnHover = true; } if (typeof data.regionActiveState !== 'undefined' && data.regionActiveState.enabled === '1') { region.activeState = true; } // auto labels properties if ( data.regionLabels && data.regionLabels.source === 'custom' && typeof data.regionLabels.customSource !== 'undefined' ) { if (data.regionLabels.customSource.includes('.')) { region.autoLabel = data.regionLabels.customSource.split('.').reduce(function (obj, i) { if (obj[i]) { return obj[i]; } return 0; }, region); } else { region.autoLabel = region[data.regionLabels.customSource]; } } if (data.heatMapRegions && data.heatMapRegions.enabled === '1') { // if it includes a reference with dot notation if (data.heatMapRegions.source.includes('.')) { region.heatMapRegionRef = data.heatMapRegions.source.split('.').reduce(function (obj, i) { if (obj[i]) { return obj[i]; } return 0; }, region); } delete region.fill; } // default action // border hover - currently doesn't exist if ( typeof data.visual.borderColorHover !== 'undefined' && data.visual.borderColorHover !== data.visual.borderColor ) { region.borderColorHover = data.visual.borderColorHover; } // border width on hover - currently doesn't exist if ( typeof data.visual.borderWidthHover !== 'undefined' && data.visual.borderWidthHover !== data.visual.borderWidth ) { region.borderWidthHover = data.visual.borderWidthHover; } region.tooltipContent = iMapsModel.prepareTooltip(region.tooltipContent, data.tooltip); region.mapID = mapID; // fix for value with "-" minus sign if (region.value && _typeof(region.value) !== undefined && region.value !== '') { region.val = region.value; region.value = parseFloat(region.value); } return region; }); // heatmap with dot notation // if it includes a reference with dot notation if (data.heatMapRegions && data.heatMapRegions.source.includes('.')) { data.heatMapRegions.source = 'heatMapRegionRef'; } } // round markers if (Array.isArray(data.roundMarkers) && data.roundMarkers.length) { data.roundMarkers.map(function (marker) { if (typeof marker.useDefaults === 'undefined' || marker.useDefaults === '1') { Object.assign(marker, data.markerDefaults); } if (marker.coordinates) { marker.latitude = marker.coordinates.latitude; marker.longitude = marker.coordinates.longitude; //Object.assign(marker, marker.coordinates); //delete marker.coordinates; } // set name to be id if (typeof marker.latitude === 'string') { marker.latitude = parseFloat(marker.latitude); } if (typeof marker.longitude === 'string') { marker.longitude = parseFloat(marker.latitude); } if (typeof marker.name === 'undefined') { marker.name = marker.id; } if (data.roundMarkersMobileSize && parseInt(data.roundMarkersMobileSize) !== 100) { if (window.innerWidth <= 780) { marker.radius = (parseFloat(marker.radius) * parseFloat(data.roundMarkersMobileSize)) / 100; } } if (data.heatMapMarkers && data.heatMapMarkers.enabled === '1') { if (!isNaN(marker.value)) { marker.value = parseFloat(marker.value); } // if it includes a reference with dot notation if (data.heatMapMarkers.source.includes('.')) { marker.heatMapMarkerRef = data.heatMapMarkers.source.split('.').reduce(function (obj, i) { if (obj[i]) { return obj[i]; } return 0; }, marker); } delete marker.fill; delete marker.radius; } // fix for value with "-" minus sign if (marker.value && _typeof(marker.value) !== undefined && marker.value !== '') { marker.val = marker.value; marker.value = parseFloat(marker.value); } // default action if (marker.action === 'default') { marker.action = data.markerDefaults.action; } if ( typeof data.markerDefaults.triggerClickOnHover !== 'undefined' && data.markerDefaults.triggerClickOnHover === '1' ) { marker.triggerClickOnHover = true; } if ( typeof data.triggerRegionHover !== 'undefined' && data.triggerRegionHover.enabled === '1' ) { marker.triggerRegionHover = true; } marker.tooltipContent = iMapsModel.prepareTooltip(marker.tooltipContent, data.tooltip); marker.mapID = mapID; return marker; }); // remove markers with empty coordinates data.roundMarkers = data.roundMarkers.filter(function (marker) { return typeof marker.latitude !== 'undefined' && marker.latitude !== '' && marker.name !== ''; }); // heatmap with dot notation // if it includes a reference with dot notation if (data.heatMapMarkers && data.heatMapMarkers.source.includes('.')) { data.heatMapMarkers.source = 'heatMapMarkerRef'; } } // icon markers if (Array.isArray(data.iconMarkers) && data.iconMarkers.length) { data.iconMarkers.map(function (marker) { if (typeof marker.useDefaults === 'undefined' || marker.useDefaults === '1') { Object.assign(marker, data.iconMarkerDefaults); } if (marker.coordinates) { marker.latitude = marker.coordinates.latitude; marker.longitude = marker.coordinates.longitude; //Object.assign(marker, marker.coordinates); //delete marker.coordinates; } // set name to be id if (typeof marker.name === 'undefined') { marker.name = marker.id; } // default action if (marker.action === 'default') { marker.action = data.iconMarkerDefaults.action; } if ( typeof data.iconMarkerDefaults.triggerClickOnHover !== 'undefined' && data.iconMarkerDefaults.triggerClickOnHover === '1' ) { marker.triggerClickOnHover = true; } // Possible approach to resize icon marker on smaller screens, but won't resize on the fly if (data.iconMarkersMobileSize && parseInt(data.iconMarkersMobileSize) !== 100) { if (window.innerWidth <= 780) { marker.scale = (parseFloat(marker.scale) * parseFloat(data.iconMarkersMobileSize)) / 100; } } // solve issue with value parameter only accepting numbers marker.val = marker.value; marker.value = parseFloat(marker.value); marker.tooltipContent = iMapsModel.prepareTooltip(marker.tooltipContent, data.tooltip); marker.mapID = mapID; return marker; }); } // image markers if (Array.isArray(data.imageMarkers) && data.imageMarkers.length) { data.imageMarkers.map(function (marker) { if (typeof marker.useDefaults === 'undefined' || marker.useDefaults === '1') { Object.assign(marker, data.imageMarkerDefaults); } if (marker.coordinates) { marker.latitude = marker.coordinates.latitude; marker.longitude = marker.coordinates.longitude; //Object.assign(marker, marker.coordinates); //delete marker.coordinates; } // set name to be id if (typeof marker.nonScaling !== 'undefined') { marker.nonScaling = iMapsManager.bool(marker.nonScaling); } if (typeof marker.size === 'undefined') { marker.size = data.imageMarkerDefaults.size; } if (typeof marker.horizontalCenter === 'undefined') { marker.horizontalCenter = data.imageMarkerDefaults.horizontalCenter; } if (typeof marker.verticalCenter === 'undefined') { marker.verticalCenter = data.imageMarkerDefaults.verticalCenter; } if (typeof marker.name === 'undefined') { marker.name = marker.id; } if (marker.action === 'default' || typeof marker.action === 'undefined') { marker.action = data.imageMarkerDefaults.action; } if ( typeof data.imageMarkerDefaults.triggerClickOnHover !== 'undefined' && data.imageMarkerDefaults.triggerClickOnHover === '1' ) { marker.triggerClickOnHover = true; } // Possible approach to resize icon marker on smaller screens, but won't resize on the fly if (data.imageMarkersMobileSize && parseInt(data.imageMarkersMobileSize) !== 100) { if (window.innerWidth <= 780) { marker.size = (parseFloat(marker.size) * parseFloat(data.imageMarkersMobileSize)) / 100; } } // solve problem of value param only accepting numbers marker.val = marker.value; marker.value = parseFloat(marker.value); marker.tooltipContent = iMapsModel.prepareTooltip(marker.tooltipContent, data.tooltip); marker.mapID = mapID; return marker; }); } // labels if (Array.isArray(data.labels) && data.labels.length) { data.labels.map(function (label) { if (typeof label.useDefaults === 'undefined' || label.useDefaults === '1') { Object.assign(label, data.labelDefaults); Object.assign(label, data.labelPosition); } if (label.coordinates) { Object.assign(label, label.coordinates); delete label.coordinates; } if (label.action === 'default') { label.action = data.labelDefaults.action; } if ( typeof data.labelDefaults.triggerClickOnHover !== 'undefined' && data.labelDefaults.triggerClickOnHover === '1' ) { label.triggerClickOnHover = true; } if (typeof label.size !== 'undefined') { label.fontSize = label.size; } // change font size on smaller screens if (data.labelsMobileSize && parseInt(data.labelsMobileSize) !== 100) { if (window.innerWidth <= 780) { label.fontSize = (parseInt(label.fontSize) * parseInt(data.labelsMobileSize)) / 100; label.size = label.fontSize; } } label.tooltipContent = iMapsModel.prepareTooltip(label.tooltipContent, data.tooltip); label.mapID = mapID; return label; }); } // lines if (Array.isArray(data.lines) && data.lines.length) { data.lines.map(function (line) { if (typeof line.useDefaults === 'undefined' || line.useDefaults === '1') { Object.assign(line, data.lineDefaults); } line.curvature = parseFloat(line.curvature); return line; }); } return data; }; iMapsModel.prepareExcludeIncludes = function (data) { if (typeof data.onlyIncludeActive !== 'undefined' && parseInt(data.onlyIncludeActive) === 1) { data.include = []; data.exclude = []; if (data.regions) { data.regions.forEach(function (region, index) { data.include.push(region.id); if (!isNaN(region.id)) { data.include.push(parseInt(region.id)); } }); } return data; } if (data.exclude && typeof data.exclude === 'string' && data.exclude.trim().length) { data.exclude = data.exclude.split(',').map(function (item) { return item.trim(); }); } if (data.include && data.include.trim().length) { data.include = data.include.split(',').map(function (item) { return item.trim(); }); data.include.map(function (item) { if (!isNaN(item)) { data.include.push(parseInt(item)); } }); } return data; }; iMapsModel.coordinatesToInt = function (data) { var convertCoordinates = function convertCoordinates(key, obj) { obj[key].latitude = Number(obj[key].latitude); obj[key].longitude = Number(obj[key].longitude); }, iterateObj = function iterateObj(Obj) { if (_typeof(Obj) !== 'object' || Obj === null) { return; } Object.keys(Obj).map(function (key, index) { if (_typeof(Obj[key]) === 'object') { if (key === 'coordinates' || key === 'homeGeoPoint') { convertCoordinates(key, Obj); } else if (typeof Obj['className'] !== 'undefined') { return; } else { iterateObj(Obj[key]); } } }); }; iterateObj(data); return data; }; iMapsModel.prepareColor = function (data) { // prepare color fields var colorFields = [ 'inactiveColor', 'activeColor', 'hoverColor', 'hover', 'inactiveHoverColor', 'backgroundColor', 'color', 'minColor', 'maxColor', 'fill', 'projectionBackgroundColor', 'borderColor', 'borderColorHover', ], createGradient = function createGradient(data) { var colours = data.split('|'); var gradient, gradientType, gradientOffset, colourIndex; gradientType = typeof igmGradientType !== 'undefined' ? igmGradientType : 'LinearGradient'; gradientOffset = typeof igmGradientOffset !== 'undefined' && Array.isArray(igmGradientOffset) ? igmGradientOffset : []; gradient = new am4core[gradientType](); colours.forEach(function (color, index) { gradient.addColor(am4core.color(color), 1, gradientOffset[index]); }); //rotation gradient.rotation = typeof igmGradientRotation !== 'undefined' ? igmGradientRotation : 0; return gradient; }, createPattern = function createPattern(data) { var url = data; // Create pattern var pattern = new am4core.Pattern(); pattern.width = 150; pattern.height = 150; pattern.strokeWidth = 0; pattern.stroke = am4core.color('#6699cc'); //pattern.patternUnits = 'objectBoundingBox'; // objectBoundingBox // userSpaceOnUse var image = new am4core.Image(); image.href = url; image.width = 150; image.height = 150; image.x = 0; image.y = 0; image.verticalCenter = 'middle'; image.valign = 'middle'; pattern.addElement(image.element); pattern.addElement(image.element); pattern.addElement(image.element); return pattern; }, checkColor = function checkColor(key, obj) { if (colorFields.includes(key)) { if (obj[key].includes('|')) { obj[key] = createGradient(obj[key]); } else if (obj[key].startsWith('http')) { obj[key] = createPattern(obj[key]); } // on hover out, amcharts is not able to resolve transparent colours, in default states, so we prevent this // by setting an almost transparent colour else if (key === 'hover' && (obj[key] === 'transparent' || obj[key].endsWith(',0)'))) { obj[key] = am4core.color('rgba(0,0,0,0.001)'); } else if (obj[key] === 'transparent') { obj[key] = am4core.color('rgba(0,0,0,0)'); } else { obj[key] = am4core.color(obj[key]); } } }, iterateObj = function iterateObj(Obj) { if (_typeof(Obj) !== 'object' || Obj === null) { return; } Object.keys(Obj).map(function (key, index) { if (_typeof(Obj[key]) === 'object') { iterateObj(Obj[key]); } else if (typeof Obj['className'] !== 'undefined') { return; } else { checkColor(key, Obj); } }); }; iterateObj(data); return data; }; /** * Retrieves object with region codes and names from geojson */ iMapsModel.extractCodes = function (data) { var obj = {}; for (var i = 0; i < data.features.length; i++) { obj[data.features[i].properties.id] = data.features[i].properties.name; } return obj; }; ('use strict'); /* jshint node: true */ /* global Promise */ /* jshint browser: true */ /* global am4maps */ /* global am4core */ /* global iMapsData */ /* global geocluster */ /* global iMapsRouter */ /* global iMaps */ /* jshint esversion:5 */ /* global iMapsActions */ /* Model Object with methods and helpers to build the maps */ var iMapsManager = {}; iMapsManager.maps = {}; iMapsManager.init = function (i) { var im = this; im.addMap(i); }; /** * Adds maps based on data index * */ iMapsManager.addMap = function (index) { var im = this, data = iMapsData.data[index], id = data.id, map, regionSeries, groupedSeries, markerSeries, labelSeries, lineSeries, clusters, mapContainer, seriesColumn, legendHover, legendActive, customLegend, imageSeries, iconSeries, graticuleSeries, clusterSeries, mapVar, bgSeries, bgImage, aspRatioContainer, container = document.getElementById(data.container); if (data.disabled) { return; } if (container === null) { return; } aspRatioContainer = container.closest('.map_aspect_ratio'); // if map was already built if (typeof im.maps[id] !== 'undefined') { im.maps[id].map.dispose(); } // map container size adjustment // if mobile if ( window.innerWidth <= 780 && typeof data.visual.paddingTop !== 'undefined' && data.visual.paddingTop !== '' ) { aspRatioContainer.style.paddingTop = String(data.visual.paddingTopMobile) + '%'; } else { aspRatioContainer.style.paddingTop = String(data.visual.paddingTop) + '%'; } window.addEventListener('resize', function () { if ( window.innerWidth <= 780 && typeof data.visual.paddingTop !== 'undefined' && data.visual.paddingTop !== '' ) { aspRatioContainer.style.paddingTop = String(data.visual.paddingTopMobile) + '%'; } else { aspRatioContainer.style.paddingTop = String(data.visual.paddingTop) + '%'; } }); if (data.visual.maxWidth !== '') { //container.closest(".map_box").style.maxWidth = String(data.visual.maxWidth) + "px"; } if (data.visual.fontFamily !== '' && data.visual.fontFamily !== 'inherit') { container.closest('.map_box').style.fontFamily = data.visual.fontFamily; } // create map and a shorter reference to it im.maps[id] = { map: am4core.create(data.container, am4maps.MapChart), series: [], clusterSeries: {}, seriesIndex: {}, seriesById: {}, data: data, allBaseSeries: [], labelSeries: [], baseRegionSeries: {}, groupedBaseRegionSeries: [], backgroundSeries: {}, }; map = im.maps[id].map; map.readerTitle = 'Interactive Map'; clusterSeries = im.maps[id].clusterSeries; // ready event // on click map debug map.events.on('ready', function (ev) { var event = new Event('mapready'); container.dispatchEvent(event); // we might move the event that triggers on ready to mapappeared because it doesn't work well with custom maps im.triggerOnReady(id, data); }); map.events.on('appeared', function (ev) { var event = new Event('mapappeared'); container.dispatchEvent(event); im.triggerOnAppeared(id, data); container.classList.remove('map_loading'); }); // locale if ( typeof iMapsData.options !== 'undefined' && typeof iMapsData.options.locale !== 'undefined' && iMapsData.options.locale && typeof window['am4lang_' + iMapsData.options.locale] !== 'undefined' ) { map.language.locale = window['am4lang_' + iMapsData.options.locale]; } // enable small map if (data.zoom && data.zoom.smallMap && im.bool(data.zoom.smallMap)) { map.smallMap = new am4maps.SmallMap(); } // load map geodata if (data.map === 'custom' || im.bool(data.useGeojson)) { map.geodataSource.url = data.mapURL; } else { mapVar = iMapsRouter.getVarByName(data.map); map.geodata = window[mapVar]; } if ( typeof iMapsData.options !== 'undefined' && typeof iMapsData.options.lang !== 'undefined' && iMapsData.options.lang && typeof window['am4geodata_lang_' + iMapsData.options.lang] !== 'undefined' ) { map.geodataNames = window['am4geodata_lang_' + iMapsData.options.lang]; } // projection - moved to the end of the function to fix issue with Albers not rendering correctly map.projection = new am4maps.projections[data.projection](); // fix issues with USA territories map if (data.map.startsWith('usaTerritories')) { map.events.on('ready', function (ev) { map.projection = new am4maps.projections[data.projection](); }); } map.fontFamily = data.visual.fontFamily; // export menu if (data.exportMenu && im.bool(data.exportMenu.enable)) { map.exporting.menu = new am4core.ExportMenu(); map.exporting.menu.items[0].icon = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMjAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CiA8Zz4KICA8dGl0bGU+YmFja2dyb3VuZDwvdGl0bGU+CiAgPHJlY3QgeD0iLTEiIHk9Ii0xIiB3aWR0aD0iNS45NDQ3NzMiIGhlaWdodD0iNS45NDQ3NzMiIGlkPSJjYW52YXNfYmFja2dyb3VuZCIgZmlsbD0ibm9uZSIvPgogPC9nPgoKIDxnPgogIDx0aXRsZT5MYXllciAxPC90aXRsZT4KICA8cGF0aCBzdHJva2U9Im51bGwiIGQ9Im0xNC40MjcwMzEsMTUuNzQwNDAxcTAsLTAuMjU3NjQzIC0wLjE4ODI3NCwtMC40NDU5MTd0LTAuNDQ1OTE3LC0wLjE4ODI3NHQtMC40NDU5MTcsMC4xODgyNzR0LTAuMTg4Mjc0LDAuNDQ1OTE3dDAuMTg4Mjc0LDAuNDQ1OTE3dDAuNDQ1OTE3LDAuMTg4Mjc0dDAuNDQ1OTE3LC0wLjE4ODI3NHQwLjE4ODI3NCwtMC40NDU5MTd6bTIuNTM2NzgsMHEwLC0wLjI1NzY0MyAtMC4xODgyNzQsLTAuNDQ1OTE3dC0wLjQ0NTkxNywtMC4xODgyNzR0LTAuNDQ1OTE3LDAuMTg4Mjc0dC0wLjE4ODI3NCwwLjQ0NTkxN3QwLjE4ODI3NCwwLjQ0NTkxN3QwLjQ0NTkxNywwLjE4ODI3NHQwLjQ0NTkxNywtMC4xODgyNzR0MC4xODgyNzQsLTAuNDQ1OTE3em0xLjI2ODM5LC0yLjIxOTY4NWwwLDMuMTcwOTdxMCwwLjM5NjM3NCAtMC4yNzc0NjEsMC42NzM4MzR0LTAuNjczODM0LDAuMjc3NDYxbC0xNC41ODY0NywwcS0wLjM5NjM3NCwwIC0wLjY3MzgzNCwtMC4yNzc0NjF0LTAuMjc3NDYxLC0wLjY3MzgzNGwwLC0zLjE3MDk3cTAsLTAuMzk2Mzc0IDAuMjc3NDYxLC0wLjY3MzgzNHQwLjY3MzgzNCwtMC4yNzc0NjFsNC42MDc4MTYsMGwxLjMzNzc1MSwxLjM0NzY1OXEwLjU3NDczOCwwLjU1NDkyMSAxLjM0NzY1OSwwLjU1NDkyMXQxLjM0NzY1OSwtMC41NTQ5MjFsMS4zNDc2NTksLTEuMzQ3NjU5bDQuNTk3OTA4LDBxMC4zOTYzNzQsMCAwLjY3MzgzNCwwLjI3NzQ2MXQwLjI3NzQ2MSwwLjY3MzgzNGwwLjAwMDAxOCwwem0tMy4yMjA1MjMsLTUuNjM4MzlxMC4xNjg0NTYsMC40MDYyODIgLTAuMTM4NzMsMC42OTM2NTFsLTQuNDM5MzYsNC40MzkzNnEtMC4xNzgzNjUsMC4xODgyNzQgLTAuNDQ1OTE3LDAuMTg4Mjc0dC0wLjQ0NTkxNywtMC4xODgyNzRsLTQuNDM5MzYsLTQuNDM5MzZxLTAuMzA3MTg3LC0wLjI4NzM2OSAtMC4xMzg3MywtMC42OTM2NTFxMC4xNjg0NTYsLTAuMzg2NDY1IDAuNTg0NjQ3LC0wLjM4NjQ2NWwyLjUzNjc4LDBsMCwtNC40MzkzNnEwLC0wLjI1NzY0MyAwLjE4ODI3NCwtMC40NDU5MTd0MC40NDU5MTcsLTAuMTg4Mjc0bDIuNTM2NzgsMHEwLjI1NzY0MywwIDAuNDQ1OTE3LDAuMTg4Mjc0dDAuMTg4Mjc0LDAuNDQ1OTE3bDAsNC40MzkzNmwyLjUzNjc4LDBxMC40MTYxOTEsMCAwLjU4NDY0NywwLjM4NjQ2NXoiIGlkPSJzdmdfMSIvPgogPC9nPgo8L3N2Zz4='; map.exporting.menu.align = data.exportMenu.align ? data.exportMenu.align : 'right'; map.exporting.menu.verticalAlign = data.exportMenu.verticalAlign ? data.exportMenu.verticalAlign : 'top'; } // touch devices options if (data.touchInterface) { if (im.bool(data.touchInterface.tapToActivate)) { map.tapToActivate = true; map.tapTimeout = data.touchInterface.tapTimeout; } if (im.bool(data.touchInterface.dragGrip)) { map.dragGrip.disabled = false; map.dragGrip.autoHideDelay = data.touchInterface.dragGripAutoHideDelay; } } // different map center //map.deltaLongitude = -10; // pan behaviours // map.panBehavior = "rotateLongLat"; // map.panBehavior = "rotateLong"; // map.deltaLatitude = -20; // map.padding(20, 20, 20, 20); // visual settings map.background.fill = data.visual.backgroundColor; map.background.fillOpacity = data.visual.backgroundOpacity; // iOS scroll issue fix - not working map.backgroundSeries.mapPolygons.template.focusable = false; map.background.focusable = false; map.backgroundSeries.focusable = false; // background image if ( typeof data.visual.bgImage !== 'undefined' && typeof data.visual.bgImage.url !== 'undefined' && data.visual.bgImage.url !== '' ) { bgSeries = map.series.push(new am4maps.MapImageSeries()); bgSeries.toBack(); bgImage = bgSeries.mapImages.template.createChild(am4core.Image); bgImage.propertyFields.href = 'src'; bgImage.width = map.width; bgImage.height = map.height; bgSeries.data = [ { src: data.visual.bgImage.url, }, ]; im.maps[id].backgroundSeries = bgSeries; } map.exporting.backgroundColor = data.visual.backgroundColor; map.exporting.filePrefix = 'interactive_map'; map.exporting.useWebFonts = false; // graticulate - grid lines if (data.projection === 'Orthographic' && data.grid) { graticuleSeries = map.series.push(new am4maps.GraticuleSeries()); graticuleSeries.mapLines.template.line.stroke = data.grid.color; graticuleSeries.mapLines.template.line.strokeOpacity = 1; graticuleSeries.fitExtent = false; map.backgroundSeries.mapPolygons.template.polygon.fillOpacity = 1; map.backgroundSeries.mapPolygons.template.polygon.fill = data.grid.projectionBackgroundColor; } im.handleZoom(id); // Auto Legend if (data.legend && im.bool(data.legend.enabled)) { map.legend = new am4maps.Legend(); // positioning if (data.legend.position === 'top' || data.legend.position === 'bottom') { map.legend.contentAlign = data.legend.align; map.legend.valign = data.legend.position; } else { map.legend.position = data.legend.position; map.legend.align = data.legend.position; map.legend.valign = data.legend.valign; } if (typeof data.legend.style !== 'undefined' && data.legend.style !== 'default') { im.setLegendStyle(id, map.legend, data.legend.style); } if (_typeof(data.legend.fill) !== undefined) { map.legend.labels.template.fill = data.legend.fill; } // interactive if (data.legend.clickable === 'false') { map.legend.itemContainers.template.interactionsEnabled = false; } if (data.legend.clickable === 'toggle') { // do nothing, it's the default event // let's just clear the events, just in case. map.legend.itemContainers.template.events.on('hit', function (ev) { iMapsManager.clearSelected(id); }); } if (data.legend.clickable === 'select') { map.legend.itemContainers.template.togglable = false; map.legend.itemContainers.template.events.on('hit', function (ev) { iMapsManager.clearSelected(id); var select = []; var seriesType = im.getTargetSeriesType(ev.target.dataItem.dataContext); var target = ''; // currently only works for region series if (seriesType === 'MapPolygonSeries') { ev.target.dataItem.dataContext.mapPolygons.each(function (polygon) { if (!polygon.dataItem.dataContext.madeFromGeoData) { polygon.setState('active'); polygon.isActive = true; polygon.isHover = false; select.push(polygon); } }); im.maps[id].selected = select; } }); map.legend.itemContainers.template.events.on('over', function (ev) { var seriesType = im.getTargetSeriesType(ev.target.dataItem.dataContext); var target = ''; // bring this series to front ev.target.dataItem.dataContext.zIndex = Number.MAX_VALUE; ev.target.dataItem.dataContext.toFront(); if (seriesType === 'MapImageSeries') { target = ev.target.dataItem.dataContext.mapImages; target.each(function (marker) { marker.setState('highlight'); }); } else if (seriesType === 'MapPolygonSeries') { target = ev.target.dataItem.dataContext.mapPolygons; target.each(function (polygon) { if (!polygon.dataItem.dataContext.madeFromGeoData) { polygon.setState('highlight'); } }); } else { return; } }); map.legend.itemContainers.template.events.on('out', function (ev) { var seriesType = im.getTargetSeriesType(ev.target.dataItem.dataContext); var target = ''; if (seriesType === 'MapImageSeries') { target = ev.target.dataItem.dataContext.mapImages; target.each(function (marker) { marker.setState('default'); }); } else if (seriesType === 'MapPolygonSeries') { target = ev.target.dataItem.dataContext.mapPolygons; target.each(function (polygon) { if (!polygon.dataItem.dataContext.madeFromGeoData) { polygon.setState('default'); } }); } else { return; } }); } } // Custom Legend if (data.customLegend && im.bool(data.customLegend.enabled)) { var customLegendType = typeof data.customLegend.type !== 'undefined' ? data.customLegend.type : 'internal'; if ( data.customLegend.data && Array.isArray(data.customLegend.data) && customLegendType === 'internal' ) { customLegend = new am4maps.Legend(); customLegend.parent = map.chartContainer; customLegend.data = data.customLegend.data; /* customLegend.data.map(function(entry){ entry.name = entry.name.replace(/&#(\d+);/g, function(match, dec) { return String.fromCharCode(dec); }); return entry; });*/ if (typeof data.customLegend.style !== 'undefined' && data.customLegend.style !== 'default') { im.setLegendStyle(id, customLegend, data.customLegend.style); } // no interaction with mouse customLegend.itemContainers.template.clickable = false; customLegend.itemContainers.template.focusable = false; customLegend.itemContainers.template.hoverable = false; customLegend.itemContainers.template.cursorOverStyle = am4core.MouseCursorStyle['default']; customLegend.labels.template.truncate = false; customLegend.labels.template.wrap = true; // positioning if (data.customLegend.position === 'top' || data.customLegend.position === 'bottom') { customLegend.contentAlign = data.customLegend.align; customLegend.valign = data.customLegend.position; } else { customLegend.position = data.customLegend.position; customLegend.align = data.customLegend.position; customLegend.valign = data.customLegend.valign; } if (_typeof(data.customLegend.fill) !== undefined) { customLegend.labels.template.fill = data.customLegend.fill; } } } // Create Main Series regionSeries = im.pushRegionSeries(id, data); im.maps[id].baseRegionSeries = regionSeries; // Check for grouped regions if (Array.isArray(data.regionGroups) && data.regionGroups.length) { groupedSeries = im.pushGroupSeries(id, data); im.maps[id].groupedBaseRegionSeries = groupedSeries; } // Overlay collections - we can add all series in the preset order if (Array.isArray(data.overlay) && data.overlay.length) { data.overlay.forEach(function (mapObj) { im.pushSeries(id, mapObj); }); } // Create Other Series - we create them after the overlay to avoid overlap if (Array.isArray(data.lines) && data.lines.length) { lineSeries = im.pushLineSeries(id, data); } if (Array.isArray(data.roundMarkers) && data.roundMarkers.length) { markerSeries = im.pushRoundMarkerSeries(id, data); if (data.clusterMarkers && im.bool(data.clusterMarkers.enabled)) { markerSeries.hidden = true; clusters = im.setupClusters(data, id); clusterSeries[id].zoomLevels[data.clusterMarkers.zoomLevel] = markerSeries; // we setup the main index series (zoom=1) to be visible // when doing it inside setupClusters function, there was a bug if (clusterSeries[id].zoomLevels[1]) { clusterSeries[id].zoomLevels[1].hidden = false; // we also add it to the base series here, since it wasn't working in the setupClusters function im.maps[id].allBaseSeries.push(clusterSeries[id].zoomLevels[1]); } } } if (Array.isArray(data.imageMarkers) && data.imageMarkers.length) { imageSeries = im.pushImageMarkerSeries(id, data); //imageSeries.hiddenInLegend = true; im.maps[id].allBaseSeries.push(imageSeries); } if (Array.isArray(data.iconMarkers) && data.iconMarkers.length) { iconSeries = im.pushIconMarkerSeries(id, data); //iconSeries.hiddenInLegend = true; im.maps[id].allBaseSeries.push(iconSeries); } if (Array.isArray(data.labels) && data.labels.length) { labelSeries = im.pushLabelSeries(id, data); //labelSeries.hiddenInLegend = true; im.maps[id].allBaseSeries.push(labelSeries); } // checks if we should display info and creates events. im.handleInfoBox(id); // map.projection = new am4maps.projections[data.projection](); }; iMapsManager.setLegendStyle = function (id, legend, style) { var sizes = { xsmall: 10, small: 13, regular: 16, large: 19, larger: 23, }; var legendSize = sizes[style]; var legendMarkerTemplate = legend.markers.template; legend.labels.template.fontSize = legendSize; legend.useDefaultMarker = true; legendMarkerTemplate.width = legendSize; legendMarkerTemplate.height = legendSize; }; iMapsManager.handleZoom = function (id) { var im = this, map = im.maps[id].map, data = im.maps[id].data, allCurrentSeries = im.maps[id].series, allBaseSeries = im.maps[id].allBaseSeries, defaultOffset = true, defaultZoom = true; // Viewport settings // Zoom Level if (typeof data.viewport !== 'undefined' && parseFloat(data.viewport.zoomLevel) >= 1) { map.homeZoomLevel = parseFloat(data.viewport.zoomLevel); defaultZoom = false; // to make sure everything else is disabled by default map.seriesContainer.resizable = false; map.seriesContainer.draggable = false; map.chartContainer.wheelable = false; } // Home center point if ( typeof data.viewport !== 'undefined' && data.viewport.homeGeoPoint && data.viewport.homeGeoPoint.latitude !== 0 && data.viewport.homeGeoPoint.longitude !== 0 ) { map.homeGeoPoint = data.viewport.homeGeoPoint; } // delta Coordinates Offset if (typeof data.viewport !== 'undefined' && data.viewport.offset) { // only change if there are values, otherwise we might mess up projections (Albers) if (data.viewport.offset.longitude !== '' && data.viewport.offset.longitude !== '0') { defaultOffset = false; map.deltaLongitude = data.viewport.offset.longitude; } if (data.viewport.offset.latitude !== '' && data.viewport.offset.latitude !== '0') { defaultOffset = false; map.deltaLatitude = data.viewport.offset.latitude; } if (defaultOffset) { iMapsManager.latLongOffsetFix(data, map, defaultZoom); } } // manual fixes for NZ and RU and Asia maps if (typeof data.viewport === 'undefined') { iMapsManager.latLongOffsetFix(data, map, defaultZoom); } // default zoom behaviour if (typeof data.zoom === 'undefined') { // default zoom behaviour when we can't find zoom settings if (typeof data.zoomMaster !== 'undefined' && im.bool(data.zoomMaster)) { map.seriesContainer.draggable = true; map.seriesContainer.resizable = true; // display zoom controls by default map.zoomControl = new am4maps.ZoomControl(); map.zoomControl.focusable = false; // iOS fix map.zoomControl.exportable = false; if (map.zoomControl.children && map.zoomControl.children.values) { map.zoomControl.children.values.forEach(function (child) { child.focusable = false; }); } map.zoomControl.align = 'right'; map.zoomControl.valign = 'bottom'; } else { map.seriesContainer.resizable = false; map.seriesContainer.draggable = false; } map.seriesContainer.events.disableType('doublehit'); map.chartContainer.background.events.disableType('doublehit'); map.chartContainer.wheelable = false; return; } // disable drag in Orthographic and leave default for the rest if (data.projection !== 'Orthographic') { map.seriesContainer.draggable = data.zoom ? im.bool(data.zoom.draggable) : false; map.seriesContainer.resizable = data.zoom ? im.bool(data.zoom.draggable) : false; // don't zoom out to center if (im.bool(data.zoom.draggable)) { // in case we want to show the grab hand on mousehover also //map.seriesContainer.cursorOverStyle = am4core.MouseCursorStyle.grab; map.seriesContainer.cursorDownStyle = am4core.MouseCursorStyle.grabbing; } // control zoom and pan behaviour map.centerMapOnZoomOut = false; map.maxPanOut = 0; // zoom is enabled, only allow drag on mobile if ( im.bool(data.zoom.enabled) && !im.bool(data.zoom.draggable) && im.bool(data.zoom.mobileResizable) && /Mobi|Android/i.test(navigator.userAgent) ) { map.seriesContainer.draggable = true; map.seriesContainer.resizable = true; } } else { map.seriesContainer.draggable = false; map.seriesContainer.resizable = false; map.panBehavior = 'rotateLongLat'; } // disable zoom if (!im.bool(data.zoom.enabled)) { map.maxZoomLevel = parseFloat(data.viewport.zoomLevel); map.seriesContainer.events.disableType('doublehit'); map.chartContainer.background.events.disableType('doublehit'); map.seriesContainer.draggable = false; map.seriesContainer.resizable = false; map.chartContainer.wheelable = false; } else { // mouse wheel zoom map.chartContainer.wheelable = im.bool(data.zoom.wheelable); // double click zoom if (!im.bool(data.zoom.doubleHitZoom)) { map.seriesContainer.events.disableType('doublehit'); map.chartContainer.background.events.disableType('doublehit'); } if (typeof data.zoom.maxZoomLevel !== 'undefined') { map.maxZoomLevel = parseInt(data.zoom.maxZoomLevel); } // Zoom Controls if (im.bool(data.zoom.controls)) { map.zoomControl = new am4maps.ZoomControl(); // iOS fix map.zoomControl.exportable = false; if (map.zoomControl.children && map.zoomControl.children.values) { map.zoomControl.children.values.forEach(function (child) { child.focusable = false; }); } map.zoomControl.exportable = false; map.zoomControl.align = data.zoom.controlsPositions ? data.zoom.controlsPositions.align : 'right'; map.zoomControl.valign = data.zoom.controlsPositions ? data.zoom.controlsPositions.valign : 'bottom'; if (typeof data.zoom.homeButton === 'undefined' || im.bool(data.zoom.homeButton)) { // home button var homeButton = new am4core.Button(); // iOS scroll fix homeButton.focusable = false; homeButton.events.on('hit', function () { map.goHome(); // in case drillDown is enabled, we hide everything else iMapsManager.resetDrilldown(id); // reset actions if ( typeof iMapsActions !== 'undefined' && typeof iMapsActions.resetActions !== 'undefined' ) { iMapsActions.resetActions(id); } }); homeButton.icon = new am4core.Sprite(); homeButton.padding(7, 5, 7, 5); homeButton.width = 30; homeButton.icon.path = 'M16,8 L14,8 L14,16 L10,16 L10,10 L6,10 L6,16 L2,16 L2,8 L0,8 L8,0 L16,8 Z M16,8'; homeButton.marginBottom = 3; homeButton.parent = map.zoomControl; homeButton.insertBefore(map.zoomControl.plusButton); // fix for Firefox home button not being loaded correctly map.events.on('inited', function () { homeButton.deepInvalidate(); }); } } // outside zoom controls if (im.bool(data.zoom.externalControls)) { // add home button iMapsManager.handleExternalZoom(data.id); } } // full screen button if (typeof data.fullScreen !== 'undefined' && im.bool(data.fullScreen.enabled)) { // full screen var fullScreenButton = map.chartContainer.createChild(am4core.Button); fullScreenButton.events.on('hit', function (ev) { var parentMap = document.querySelector('#map_' + ev.target.icon.mapID); var mainParent = parentMap.closest('.map_wrapper'); // browser fullscreen iMapsManager.toggleFullscreen(mainParent); iMapsManager.isFullScreen = ev.target; }); // Add button fullScreenButton.align = data.fullScreen.align; fullScreenButton.valign = data.fullScreen.valign; fullScreenButton.margin(5, 5, 5, 5); fullScreenButton.padding(5, 0, 5, 0); fullScreenButton.width = 30; fullScreenButton.icon = new am4core.Sprite(); fullScreenButton.icon.path = iMapsManager.library.icons.goFullIconPath; fullScreenButton.icon.isFullScreen = false; fullScreenButton.icon.mapID = id; // if mobile only, add class if (im.bool(data.fullScreen.mobileOnly)) { fullScreenButton.id = '_fullscreen_button_only_mobile'; } else { fullScreenButton.id = '_fullscreen_button'; } // Solution for Firefox issue with button container map.events.on('inited', function () { fullScreenButton.deepInvalidate(); }); } // pan events? map.events.on('mappositionchanged', function (ev) { // what to do here to have tooltips always display follow pan? }); // zoom level changed events map.events.on('zoomlevelchanged', function (ev) { var clusterSeries = im.maps[id].clusterSeries, closest, zlevel = ev.target.zoomLevel, isDrill = im.bool(data.drillDownOnClick), // Check if data.liveFilter exists and has the property 'enabled' before accessing it isDrilling = im.maps[id].isDrilling, drilledTo = im.maps[id].drilledTo, activeMap = im.filteredMap, keepBase = false; var hasLiveFilter = false; if (typeof data.liveFilter !== 'undefined') { var hasLiveFilter = im.bool(data.liveFilter.enabled); } // for drilldown if (isDrill && typeof data.alwaysKeepBase !== 'undefined' && im.bool(data.alwaysKeepBase)) { keepBase = true; } // for live filter // Added check to ensure data.liveFilter exists before checking its 'keepBase' property if (hasLiveFilter && data.liveFilter && im.bool(data.liveFilter.keepBase)) { keepBase = true; } // where the magic happens to show/hide series from cluster markers if (clusterSeries && Object.keys(clusterSeries).length) { Object.keys(clusterSeries).forEach(function (key) { // if the drilldown or live filter are enabled if (isDrill || hasLiveFilter) { if ( //active map in live filter is the map the cluster belongs to (hasLiveFilter && parseInt(activeMap) === parseInt(clusterSeries[key].overlay)) || // current active map is the base map in the filter (hasLiveFilter && parseInt(activeMap) === parseInt(id)) || // there's a filter, but the keepBase is enabled, so clusters that are not overlay still display (hasLiveFilter && keepBase && !im.bool(clusterSeries[key].overlay)) || //map being drilled to is the map the cluster belongs to (isDrilling && drilledTo && parseInt(drilledTo) === parseInt(clusterSeries[key].overlay)) || //drilldown is enabled and cluster is from the base map and we are not drilling (isDrill && !im.bool(clusterSeries[key].overlay) && !keepBase && !drilledTo) || //drilldown is enabled, cluster is from base map and keepBase is enabled (isDrill && !im.bool(clusterSeries[key].overlay) && keepBase) ) { // first we get the closest zoom value compared to the current zom closest = im.getClosest(clusterSeries[key].zoomLevels, zlevel); Object.keys(clusterSeries[key].zoomLevels).forEach(function (zkey) { clusterSeries[key].zoomLevels[zkey].hide(); if (parseFloat(zkey) === closest) { clusterSeries[key].zoomLevels[zkey].show(); } else { clusterSeries[key].zoomLevels[zkey].hide(); } }); } else { // not drilling and not active map Object.keys(clusterSeries[key].zoomLevels).forEach(function (zkey) { clusterSeries[key].zoomLevels[zkey].hide(); }); } return; } // it's not a drilldown, proceed // we go through the series to show the closest one and hide the others closest = im.getClosest(clusterSeries[key].zoomLevels, zlevel); Object.keys(clusterSeries[key].zoomLevels).forEach(function (zkey) { // we hide them by default clusterSeries[key].zoomLevels[zkey].hide(); if (parseFloat(zkey) === closest) { clusterSeries[key].zoomLevels[zkey].show(); } else { clusterSeries[key].zoomLevels[zkey].hide(); } }); }); } }); }; /** * Gets closest zoom level based on current zoom and array of zoom levels for clusters * @param {*} zoomLevels * @param {*} zlevel */ iMapsManager.getClosest = function (zoomLevels, zlevel) { var closest = Object.keys(zoomLevels).reduce(function (prev, curr) { prev = parseInt(prev); curr = parseInt(curr); return Math.abs(curr - zlevel) < Math.abs(prev - zlevel) ? curr : prev; }); return closest; }; /* Reset drilldown behaviour */ iMapsManager.resetDrilldown = function (id) { var im = this, data = im.maps[id].data, allCurrentSeries = im.maps[id].series, allBaseSeries = im.maps[id].allBaseSeries, bgSeries = im.maps[id].backgroundSeries; if (im.bool(data.drillDownOnClick)) { for (var i = 0, len = allCurrentSeries.length; i < len; i++) { allCurrentSeries[i].hide(); //map.deltaLongitude = 0; } for (var ib = 0, lenb = allBaseSeries.length; ib < lenb; ib++) { // this is messing the cluster markers on base map allBaseSeries[ib].show(); // make sure there's a bg series if (bgSeries.mapPolygons) { bgSeries.show(); } } iMapsManager.maps[id].drilledTo = false; iMapsManager.maps[id].isDrilling = false; } }; /** Manually fix lat/long offset for some countries in default projections/values */ iMapsManager.latLongOffsetFix = function (data, mapObj, defaultZoom) { var mapSelected = data.map; var mapsFixInclude = [ 'russiaLow', 'russiaHigh', 'russiaCrimeaLow', 'russiaCrimeaHigh', 'region/world/asiaLow', 'region/world/asiaHigh', 'region/world/asiaUltra', 'region/world/asiaIndiaLow', 'region/world/asiaIndiaHigh', 'region/world/asiaIndiaUltra', ]; // only do the fix if Russia is included if (mapsFixInclude.includes(mapSelected) && data.exclude && !data.exclude.includes('RU')) { mapObj.deltaLongitude = -100; if (defaultZoom) { mapObj.homeZoomLevel = 1.5; } } if (mapSelected === 'newZealandLow' || mapSelected == 'newZealandHigh') { mapObj.deltaLongitude = 20; } }; /** * Push region series that are grouped together */ iMapsManager.pushGroupSeries = function (id, data) { var series = []; var regionSerie; data.regionGroups.forEach(function (group) { var newData = {}, include = group.map(function (a) { return a.id; }); // let's get all the options from the main map and change the group option to true newData = Object.assign({}, data); newData.regionsGroupHover = true; newData.regions = group; newData.include = include; // include only the regions we're grouping regionSerie = iMapsManager.pushRegionSeries(id, newData, true); series.push(regionSerie); }); return series; }; /** * Push different series of overlay/child maps * int i - index of the map data * data - object with map data to push */ iMapsManager.pushSeries = function (id, data) { var im = this, regionSeries, markerSeries, labelSeries, lineSeries, iconSeries, imageSeries, groupedSeries, clusters, clusterSeries = im.maps[id].clusterSeries, parentData = im.maps[id].data, seriesIndex = im.maps[id].seriesIndex, seriesById = im.maps[id].seriesById, isDrill = im.bool(im.maps[id].data.drillDownOnClick), cleanMapName = iMapsRouter.getCleanMapName(data.map, data.id), defaultSeries = false; if (false === cleanMapName) { return; } if (typeof data.id === 'undefined') { return; } seriesById[data.id] = []; // check if it's set a map overlay by default if ( typeof parentData.liveFilter !== 'undefined' && parseInt(parentData.liveFilter['default']) !== parentData.id ) { defaultSeries = parseInt(parentData.liveFilter['default']); im.filteredMap = defaultSeries; } // setup series index if (!Array.isArray(im.maps[id].seriesIndex[data.map])) { im.maps[id].seriesIndex[cleanMapName] = []; } // to allow empty maps to overlay, we removed the check && data.regions.length and send empty array if (!Array.isArray(data.regions)) { data.regions = []; } if (typeof parentData.allowEmpty === 'undefined') { parentData.allowEmpty = 0; } // in case we want empty maps to overlay, we remove this check. // in are not allowing the empty so that the live filter works better and markers are not hidden behind the map // reference: https://interactivegeomaps.com/feature/live-filter/ // but other overlays and empty maps might need to be added... // we don't check if the regions.length exist, because maybe user wants to add an empty map, only to show divisions if (data.regions.length || im.bool(parentData.allowEmpty)) { // in case we don't allow empty, we only include the active regions if (!im.bool(parentData.allowEmpty)) { data.include = []; data.regions.forEach(function (region, index) { data.include.push(region.id); if (!isNaN(region.id)) { data.include.push(parseInt(region.id)); } }); } regionSeries = iMapsManager.pushRegionSeries(id, data); seriesIndex[cleanMapName].push(regionSeries); seriesById[data.id].push(regionSeries); if (isDrill) { regionSeries.hidden = true; regionSeries.mapID = data.id; } // hide in case we have a live filter and this is not the default if ( parentData.liveFilter && im.bool(parentData.liveFilter.enabled) && defaultSeries && defaultSeries !== data.id ) { regionSeries.hidden = true; } } // Check for grouped regions if (Array.isArray(data.regionGroups) && data.regionGroups.length) { groupedSeries = im.pushGroupSeries(id, data); groupedSeries.forEach(function (regionSerie) { seriesIndex[cleanMapName].push(regionSerie); seriesById[data.id].push(regionSerie); if (isDrill) { regionSerie.hidden = true; regionSerie.mapID = data.id; } // hide in case we have a live filter and this is not the default if ( parentData.liveFilter && im.bool(parentData.liveFilter.enabled) && defaultSeries && defaultSeries !== data.id ) { regionSerie.hidden = true; } }); } // let's add lines before markers, so that the markers then overlay the end of lines, in case if (Array.isArray(data.lines) && data.lines.length) { lineSeries = iMapsManager.pushLineSeries(id, data); seriesIndex[cleanMapName].push(lineSeries); seriesById[data.id].push(lineSeries); if (isDrill) { lineSeries.hidden = true; } // hide in case we have a live filter and this is not the default if ( parentData.liveFilter && im.bool(parentData.liveFilter.enabled) && defaultSeries && defaultSeries !== data.id ) { lineSeries.hidden = true; } } if (Array.isArray(data.roundMarkers) && data.roundMarkers.length) { markerSeries = iMapsManager.pushRoundMarkerSeries(id, data); seriesIndex[cleanMapName].push(markerSeries); seriesById[data.id].push(markerSeries); if (isDrill) { markerSeries.hidden = true; markerSeries.mapID = data.id; } // clusters in overlay maps if (data.clusterMarkers && im.bool(data.clusterMarkers.enabled)) { markerSeries.hidden = true; clusters = im.setupClusters(data, id, data.id); clusterSeries[data.id].zoomLevels[data.clusterMarkers.zoomLevel] = markerSeries; // we setup the main index series (zoom=1) to be visible // when doing it inside setupClusters function, there was a bug if (!isDrill && clusterSeries[data.id].zoomLevels[1]) { clusterSeries[data.id].zoomLevels[1].hidden = false; // if there's a live filter, double check - we hide them. // otherwise overlaid series with clusters were always displaying, even when they shouldn't if ( parentData.liveFilter && im.bool(parentData.liveFilter.enabled) && defaultSeries && defaultSeries !== data.id ) { clusterSeries[data.id].zoomLevels[1].hidden = true; } } } // hide in case we have a live filter and this is not the default if ( parentData.liveFilter && im.bool(parentData.liveFilter.enabled) && defaultSeries && defaultSeries !== data.id ) { markerSeries.hidden = true; } } if (Array.isArray(data.iconMarkers) && data.iconMarkers.length) { iconSeries = iMapsManager.pushIconMarkerSeries(id, data); seriesIndex[cleanMapName].push(iconSeries); seriesById[data.id].push(iconSeries); if (isDrill) { iconSeries.hidden = true; } // hide in case we have a live filter and this is not the default if ( parentData.liveFilter && im.bool(parentData.liveFilter.enabled) && defaultSeries && defaultSeries !== data.id ) { iconSeries.hidden = true; } } if (Array.isArray(data.imageMarkers) && data.imageMarkers.length) { imageSeries = iMapsManager.pushImageMarkerSeries(id, data); seriesIndex[cleanMapName].push(imageSeries); seriesById[data.id].push(imageSeries); if (isDrill) { imageSeries.hidden = true; } // hide in case we have a live filter and this is not the default if ( parentData.liveFilter && im.bool(parentData.liveFilter.enabled) && defaultSeries && defaultSeries !== data.id ) { imageSeries.hidden = true; } } if (Array.isArray(data.labels) && data.labels.length) { labelSeries = iMapsManager.pushLabelSeries(id, data); seriesIndex[cleanMapName].push(labelSeries); seriesById[data.id].push(labelSeries); if (isDrill) { labelSeries.hidden = true; } // hide in case we have a live filter and this is not the default if ( parentData.liveFilter && im.bool(parentData.liveFilter.enabled) && defaultSeries && defaultSeries !== data.id ) { labelSeries.hidden = true; } } }; iMapsManager.pushRegionSeries = function (id, data, groupHover) { var im = this, map = im.maps[id].map, // shorter reference for the map regionSeries, tooltipConfig = data.tooltip || {}, regionTemplate, hover, active, highlight, mapVar, clkLabel, groupHover = groupHover || false; data = data || {}; regionSeries = map.series.push(new am4maps.MapPolygonSeries()); if (data.map === 'custom' || im.bool(data.useGeojson)) { regionSeries.geodataSource.url = data.mapURL; } else { mapVar = iMapsRouter.getVarByName(data.map); regionSeries.geodata = window[mapVar]; } regionSeries.name = data.regionLegend && data.regionLegend.title !== '' ? data.regionLegend.title : data.title; regionSeries.hiddenInLegend = data.regionLegend ? !im.bool(data.regionLegend.enabled) : true; // if it's a base series // override hide in legend for scenarios where there are grouped entries for example if (data.regionLegend && data.regionLegend.enabled === 'onlyGroups' && groupHover) { regionSeries.hiddenInLegend = false; } if (data.regionLegend && data.regionLegend.enabled === 'onlyGroups' && !groupHover) { regionSeries.hiddenInLegend = true; } if (data.regionLegend && data.regionLegend.enabled === 'ignoreGroups' && groupHover) { regionSeries.hiddenInLegend = true; } if (data.regionLegend && data.regionLegend.enabled === 'ignoreGroups' && !groupHover) { regionSeries.hiddenInLegend = false; } if (id === data.id) { // add it as the baseSeries - which will contain all region base series if (typeof im.maps[id].baseSeries === 'undefined') { im.maps[id].baseSeries = []; } im.maps[id].baseSeries.push(regionSeries); im.maps[id].allBaseSeries.push(regionSeries); } // Make map load polygon (like country names) data from GeoJSON regionSeries.useGeodata = true; // Exclude if (Array.isArray(data.exclude) && data.exclude.length) { regionSeries.exclude = data.exclude; } // Include if (Array.isArray(data.include) && data.include.length) { regionSeries.include = data.include; } // Setup Heatmap rules if (data.heatMapRegions && im.bool(data.heatMapRegions.enabled)) { im.setupHeatMap(regionSeries, id, data); } // Data // if (Array.isArray(data.regions)) { regionSeries.data = data.regions; // Configure series if (groupHover) { regionSeries.groupHover = true; } regionTemplate = regionSeries.mapPolygons.template; im.setupTooltip(id, regionSeries, data); // check for custom tooltip template if ( typeof data.regionsTooltipTemplate !== 'undefined' && data.regionsTooltipTemplate.trim() !== '' ) { regionTemplate.tooltipText = data.regionsTooltipTemplate; regionTemplate.tooltipHTML = data.regionsTooltipTemplate; } else { regionTemplate.tooltipText = tooltipConfig.template ? tooltipConfig.template : '{tooltipContent}'; regionTemplate.tooltipHTML = tooltipConfig.template ? tooltipConfig.template : '{tooltipContent}'; } regionTemplate.adapter.add('tooltipHTML', function (value, target, key) { if ( _typeof(target.dataItem.dataContext) === 'object' && typeof tooltipConfig.onlyWithData !== 'undefined' ) { // check if we don't return tooltip on empty regions if (im.bool(tooltipConfig.onlyWithData)) { if (target.dataItem.dataContext.madeFromGeoData === true) { target.tooltip.tooltipText = undefined; target.tooltip.tooltipHTML = undefined; return ''; } } } if (value === '') { return value; } return value.replace(/\\/g, ''); }); regionTemplate.adapter.add('tooltipText', function (value, target, key) { if ( _typeof(target.dataItem.dataContext) === 'object' && typeof tooltipConfig.onlyWithData !== 'undefined' ) { // check if we don't return tooltip on empty regions if (im.bool(tooltipConfig.onlyWithData)) { if (target.dataItem.dataContext.madeFromGeoData === true) { target.tooltip.tooltipText = undefined; target.tooltip.tooltipHTML = undefined; return ''; } } } if (value === '') { return value; } return value.replace(/\\/g, ''); }); // For legend color regionSeries.fill = data.regionDefaults.fill; regionTemplate.fill = data.regionDefaults.inactiveColor; regionTemplate.stroke = data.visual.borderColor; regionTemplate.strokeWidth = data.visual.borderWidth; // fill regionTemplate.propertyFields.fill = 'fill'; // exploring adapter /* regionTemplate.adapter.add("fill", function(fill, target) { if(Array.isArray(fill)){ let gradient = new am4core.LinearGradient(); fill.forEach(function(color){ gradient.addColor(am4core.color(color)); }); fill = gradient; } return fill; }); */ // hover - only create if it's not a group hover series if (!groupHover) { // also don't consider it on heatmaps with hover disabled if ( typeof data.heatMapRegions !== 'undefined' && im.bool(data.heatMapRegions.enabled) && im.bool(data.heatMapRegions.noHover) ) { // do nothing for now, we don't need hover, might implement something at some point } else { hover = regionTemplate.states.create('hover'); hover.propertyFields.fill = 'hover'; } //hover.propertyFields.strokeWidth = "borderWidthHover"; //hover.propertyFields.stroke = "borderColorHover"; } // active state if (data.regionActiveState && im.bool(data.regionActiveState.enabled)) { active = regionTemplate.states.create('active'); if (data.regionActiveState.source === 'custom') { active.properties.fill = data.regionActiveState.fill; } else { active.propertyFields.fill = 'hover'; } } // highlight - for group hover highlight = regionTemplate.states.create('highlight'); highlight.propertyFields.fill = 'hover'; if (groupHover) { regionTemplate.events.on('out', function (ev) { im.groupHoverOut(id, ev); }); regionTemplate.events.on('over', function (ev) { im.groupHover(id, ev); // bring this series (and label series if it exists) to front when group is hovered regionSeries.toFront(); if (typeof regionSeries.autoLabelSeries !== 'undefined') { regionSeries.autoLabelSeries.toFront(); } }); regionTemplate.events.on('hit', function (ev) { im.groupHit(id, ev); }); } else { regionTemplate.events.on('hit', function (ev) { im.singleHit(id, ev); }); regionTemplate.events.on('over', function (ev) { im.setupHoverEvents(id, ev); }); } // Events // iOS bug fix, remove focus regionTemplate.focusable = false; regionTemplate.events.on('hit', function (ev) { im.setupHitEvents(id, ev); }); // enable small map if (im.bool(data.smallMap)) { map.smallMap.series.push(regionSeries); } // auto Labels if (data.regionLabels && im.bool(data.regionLabels.source)) { regionSeries.calculateVisualCenter = true; // Configure label series var labelSeries = map.series.push(new am4maps.MapImageSeries()); var labelTemplate = labelSeries.mapImages.template.createChild(am4core.Label); // Label Background - currently only possible with global var background = typeof igmLabelsBackground !== 'undefined' ? igmLabelsBackground : false; if (typeof background === 'object') { labelTemplate.background = new am4core.RoundedRectangle(); labelTemplate.background.cornerRadius(...background.cornerRadius); labelTemplate.background.fill = am4core.color(background.color); labelTemplate.padding(...background.padding); labelTemplate.background.stroke = am4core.color(background.stroke); } im.maps[id].labelSeries.push(labelSeries); // add reference to parent region series regionSeries.autoLabelSeries = labelSeries; labelTemplate.horizontalCenter = data.regionLabels.horizontalCenter; labelTemplate.verticalCenter = data.regionLabels.verticalCenter; labelTemplate.fontSize = data.regionLabels.fontSize; labelTemplate.fill = data.regionLabels.fill; // automatic region label hover state const hoverState = labelTemplate.states.create('hover'); hoverState.properties.fill = am4core.color(data.regionLabels.hover); labelTemplate.fontFamily = data.regionLabels.fontFamily ? data.regionLabels.fontFamily : 'inherit'; labelTemplate.fontWeight = data.regionLabels.fontWeight ? data.regionLabels.fontWeight : 'normal'; // let's set a listener to the hide event of main series to hide this one also if (data.regionLabels.mobileFontSize && parseInt(data.regionLabels.mobileFontSize) !== 100) { if (window.innerWidth <= 780) { labelTemplate.fontSize = (parseInt(labelTemplate.fontSize) * parseInt(data.regionLabels.mobileFontSize)) / 100; labelTemplate.size = labelTemplate.fontSize; } } regionSeries.events.on('hidden', function (ev) { labelSeries.hide(); }); regionSeries.events.on('shown', function (ev) { labelSeries.show(); }); // label events labelTemplate.events.on('hit', function (ev) { clkLabel = regionSeries.getPolygonById(ev.target.parent.LabelForRegion); clkLabel.dispatchImmediately('hit'); }); labelTemplate.events.on('over', function (ev) { iMapsManager.hover(id, ev.target.parent.LabelForRegion, false); }); labelTemplate.events.on('out', function (ev) { iMapsManager.clearHovered(id, ev.target.parent.LabelForRegion); }); im.setupTooltip(id, labelSeries, data, labelTemplate); labelTemplate.interactionsEnabled = true; labelTemplate.nonScaling = im.bool(data.regionLabels.nonScaling); labelSeries.hiddenInLegend = true; // fix initially hidden series - for example drilldown overlay regionSeries.events.on('inited', function () { if (regionSeries.hidden) { labelSeries.hide(); labelSeries.hidden = true; } }); // set labels drag listener // allow labels to be dragged if in admin if (im.bool(data.admin)) { labelTemplate.draggable = true; labelTemplate.cursorOverStyle = am4core.MouseCursorStyle.grab; labelTemplate.events.on('dragstop', function (ev) { var svgPoint = am4core.utils.spritePointToSvg( { x: 0, y: 0, }, ev.target ); svgPoint = am4core.utils.spritePointToSvg( { x: 0 - ev.target.maxLeft, y: 0 - ev.target.maxTop, }, ev.target ); var geo = map.svgPointToGeo(svgPoint); // check if field to add json object with adjustments exists var adjField = document.querySelector( '[data-depend-id=' + data.regionLabelCustomCoordinates + ']' ); if (adjField) { var jsonAdjustments; if (iMapsManager.isJSON(adjField.value)) { jsonAdjustments = JSON.parse(adjField.value); } else { jsonAdjustments = {}; } jsonAdjustments[ev.target.parent.LabelForRegion] = { latitude: geo.latitude, longitude: geo.longitude, }; adjField.value = JSON.stringify(jsonAdjustments); } map.seriesContainer.draggable = im.bool(data.zoom.draggable); ev.target.cursorOverStyle = am4core.MouseCursorStyle.grab; }); labelTemplate.events.on('down', function (ev) { map.seriesContainer.draggable = false; ev.target.cursorOverStyle = am4core.MouseCursorStyle.grabbing; }); } // end drag event // convert custom json position string to object var regionLabelCustomCoordinates = im.isJSON(data.regionLabels.regionLabelCustomCoordinates) ? JSON.parse(data.regionLabels.regionLabelCustomCoordinates) : false; let event = 'inited'; if (data.map === 'custom') { event = 'datavalidated'; } regionSeries.events.on(event, function () { regionSeries.mapPolygons.each(function (polygon) { // if they are not displaying with lat/long, skip, unless we're using a custom map if (data.map !== 'custom' && typeof polygon.visualLatitude === 'undefined') { return; } var label = labelSeries.mapImages.create(), text; // if we're only adding labels to active regions if ( im.bool(data.regionLabels.activeOnly) && polygon.dataItem.dataContext && typeof polygon.dataItem.dataContext.tooltipContent === 'undefined' ) { return; } // if it's a group entry, ignore if (polygon.dataItem.dataContext.id.includes(',')) { return; } if (data.regionLabels.source === 'code') { text = polygon.dataItem.dataContext.id.split('-').pop(); } if (data.regionLabels.source === '{name}') { text = polygon.dataItem.dataContext.name; } if (data.regionLabels.source === '{id}') { text = polygon.dataItem.dataContext.id; } if ( data.regionLabels.source === 'custom' && typeof data.regionLabels.customSource !== 'undefined' ) { text = polygon.dataItem.dataContext.autoLabel; } label.LabelForRegion = polygon.dataItem.dataContext.id; // if it was a group if (typeof polygon.dataItem.dataContext.originalID !== 'undefined') { label.groupRegion = polygon.dataItem.dataContext.originalID; } // tooltip content label.tooltipDataItem = polygon.tooltipDataItem; label.tooltip = polygon.tooltip; label.tooltipHTML = polygon.tooltipHTML; label.tooltipPosition = im.bool(data.tooltip.fixed) ? 'fixed' : 'pointer'; // cursor style if (polygon.dataItem.dataContext.action && polygon.dataItem.dataContext.action !== 'none') { label.cursorOverStyle = am4core.MouseCursorStyle.pointer; } // use custom coordinates adjustments or use auto position if ( regionLabelCustomCoordinates && regionLabelCustomCoordinates.hasOwnProperty(polygon.dataItem.dataContext.id) ) { label.latitude = regionLabelCustomCoordinates[polygon.dataItem.dataContext.id].latitude; label.longitude = regionLabelCustomCoordinates[polygon.dataItem.dataContext.id].longitude; } else { if (polygon.visualLatitude) { label.latitude = polygon.visualLatitude; label.longitude = polygon.visualLongitude; } } if (label.children.getIndex(0)) { label.children.getIndex(0).text = text; } }); }); } // if the external dropdown is enabled, calculate visual center if (typeof data.externalDropdown !== 'undefined' && im.bool(data.externalDropdown.enabled)) { regionSeries.calculateVisualCenter = true; } // add this series to map series to reference it later if needed im.maps[id].series.push(regionSeries); return regionSeries; }; iMapsManager.pushRoundMarkerSeries = function (id, data) { var im = this, map = im.maps[id].map, // shorter reference for the map markerSeries, markerSeriesTemplate, circle, label, activeState, highlightState, hoverState; if (Array.isArray(data.roundMarkers) && data.roundMarkers.length) { // Create image series markerSeries = map.series.push(new am4maps.MapImageSeries()); markerSeries.name = data.roundMarkersLegend && data.roundMarkersLegend.title !== '' ? data.roundMarkersLegend.title : data.title; markerSeries.hiddenInLegend = data.roundMarkersLegend ? !im.bool(data.roundMarkersLegend.enabled) : false; // Create a circle image in image series template so it gets replicated to all new images markerSeriesTemplate = markerSeries.mapImages.template; circle = markerSeriesTemplate.createChild(am4core.Circle); im.setupTooltip(id, markerSeries, data, circle); //iOS focus scroll bug fix markerSeriesTemplate.focusable = false; circle.radius = data.markerDefaults.radius; circle.fill = data.markerDefaults.fill; circle.stroke = am4core.color('#FFFFFF'); circle.strokeWidth = 1; circle.nonScaling = typeof igmRoundMarkersNonScaling !== 'undefined' ? igmRoundMarkersNonScaling : true; // label label = markerSeriesTemplate.createChild(am4core.Label); label.text = '{label}'; label.fill = am4core.color('#fff'); label.verticalCenter = 'middle'; label.horizontalCenter = 'middle'; label.nonScaling = typeof igmRoundMarkersNonScaling !== 'undefined' ? igmRoundMarkersNonScaling : true; label.fontSize = typeof igmClusterMarkerFontSize !== 'undefined' ? igmClusterMarkerFontSize : data.markerDefaults.radius; label.clickable = false; label.focusable = false; label.hoverable = false; // check for custom tooltip template if ( typeof data.roundMarkersTooltipTemplate !== 'undefined' && data.roundMarkersTooltipTemplate.trim() !== '' ) { markerSeriesTemplate.tooltipText = data.roundMarkersTooltipTemplate; markerSeriesTemplate.tooltipHTML = data.roundMarkersTooltipTemplate; } else { markerSeriesTemplate.tooltipText = data.tooltip && data.tooltip.template ? data.tooltip.template : '{tooltipContent}'; markerSeriesTemplate.tooltipHTML = data.tooltip && data.tooltip.template ? data.tooltip.template : '{tooltipContent}'; } markerSeriesTemplate.propertyFields.tooltipText = 'tooltipTemplate'; markerSeriesTemplate.propertyFields.tooltipHTML = 'tooltipTemplate'; // fill can only be set if heatmap is not enabled and not a range if (data.heatMapMarkers && im.bool(data.heatMapMarkers.enabled)) { im.setupHeatMap(markerSeries, id, data); // setup the fill and radius if type is range if (typeof data.heatMapMarkers.type !== 'undefined' && data.heatMapMarkers.type === 'range') { circle.propertyFields.radius = 'radius'; circle.propertyFields.fill = 'fill'; } } else { circle.propertyFields.radius = 'radius'; circle.propertyFields.fill = 'fill'; } circle.propertyFields.userClassName = 'customClass'; // Set property fields markerSeriesTemplate.propertyFields.radius = 'radius'; markerSeriesTemplate.propertyFields.latitude = 'latitude'; markerSeriesTemplate.propertyFields.longitude = 'longitude'; markerSeriesTemplate.setStateOnChildren = true; // hover & active hoverState = circle.states.create('hover'); hoverState.properties.fill = data.hover; hoverState.propertyFields.fill = 'hover'; // active activeState = circle.states.create('active'); activeState.properties.fill = data.hover; activeState.propertyFields.fill = 'hover'; // highlight - for legend hover highlightState = circle.states.create('highlight'); highlightState.properties.fill = data.hover; highlightState.propertyFields.fill = 'hover'; // text label below if (data.roundMarkerLabels && im.bool(data.roundMarkerLabels.enabled)) { // create dummy state to force all object to get this state markerSeriesTemplate.states.create('hover'); var markerLabel = markerSeriesTemplate.createChild(am4core.Label); var markerLabelPosition = typeof data.roundMarkerLabels.position !== 'undefined' && data.roundMarkerLabels.position !== '' ? data.roundMarkerLabels.position : 'bottom'; markerLabel.text = typeof data.roundMarkerLabels.source !== 'undefined' && data.roundMarkerLabels.source !== '' ? data.roundMarkerLabels.source : '{name}'; markerLabel.fontSize = data.roundMarkerLabels.fontSize; // set state on parent markerLabel.setStateOnChildren = true; //for mobile devices if ( data.roundMarkerLabels.mobileSize && parseInt(data.roundMarkerLabels.mobileSize) !== 100 ) { if (window.innerWidth <= 780) { markerLabel.fontSize = (parseInt(data.roundMarkerLabels.fontSize) * parseInt(data.roundMarkerLabels.mobileSize)) / 100; } } markerLabel.nonScaling = typeof igmRoundMarkersNonScaling !== 'undefined' ? igmRoundMarkersNonScaling : true; //im.bool(data.roundMarkerLabels.nonScaling); markerLabel.fill = data.roundMarkerLabels.fill; markerLabel.clickable = false; markerLabel.focusable = false; markerLabel.hoverable = false; // control position markerLabel.padding(0, 0, 0, 0); // this is a first iteration of this feature // in the future we can have the position individually for each marker and use the propertyFields approach if (markerLabelPosition === 'bottom') { markerLabel.horizontalCenter = 'middle'; markerLabel.verticalCenter = 'top'; markerLabel.propertyFields.paddingTop = 'radius'; } if (markerLabelPosition === 'top') { markerLabel.horizontalCenter = 'middle'; markerLabel.verticalCenter = 'bottom'; markerLabel.propertyFields.paddingBottom = 'radius'; } if (markerLabelPosition === 'right') { markerLabel.horizontalCenter = 'left'; markerLabel.verticalCenter = 'middle'; markerLabel.propertyFields.paddingLeft = 'radius'; } if (markerLabelPosition === 'left') { markerLabel.horizontalCenter = 'right'; markerLabel.verticalCenter = 'middle'; markerLabel.propertyFields.paddingRight = 'radius'; } } // send to front (so on overlay they are always on top of regions) markerSeries.zIndex = Number.MAX_VALUE; markerSeries.toFront(); // Add data markerSeries.data = data.roundMarkers; // For legend color markerSeries.fill = data.markerDefaults.fill; // Events markerSeriesTemplate.events.on('hit', function (ev) { im.singleHit(id, ev); im.setupHitEvents(id, ev); }); markerSeriesTemplate.events.on('over', function (ev) { im.setupHoverEvents(id, ev); }); } // enable small map if (data.zoom && data.zoom.smallMap && im.bool(data.zoom.smallMap)) { map.smallMap.series.push(markerSeries); } // add this series to map series to reference it later if needed im.maps[id].series.push(markerSeries); // if part of the parent map if (id === data.id) { // only add as base if not a cluster if (data.clusterMarkers && !im.bool(data.clusterMarkers.enabled)) { im.maps[id].allBaseSeries.push(markerSeries); } else if (data.clusterMarkers && im.bool(data.clusterMarkers.enabled)) { // it's a cluster, so the main series with all markers // markerSeries.hidden = true; } } return markerSeries; }; iMapsManager.pushImageMarkerSeries = function (id, data) { var im = this, map = im.maps[id].map, // shorter reference for the map markerSeries, markerSeriesTemplate, imageMarker; if (Array.isArray(data.imageMarkers) && data.imageMarkers.length) { // Create image series markerSeries = map.series.push(new am4maps.MapImageSeries()); markerSeries.name = data.imageMarkersLegend && data.imageMarkersLegend.title !== '' ? data.imageMarkersLegend.title : data.title; markerSeries.hiddenInLegend = data.imageMarkersLegend ? !im.bool(data.imageMarkersLegend.enabled) : false; // Create a circle image in image series template so it gets replicated to all new images markerSeriesTemplate = markerSeries.mapImages.template; //iOS focus scroll bug fix markerSeriesTemplate.focusable = false; imageMarker = markerSeriesTemplate.createChild(am4core.Image); imageMarker.propertyFields.href = 'href'; imageMarker.propertyFields.width = 'size'; imageMarker.propertyFields.height = 'size'; imageMarker.propertyFields.userClassName = 'customClass'; //imageMarker.propertyFields.height = "height"; imageMarker.propertyFields.horizontalCenter = 'horizontalCenter'; imageMarker.propertyFields.verticalCenter = 'verticalCenter'; imageMarker.nonScaling = true; imageMarker.propertyFields.nonScaling = 'nonScaling'; im.setupTooltip(id, markerSeries, data, imageMarker); // to have tooltip display above the image //imageMarker.tooltipX = am4core.percent(50); //imageMarker.tooltipY = am4core.percent(0); // check for custom tooltip template if ( typeof data.imageMarkersTooltipTemplate !== 'undefined' && data.imageMarkersTooltipTemplate.trim() !== '' ) { imageMarker.tooltipText = data.imageMarkersTooltipTemplate; imageMarker.tooltipHTML = data.imageMarkersTooltipTemplate; } else { imageMarker.tooltipText = data.tooltip.template ? data.tooltip.template : '{tooltipContent}'; imageMarker.tooltipHTML = data.tooltip.template ? data.tooltip.template : '{tooltipContent}'; } // Set property fields markerSeriesTemplate.propertyFields.latitude = 'latitude'; markerSeriesTemplate.propertyFields.longitude = 'longitude'; // Add data for the three cities // send to front (so on overlay they are always on top of regions) markerSeries.zIndex = Number.MAX_VALUE; markerSeries.toFront(); markerSeries.data = data.imageMarkers; // Events markerSeriesTemplate.events.on('hit', function (ev) { im.singleHit(id, ev); im.setupHitEvents(id, ev); }); markerSeriesTemplate.events.on('over', function (ev) { im.setupHoverEvents(id, ev); }); } // enable small map if (data.zoom && data.zoom.smallMap && im.bool(data.zoom.smallMap)) { map.smallMap.series.push(markerSeries); } // add this series to map series to reference it later if needed im.maps[id].series.push(markerSeries); return markerSeries; }; iMapsManager.pushIconMarkerSeries = function (id, data) { var im = this, map = im.maps[id].map, // shorter reference for the map markerSeries, markerSeriesTemplate, icon, hover, active, highlightState, label, clickableOverlay; if (Array.isArray(data.iconMarkers) && data.iconMarkers.length) { // Create image series markerSeries = map.series.push(new am4maps.MapImageSeries()); markerSeries.hiddenInLegend = data.iconMarkersLegend ? !im.bool(data.iconMarkersLegend.enabled) : false; markerSeries.name = data.iconMarkersLegend && data.iconMarkersLegend.title !== '' ? data.iconMarkersLegend.title : data.title; markerSeriesTemplate = markerSeries.mapImages.template; markerSeriesTemplate.nonScaling = true; markerSeriesTemplate.setStateOnChildren = true; //apply parent's current state to children //iOS focus scroll bug fix markerSeriesTemplate.focusable = false; markerSeriesTemplate.states.create('hover'); //create dummy state for hover // Create a circle image in image series template so it gets replicated to all new images icon = markerSeriesTemplate.createChild(am4core.Sprite); icon.propertyFields.scale = 'scale'; icon.propertyFields.path = 'path'; icon.propertyFields.rotation = 'rotation'; /* if (typeof data.iconMarkersTooltipTemplate !== 'undefined' && data.iconMarkersTooltipTemplate.trim() !== '') { icon.tooltipText = data.iconMarkersTooltipTemplate; icon.tooltipHTML = data.iconMarkersTooltipTemplate; } else { icon.tooltipText = data.tooltip.template ? data.tooltip.template : "{tooltipContent}"; icon.tooltipHTML = data.tooltip.template ? data.tooltip.template : "{tooltipContent}"; }*/ icon.propertyFields.horizontalCenter = 'horizontalCenter'; icon.propertyFields.verticalCenter = 'verticalCenter'; icon.propertyFields.fill = 'fill'; // For legend color markerSeries.fill = data.iconMarkerDefaults.fill; // clickable overlay clickableOverlay = markerSeriesTemplate.createChild(am4core.Sprite); clickableOverlay.propertyFields.scale = 'scale'; clickableOverlay.path = 'M-10,0a10,10 0 1,0 20,0a10,10 0 1,0 -20,0'; clickableOverlay.opacity = 0; clickableOverlay.propertyFields.horizontalCenter = 'horizontalCenter'; clickableOverlay.propertyFields.verticalCenter = 'verticalCenter'; markerSeriesTemplate.tooltipText = data.tooltip.template ? data.tooltip.template : '{tooltipContent}'; markerSeriesTemplate.tooltipHTML = data.tooltip.template ? data.tooltip.template : '{tooltipContent}'; im.setupTooltip(id, markerSeries, data, clickableOverlay); if (data.iconMarkerLabels && im.bool(data.iconMarkerLabels.enabled)) { var markerLabel = markerSeriesTemplate.createChild(am4core.Label); markerLabel.text = typeof data.iconMarkerLabels.source !== 'undefined' && data.iconMarkerLabels.source !== '' ? data.iconMarkerLabels.source : '{name}'; markerLabel.horizontalCenter = 'middle'; markerLabel.verticalCenter = 'top'; markerLabel.fontSize = data.iconMarkerLabels.fontSize; markerLabel.nonScaling = false; markerLabel.fill = data.iconMarkerLabels.fill; markerLabel.clickable = false; markerLabel.focusable = false; markerLabel.hoverable = false; markerLabel.padding(0, 0, 0, 0); markerLabel.adapter.add('dy', function (dy, target) { var icon = target.parent.children.getIndex(0); var scale = icon.scale; var space; if (icon.verticalCenter === 'middle') { space = 10 * scale; } if (icon.verticalCenter === 'bottom') { space = 0; } if (icon.verticalCenter === 'top') { space = 20 * scale + 5; } // formula to get the label to distance at an accepted level return space; }); } // hover & active hover = icon.states.create('hover'); hover.propertyFields.fill = 'hover'; active = icon.states.create('active'); active.propertyFields.fill = 'hover'; // highlight - for legend hover highlightState = icon.states.create('highlight'); highlightState.properties.fill = data.hover; highlightState.propertyFields.fill = 'hover'; // Set property fields markerSeriesTemplate.propertyFields.latitude = 'latitude'; markerSeriesTemplate.propertyFields.longitude = 'longitude'; // Add data // send to front (so on overlay they are always on top of regions) markerSeries.zIndex = Number.MAX_VALUE; markerSeries.toFront(); markerSeries.data = data.iconMarkers; // Events markerSeriesTemplate.events.on('hit', function (ev) { im.singleHit(id, ev); im.setupHitEvents(id, ev); }); markerSeriesTemplate.events.on('over', function (ev) { im.setupHoverEvents(id, ev); }); } // enable small map if (data.zoom && data.zoom.smallMap && im.bool(data.zoom.smallMap)) { map.smallMap.series.push(markerSeries); } // add this series to map series to reference it later if needed im.maps[id].series.push(markerSeries); return markerSeries; }; iMapsManager.pushLineSeries = function (id, data) { var im = this, map = im.maps[id].map, // shorter reference for the map lineSeries = {}, lineData = []; // Lines if (Array.isArray(data.lines) && data.lines.length) { // Add line series if (data.projection === 'Orthographic') { lineSeries = map.series.push(new am4maps.MapLineSeries()); lineSeries.mapLines.template.propertyFields.shortestDistance = true; } else { lineSeries = map.series.push(new am4maps.MapArcSeries()); lineSeries.mapLines.template.line.propertyFields.controlPointDistance = 'curvature'; lineSeries.mapLines.template.line.controlPointPosition = 0.5; } lineSeries.name = data.linesLegend && data.linesLegend.title !== '' ? data.linesLegend.title : data.title; lineSeries.hiddenInLegend = data.linesLegend ? !im.bool(data.linesLegend.enabled) : false; lineSeries.mapLines.template.nonScalingStroke = true; lineSeries.mapLines.template.propertyFields.strokeWidth = 'strokeWidth'; lineSeries.mapLines.template.propertyFields.strokeDasharray = 'strokeDash'; lineSeries.mapLines.template.propertyFields.stroke = 'stroke'; // arrow lineSeries.mapLines.template.arrow.position = 1; // 1 is the end lineSeries.mapLines.template.arrow.nonScaling = true; lineSeries.mapLines.template.arrow.propertyFields.fill = 'stroke'; lineSeries.mapLines.template.arrow.horizontalCenter = 'middle'; //lineSeries.mapLines.template.arrow.tooltipHTML = "{title}"; //lineSeries.mapLines.template.arrow.disabled = true; lineSeries.mapLines.template.arrow.propertyFields.disabled = 'arrowDisabled'; data.lines.forEach(function (lineObj, index) { // make sure multiGeoLine is array of arrays: lineObj.multiGeoLine = [lineObj.multiGeoLine]; lineData.push(lineObj); }); // events - to do lineSeries.mapLines.template.events.on('hit', function (ev) { im.singleHit(id, ev); im.setupHitEvents(id, ev); }); lineSeries.mapLines.template.events.on('over', function (ev) { im.setupHoverEvents(id, ev); }); lineSeries.data = lineData; // bring to front lineSeries.zIndex = Number.MAX_VALUE - 1; lineSeries.toFront(); // enable small map if (data.zoom && data.zoom.smallMap && im.bool(data.zoom.smallMap)) { map.smallMap.series.push(lineSeries); } //let's hide this from legend, since they don't group in the same Series //lineSeries.hiddenInLegend = true; // add this series to map series to reference it later if needed im.maps[id].series.push(lineSeries); // For legend color lineSeries.fill = data.lineDefaults.stroke; // if it's part of the parent map if (id === data.id) { im.maps[id].allBaseSeries.push(lineSeries); } } return lineSeries; }; iMapsManager.pushLabelSeries = function (id, data) { var im = this, map = im.maps[id].map, // shorter reference for the map labelSeries, labelSeriesTemplate, label, activeState, highlightState, hoverState, background = false; if (Array.isArray(data.labels) && data.labels.length) { // Create image series labelSeries = map.series.push(new am4maps.MapImageSeries()); labelSeries.name = data.labelsLegend && data.labelsLegend.title !== '' ? data.labelsLegend.title : data.title; labelSeries.hiddenInLegend = data.labelsLegend ? !im.bool(data.labelsLegend.enabled) : false; labelSeriesTemplate = labelSeries.mapImages.template; labelSeriesTemplate.setStateOnChildren = true; // label label = labelSeriesTemplate.createChild(am4core.Label); label.text = '{id}'; //label.nonScaling = true; label.nonScaling = typeof igmLabelsNonScaling !== 'undefined' ? igmLabelsNonScaling : true; if (data.labelStyle) { label.fontFamily = data.labelStyle.fontFamily; label.fontWeight = data.labelStyle.fontWeight; } label.horizontalCenter = data.labelPosition.horizontalCenter; label.verticalCenter = data.labelPosition.verticalCenter; label.propertyFields.fill = 'fill'; label.propertyFields.fontSize = 'fontSize'; im.setupTooltip(id, labelSeries, data, label); // custom fields if ( typeof data.labelsTooltipTemplate !== 'undefined' && data.labelsTooltipTemplate.trim() !== '' ) { label.tooltipText = data.labelsTooltipTemplate; label.tooltipHTML = data.labelsTooltipTemplate; } else { label.tooltipText = data.tooltip && data.tooltip.template ? data.tooltip.template : '{tooltipContent}'; label.tooltipHTML = data.tooltip && data.tooltip.template ? data.tooltip.template : '{tooltipContent}'; } // Set property fields labelSeriesTemplate.propertyFields.latitude = 'latitude'; labelSeriesTemplate.propertyFields.longitude = 'longitude'; labelSeriesTemplate.propertyFields.fill = 'fill'; labelSeriesTemplate.propertyFields.fontSize = 'fontSize'; label.propertyFields.verticalCenter = 'verticalCenter'; label.propertyFields.horizontalCenter = 'horizontalCenter'; // labels background - currently only possible through global background = typeof igmLabelsBackground !== 'undefined' ? igmLabelsBackground : false; if (typeof background === 'object') { label.background = new am4core.RoundedRectangle(); label.background.cornerRadius(...background.cornerRadius); label.background.fill = am4core.color(background.color); label.padding(...background.padding); label.background.stroke = am4core.color(background.stroke); } // hover & active hoverState = label.states.create('hover'); hoverState.properties.fill = data.hover; hoverState.propertyFields.fill = 'hover'; // active activeState = label.states.create('active'); activeState.properties.fill = data.hover; activeState.propertyFields.fill = 'hover'; // highlight - for legend hover highlightState = label.states.create('highlight'); highlightState.properties.fill = data.hover; highlightState.propertyFields.fill = 'hover'; // send to front (so on overlay they are always on top of regions) labelSeries.zIndex = Number.MAX_VALUE; labelSeries.toFront(); // Add data labelSeries.data = data.labels; // For legend color labelSeries.fill = data.labelDefaults.fill; // Events labelSeriesTemplate.events.on('hit', function (ev) { im.singleHit(id, ev); im.setupHitEvents(id, ev); }); labelSeriesTemplate.events.on('over', function (ev) { im.setupHoverEvents(id, ev); }); } // enable small map if (data.zoom && data.zoom.smallMap && im.bool(data.zoom.smallMap)) { map.smallMap.series.push(labelSeries); } // add this series to map series to reference it later if needed im.maps[id].labelSeries.push(labelSeries); im.maps[id].series.push(labelSeries); // if part of the parent map if (id === data.id) { im.maps[id].allBaseSeries.push(labelSeries); } return labelSeries; }; iMapsManager.setupTooltip = function (id, series, data, marker) { var im = this, tooltip = data.tooltip, map = im.maps[id].map, shadow; marker = marker || false; // don't include it in export // regionSeries.tooltip.exportable = false; // default behaviour if (typeof data.tooltip === 'undefined') { series.tooltip.disabled = false; series.tooltip.getFillFromObject = false; series.tooltip.getStrokeFromObject = false; series.tooltip.label.fill = am4core.color('#000000'); series.tooltip.background.fill = am4core.color('#FFFFFF'); return; } if (!im.bool(data.tooltip.enabled)) { series.tooltip.disabled = true; return series; } // if it's disabled on mobile/smaller screens if ( _typeof(data.tooltip.disableMobile) !== undefined && im.bool(data.tooltip.disableMobile) && window.innerWidth <= 780 ) { series.tooltip.disabled = true; return series; } //if it's overlay, it might have a custom config // tooltip settings from map config series.tooltip.label.interactionsEnabled = im.bool(tooltip.fixed); series.tooltip.background.cornerRadius = tooltip.cornerRadius; if (tooltip.pointerLength) { series.tooltip.background.pointerLength = parseInt(tooltip.pointerLength); } series.tooltip.getFillFromObject = false; series.tooltip.getStrokeFromObject = false; series.tooltip.label.fill = tooltip.color; series.tooltip.background.fill = tooltip.backgroundColor; series.tooltip.fontFamily = tooltip.fontFamily; series.tooltip.fontSize = tooltip.fontSize; series.tooltip.fontWeight = tooltip.fontWeight; // animation - disable the fly out effect on markers series.tooltip.animationDuration = 0; // border if (tooltip.borderColor !== 'undefined') { series.tooltip.background.stroke = tooltip.borderColor; series.tooltip.background.strokeWidth = tooltip.borderWidth; } if (typeof tooltip.maxWidth !== 'undefined' && tooltip.maxWidth !== '') { series.tooltip.maxWidth = parseInt(tooltip.maxWidth); series.tooltip.contentWidth = parseInt(tooltip.maxWidth); } series.tooltip.label.wrap = true; // box-shadow if (typeof tooltip.customShadow !== 'undefined' && im.bool(tooltip.customShadow)) { shadow = series.tooltip.background.filters.getIndex(0); shadow.dx = tooltip.customShadowOpts.dx; shadow.dy = tooltip.customShadowOpts.dy; shadow.blur = tooltip.customShadowOpts.blur; shadow.opacity = tooltip.customShadowOpts.opacity; shadow.color = tooltip.customShadowOpts.color; } // Set up fixed tooltips if (im.bool(tooltip.fixed)) { if (tooltip.showTooltipOn === 'always') { //allow tooltip to be hidden outside viewport series.tooltip.ignoreBounds = true; series.tooltip.pointerOrientation = (function (toolTipPosition) { switch (toolTipPosition) { case 'horizontal': return 'horizontal'; case 'below': return 'up'; case 'above': return 'down'; case 'right': return 'left'; case 'left': return 'right'; default: return 'vertical'; } })(tooltip.toolTipPosition); } if (series.mapPolygons) { series.calculateVisualCenter = true; series.mapPolygons.template.tooltipPosition = 'fixed'; // in case it's a group and the tooltip is set to fixed, // we need to set the tooltip hover to have the regions keep the hover state if (series.groupHover) { // set to false so that the tooltip does not // inherit the highlight state color on hover series.tooltip.getFillFromObject = false; series.tooltip.events.on('over', function (ev) { ev.target.dataItem.component.mapPolygons.each(function (polygon) { polygon.setState('highlight'); }); }); series.tooltip.events.on('out', function (ev) { ev.target.dataItem.component.mapPolygons.each(function (polygon) { polygon.setState('default'); }); }); } series.tooltip.keepTargetHover = true; if (tooltip.showTooltipOn) { series.mapPolygons.template.showTooltipOn = tooltip.showTooltipOn; // to have the tooltip in the marker fixed, even after over out if (tooltip.showTooltipOn === 'hit') { series.tooltip.keepTargetHover = false; } } } else { // for markers series.mapImages.template.tooltipPosition = 'fixed'; series.tooltip.keepTargetHover = true; if (tooltip.showTooltipOn && marker) { series.mapImages.template.showTooltipOn = tooltip.showTooltipOn; marker.showTooltipOn = tooltip.showTooltipOn; // to have the tooltip in the marker fixed, even after over out if (tooltip.showTooltipOn === 'hit') { series.tooltip.keepTargetHover = false; } // to have the tooltip display after the map loads if (tooltip.showTooltipOn === 'always') { map.events.on('appeared', function () { marker.clones.each(function (clone) { clone.showTooltip(); }); }); map.events.on('mappositionchanged', function (ev) { marker.clones.each(function (clone) { clone.showTooltip(); }); }); } } } } return series; }; iMapsManager.prepareURL = function (str) { if (typeof str !== 'string') { return str; } // parse html entities str = str.replace(/&/gi, '&'); str.replace(/&#(\d+);/g, function (match, dec) { return String.fromCharCode(dec); }); // check if allowed url var url, protocols; try { url = new URL(str); } catch (_) { url = false; } // check if valid url. If string it might be a anchor or relative path, so keep going if (url) { // accepted protocols protocols = [null, 'http:', 'https:', 'mailto:', 'tel:']; if (!protocols.includes(url.protocol)) { console.log('URL protocol not allowed'); return ''; } } return str; }; iMapsManager.setupHitEvents = function (id, ev) { var im = this, data = im.maps[id].data, dataContext, map = im.maps[id].map, customActionName, targetType = im.getTargetSeriesType(ev.target), clicked = im.maps[id].clicked || false, zoomCluster = data.clusterMarkers ? parseFloat(data.clusterMarkers.zoomLevel) : 1, currentRegion, mapFunction, container = document.getElementById(data.container), event = new Event('mapEntryClicked'); if (ev.target.isLabels) { dataContext = ev.target.dataItems.first.dataContext; } else { dataContext = ev.target.dataItem.dataContext; } // on certain devices, the click gets triggered twice, so the 'hold click actions' won't work properly // but we minize the issue by allowing it to run anyway - so on devices where the tap runs twice, the hold won't work at the moment if (map.lastClickedEntry === ev.target && !im.maps[id].clicked) { // the clicked is used to control the "hold click actions" feature im.maps[id].clicked = dataContext; // we reset after 1 second, to allow real second clicks to run // this also has the side effect that when the 'hold click actions' is enabled, only after 1 second, the second tap will actually work setTimeout(function () { map.lastClickedEntry = null; im.maps[id].clicked = null; }, 1000); return; } // we reset it to null, in case it was clicked before, double tap im.maps[id].clicked = null; map.lastClickedEntry = ev.target; container.dispatchEvent(event); // if it's a cluster, zoom in a bit if (targetType === 'MapImage') { if (dataContext.cluster) { // if we're far from the max, let's just zoom half if (zoomCluster - parseInt(map.zoomLevel) > 5) { zoomCluster = parseInt(map.zoomLevel) + zoomCluster / 2; } ev.target.series.chart.zoomToMapObject(ev.target, zoomCluster); } } // for admin log console.log(dataContext); // Zoom on click if (data.zoom && im.bool(data.zoom.enabled) && im.bool(data.zoom.zoomOnClick)) { ev.zooming = true; im.zoomToRegion(ev, id); } // drill down if (targetType === 'MapPolygon' && im.bool(data.drillDownOnClick)) { im.drillDown(id, ev); } if (dataContext.madeFromGeoData) { // check if we should clear selected or not? // iMapsManager.clearSelected(id); mapFunction = 'igm_inactive_' + data.id; if (typeof window[mapFunction] === 'function') { data = window[mapFunction](data); } return; } // if it's a marker with action to open specific map if ( targetType === 'MapImage' && _typeof(dataContext.action) !== undefined && dataContext.action === 'igm_display_map' ) { if (Array.isArray(iMapsManager.maps[id].seriesById[parseInt(dataContext.content)])) { currentRegion = iMapsManager.maps[id].seriesById[parseInt(dataContext.content)]; im.drillTo(id, ev, currentRegion, true); } } // if admin, we don't trigger the actions if (im.bool(data.admin)) { return; } // if we need to hold action on mobile/smaller screens to show tooltip if ( data.tooltip && _typeof(data.tooltip.enabled) !== undefined && im.bool(data.tooltip.enabled) && _typeof(data.tooltip.disableMobile) !== undefined && !im.bool(data.tooltip.disableMobile) && _typeof(data.tooltip.holdAction) !== undefined && im.bool(data.tooltip.holdAction) && window.innerWidth <= 780 ) { if (!clicked || clicked !== dataContext) { console.log('Holding action for second tap.'); im.maps[id].clicked = dataContext; return; } // in case the previously clicked will now perform action, we reset it if (clicked === dataContext) { im.maps[id].clicked = false; } // if it's a different one, set it normally else { im.maps[id].clicked = dataContext; } } if (dataContext.action === 'none') { // do nothing return; } // check urls if (dataContext.action === 'open_url' || dataContext.action === 'open_url_new') { dataContext.content = iMapsManager.prepareURL(dataContext.content); } // open new url if (dataContext.action === 'open_url' && dataContext.content !== '') { document.location = dataContext.content; } else if (dataContext.action === 'open_url_new' && dataContext.content !== '') { window.open(dataContext.content); } // custom else if (dataContext.action && dataContext.action.includes('custom')) { customActionName = dataContext.action + '_' + dataContext.mapID; if (typeof window[customActionName] !== 'undefined') { window[customActionName](dataContext); } } else { if (typeof window[dataContext.action] !== 'undefined') { window[dataContext.action](id, dataContext); } } }; iMapsManager.zoomToMap = function (ev, target, id) { var im = this; var baseMap = false; target.forEach(function (series) { if ('MapPolygonSeries' == im.getTargetSeriesType(series)) { baseMap = series; return; } }); if (baseMap) { ev.target.series.chart.zoomToRectangle( baseMap.north, baseMap.east, baseMap.south, baseMap.west, 1, true ); console.log('zoomed to specific map'); return; } console.log('failed to zoom to specific map'); }; iMapsManager.zoomToRegion = function (ev, id) { var im = this, seriesType = im.getTargetSeriesType(ev.target), data = im.maps[id].data, map = im.maps[id].map, markerZoomLevel, dataContext; // do nothing if we clicked a label if (ev.target.isLabels) { return; } dataContext = ev.target.dataItem.dataContext; // if it's a marker, handle it differently if (seriesType == 'MapImage') { // if it's a cluster marker if (dataContext.cluster) { //do nothing, we already zoomed } else { markerZoomLevel = typeof igmMarkerZoomLevelOnClick !== 'undefined' ? igmMarkerZoomLevelOnClick : ev.target.parent.chart.zoomLevel * 2; ev.target.series.chart.zoomToMapObject(ev.target, markerZoomLevel, true); } } else { if (dataContext.id === 'asia' && !data.map.startsWith('continents')) { ev.target.series.chart.deltaLongitudeOriginal = ev.target.series.chart.deltaLongitude; ev.target.series.chart.deltaLongitude = -10; ev.target.series.chart.zoomToGeoPoint( { latitude: 34.076842, longitude: 100.693068, }, 1.7, true ); } else if (dataContext.id === 'asia' && data.map.startsWith('continents')) { ev.target.series.chart.zoomToGeoPoint( { latitude: 34.076842, longitude: 100.693068, }, 2.2, true ); } else if ( dataContext.id === 'northAmerica' && data.map.startsWith('region/world/worldRegion') ) { ev.target.series.chart.zoomToGeoPoint( { latitude: 55.5, longitude: -105.5, }, 3, true ); } else if (dataContext.id === 'northAmerica' && data.map.startsWith('continents')) { ev.target.series.chart.zoomToGeoPoint( { latitude: 55.5, longitude: -105.5, }, 2.3, true ); } else if (dataContext.id === 'ZA' && data.map.startsWith('world')) { ev.target.series.chart.zoomToGeoPoint( { latitude: -28.6, longitude: 24.7, }, 12.2, true ); } else if (dataContext.id === 'US' && data.map.startsWith('world')) { ev.target.series.chart.zoomToGeoPoint( { latitude: 49, longitude: -119, }, 2.6, true ); } else if (dataContext.id === 'RU' && data.map.startsWith('world')) { ev.target.series.chart.zoomToGeoPoint( { latitude: 64.5, longitude: 105, }, 2.1, true ); } else { if (typeof ev.target.series.chart.deltaLongitudeOriginal !== 'undefined') { ev.target.series.chart.deltaLongitude = ev.target.series.chart.deltaLongitude; } ev.target.series.chart.zoomToMapObject(ev.target, ev.target.zoomLevel * 2); } } }; iMapsManager.setupHoverEvents = function (id, ev) { var im = this, selected = im.maps[id].selected || false, dataContext; if (ev.target.isLabels) { dataContext = ev.target.dataItems.first.dataContext; } else { dataContext = ev.target.dataItem.dataContext; } // when tooltip position is fixed, hovering regions might leave hovered state behind, when the overout goes through the tooltip if (ev.target.className !== 'MapImage' && ev.target.tooltipPosition === 'fixed') { if (Array.isArray(im.tempHover) && im.tempHover.length >= 0) { im.tempHover[0].setState('default'); im.tempHover = []; } im.tempHover = [ev.target]; } if (dataContext.action && dataContext.action !== 'none') { ev.target.cursorOverStyle = am4core.MouseCursorStyle.pointer; } if ( Array.isArray(selected) && !selected.includes(ev.target) && ev.target.dataItem && typeof ev.target.dataItem.dataContext.madeFromGeoData === 'undefined' ) { selected.forEach(function (sel, index) { if (typeof sel === 'object' && typeof sel.isHover !== 'undefined') { sel.isHover = false; } }); } // we exclude touch devices, since the hover event is also triggered on tap, otherwise we have 2 select events triggered if ( im.bool(dataContext.triggerClickOnHover) && (!iMapsManager.isTouchScreenDevice() || ev.type === 'over') && (typeof iMaps.maps[id].mapClicked === 'undefined' || iMaps.maps[id].mapClicked === false) ) { iMapsManager.select(id, dataContext.id, false, true, dataContext.mapID, false); } // if it's a marker and we want to trigger hover event also on associated regions in marker value if (im.bool(dataContext.triggerRegionHover) && dataContext.val && dataContext.val !== '') { iMapsManager.hover(id, dataContext.val); ev.target.events.on('out', function (ev) { iMapsManager.clearHovered(id); }); } }; iMapsManager.singleHit = function (id, ev) { var im = this, dataContext, data = im.maps[id].data; if (ev.target.isLabels) { dataContext = ev.target.dataItems.first.dataContext; } else { dataContext = ev.target.dataItem.dataContext; } if (dataContext.madeFromGeoData) { return; } if (iMaps.maps[id].activeStateControl === true && dataContext.activeState) { iMaps.maps[id].mapClicked = true; } // causes issues on tap on mobile, we need to click twice? // seems the clear selected is affecting // update: // let's try passing second argument as false if tooltip is set to display on click? let keepThis = true; if (data.tooltip && data.tooltip.showTooltipOn === 'hit') { keepThis = false; } iMapsManager.clearSelected(id, keepThis, true); ev.target.isActive = true; ev.target.isHover = true; ev.target.setState('active'); im.maps[id].selected = [ev.target]; }; iMapsManager.groupHit = function (id, ev) { var im = this, selected = im.maps[id].selected || false; if (ev.target.dataItem.dataContext.madeFromGeoData) { return; } if (iMaps.maps[id].activeStateControl === true && ev.target.dataItem.dataContext.activeState) { iMaps.maps[id].mapClicked = true; } //removed to improve click on group ? iMapsManager.clearSelected(id); selected = []; ev.target.parent.mapPolygons.each(function (polygon) { // added the typeof control to only run code on original clicked/hit entry and not the individual shape // removed, because the code wasn't running otherwise -> && typeof polygon.dataItem.dataContext.originalID === 'undefined' if (!polygon.dataItem.dataContext.madeFromGeoData) { // toggle active state polygon.setState('active'); if (ev.target === polygon) { polygon.isHover = true; } //removed to fix bug when hovering groups after clicked ? polygon.isActive = true; polygon.isGroupActive = true; selected.push(polygon); } }); im.maps[id].selected = selected; }; iMapsManager.groupHover = function (id, ev) { var im = this, dataContext; if (ev.target.isLabels) { dataContext = ev.target.dataItems.first.dataContext; } else { dataContext = ev.target.dataItem.dataContext; } if (ev.target.dataItem.dataContext.madeFromGeoData) { return; } if ( im.bool(dataContext.triggerClickOnHover) && (typeof iMaps.maps[id].mapClicked === 'undefined' || iMaps.maps[id].mapClicked === false) ) { iMapsManager.select(id, dataContext.id, false, true, dataContext.mapID, false); } // set mouse hover pointer cursor if (ev.target.dataItem.dataContext.action && ev.target.dataItem.dataContext.action != 'none') { ev.target.cursorOverStyle = am4core.MouseCursorStyle.pointer; } // highlight all polygons from this group ev.target.parent.mapPolygons.each(function (polygon) { if (!polygon.dataItem.dataContext.madeFromGeoData) { if (!polygon.isActive) { polygon.setState('highlight'); } } }); }; iMapsManager.groupHoverOut = function (id, ev) { if (ev.target.dataItem.dataContext.madeFromGeoData) { return; } ev.target.parent.mapPolygons.each(function (polygon) { if (!polygon.isGroupActive) { polygon.setState('default'); polygon.isActive = false; polygon.isHover = false; } }); }; /** * Get regions object collection based on value of a specific parameter * id - map id * value - value you want to filter by * parameter - parameter to search */ iMapsManager.getRegionsByValue = function (id, value, parameter) { var im = this, map = im.maps[id], series = map.series, seriesType = false, regions = [], tempArray; value = value || '*'; parameter = parameter || 'val'; series.forEach(function (serie) { seriesType = im.getTargetSeriesType(serie); if (seriesType === 'MapPolygonSeries') { tempArray = serie.mapPolygons.values.filter(function (entry) { if (entry.dataItem.dataContext[parameter] === value || value === '*') { return true; } }); regions = regions.concat(tempArray); } }); return regions; }; /** Get markers object collection based on value of a specific parameter * It will search for all kinds of markers (icons, images, round markers) * id - map id * value - value you want to filter by * parameter - parameter to search */ iMapsManager.getMarkersByValue = function (id, value, parameter) { var im = this, map = im.maps[id], series = map.series, seriesType = false, markers = [], tempArray; value = value || '*'; parameter = parameter || 'val'; series.forEach(function (serie) { seriesType = im.getTargetSeriesType(serie); if (seriesType === 'MapImageSeries') { tempArray = serie.mapImages.values.filter(function (entry) { if (entry.dataItem.dataContext[parameter] === value || value === '*') { return true; } }); markers = markers.concat(tempArray); } }); return markers; }; /** * Selects a element in the map * id - id of the map * elID - id of the element to select * forceFixedTooltip - if we should force the tooltip to be fixed or not * showTooltip * seriesMapID - map id of the series, which could be ab overlay * click - boolean - trigger true click event */ iMapsManager.select = function (id, elID, forceFixedTooltip, showTooltip, seriesMapID, click) { var im = this, map = im.maps[id], data = im.maps[id].data, series = map.series.slice().reverse(), // we clone and reverse so that the grouped region series are at the end, which solves a particular bug with the select() function and the tooltip selected = [], select, group, defaultTooltipPosition, defaultTooltipShowOn, seriesByID = iMaps.maps[id].seriesById, thisSeries = false, ogID, customRegionGroup = true, triggered = false, // temp solution to prevent map from triggering multiple click actions if there are entries with same ID in different layers isGroup = false, keepThis = true; // map sure it's string if (Number.isInteger(elID)) { elID = elID.toString(); } ogID = elID; // Force fixed position? if (typeof forceFixedTooltip === 'undefined') { forceFixedTooltip = true; } if (typeof showTooltip === 'undefined') { showTooltip = true; } if (typeof seriesMapID === 'undefined') { seriesMapID = id; } if (seriesByID.hasOwnProperty(seriesMapID)) { thisSeries = seriesByID[seriesMapID]; } if (typeof click === 'undefined') { click = true; } else { click = false; } // we pass the click as the 'skipReset' - if not a real click, its the hover and we skip the reset to prevent flickering // second argument, the keepThis set to true, to avoid removing hover status of selected/hovered entry // update - second argument might be set to false, if tooltip only displays on hit to fix issue where the tooltip wouldn't close if (data.tooltip && data.tooltip.showTooltipOn === 'hit') { keepThis = false; } iMapsManager.clearSelected(id, keepThis, !click); if (click) { iMaps.maps[id].activeStateControl = true; } else { iMaps.maps[id].activeStateControl = false; } // if we have to search in specific series and this is not that one, skip if (thisSeries) { series = thisSeries; } if (Array.isArray(series)) { for (var i = 0, len = series.length; i < len; i++) { if (triggered) { break; } // if it's one of the cluster series, ignore if (typeof series[i].isCluster !== 'undefined' && series[i].isCluster) { continue; } // regionSeries if (series[i].mapPolygons) { //first trigger click // this might return false if there is no entry created for this group and we are selecting programmatically select = series[i].getPolygonById(elID); if ( select && typeof select.dataItem.dataContext.originalID !== 'undefined' && select.dataItem.dataContext.originalID.includes(',') ) { elID = select.dataItem.dataContext.originalID; } // show series if it's hidden if (select) { series[i].show(); } // check if group if (elID.includes(',')) { if (typeof select !== 'undefined' && select) { select.tooltip = false; select.dispatchImmediately('hit'); select.hideTooltip(); customRegionGroup = false; // reset true click events controller iMaps.maps[id].activeStateControl = true; } //then highlight the first one group = elID.split(','); group.forEach(function (rxid, indx) { select = series[i].getPolygonById(rxid.trim()); if (typeof select !== 'undefined' && select) { if (select.dataItem.dataContext.madeFromGeoData) { return; } // if originalID is not the same as the current originalID from the group, let's ignore it, it could belong to another group if (select.dataItem.dataContext.originalID !== elID && !customRegionGroup) { return; } //the first one, let's trigger the tooltip and the hit if (indx === 0 && showTooltip) { triggered = true; if (forceFixedTooltip) { defaultTooltipPosition = typeof select !== 'undefined' && typeof select.tooltipPosition !== 'undefined' ? select.tooltipPosition : 'hover'; select.tooltipPosition = 'fixed'; // experimental code to check if on hit tooltip remains defaultTooltipShowOn = select.showTooltipOn; select.showTooltipOn = 'always'; iMaps.maps[id].activeStateControl = true; select.tooltipPosition = defaultTooltipPosition; select.showTooltipOn = defaultTooltipShowOn; select.selectedFromGroup = true; } else { select.isHover = true; } } // if we have the active state, use it instead if (data.regionActiveState && im.bool(data.regionActiveState.enabled)) { select.setState('active'); } else { select.setState('highlight'); } selected.push(select); } }); } else { // individual region select = series[i].getPolygonById(elID); if (typeof select !== 'undefined' && select) { let timeout = 0; //check if globe to rotate if (data.projection === 'Orthographic') { let map = iMaps.maps[id].map; map.animate( { property: 'deltaLongitude', to: -select.longitude, }, 500, am4core.ease.linear ); map.animate( { property: 'deltaLatitude', to: -select.latitude, }, 550, am4core.ease.linear ); timeout = 550; } setTimeout( function (select) { console.log(select.dataItem.dataContext); if ( forceFixedTooltip && select.dataItem.dataContext && select.dataItem.dataContext.action !== 'igm_display_map' ) { defaultTooltipPosition = typeof select !== 'undefined' && typeof select.tooltipPosition !== 'undefined' ? select.tooltipPosition : 'hover'; select.tooltipPosition = 'fixed'; // experimental code to check if on hit tooltip remains defaultTooltipShowOn = select.showTooltipOn; select.showTooltipOn = 'always'; select.dispatchImmediately('hit'); triggered = true; select.tooltipPosition = defaultTooltipPosition; select.showTooltipOn = defaultTooltipShowOn; selected.push(select); } // if action is set to display map it will zoom and we don't need to show tooltip else if ( select.dataItem.dataContext && select.dataItem.dataContext.action === 'igm_display_map' ) { select.dispatchImmediately('hit'); select.hideTooltip(); triggered = true; selected.push(select); } else { select.dispatchImmediately('hit'); triggered = true; selected.push(select); } }, timeout, select ); } } } // imageSeries if (series[i].mapImages) { // multiple markers if (elID.includes(',')) { // hilight group = elID.split(','); group.forEach(function (rxid, indx) { select = series[i].getImageById(rxid); if (typeof select !== 'undefined' && select) { // show series if it's hidden series[i].show(); if (forceFixedTooltip) { defaultTooltipShowOn = select.showTooltipOn; select.showTooltipOn = 'always'; defaultTooltipPosition = typeof select && typeof select.tooltipPosition !== 'undefined' ? select.tooltipPosition : 'hover'; select.tooltipPosition = 'fixed'; select.dispatchImmediately('hit'); triggered = true; select.tooltipPosition = defaultTooltipPosition; select.showTooltipOn = defaultTooltipShowOn; selected.push(select); } else { select.dispatchImmediately('hit'); triggered = true; selected.push(select); } } }); } // single marker else { let selectMarker = series[i].getImageById(elID); if (selectMarker) { // show series if it's hidden series[i].show(); let timeoutM = 0; //check if globe to rotate if (data.projection === 'Orthographic') { let map = iMaps.maps[id].map; map.animate( { property: 'deltaLongitude', to: -selectMarker.longitude, }, 500, am4core.ease.linear ); map.animate( { property: 'deltaLatitude', to: -selectMarker.latitude, }, 550, am4core.ease.linear ); timeoutM = 550; } setTimeout(function () { if (forceFixedTooltip) { defaultTooltipPosition = selectMarker.tooltipPosition; defaultTooltipShowOn = selectMarker.showTooltipOn; selectMarker.tooltipPosition = 'fixed'; selectMarker.showTooltipOn = 'always'; selectMarker.isHover = true; selectMarker.isActive = true; // to also show tooltip when using the select method selectMarker.dispatchImmediately('hit'); selectMarker.setState('active'); selectMarker.children.each(function (child) { //if(child.className === 'Circle'){ child.showTooltip(0); //} }); triggered = true; selectMarker.tooltipPosition = defaultTooltipPosition; selectMarker.showTooltipOn = defaultTooltipShowOn; selected.push(selectMarker); } else { selectMarker.dispatchImmediately('hit'); triggered = true; selected.push(selectMarker); } }, timeoutM); } } } } } im.maps[id].selected = selected; return select; }; iMapsManager.setupRangeHeatMap = function (series, id, data) { var im = this, regions = data.regions, markers = data.roundMarkers, reordered, seriesType = im.getTargetSeriesType(series); if (seriesType === 'MapImageSeries') { if (!Array.isArray(data.heatMapMarkers.range) || data.heatMapMarkers.range.length === 0) { return; } // reorder ranges by value reordered = data.heatMapMarkers.range.slice(0); reordered.sort(function (a, b) { if (isNaN(a.rule)) { var x = a.rule.toLowerCase(); var y = b.rule.toLowerCase(); return x < y ? -1 : x > y ? 1 : 0; } else { return parseFloat(a.rule) - parseFloat(b.rule); } }); if (Array.isArray(markers) && markers.length > 0) { markers.forEach(function (entry, index) { if (typeof entry[data.heatMapMarkers.source] === 'undefined') { return; } // default to correct value reference, since value will be only numeric if (data.heatMapMarkers.source === 'value') { data.heatMapMarkers.source = 'val'; } var val = entry[data.heatMapMarkers.source]; var start; if (!isNaN(val)) { val = parseFloat(val); } reordered.forEach(function (ruleData, index) { if (isNaN(ruleData.rule)) { start = ruleData.rule.trim(); // if it's not a number if (val == start) { entry.fill = ruleData.fill; entry.radius = parseFloat(ruleData.radius); } } else { start = parseFloat(ruleData.rule); // if it's a number if (val >= start) { entry.fill = ruleData.fill; entry.radius = parseFloat(ruleData.radius); } } }); }); } } else if (seriesType === 'MapPolygonSeries') { if (!Array.isArray(data.heatMapRegions.range) || data.heatMapRegions.range.length === 0) { return; } // reorder ranges by value reordered = data.heatMapRegions.range.slice(0); reordered.sort(function (a, b) { if (isNaN(a.rule)) { var x = a.rule.toLowerCase(); var y = b.rule.toLowerCase(); return x < y ? -1 : x > y ? 1 : 0; } else { return parseFloat(a.rule) - parseFloat(b.rule); } }); if (Array.isArray(regions) && regions.length > 0) { if (!isNaN(data.heatMapRegions.source)) { data.heatMapRegions.source = parseInt(data.heatMapRegions.source); } regions.forEach(function (entry, index) { if (typeof entry[data.heatMapRegions.source] === 'undefined') { return; } if (data.heatMapRegions.source === 'value') { data.heatMapRegions.source = 'val'; } var val = entry[data.heatMapRegions.source]; var start; if (!isNaN(val)) { val = parseFloat(val); } reordered.forEach(function (ruleData, index) { if (isNaN(ruleData.rule)) { start = ruleData.rule.trim(); // if it's a number if (val == start) { entry.fill = ruleData.fill; } } else { start = parseFloat(ruleData.rule); // if it's a number if (val >= start) { entry.fill = ruleData.fill; } } }); }); } } }; /** * * @param {object} series * @param {int} id - parent map id * @param {object} data - current map data */ iMapsManager.setupHeatMap = function (series, id, data) { var im = this, map = im.maps[id].map, heatLegend, legendLabel, legendContainer, minRange, maxRange, target, minProp, maxProp, propTargets, dataSource, seriesType = im.getTargetSeriesType(series); // setup target if (seriesType === 'MapImageSeries') { if (typeof data.heatMapMarkers.type !== 'undefined' && data.heatMapMarkers.type === 'range') { im.setupRangeHeatMap(series, data.id, data); return; } target = series.mapImages.template.children.values[0]; propTargets = ['fill', 'radius']; dataSource = data.heatMapMarkers; } else if (seriesType === 'MapPolygonSeries') { if (typeof data.heatMapRegions.type !== 'undefined' && data.heatMapRegions.type === 'range') { im.setupRangeHeatMap(series, data.id, data); return; } target = series.mapPolygons.template; propTargets = ['fill']; dataSource = data.heatMapRegions; } else { return; } // make sure we read data form the correct property series.dataFields.value = dataSource.source; if (!Array.isArray(propTargets)) { propTargets = [propTargets]; } propTargets.map(function (prop) { // setup min/max sources if (prop === 'fill') { minProp = dataSource.minColor; maxProp = dataSource.maxColor; } else if (prop === 'radius') { minProp = dataSource.minRadius; maxProp = dataSource.maxRadius; } series.heatRules.push({ property: prop, target: target, min: minProp, max: maxProp, }); }); if (im.bool(dataSource.legend)) { legendContainer = map.createChild(am4core.Container); legendContainer.align = typeof dataSource.legendAlign !== 'undefined' ? dataSource.legendAlign : 'right'; legendContainer.valign = typeof dataSource.legendValign !== 'undefined' ? dataSource.legendValign : 'bottom'; legendContainer.userClassName = 'mapLegendContainer'; legendContainer.marginRight = am4core.percent(4); //legendContainer.parent = map; if (window.innerWidth <= 780) { legendContainer.width = am4core.percent(40); } else { legendContainer.width = am4core.percent(25); } // legend title if (typeof dataSource.label !== 'undefined' && dataSource.label !== '') { legendLabel = legendContainer.createChild(am4core.Label); legendLabel.text = dataSource.label; legendLabel.horizontalCenter = 'left'; legendLabel.verticalCenter = 'top'; legendLabel.fontSize = 12; legendLabel.paddingBottom = 40; if (window.innerWidth <= 780) { legendLabel.fontSize = 10; legendLabel.paddingBottom = 48; } else { legendLabel.fontSize = 12; legendLabel.paddingBottom = 48; } legendLabel.nonScaling = false; legendLabel.clickable = false; legendLabel.focusable = false; legendLabel.hoverable = false; } //legendLabel.parent = heatLegend; //label.insertAfter(heatLegend.valueAxis); //heatLegend.valueAxis.title.text = 'test title'; heatLegend = legendContainer.createChild(am4maps.HeatLegend); heatLegend.series = series; heatLegend.align = 'right'; heatLegend.valign = 'bottom'; heatLegend.width = am4core.percent(100); //heatLegend.marginRight = am4core.percent(4); heatLegend.minValue = 0; heatLegend.maxValue = 99999999999999; // Set up custom heat map legend labels using axis ranges minRange = heatLegend.valueAxis.axisRanges.create(); minRange.value = heatLegend.minValue; minRange.label.text = dataSource.minLabel; maxRange = heatLegend.valueAxis.axisRanges.create(); maxRange.value = heatLegend.maxValue; maxRange.label.text = dataSource.maxLabel; minRange.label.fontSize = 12; maxRange.label.fontSize = 12; if (window.innerWidth <= 780) { minRange.label.fontSize = 10; maxRange.label.fontSize = 10; } else { minRange.label.fontSize = 12; maxRange.label.fontSize = 12; } // Blank out internal heat legend value axis labels heatLegend.valueAxis.renderer.labels.template.adapter.add('text', function () { return ''; }); } }; iMapsManager.drillTo = function (id, ev, currentRegion, customMap) { var im = iMapsManager, map = im.maps[id].map, data = im.maps[id].data, allCurrentSeries = iMapsManager.maps[id].series, baseSeries = iMapsManager.maps[id].baseSeries, allBaseSeries = iMapsManager.maps[id].allBaseSeries, bgSeries = im.maps[id].backgroundSeries, opacity, i, len; (opacity = typeof igmDrilldownBaseMapOpacity !== 'undefined' ? igmDrilldownBaseMapOpacity : 0.3), // hide all others except this one and baseSeries // if were opening a custom map (customMap = customMap || false); // hide or fade series for (i = 0, len = allCurrentSeries.length; i < len; i++) { if (baseSeries.includes(allCurrentSeries[i])) { // let's check if new option to keep base map is enabled if (typeof data.alwaysKeepBase === 'undefined' || !im.bool(data.alwaysKeepBase)) { allCurrentSeries[i].opacity = opacity; // hide image background bgSeries.opacity = opacity; } } else if (allBaseSeries.includes(allCurrentSeries[i])) { // let's check if new option to keep base map is enabled if (typeof data.alwaysKeepBase === 'undefined' || !im.bool(data.alwaysKeepBase)) { allCurrentSeries[i].hide(); } } else { allCurrentSeries[i].hide(); } } for (i = 0, len = currentRegion.length; i < len; i++) { currentRegion[i].show(); } // is drilling iMapsManager.maps[id].isDrilling = true; // zoom to region - it will only work when zoom is enabled so that controls exist if (!ev.zooming) { if (customMap) { console.log('drill to specific map'); im.zoomToMap(ev, currentRegion); return; } im.zoomToRegion(ev, id); } }; iMapsManager.drillDown = function (id, ev) { var im = iMapsManager, mapName = iMapsRouter.iso2cleanName(ev.target.dataItem.dataContext.id, id), targetID = ev.target.dataItem.dataContext.id, allCurrentSeries = iMapsManager.maps[id].series, clicked = ev.target.dataItem.dataContext, currentRegion, customMap = false, baseSeries = iMapsManager.maps[id].baseSeries, allBaseSeries = iMapsManager.maps[id].allBaseSeries, i, len, checkSeries = [], seriesExists = false; console.log('Map Name:', mapName); console.log('Available Series:', iMapsManager.maps[id].seriesIndex); console.log('Available Series by ID: ', iMapsManager.maps[id].seriesById); // check if geofile info exists or return if the id is numeric if (!mapName || !isNaN(targetID)) { // well, let's only return if it's not showing another map // code needs better logic... if (_typeof(clicked) === undefined || clicked.action !== 'igm_display_map') { return false; } } if (ev.target.polygon) { // what we need to check checkSeries.push(mapName); // check if series exists for this map checkSeries.forEach(function (ser) { if (_typeof(clicked) !== undefined && clicked.action == 'igm_display_map') { console.log('Display custom map ' + clicked.content); if (Array.isArray(iMapsManager.maps[id].seriesById[parseInt(clicked.content)])) { seriesExists = true; currentRegion = iMapsManager.maps[id].seriesById[parseInt(clicked.content)]; customMap = true; } } else { if (Array.isArray(iMapsManager.maps[id].seriesIndex[ser])) { seriesExists = true; currentRegion = iMapsManager.maps[id].seriesIndex[ser]; } } }); if (seriesExists) { iMapsManager.drillTo(id, ev, currentRegion, customMap); iMapsManager.maps[id].drilledTo = currentRegion[0].mapID; } else { // if the target is part of current series, do nothing if (currentRegion === ev.target.series) { iMapsManager.maps[id].isDrilling = false; return; } // if target is base series, show it if (baseSeries.includes(ev.target.series)) { iMapsManager.maps[id].isDrilling = false; iMapsManager.maps[id].drilledTo = false; // hide all except baseSeries for (i = 0, len = allCurrentSeries.length; i < len; i++) { if (allBaseSeries.includes(allCurrentSeries[i])) { allCurrentSeries[i].show(); } else { allCurrentSeries[i].hide(); } } } } } }; /*HELPERS*/ iMapsManager.getSelected = function (id) { var im = this, map = im.maps[id], selected = map.selected || false, multiple = []; if (selected) { if (Array.isArray(selected)) { selected.forEach(function (sel) { multiple.push(sel.dataItem.dataContext); }); return multiple; } return selected.dataItem.dataContext; } else { return false; } }; iMapsManager.getHovered = function (id) { var im = this, map = im.maps[id], hovered = map.hovered || false, multiple = []; if (hovered) { if (Array.isArray(hovered)) { hovered.forEach(function (sel) { multiple.push(sel.dataItem.dataContext); }); return multiple; } } else { return false; } }; iMapsManager.getHighlighted = function (id) { var im = this, map = im.maps[id], highlighted = map.highlighted || false, multiple = []; if (highlighted) { if (Array.isArray(highlighted)) { highlighted.forEach(function (sel) { multiple.push(sel.dataItem.dataContext); }); return multiple; } return highlighted.dataItem.dataContext; } else { return false; } }; iMapsManager.clearSelected = function (id, keepThis, skipReset) { var im = this, map = im.maps[id], selected = map.selected || []; // to keep the state of this element keepThis = keepThis || false; skipReset = skipReset || false; if (Array.isArray(selected) && selected.length > 0) { selected.forEach(function (polygon, index) { if ( polygon !== keepThis && typeof polygon === 'object' && typeof polygon.isHover !== 'undefined' ) { polygon.isHover = false; polygon.isActive = false; polygon.isGroupActive = false; // there were some issues on mobile tap (singleHit) where the tooltip would disappear, // so we added this check before hiding the tooltip if (!keepThis) { polygon.hideTooltip(0); // needed to hide tooltip } if (polygon.selectedFromGroup) { polygon.hideTooltip(0); // needed to hide tooltip when group was selected polygon.selectedFromGroup = false; } polygon.setState('default'); } }); selected = []; } if (!keepThis && typeof iMapsActions !== 'undefined') { if (!skipReset) { //iMapsActions.resetActions(id); } map.selected = []; } else { map.selected = [keepThis]; } return map.selected; }; iMapsManager.clearHighlighted = function (id) { var im = this, map = im.maps[id], highlighted = map.highlighted || []; if (Array.isArray(highlighted) && highlighted.length > 0) { highlighted.forEach(function (polygon, index) { polygon.isHover = false; polygon.isActive = false; polygon.setState('default'); }); highlighted = []; } return highlighted; }; /* * setup hover events * id - id of the map * eID - hovered element ID * forcedFixedTooltip - if we should force the tooltip to be fixed or not */ iMapsManager.hover = function (id, eID, forceFixedTooltip) { var im = this, map = im.maps[id], data = map.data, series = map.series, hovered = map.hovered || [], hover, group, defaultShowTooltipOn, defaultTooltipPosition; // map sure it's string if (Number.isInteger(eID)) { eID = eID.toString(); } // Force fixed position? if (typeof forceFixedTooltip === 'undefined') { forceFixedTooltip = true; } iMapsManager.clearHovered(id); hovered = []; if (Array.isArray(series)) { for (var i = 0, len = series.length; i < len; i++) { // regionSeries if (series[i].mapPolygons) { // multiple // check if group if (eID.includes(',')) { // foreach code group = eID.split(','); group.forEach(function (rxid, indx) { // single hover = series[i].getPolygonById(rxid.trim()); if (hover) { if (forceFixedTooltip) { defaultTooltipPosition = typeof hover.tooltipPosition !== 'undefined' ? hover.tooltipPosition : 'fixed'; defaultShowTooltipOn = typeof hover.showTooltipOn !== 'undefined' ? hover.showTooltipOn : 'hover'; hover.tooltipPosition = 'fixed'; hover.showTooltipOn = 'always'; hovered.push(hover); hover.dispatchImmediately('over'); hover.isHover = true; hover.tooltipPosition = defaultTooltipPosition; hover.showTooltipOn = defaultShowTooltipOn; } else { hovered.push(hover); hover.dispatchImmediately('over'); hover.isHover = true; } } }); } else { // single region hover = series[i].getPolygonById(eID); if (hover) { if (forceFixedTooltip) { defaultTooltipPosition = hover.tooltipPosition; hover.tooltipPosition = 'fixed'; hovered = [hover]; hover.dispatchImmediately('over'); hover.isHover = true; hover.tooltipPosition = defaultTooltipPosition; } else { hovered = [hover]; hover.dispatchImmediately('over'); hover.isHover = true; } } } } // imageSeries if (series[i].mapImages) { // multiple markers if (eID.includes(',')) { // foreach code group = eID.split(','); group.forEach(function (rxid, indx) { hover = series[i].getImageById(rxid); if (hover) { if (forceFixedTooltip) { defaultTooltipPosition = hover.tooltipPosition; defaultShowTooltipOn = hover.showTooltipOn; hover.tooltipPosition = 'fixed'; hover.showTooltipOn = 'always'; hovered = [hover]; hover.dispatchImmediately('over'); hover.isHover = true; hover.setState('hover'); hover.children.each(function (child) { if (child.className === 'Circle') { child.showTooltip(0); } }); hover.tooltipPosition = defaultTooltipPosition; hover.showTooltipOn = defaultShowTooltipOn; } else { hovered = [hover]; hover.dispatchImmediately('over'); hover.isHover = true; hover.setState('hover'); } } }); } // single marker else { hover = series[i].getImageById(eID); if (hover) { if (forceFixedTooltip) { defaultTooltipPosition = hover.tooltipPosition; hover.tooltipPosition = 'fixed'; hovered = [hover]; hover.dispatchImmediately('over'); hover.isHover = true; hover.setState('hover'); hover.children.each(function (child) { if (child.className === 'Circle') { child.showTooltip(0); } }); hover.tooltipPosition = defaultTooltipPosition; } else { hovered = [hover]; hover.dispatchImmediately('over'); hover.isHover = true; hover.setState('hover'); } } } } } } map.hovered = hovered; return hover; }; iMapsManager.clearHovered = function (id, eID) { var im = this, map = im.maps[id], hovered = map.hovered || false, series = map.series, hover; eID = eID || false; if (eID) { if (Array.isArray(series)) { for (var i = 0, len = series.length; i < len; i++) { // regionSeries - specific region if (series[i].mapPolygons) { hover = series[i].getPolygonById(eID); if (hover) { hover.dispatchImmediately('out'); hover.isHover = false; } } // imageSeries - specific marker if (series[i].mapImages) { hover = series[i].getImageById(eID); if (hover) { hover.isHover = false; hover.setState('default'); hover.dispatchImmediately('out'); } } } } } // all of the regions or markers else { if (hovered) { hovered.forEach(function (hov) { hov.dispatchImmediately('out'); if (typeof hov.isGroupActive === 'undefined' || !hov.isGroupActive) { hov.setState('default'); hov.isHover = false; } if (typeof hov.children !== 'undefined') { hov.children.each(function (child) { if (child.className === 'Circle') { child.hideTooltip(0); } }); } }); map.hovered = []; return true; } } return false; }; /** * id - map ID * elID - element to highlight */ iMapsManager.highlight = function (id, elID) { var im = this, map = im.maps[id], series = map.series, select, highlighted = map.highlighted || [], group; // map sure it's string if (Number.isInteger(elID)) { elID = elID.toString(); } //iMapsManager.clearSelected(id); if (Array.isArray(series)) { for (var i = 0, len = series.length; i < len; i++) { // regionSeries if (series[i].mapPolygons) { // check if group if (elID.includes(',')) { group = elID.split(','); group.forEach(function (rxid, indx) { select = series[i].getPolygonById(rxid.trim()); if (typeof select !== 'undefined' && select) { if (select.dataItem.dataContext.madeFromGeoData) { return; } select.setState('highlight'); highlighted.push(select); } }); } else { select = series[i].getPolygonById(elID); if (typeof select !== 'undefined' && select) { select.setState('highlight'); highlighted.push(select); } } } // imageSeries if (series[i].mapImages) { if (elID.includes(',')) { group = elID.split(','); group.forEach(function (rxid, indx) { select = series[i].getImageById(rxid); if (typeof select !== 'undefined' && select) { select.setStateOnChildren = true; select.setState('highlight'); highlighted.push(select); } }); } else { select = series[i].getImageById(elID); if (typeof select !== 'undefined' && select) { select.setStateOnChildren = true; select.setState('highlight'); highlighted.push(select); } } } } } map.highlighted = highlighted; return select; }; iMapsManager.getTargetSeriesType = function (el) { var className = el.className; return className; }; /** * Setups clustered series based on coordinate values from data */ iMapsManager.setupClusters = function (data, id, overlay) { var im = this, map = im.maps[id], series = [], markerSeries, tempData = {}, biasLevels = [], zoomLevels = [], biasSteps = 4, i = 0, prevBias = parseFloat(data.clusterMarkers.maxBias), maxZoomLevel = parseFloat(data.clusterMarkers.zoomLevel) || 20, tooltipTemplate = typeof data.clusterMarkers.tooltipTemplate !== 'undefined' ? data.clusterMarkers.tooltipTemplate : false; overlay = overlay || false; var currentMap = overlay ? overlay : id; while (i <= biasSteps) { biasLevels.push(prevBias); prevBias = prevBias / 2; zoomLevels.push(maxZoomLevel); // make sure the last one is set to 1, otherwise it will be hidden maxZoomLevel = i == 3 ? 1 : Math.ceil(maxZoomLevel / 2); i++; } // reverse array to match detail level zoomLevels.reverse().pop(); biasLevels.pop(); if (typeof map.clusterSeries[currentMap] === 'undefined') { map.clusterSeries[currentMap] = { zoomLevels: {}, overlay: overlay, }; } if (Array.isArray(data.roundMarkers)) { biasLevels.forEach(function (item, index) { series = geocluster(data.roundMarkers, item, data.markerDefaults, tooltipTemplate); tempData = Object.assign({}, data); tempData.roundMarkers = series; markerSeries = im.pushRoundMarkerSeries(id, tempData); markerSeries.name = tempData.title || 'Map'; markerSeries.hiddenInLegend = true; // set as cluster to be identified later in select method markerSeries.isCluster = true; map.clusterSeries[currentMap].zoomLevels[zoomLevels[index]] = markerSeries; if (index === 0 && !overlay) { // this somehow interferes with the clusters after home button is clicked // im.maps[id].allBaseSeries.push(markerSeries); } // hide all series except the first //if (index > 0 ) { markerSeries.hidden = true; //} }); } return true; }; /** * triggers when map is ready * @param {int} id of the map */ iMapsManager.triggerOnReady = function (id, data) { let isCustom = data.map == 'custom' ? true : false; let urlParams = new URLSearchParams(window.location.search); let myParam = urlParams.get('mregion'); if (myParam) { //if it's a custom map, do nothing, we will trigger it on the map appeared event if (!isCustom) { iMapsManager.select(id, myParam, true, true); } } }; /** * triggers when map is ready * @param {int} id of the map */ iMapsManager.triggerOnAppeared = function (id, data) { let isCustom = data.map == 'custom' ? true : false; let urlParams = new URLSearchParams(window.location.search); let myParam = urlParams.get('mregion'); if (myParam) { //if it's a custom map, do nothing, we will trigger it on the map appeared event if (isCustom) { setTimeout(function () { iMapsManager.select(id, myParam, true, true); }, 500); } } }; /** * Adds a new region Series currently not loaded, adding the script to the body of the page and creating a new series after * id - map id to attach series * dataContent - object with data that would typically be a polygon dataContent, like name and id * config - config object for the new series being added * * @return newSeries - the new created series object */ iMapsManager.addGeoFileSeries = function (id, dataContext, data) { var newSeries, geoFiles = iMapsRouter.getGeoFiles(dataContext); var scriptPromise = new Promise(function (resolve, reject) { var script = document.createElement('script'); document.body.appendChild(script); script.onload = resolve; script.onerror = reject; script.async = true; script.src = geoFiles.src; }); scriptPromise.then(function () { var data = { title: geoFiles.title, map: geoFiles.map, regions: [], config: data, // not working, we changed the config to be at parent level with data }; iMapsManager.maps[id].seriesIndex[geoFiles.map] = []; newSeries = iMapsManager.pushRegionSeries(id, data); iMapsManager.maps[id].seriesIndex[geoFiles.map].push(newSeries); return newSeries; }); return false; }; iMapsManager.handleInfoBox = function (id) { var im = this, map = im.maps[id].map, events = ['ready', 'mappositionchanged', 'zoomlevelchanged'], container = document.getElementById('map_visual_info'), coordinatesc = document.getElementById('map_click_events_coordinates'), series = im.maps[id].series; if (container) { iMapsManager.populateInfo(id, container); // zoom, etc events.forEach(function (event) { map.events.on( event, function (ev) { iMapsManager.populateInfo(id, container); }, this ); }); } if (coordinatesc) { map.events.on( 'hit', function (ev) { var coordinates = map.svgPointToGeo(ev.svgPoint); var lat = Number(coordinates.latitude).toFixed(6); var _long = Number(coordinates.longitude).toFixed(6); // latitude var latEl = document.createElement('div'); var latLabelEl = document.createElement('span'); var latValueEl = document.createElement('span'); latLabelEl.classList.add('info--unselectable'); latValueEl.classList.add('map_clicked_lat'); latLabelEl.innerHTML = 'LAT: '; latValueEl.innerHTML = lat; latEl.appendChild(latLabelEl); latEl.appendChild(latValueEl); // longitude var longEl = document.createElement('div'); var longLabelEl = document.createElement('span'); var longValueEl = document.createElement('span'); longLabelEl.classList.add('info--unselectable'); longValueEl.classList.add('map_clicked_long'); longLabelEl.innerHTML = 'LON: '; longValueEl.innerHTML = _long; longEl.appendChild(longLabelEl); longEl.appendChild(longValueEl); coordinatesc.innerHTML = ''; coordinatesc.appendChild(latEl); coordinatesc.appendChild(longEl); coordinatesc.parentElement.style.display = 'block'; var event = new CustomEvent('mapPointClicked', { detail: { latitude: lat, longitude: _long, }, }); document.dispatchEvent(event); }, this ); } }; iMapsManager.populateInfo = function (id, container) { var im = this, map = im.maps[id].map, info = ''; info += 'Zoom Level: ' + parseFloat(Number(map.zoomLevel).toFixed(2)) + '
'; info += 'Center Coordinates:
' + "LAT " + Number(map.zoomGeoPoint.latitude).toFixed(6) + ''; info += "
LONG " + Number(map.zoomGeoPoint.longitude).toFixed(6) + '
'; container.innerHTML = info; let centerData = { zoom: parseFloat(Number(map.zoomLevel).toFixed(2)), lat: Number(map.zoomGeoPoint.latitude).toFixed(6), long: Number(map.zoomGeoPoint.longitude).toFixed(6), }; container.setAttribute('data-visual', JSON.stringify(centerData)); }; iMapsManager.hideAllSeries = function (id, keepBase) { id = parseInt(id); if (!id) { return; } keepBase = keepBase || false; var map = iMaps.maps[id]; var baseRegionSeries = map.baseRegionSeries; var groupedSeries = map.groupedBaseRegionSeries; var allbaseSeries = map.allBaseSeries; for (var index = 0; index < map.series.length; index++) { var serie = map.series[index]; //if (baseRegionSeries !== serie && !groupedSeries.includes(serie)) { // we don't need to check if it's not the base series at this point if (!groupedSeries.includes(serie)) { // in case we need to check also if we keep base series if (!keepBase || (keepBase && !allbaseSeries.includes(serie))) { serie.hide(); } } } }; iMapsManager.showAllSeries = function (id) { id = parseInt(id); if (!id) { return; } var map = iMaps.maps[id]; for (var index = 0; index < map.series.length; index++) { var serie = map.series[index]; if (typeof serie.isCluster === 'undefined' || serie.isCluster === false) { serie.show(); } } }; /** Util function to return boolean value of string */ iMapsManager.bool = function (string) { var bool = Number(string) === 0 || string === 'false' || typeof string === 'undefined' ? false : true; return bool; }; iMapsManager.isJSON = function (str) { try { JSON.parse(str); } catch (e) { return false; } return true; }; /* Closest Polyfill */ if (!Element.prototype.matches) { Element.prototype.matches = Element.prototype.msMatchesSelector || Element.prototype.webkitMatchesSelector; } if (!Element.prototype.closest) { Element.prototype.closest = function (s) { var el = this; do { if (el.matches(s)) return el; el = el.parentElement || el.parentNode; } while (el !== null && el.nodeType === 1); return null; }; } /** Library for elements */ iMapsManager.library = { icons: { goFullIconPath: 'm15.78742,5.93715l-3.95414,3.95414l3.95414,3.95414l1.60393,-1.60393q0.32301,-0.34529 0.77969,-0.15594q0.4344,0.18935 0.4344,0.65717l0,4.99002q0,0.2896 -0.21163,0.50123t-0.50123,0.21163l-4.99002,0q-0.46781,0 -0.65717,-0.44554q-0.18935,-0.4344 0.15594,-0.76855l1.60393,-1.60393l-3.95414,-3.95414l-3.95414,3.95414l1.60393,1.60393q0.34529,0.33415 0.15594,0.76855q-0.18935,0.44554 -0.65717,0.44554l-4.99002,0q-0.2896,0 -0.50123,-0.21163t-0.21163,-0.50123l0,-4.99002q0,-0.46781 0.44554,-0.65717q0.4344,-0.18935 0.76855,0.15594l1.60393,1.60393l3.95414,-3.95414l-3.95414,-3.95414l-1.60393,1.60393q-0.21163,0.21163 -0.50123,0.21163q-0.13366,0 -0.26732,-0.05569q-0.44554,-0.18935 -0.44554,-0.65717l0,-4.99002q0,-0.2896 0.21163,-0.50123t0.50123,-0.21163l4.99002,0q0.46781,0 0.65717,0.44554q0.18935,0.4344 -0.15594,0.76855l-1.60393,1.60393l3.95414,3.95414l3.95414,-3.95414l-1.60393,-1.60393q-0.34529,-0.33415 -0.15594,-0.76855q0.18935,-0.44554 0.65717,-0.44554l4.99002,0q0.2896,0 0.50123,0.21163t0.21163,0.50123l0,4.99002q0,0.46781 -0.4344,0.65717q-0.1448,0.05569 -0.27846,0.05569q-0.2896,0 -0.50123,-0.21163l-1.60393,-1.60393z', exitFullIconPath: 'm10.04411,10.81638l0,5.40556q0,0.31372 -0.22925,0.54297t-0.54297,0.22925t-0.54297,-0.22925l-1.7375,-1.7375l-4.00591,4.00591q-0.12066,0.12066 -0.27752,0.12066t-0.27752,-0.12066l-1.37552,-1.37552q-0.12066,-0.12066 -0.12066,-0.27752t0.12066,-0.27752l4.00591,-4.00591l-1.7375,-1.7375q-0.22925,-0.22925 -0.22925,-0.54297t0.22925,-0.54297t0.54297,-0.22925l5.40556,0q0.31372,0 0.54297,0.22925t0.22925,0.54297zm9.10982,-8.10834q0,0.15686 -0.12066,0.27752l-4.00591,4.00591l1.7375,1.7375q0.22925,0.22925 0.22925,0.54297t-0.22925,0.54297t-0.54297,0.22925l-5.40556,0q-0.31372,0 -0.54297,-0.22925t-0.22925,-0.54297l0,-5.40556q0,-0.31372 0.22925,-0.54297t0.54297,-0.22925t0.54297,0.22925l1.7375,1.7375l4.00591,-4.00591q0.12066,-0.12066 0.27752,-0.12066t0.27752,0.12066l1.37552,1.37552q0.12066,0.12066 0.12066,0.27752z', }, }; iMapsManager.handleExternalZoom = function (id) { var mapContainer, mapBox, controls, homeBtn, zoomInBtn, zoomOutBtn; mapContainer = document.getElementById('map_wrapper_' + id); var im = this, data = im.maps[id].data, allCurrentSeries = im.maps[id].series, allBaseSeries = im.maps[id].allBaseSeries; if (!mapContainer) { return; } mapBox = mapContainer.querySelector('.map_box'); mapContainer.classList.add('map_has_external_controls'); // home button homeBtn = document.createElement('div'); homeBtn.setAttribute('id', 'map_home_buttom_' + id); homeBtn.setAttribute('data-map-id', id); homeBtn.classList.add('map_home_button'); homeBtn.innerHTML = ''; homeBtn.addEventListener('click', function (ev) { var id = this.getAttribute('data-map-id'); iMaps.maps[id].map.goHome(); // in case drillDown is enabled, we hide everything else if (im.bool(data.drillDownOnClick)) { for (var i = 0, len = allCurrentSeries.length; i < len; i++) { allCurrentSeries[i].hide(); //map.deltaLongitude = 0; } for (var ib = 0, lenb = allBaseSeries.length; ib < lenb; ib++) { // this is messing the cluster markers on base map allBaseSeries[ib].show(); } iMapsManager.maps[id].drilledTo = false; iMapsManager.maps[id].isDrilling = false; } // reset actions if (typeof iMapsActions !== 'undefined' && typeof iMapsActions.resetActions !== 'undefined') { iMapsActions.resetActions(id); } }); // zoom in zoomInBtn = document.createElement('div'); zoomInBtn.setAttribute('id', 'map_zoomin_buttom_' + id); zoomInBtn.setAttribute('data-map-id', id); zoomInBtn.classList.add('map_zoomin_button'); zoomInBtn.innerHTML = '+'; zoomInBtn.addEventListener('click', function (ev) { var id = this.getAttribute('data-map-id'); iMaps.maps[id].map.zoomIn(); }); // zoom out zoomOutBtn = document.createElement('div'); zoomOutBtn.setAttribute('id', 'map_zoomout_buttom_' + id); zoomOutBtn.setAttribute('data-map-id', id); zoomOutBtn.classList.add('map_zoomout_button'); zoomOutBtn.innerHTML = '-'; zoomOutBtn.addEventListener('click', function (ev) { var id = this.getAttribute('data-map-id'); iMaps.maps[id].map.zoomOut(); }); // controls container controls = document.createElement('div'); controls.setAttribute('id', 'map_controls_' + id); controls.classList.add('map_controls'); controls.appendChild(homeBtn); controls.appendChild(zoomInBtn); controls.appendChild(zoomOutBtn); // add buttons mapBox.parentNode.insertBefore(controls, mapBox.nextSibling); }; iMapsManager.toggleFullscreen = function (element) { // se não está em tela cheia if (!document.fullscreen && !document.webkitIsFullScreen) { // solicitar tela cheia if (element.requestFullscreen) { // default element.requestFullscreen(); } else if (element.mozRequestFullScreen) { // Mozilla element.mozRequestFullScreen(); } else if (element.webkitRequestFullscreen) { // Chrome e Safari element.webkitRequestFullscreen(); } else if (element.msRequestFullscreen) { // Internet Explorer element.msRequestFullscreen(); } } else { // exit fullscreen if (document.exitFullscreen) { // default document.exitFullscreen(); } else if (document.mozCancelFullScreen) { // Mozilla document.mozCancelFullScreen(); } else if (document.webkitExitFullscreen) { // Chrome e Safari document.webkitExitFullscreen(); } else if (document.msExitFullscreen) { // Internet Explorer document.msExitFullscreen(); } iMapsManager.isFullScreen = false; } }; iMapsManager.isTouchScreenDevice = function () { return 'ontouchstart' in window || navigator.maxTouchPoints > 0 || navigator.msMaxTouchPoints > 0; }; /** * This function is same as PHP's nl2br() with default parameters. * * @param {string} str Input text * @param {boolean} replaceMode Use replace instead of insert * @param {boolean} isXhtml Use XHTML * @return {string} Filtered text */ iMapsManager.nl2br = function (str) { var replaceStr = 'HI
HI'; return (str + '').replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, replaceStr); }; iMapsManager.isFullScreen = false; /** * Main app file. Initializes app components. */ /** * The main app object. * */ var iMaps = { originalData: JSON.parse(JSON.stringify(iMapsData)), }; iMaps.reset = function () { if (iMaps.maps) { Object.keys(iMaps.maps).forEach(function (id) { iMaps.maps[id].map.dispose(); }); } iMapsData = iMaps.originalData; iMaps.init(); }; /** * Initializes the interactiveMaps app * */ iMaps.init = function (hold) { // if map should not render if ( typeof hold === 'undefined' && typeof iMapsData.options !== 'undefined' && typeof iMapsData.options.hold !== 'undefined' && iMapsData.options.hold === '1' ) { hold = true; } if (hold) { return; } if (typeof am4core === 'undefined') { console.log('Map files not loaded properly.'); let oxygen = document.querySelector('.oxygen-body .map_wrapper .map_render'); if (oxygen) { oxygen.innerHTML = 'Map Container.
Map will not render in Oxygen preview, but will render in live page.
Consider enabling the "Async Loading" option in the Settings > Performance page.'; } return; } am4core.ready(function () { var data; am4core.options.autoSetClassName = true; am4core.options.classNamePrefix = 'imaps'; am4core.options.commercialLicense = true; am4core.options.queue = true; if ( typeof iMapsData.options !== 'undefined' && typeof iMapsData.options.lazyLoad !== 'undefined' && iMapsData.options.lazyLoad === '1' ) { am4core.options.onlyShowOnViewport = true; } // animated if ( typeof iMapsData.options !== 'undefined' && typeof iMapsData.options.animations !== 'undefined' && (iMapsData.options.animations === '1' || iMapsData.options.animations === true) ) { am4core.useTheme(am4themes_animated); } data = iMapsModel.prepareData(iMapsData.data); data.forEach(function (data, index) { if (index.disabled) { return; } iMapsManager.init(index); }); iMaps.maps = iMapsManager.maps; }); }; iMaps.loadScript = function (url, callback) { var script = document.createElement('script'); script.type = 'text/javascript'; script.src = url; script.onreadystatechange = callback; script.onload = callback; document.head.appendChild(script); }; iMaps.loadScripts = function (urls, callback) { var loadedCount = 0; var multiCallback = function multiCallback() { loadedCount++; if (loadedCount >= urls.length) { callback.call(this, arguments); } }; urls.forEach(function (url, index) { iMaps.loadScript(url, multiCallback); }); }; if ( typeof iMapsData.async !== 'undefined' && Array.isArray(iMapsData.async) && iMapsData.async.length > 0 ) { // we need to load the core file first iMaps.loadScript(iMapsData.async[0], function () { // then we load the rest - shift() removes the first entry iMapsData.async.shift(); iMaps.loadScripts(iMapsData.async, function () { iMaps.init(); }); }); } else { iMaps.init(); }