import {getLayers} from "./coz-helpers.js";
import {getQuery} from "./coz-helpers.js";
import {hasLayer} from "./coz-helpers.js";
// import {compile} from "handlebars";
// TODO ACCOUNT FOR GEOJSON TYPE
// TODO MAKE SEPARATE HIGHLIGHT AND CLICK FUNCTIONS
var highlightState = {
"click": {
id: 0,
layer: "",
source: "",
sourceLayer: ""
},
"highlight": {
id: 0,
layer: "",
source: "",
sourceLayer: ""
}
}
/**
*
* @param {*} map
* @param {*} feature
* @param {*} highlightOnly
*/
function highlightAddFeature(map, feature, highlightOnly) {
// console.log(feature)
var highlightMap = map;
var highlightType = (!highlightOnly) ? "click" : "highlight";
var highlightStateType = {
"click": {
"click": true
},
"highlight": {
"highlight": true
}
}
if (!feature.id && feature.id != 0) {
console.warn("no feature id, highlight error")
return
}
highlightClearFeature(highlightMap, highlightType)
var clone = [feature.layer].slice();
var highlightLayer = clone[0]
var height = (highlightLayer.type === "fill-extrusion") ? highlightLayer.paint["fill-extrusion-height"] : 0;
highlightLayer.id = "cozHighlight_" + (highlightLayer.id).replace("cozHighlight_", "");
highlightLayer.type = (highlightLayer.type === "fill" || highlightLayer.type === "line") ? "line" : (highlightLayer.type === "fill-extrusion") ? "fill": "circle";
highlightLayer.paint = (!height) ? highlightLayerPaint(highlightLayer.type) : highlightLayerPaint(highlightLayer.type, highlightLayer.paint["fill-extrusion-color"])
if (highlightLayer.type === "fill-extrusion") {
highlightLayer.paint["fill-extrusion-height"] = height
}
highlightLayer.layout = {
"visibility": "none"
};
if (!hasLayer(highlightMap, highlightLayer.id)) {
console.log("adding highlight layer", highlightLayer.id)
map.addLayer(highlightLayer);
}
highlightState[highlightType].id = feature.id;
highlightState[highlightType].layer = highlightLayer.id;
highlightState[highlightType].source = highlightLayer.source;
highlightState[highlightType].sourceLayer = highlightLayer["source-layer"];
map.setFeatureState({source: highlightLayer.source, sourceLayer: highlightLayer["source-layer"], id: feature.id}, highlightStateType[highlightType]);
map.setLayoutProperty(highlightLayer.id, "visibility", "visible");
}
/**
*
* @param {*} e
*/
function highlightClearFeature(e, type, noFeatures) {
var type = (!type) ? "click" : type;
if (noFeatures) {
//GET RID OF POPUP WINDOW ON CLICK HIGHLIGHT
if (document.getElementById("map--info-window")) {
document.getElementById("map--info-window").style.display = "none";
}
}
var layers = getLayers(e);
if (layers.indexOf(highlightState[type].layer) > -1) {
if (type === "highlight") {
e.setFeatureState({source: highlightState[type].source, sourceLayer: highlightState[type].sourceLayer, id: highlightState[type].id}, {highlight: false});
var click = e.getFeatureState({source: highlightState[type].source, sourceLayer: highlightState[type].sourceLayer, id: highlightState[type].id});
if (!click.click) e.setLayoutProperty(highlightState[type].layer, "visibility", "none");
}else{
e.setFeatureState({source: highlightState[type].source, sourceLayer: highlightState[type].sourceLayer, id: highlightState[type].id}, {click: false});
e.setLayoutProperty(highlightState[type].layer, "visibility", "none");
//GET RID OF POPUP WINDOW ON CLICK HIGHLIGHT
if (document.getElementById("map--info-window")) {
document.getElementById("map--info-window").style.display = "none";
}
}
}
}
/**
*
* @param {*} type
*/
function highlightLayerPaint(featureType, color) {
if (color) {
color = "rgba(" + color.r * 1000 + "," + color.g * 1000 + "," + color.b * 1000 + "," + 0.5 + ")"
}
var paint = {
"line": {
"line-color": "yellow",
"line-width": ["case",
["==", ["feature-state", "click"], true] ,6,
["==", ["feature-state", "highlight"], true] ,2,
0],
"line-opacity": ["case",
["==", ["feature-state", "click"], true] ,1,
["==", ["feature-state", "highlight"], true] ,0.7,
0]
},
"circle": {
"circle-radius": 14,
"circle-color": "transparent",
"circle-stroke-color": "yellow",
"circle-stroke-width": 4,
"circle-opacity": ["case",
["==", ["feature-state", "click"], true] ,1,
["==", ["feature-state", "highlight"], true] ,0.7,
0],
"circle-stroke-opacity": ["case",
["==", ["feature-state", "click"], true] ,1,
["==", ["feature-state", "highlight"], true] ,0.7,
0]
},
"fill": {
"fill-color": ["case",
["==", ["feature-state", "click"], true] , "yellow",
["==", ["feature-state", "highlight"], true] ,"yellow",
"transparent"],
"fill-opacity": 0.7
},
"fill-extrusion": {
"fill-extrusion-color": ["case",
["==", ["feature-state", "click"], true] ,"yellow",
["==", ["feature-state", "highlight"], true] ,"yellow",
color],
"fill-extrusion-opacity": 0.6,
}
}
return paint[featureType]
}
/**
*
* @memberof cozMap
* @method getFeatures
* @param {*} map
* @param {*} e
* @param {*} l
*/
function getFeatures(map, e, l) {
var layers = map.getStyle().layers;
if (layers.indexOf("gl-draw-polygon-stroke-inactive.cold") > -1) {
if (map.getPaintProperty("gl-draw-polygon-stroke-inactive.cold", "line-color") && map.getPaintProperty("gl-draw-polygon-stroke-inactive.cold", "line-color") != "red") {
return []
}
}
//SHOULD MAYBE MOVE THIS OUT OF HERE TO A HELPER FILE
var point;
if (e.point && e.lngLat) {
point = e.point;
}
if (!e.point && e.lngLat) {
point = map.project(e.lngLat);
}
if (!e.point && !e.lngLat) {
point = map.project(e.coordinates);
}
var bboxClickTargetSize = ((map.getZoom() * 1.5) < 18) ? 18 : map.getZoom() * 1.5;
var bbox = [
[point.x - bboxClickTargetSize / 2, point.y - bboxClickTargetSize / 2 ],
[point.x + bboxClickTargetSize /2, point.y + bboxClickTargetSize /2]
]
var bboxCoords = [map.unproject(bbox[0]), map.unproject(bbox[1])]
var getFeatureOpts = getQuery((window.location.search).substring(1));
if (getFeatureOpts && getFeatureOpts.debug === "true") {
var poly = [
[bboxCoords[0].lng, bboxCoords[0].lat],
[bboxCoords[1].lng, bboxCoords[0].lat],
[bboxCoords[1].lng, bboxCoords[1].lat],
[bboxCoords[0].lng, bboxCoords[1].lat],
[bboxCoords[0].lng, bboxCoords[0].lat]
]
if (!map.getSource("clicked-bbox")) {
map.addSource("clicked-bbox", {
'type': 'geojson',
'data': {
'type': 'Feature',
'geometry': {
'type': 'Polygon',
'coordinates': [poly]
}
}
});
map.addLayer({
'id': 'clicked-bbox',
'type': 'fill',
'source': 'clicked-bbox',
'layout': {},
'paint': {
'fill-color': 'firebrick',
'fill-opacity': 0.5
}
});
}else{
map.getSource("clicked-bbox").setData({
'type': 'Feature',
'geometry': {
'type': 'Polygon',
'coordinates': [poly]
}
})
}
}
var queriedFeatures, bboxFeatures;
if (l && l.length > 0) {
queriedFeatures = map.queryRenderedFeatures(point, {
layers: l
});
bboxFeatures = map.queryRenderedFeatures(bbox, {
layers: l
});
} else {
queriedFeatures = map.queryRenderedFeatures(point);
bboxFeatures = map.queryRenderedFeatures(bbox);
}
const combinedFeatures = [...queriedFeatures, ...bboxFeatures]
const features = cleanFeatures(combinedFeatures);
if (getFeatureOpts && getFeatureOpts.debug === "true") {
console.log({combinedFeatures});
console.log({features})
}
function cleanFeatures(objects) {
const features = [];
const props = [];
objects.forEach(function (obj) {
obj.properties["__vt__id__index"] = obj.id
const p = JSON.stringify(obj.properties);
let unique = true;
props.forEach(e => {
if (p === e) {
unique = false
}
});
if (unique) props.push(p)
if (obj && unique && obj.source != "composite" && obj.layer.metadata && obj.layer.metadata.popup) {
features.push(obj)
}
})
return features
}
if (!features) {
return
}
//SORT FEATURE PROPERTIES BY FIELD NAME
features.forEach(function(f) {
var props = {};
Object.keys(f.properties).sort().forEach(function(key) {
props[key] = f.properties[key];
});
f.properties = props;
})
return features
}
function popup(map, features, n, highlightOnly) {
map.once("contextmenu", function() {
highlightClearFeature(popupMap, highlightType)
if (document.getElementById("map--info-window-close")) {
document.getElementById("map--info-window-close").click();
}
})
var popupMap = map;
var highlightType = (!highlightOnly) ? "click" : "highlight"
if (!features || features.length === 0) {
highlightClearFeature(popupMap, highlightType, true)
return
}
var featuresTemp = features.filter(f => {
return f != null
})
var popupFeatures = featuresTemp
if (!popupFeatures.length === 0 && !highlightOnly) {
highlightClearFeature(popupMap, "click")
return
}
if (highlightOnly && popupFeatures.length > 0) {
highlightAddFeature(map, features[0], true);
return
}
var x = (!n) ? 0 : n;
highlightAddFeature(popupMap, popupFeatures[x])
var popupDiv = document.createElement("div");
// GET TITLE OR USE FEATURE PROPERTIES
var popupHeading = "Feature Properties";
for (var v in popupFeatures[x].properties) {
if ((v.toUpperCase() === "NAME" || v.toUpperCase() === "TITLE") && popupFeatures[x].properties[v] && popupFeatures[x].properties[v] != null && popupFeatures[x].properties[v] != "null") {
popupHeading = (popupFeatures[x].properties[v])//.substring(0,20).trim();
// console.log(popupFeatures[x].properties[v] )
//if ((popupFeatures[x].properties[v]).length > 20) popupHeading += "..."
}
}
if (popupHeading === "Feature Properties" && popupFeatures[x].layer.metadata && popupFeatures[x].layer.metadata.name) {
popupHeading = popupFeatures[x].layer.metadata.name//.substring(0,20).trim()
}
var popupDivTitle = popupShowMoreFeatures(popupMap, popupFeatures, popupHeading, x);
popupDiv.appendChild(popupDivTitle);
var popupHtml = popupBuildHtml(map, popupFeatures[x]);
popupDiv.appendChild(popupHtml);
/**
* SHOULD MOVE THIS TO CREATING THE DOM ELEMENT IN THE JS FILE INSTEAD OF IN THE HTML WOULD NEED TO HAVE A STYLESHEET TO GO ALONG WITH IT
*/
var popupWindow = document.getElementById("map--info-window-content");
if (popupWindow.children.length > 0) popupWindow.removeChild(popupWindow.childNodes[0]);
popupWindow.appendChild(popupDiv);
document.getElementById("map--info-window").style.display = "block";
if (window.innerWidth > 768) {
// var width = document.querySelector("#map--info-window-content").children[0].children[1].offsetWidth
// console.log(document.querySelector("#map--info-window-content").children[0].children[1], width)
document.getElementById("map--info-window").style.width = "300px"
}else{
document.getElementById("map--info-window").style.width = "100%"
}
}
function popupShowMoreFeatures(map, features, heading, x) {
var newMap = map;
var div = document.createElement("div");
div.style.textAlign = "center";
div.style.height = "auto"
div.style.padding = "0 0 10px 0"
div.style.borderBottom = "solid thin lightgray"
var prev = document.createElement("button");
prev.classList = "btn btn-link btn-sm";
prev.innerHTML = "◀";
prev.style.marginLeft = "10px"
prev.style.float = "left"
prev.onclick = function() {
popup(newMap, features, x-1)
}
var next = document.createElement("button");
next.classList = "btn btn-link btn-sm";
next.style.marginRight = "10px";
next.innerHTML = "▶";
next.style.float = "right";
next.style.position = "absolute";
next.style.top = "10px";
next.style.right = "20px";
next.onclick = function() {
popup(newMap, features, x+1)
}
if (x == (features.length - 1)) {
next.style.opacity = "0";
next.style.visibility = "hidden";
next.style.cursor = "default";
}
if (x == 0) {
prev.style.opacity = "0";
prev.style.visibility = "hidden";
prev.style.cursor = "default";
}
div.appendChild(prev);
var span = document.createElement("div");
span.textContent = heading;
span.style.fontSize = "18px";
span.style.lineHeight = "28px";
span.style.fontWeight = "600";
span.style.margin = "0 48px";
div.appendChild(span)
div.appendChild(next);
return div
}
function popupBuildHtml(map, f) {
var popupDiv = document.createElement("div");
var popupTemplate = (!f.layer.metadata) ? true : (f.layer.metadata.popup && f.layer.metadata.popup === false) ? false : (f.layer.metadata.popup === true) ? [] : f.layer.metadata.popup
if (popupTemplate || popupTemplate === undefined) {
var table = document.createElement("table");
table.classList = "table table-striped";
// table.style.width = "auto";
table.style.minWidth = "280px"
var tbody = document.createElement("tbody");
var keys = Object.keys(f.properties);
const aliasFields = {
// "AADT": "Average Annual Daily Traffic",
// "PARCELNUM": "Parcel ID",
// "Land_Use": "Land Use",
// "MASTER_TNM": "Tax Name",
// "FIRST_OWNE": "Owner",
// "ERU": "ERU for Account",
// "Sump_Depth": "Sump Depth",
// "Owner_1": "Owner",
// "Legal_Description": "Legal Description",
// "ELEV": "Elevation",
// "SQFT": "Impervious Surface",
// "FIELDID": "Field ID",
// "url_pdf": "URL for Inpsection Report",
// "IDUP": "Upstream Asset ID",
// "IDDN": "Downstream Asset ID",
// "INSPECTLOG": "Inspection Log",
// "audlink": "Auditor Link",
"CreationDate": "Creation Date",
"EditDate": "Edit Date",
"url_1": "Image 1",
"url_2": "Image 2",
"url_3": "Image 3",
"url_4": "Image 4",
"esrignss_positionsourcetype": "Position Source Type",
}
const excludeFields = [
"ACCOUNT_NUM",
"CENTROID_X",
"CENTROID_Y",
"Creator",
"DATEUPDATE",
"END_X",
"END_Y",
"ERU_CHARGE",
"Editor",
"Enabled",
"FID",
"FIRST_NOTE",
"GEO_ID",
"GlobalID",
"IKey",
"INSIDE_X",
"INSIDE_Y",
"Land_Use_Name",
"Log_Date",
"MID_X",
"MID_Y",
"OBJECTID",
"OID",
"OLDFIELDID",
"SHAPE_Area",
"SHAPE_Leng",
"SHAPE_Length",
"START_X",
"START_Y",
"Shape_Area",
"Shape_Leng",
"Shape_Leng",
"Shape_Length",
"Shape_Length",
"TEMP",
"acres_1",
"ca",
"color",
"dt_created",
"dt_edited",
"dt_updated",
"id",
"id",
"l_bearing",
"obs_pk",
"pano",
"point_y",
"point_x",
"shape_leng",
"skey",
"userkey",
"vtlid",
"xcoord",
"ycoord",
"extrudeheight",
"parcel_number",
"uuid",
"plan_local",
"Ownedby",
"Maintby",
"__vt__id__index",
];
excludeFields.map(function(f,i) {
excludeFields[i] = f.toUpperCase()
});
//GET POPUP VALUES FROM LAYER METADATA IF THEY EXIST
var layers = map.getStyle().layers;
var layer = layers.filter(l => {
return l.id === (f.layer.id).replace("cozHighlight_", "")
});
var popupValues = (!layer[0].metadata) ? null : (!layer[0].metadata.popup) ? null : layer[0].metadata.popup;
if (popupValues && popupValues != true) {
// console.log(popupValues.length)
keys = keys.filter(k => {
return popupValues.indexOf(k) > -1
})
}
var hasImage = false;
table.innerHTML += `<!-- ${f.layer.id} -->`
keys.forEach(function(k, i) {
const key = aliasFields[k] ? aliasFields[k] : k.replace(/esrignss/g, "gps_");
var val = f.properties[k];
if (excludeFields.indexOf(k.toUpperCase()) < 0) {
if (val && val != null && val != "" && val != " " && val != undefined && val != "null") {
var property = (isMapillary(val, f)) ? formatMapillary(val) : (isEpochDate(val)) ? formatDate(val) : (isLink(val)) ? formatLink(val) : (isParcel(val)) ? formatParcel(val) : (isNumber(val)) ? formatNumber(val) : val;
tbody.innerHTML += `<tr><td>${formatTitle(key)}</td><td>${property}</td></tr>`;
}
if (!hasImage) hasImage = isImage(k, val)
}
});
if (hasImage != false) {
var popupImageLink = document.createElement("a");
popupImageLink.href = hasImage;
popupImageLink.target = "_blank";
var popupImg = document.createElement("img");
popupImg.src = hasImage;
popupImg.style.width = "100%";
popupImageLink.appendChild(popupImg)
popupDiv.appendChild(popupImageLink)
}
table.appendChild(tbody)
popupDiv.appendChild(table)
}
return popupDiv
}
export {
getFeatures,
popup,
highlightAddFeature,
highlightClearFeature
}
/****
* HELPERS
*/
function isImage(key, value) {
if (!value) return false
if (key === "alt_link") return false
if (value.toString().indexOf("https") != 0) return false
if (value.split(",")) {
value = value.split(",")[0]
}
let img = value.split(".");
let images = ["JPG", "JPEG", "PNG"]
console.log(images.indexOf(img[img.length -1].toUpperCase()) )
if (images.indexOf(img[img.length -1].toUpperCase()) < 0) {
return false
}else{
return value
}
}
function formatTitle(str) {
return str.replace(/_/g, " ");
}
function isMapillary(value, feature) {
return (value === 'key' && feature.source === 'mapillary')
}
function formatMapillary(value) {
return `<img src='https://d1cuyjsrcm0gby.cloudfront.net/${value}/thumb-320.jpg'>`
}
function isEpochDate (value) {
var simpleDateRegex = new RegExp(/^\d{13}$/)
return simpleDateRegex.test(value)
}
function formatDate(value) {
var valueDate = new Date(value)
return valueDate.toLocaleDateString()
}
function isLink(value) {
var urlRegExCheck = new RegExp(/[-a-zA-Z0-9@:%_\+.~#?&//=]{2,256}\.[a-z]{2,4}\b(\/[-a-zA-Z0-9@:%_\+.~#?&//=]*)?/gi);
return urlRegExCheck.test(value)
}
function formatLink(value) {
if (value.split(",").length > 1) {
value = value.split(",")[0]
}
return `<a href="${(value)}" target='_blank'><i class='fas fa-external-link-alt'></i> View Resource</a></td></tr>`
}
function isParcel(value) {
var parcelRegex = new RegExp(/^\d{2}[-]\d{2}[-]\d{2}[-]\d{2}[-]\d{3}$/)
return (value == "PARCELNUM" || parcelRegex.test(value))
}
function formatParcel(value) {
return `<a href='https://muskingumoh-auditor-classic.ddti.net/Data.aspx?ParcelID=${value}' target='_blank' class='text-center'><i class='fas fa-external-link-alt'></i> ${value}</a>`
}
function isNumber(value) {
var isNumber = /^\d*\.?\d*$/;
var isYear = /^[12][0-9]{3}$/;
return isNumber.test(value) && !isYear.test(value)
}
function formatNumber(value) {
return Number(value).toLocaleString()
}
/**
* genId
* Generate an ID of x length, NOT IN USE
* @param {Number} length
*/
function genId(length) {
var result = '';
var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
var charactersLength = characters.length;
for ( var i = 0; i < length; i++ ) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return result;
}