Commit f67b3596 by Mac Stephens

Update map markers, agency-based unit grouping, list search, and disabled map…

Update map markers, agency-based unit grouping, list search, and disabled map buttons for unmapped units
parent 6ff4c10a
......@@ -139,6 +139,7 @@ object ApiDatabaseModule: TApiDatabaseModule
'SELECT'
' ag.AGENCYID AS AGENCY,'
' ag.AGENCYNAME AS AGENCY_NAME,'
' ag.AGENCYTYPE AS AGENCYTYPE,'
' dua.UNITID,'
' dua.UNITNAME,'
' cun.CODE_DESC AS CARNUMBER_DESC,'
......@@ -307,6 +308,11 @@ object ApiDatabaseModule: TApiDatabaseModule
FieldName = 'gps_longitude'
ReadOnly = True
end
object uqUnitListagencytype: TStringField
FieldName = 'agencytype'
ReadOnly = True
Size = 10
end
end
object uqComplaintUnits: TUniQuery
Connection = ucENTCAD
......
......@@ -201,6 +201,7 @@ type
uqMapComplaintscomplaintStatusKey: TStringField;
uqMapComplaintspngName: TStringField;
uqMapUnitsagencytype: TStringField;
uqUnitListagencytype: TStringField;
procedure uqComplaintListCalcFields(DataSet: TDataSet);
procedure uqMapComplaintsCalcFields(DataSet: TDataSet);
procedure DataModuleCreate(Sender: TObject);
......
......@@ -367,7 +367,7 @@ end;
function TApiService.GetUnitList: TJSONObject;
var
data: TJSONArray;
lastDistrict: string;
lastAgency: string;
begin
Logger.Log(3, '---TApiService.GetUnitList initiated');
......@@ -376,7 +376,7 @@ begin
data := TJSONArray.Create;
try
lastDistrict := '';
lastAgency := '';
with ApiDB.uqUnitList do
begin
Open;
......@@ -386,22 +386,51 @@ begin
var item := TJSONObject.Create;
// Group header: show once when district changes
var curDistrict := ApiDB.uqUnitListDISTRICT_DESC.AsString;
var header := IfThen(curDistrict <> '', curDistrict + ' District', '');
if (header <> '') and not SameText(header, lastDistrict) then
item.AddPair('DistrictHeader', header);
lastDistrict := header;
// Group header: show once when agency changes
var curAgency := Trim(ApiDB.uqUnitListAGENCY_NAME.AsString);
if curAgency = '' then
curAgency := Trim(ApiDB.uqUnitListAGENCY.AsString);
if curAgency = '' then
curAgency := 'Unknown Agency';
if not SameText(curAgency, lastAgency) then
begin
item.AddPair('AgencyHeader', curAgency);
lastAgency := curAgency;
end
else
item.AddPair('AgencyHeader', '');
// Core unit identity
item.AddPair('UnitId', ApiDB.uqUnitListUNITID.AsString);
item.AddPair('UnitName', ApiDB.uqUnitListUNITNAME.AsString);
item.AddPair('Agency', ApiDB.uqUnitListAGENCY.AsString);
item.AddPair('AgencyName', ApiDB.uqUnitListAGENCY_NAME.AsString);
item.AddPair('AgencyType', ApiDB.uqUnitListAGENCYTYPE.AsString);
item.AddPair('CarNumberDesc', ApiDB.uqUnitListCARNUMBER_DESC.AsString);
item.AddPair('District', curDistrict);
item.AddPair('District', ApiDB.uqUnitListDISTRICT_DESC.AsString);
item.AddPair('Sector', ApiDB.uqUnitListSECTOR_DESC.AsString);
item.AddPair('CallType', ApiDB.uqUnitListCALL_TYPE.AsString);
var canShowMap := False;
if (not ApiDB.uqUnitListGPS_LATITUDE.IsNull) and
(not ApiDB.uqUnitListGPS_LONGITUDE.IsNull) then
begin
var mapLat := ApiDB.uqUnitListGPS_LATITUDE.AsFloat;
var mapLng := ApiDB.uqUnitListGPS_LONGITUDE.AsFloat;
canShowMap :=
((mapLat <> 0) or (mapLng <> 0)) and
(Abs(mapLat) <= 90) and
(Abs(mapLng) <= 180);
end;
item.AddPair('CanShowMap', IfThen(canShowMap, 'true', 'false'));
item.AddPair('MapButtonClass', IfThen(canShowMap, 'btn-primary', 'btn-secondary disabled'));
item.AddPair('MapButtonDisabled', IfThen(canShowMap, '', 'disabled="disabled" aria-disabled="true"'));
item.AddPair('MapButtonTitle', IfThen(canShowMap, 'Show on map', 'No map location available'));
// Current assignment (if any)
item.AddPair('Location', ApiDB.uqUnitListLOCATION.AsString);
......
[Settings]
LogFileNum=712
LogFileNum=719
webClientVersion=0.1.0
[Database]
......
......@@ -8,7 +8,7 @@ uses
WEBLib.ExtCtrls, DB, WEBLib.WebCtrls, WEBLib.REST, VCL.TMSFNCTypes, VCL.TMSFNCUtils,
VCL.TMSFNCGraphics, VCL.TMSFNCGraphicsTypes, VCL.TMSFNCCustomControl, VCL.TMSFNCWebBrowser,
VCL.TMSFNCMaps, VCL.TMSFNCLeaflet, VCL.TMSFNCMapsCommonTypes, System.StrUtils, XData.Web.Client,
XData.Web.Connection, ConnectionModule, Utils, uMapFilters, JS, Web;
XData.Web.Connection, ConnectionModule, Utils, uMapFilters, JS, Web, uMapMarkerJs;
type
TFViewMap = class(TWebForm)
......@@ -21,8 +21,7 @@ type
procedure lfMapMapInitialized(Sender: TObject);
[async] procedure httpReqGeoJsonResponse(Sender: TObject; AResponse: string);
procedure lfMapCustomizeMarker(Sender: TObject;
var ACustomizeMarker: string);
procedure lfMapCustomizeMarker(Sender: TObject; var ACustomizeMarker: string);
procedure lfMapCustomizeCSS(Sender: TObject; var ACustomizeCSS: string);
procedure btnFindLocationClick(Sender: TObject);
procedure tmrLocateTimer(Sender: TObject);
......@@ -41,7 +40,6 @@ type
FGeoJsonLoadStep: Integer;
[async] procedure LoadPointsAsync(showBusy: Boolean);
function CarIconForAgency(const AgencyId: string): string;
procedure UpdateDeviceLocation(lat, lng: Double);
procedure StartDeviceLocation;
procedure ApplyPendingUnitFocus;
......@@ -65,6 +63,7 @@ implementation
procedure TFViewMap.lfMapMapInitialized(Sender: TObject);
begin
RegisterMapMarkerJs;
ShowSpinner('spinner');
FUnitsLoaded := False;
FComplaintsLoaded := False;
......@@ -231,11 +230,6 @@ begin
end;
end;
function TFViewMap.CarIconForAgency(const AgencyId: string): string;
begin
Result := 'assets/markers/car_default.png';
end;
function TFViewMap.GetJsonString(obj: TJSObject; name: string): string;
begin
......@@ -425,6 +419,8 @@ begin
m.Title :=
'<span class="emi-marker-meta" data-marker-type="unit" data-unit-badge="' +
HtmlAttrEncode(unitBadge) +
'" data-picker-label="' +
HtmlAttrEncode('Unit ' + unitBadge) +
'" style="display:none"></span>' +
'<div class="d-flex flex-column gap-1 px-1 py-1" style="width:260px;">' +
'<div class="fw-semibold small">' +
......@@ -543,6 +539,8 @@ begin
HtmlAttrEncode(priorityBadge) +
'" data-complaint-status="' +
HtmlAttrEncode(complaintStatusKey) +
'" data-picker-label="' +
HtmlAttrEncode('Complaint ' + complaintId + IfThen(Trim(codeDesc) <> '', ' - ' + codeDesc, '')) +
'" style="display:none"></span>' +
'<div class="d-flex flex-column gap-1 px-1 py-1" style="width:260px;">' +
'<div class="fw-semibold small">' +
......@@ -610,80 +608,7 @@ end;
procedure TFViewMap.lfMapCustomizeMarker(Sender: TObject; var ACustomizeMarker: string);
begin
ACustomizeMarker :=
'var m=' + MARKERVAR + ', o=m.options||{};' + #13#10 +
'var rawTitle = (o && o.title) ? o.title : "";' + #13#10 +
'var ds = (o && o.datastring) ? o.datastring : "";' + #13#10 +
'if (ds === "device") {' + #13#10 +
' try { if (m.unbindTooltip) m.unbindTooltip(); } catch(e) {}' + #13#10 +
' try { if (m.unbindPopup) m.unbindPopup(); } catch(e) {}' + #13#10 +
' return;' + #13#10 +
'}' + #13#10 +
'o.tooltipHtml = rawTitle;' + #13#10 +
'o.title = "";' + #13#10 +
'var t = o.tooltipHtml || "";' + #13#10 +
'var u = (o.icon && o.icon.options && o.icon.options.iconUrl) ? o.icon.options.iconUrl : null;' + #13#10 +
'var markerType = "";' + #13#10 +
'var unitBadgeText = "";' + #13#10 +
'var complaintBadgeText = "";' + #13#10 +
'var complaintStatus = "";' + #13#10 +
'try {' + #13#10 +
' var temp = document.createElement("div");' + #13#10 +
' temp.innerHTML = t;' + #13#10 +
' var meta = temp.querySelector(".emi-marker-meta");' + #13#10 +
' if (meta) {' + #13#10 +
' complaintBadgeText = meta.getAttribute("data-priority-badge") || "";' + #13#10 +
' markerType = meta.getAttribute("data-marker-type") || "";' + #13#10 +
' unitBadgeText = meta.getAttribute("data-unit-badge") || "";' + #13#10 +
' complaintStatus = meta.getAttribute("data-complaint-status") || "";' + #13#10 +
' }' + #13#10 +
'} catch(e) {}' + #13#10 +
'var isUnit = markerType === "unit";' + #13#10 +
'var isComplaint = markerType === "complaint";' + #13#10 +
'var unitBadgeColor = "#2563EB";' + #13#10 +
'if (u && u.indexOf("car_fire") >= 0) unitBadgeColor = "#DC2626";' + #13#10 +
'else if (u && u.indexOf("car_ems") >= 0) unitBadgeColor = "#16A34A";' + #13#10 +
'else if (u && u.indexOf("car_police") >= 0) unitBadgeColor = "#2563EB";' + #13#10 +
'var complaintBadgeColor = "#FF8080";' + #13#10 +
'var complaintBadgeTextColor = "#FFFFFF";' + #13#10 +
'if (complaintStatus === "onscene") {' + #13#10 +
' complaintBadgeColor = "#22C55E";' + #13#10 +
' complaintBadgeTextColor = "#FFFFFF";' + #13#10 +
'} else if (complaintStatus === "attached") {' + #13#10 +
' complaintBadgeColor = "#FFFF00";' + #13#10 +
' complaintBadgeTextColor = "#111827";' + #13#10 +
'}' + #13#10 +
'try { if (m.unbindTooltip) m.unbindTooltip(); } catch(e) {}' + #13#10 +
'try { if (m.unbindPopup) m.unbindPopup(); } catch(e) {}' + #13#10 +
'if (u) {' + #13#10 +
' var wrapClass = isUnit ? "emi-marker-wrap emi-marker-wrap-unit" : (isComplaint ? "emi-marker-wrap emi-marker-wrap-complaint" : "emi-marker-wrap");' + #13#10 +
' var html = ''<div class="'' + wrapClass + ''"><img src="'' + u + ''" class="emi-marker-img">'';' + #13#10 +
' if (isUnit && unitBadgeText) {' + #13#10 +
' html += ''<div class="emi-unit-badge" style="background:'' + unitBadgeColor + '';">'' + unitBadgeText + ''</div>'';' + #13#10 +
' } else if (isComplaint && complaintBadgeText) {' + #13#10 +
' html += ''<div class="emi-complaint-badge" style="background:'' + complaintBadgeColor + '';color:'' + complaintBadgeTextColor + '';">'' + complaintBadgeText + ''</div>'';' + #13#10 +
' }' + #13#10 +
' html += ''</div>'';' + #13#10 +
' m.setIcon(L.divIcon({' + #13#10 +
' html: html,' + #13#10 +
' iconSize: [32,40],' + #13#10 +
' iconAnchor: [16,32],' + #13#10 +
' popupAnchor: [0,-24],' + #13#10 +
' className: ""' + #13#10 +
' }));' + #13#10 +
' try { if (m._icon) m._icon.removeAttribute("title"); } catch(e) {}' + #13#10 +
'}' + #13#10 +
'm.bindPopup(t, {' + #13#10 +
' className: "emi-tip",' + #13#10 +
' maxWidth: 260,' + #13#10 +
' closeButton: false,' + #13#10 +
' autoPan: true' + #13#10 +
'});';
'if (window.emiCustomizeMarker) window.emiCustomizeMarker(' + MARKERVAR + ');';
end;
......@@ -702,6 +627,10 @@ begin
'width:auto;' +
'max-width:260px;' +
'}' + #13#10 +
'.emi-marker-picker .btn{font-size:12px;line-height:1.2;border:1px solid rgba(0,0,0,.15);}'+#13#10+
'.emi-marker-picker .btn:hover{background:#e9ecef;}'+#13#10+
'.emi-marker-picker-section{font-size:11px;font-weight:700;color:#6c757d;margin:.15rem 0 .25rem;}'+#13#10+
'.emi-marker-picker-divider{border-top:1px solid rgba(0,0,0,.18);margin:.35rem 0 .45rem;}'+#13#10+
// --- Media Query: on very small screens, lets it grow almost full width ------
'@media (max-width:480px){' +
'.emi-tip .leaflet-popup-content{' +
......
......@@ -40,20 +40,22 @@ object FViewUnits: TFViewUnits
DataSource = wdsUnits
ItemTemplate =
'<div class="list-section-header small fw-semibold bg-body-second' +
'ary text-dark rounded-1 px-2 mb-1">(%DistrictHeader%)</div><div ' +
'class="card border shadow-sm"> <div class="card-body py-2 px-3 ' +
'd-flex gap-2"> <div class="flex-grow-1"> <div class="fw-' +
'bold text-uppercase small">(%UnitName%)&nbsp;-&nbsp;(%Status%)</' +
'div> <div class="small text-body-secondary mb-1">(%Location' +
'%)</div> <div class="small">(%CallType%)</div> <hr cla' +
'ss="unit-divider my-1" style="width: 80px; margin-left: 0" /> ' +
' <div class="small officer1">(%Officer1%)</div> <div clas' +
's="small officer2">(%Officer2%)</div> </div> <div class="d' +
'-flex flex-column justify-content-center gap-1"> <button ty' +
'pe="button" class="btn btn-primary btn-sm btn-unit-details" data' +
'-unitid="(%UnitId%)">Details</button> <button type="button"' +
' class="btn btn-primary btn-sm btn-unit-map" data-unitid="(%Unit' +
'Id%)">Map</button> </div> </div></div>'
'ary text-dark rounded-1 px-2 mb-1">(%AgencyHeader%)</div><div cl' +
'ass="card border shadow-sm"> <div class="card-body py-2 px-3 d-' +
'flex gap-2"> <div class="flex-grow-1"> <div class="fw-bo' +
'ld text-uppercase small">(%UnitName%)&nbsp;-&nbsp;(%Status%)</di' +
'v> <div class="small text-body-secondary mb-1">(%Location%)' +
'</div> <div class="small">(%CallType%)</div> <hr class' +
'="unit-divider my-1" style="width: 80px; margin-left: 0" /> ' +
' <div class="small officer1">(%Officer1%)</div> <div class=' +
'"small officer2">(%Officer2%)</div> </div> <div class="d-f' +
'lex flex-column justify-content-center gap-1"> <button type' +
'="button" class="btn btn-primary btn-sm btn-unit-details" data-u' +
'nitid="(%UnitId%)">Details</button> <button type="button" ' +
' class="btn btn-sm btn-unit-map (%MapButtonClass%)" ' +
'data-unitid="(%UnitId%)" data-canmap="(%CanShowMap%)" ' +
' title="(%MapButtonTitle%)" (%MapButtonDisabled%)> Ma' +
'p</button> </div> </div></div>'
ListSource = wdsUnits
end
object wdsUnits: TWebDataSource
......@@ -66,8 +68,8 @@ object FViewUnits: TFViewUnits
Connection = DMConnection.ApiConnection
Left = 260
Top = 412
object xdwdsUnitsDistrictHeader: TStringField
FieldName = 'DistrictHeader'
object xdwdsUnitsAgencyHeader: TStringField
FieldName = 'AgencyHeader'
end
object xdwdsUnitsUnitId: TStringField
FieldName = 'UnitId'
......@@ -90,6 +92,29 @@ object FViewUnits: TFViewUnits
object xdwdsUnitsCallType: TStringField
FieldName = 'CallType'
end
object xdwdsUnitsAgency: TStringField
FieldName = 'Agency'
Size = 80
end
object xdwdsUnitsAgencyName: TStringField
FieldName = 'AgencyName'
Size = 80
end
object xdwdsUnitsAgencyType: TStringField
FieldName = 'AgencyType'
end
object xdwdsUnitsCanShowMap: TStringField
FieldName = 'CanShowMap'
end
object xdwdsUnitsMapButtonClass: TStringField
FieldName = 'MapButtonClass'
end
object xdwdsUnitsMapButtonDisabled: TStringField
FieldName = 'MapButtonDisabled'
end
object xdwdsUnitsMapButtonTitle: TStringField
FieldName = 'MapButtonTitle'
end
end
object xdwcUnits: TXDataWebClient
Connection = DMConnection.ApiConnection
......
......@@ -17,7 +17,7 @@ type
xdwdsUnits: TXDataWebDataSet;
xdwcUnits: TXDataWebClient;
lblEntries: TWebLabel;
xdwdsUnitsDistrictHeader: TStringField;
xdwdsUnitsAgencyHeader: TStringField;
xdwdsUnitsUnitId: TStringField;
xdwdsUnitsUnitName: TStringField;
xdwdsUnitsLocation: TStringField;
......@@ -25,6 +25,13 @@ type
xdwdsUnitsOfficer1: TStringField;
xdwdsUnitsOfficer2: TStringField;
xdwdsUnitsCallType: TStringField;
xdwdsUnitsAgency: TStringField;
xdwdsUnitsAgencyName: TStringField;
xdwdsUnitsAgencyType: TStringField;
xdwdsUnitsCanShowMap: TStringField;
xdwdsUnitsMapButtonClass: TStringField;
xdwdsUnitsMapButtonDisabled: TStringField;
xdwdsUnitsMapButtonTitle: TStringField;
procedure WebFormCreate(Sender: TObject);
private
FLoading: Boolean;
......@@ -65,6 +72,7 @@ var
el: TJSElement;
btn: TJSElement;
unitId: string;
canShowMap: string;
begin
btn := nil;
el := TJSElement(e.target);
......@@ -93,10 +101,16 @@ begin
if (btn <> nil) and (btn is TJSHtmlElement) then
begin
unitId := string(TJSHtmlElement(btn).getAttribute('data-unitid'));
canShowMap := string(TJSHtmlElement(btn).getAttribute('data-canmap'));
e.preventDefault;
e.stopPropagation;
if not SameText(canShowMap, 'true') then
Exit;
unitId := string(TJSHtmlElement(btn).getAttribute('data-unitid'));
asm
pas['View.Main'].FViewMain.ShowMapFocusUnit(unitId);
end;
......
unit uMapMarkerJs;
interface
procedure RegisterMapMarkerJs;
implementation
procedure RegisterMapMarkerJs;
begin
asm
if (window.emiMapMarkerJsRegistered) {
return;
}
window.emiMapMarkerJsRegistered = true;
function htmlEncode(value) {
return String(value || '')
.replace(new RegExp('&', 'g'), '&amp;')
.replace(new RegExp('<', 'g'), '&lt;')
.replace(new RegExp('>', 'g'), '&gt;')
.replace(new RegExp(String.fromCharCode(34), 'g'), '&quot;');
}
function readMarkerMeta(popupHtml) {
var result = {
markerType: "",
unitBadgeText: "",
complaintBadgeText: "",
complaintStatus: "",
pickerLabel: ""
};
try {
var temp = document.createElement("div");
temp.innerHTML = popupHtml || "";
var meta = temp.querySelector(".emi-marker-meta");
if (meta) {
result.markerType = meta.getAttribute("data-marker-type") || "";
result.unitBadgeText = meta.getAttribute("data-unit-badge") || "";
result.complaintBadgeText = meta.getAttribute("data-priority-badge") || "";
result.complaintStatus = meta.getAttribute("data-complaint-status") || "";
result.pickerLabel = meta.getAttribute("data-picker-label") || "";
}
} catch(e) {}
return result;
}
function getUnitBadgeColor(iconUrl) {
if (iconUrl && iconUrl.indexOf("car_fire") >= 0) {
return "#DC2626";
}
if (iconUrl && iconUrl.indexOf("car_ems") >= 0) {
return "#16A34A";
}
if (iconUrl && iconUrl.indexOf("car_police") >= 0) {
return "#2563EB";
}
return "#2563EB";
}
function getComplaintBadgeColors(complaintStatus) {
if (complaintStatus === "onscene") {
return {
background: "#22C55E",
text: "#FFFFFF"
};
}
if (complaintStatus === "attached") {
return {
background: "#FFFF00",
text: "#111827"
};
}
return {
background: "#FF8080",
text: "#FFFFFF"
};
}
function clearDefaultMarkerBindings(marker) {
try {
if (marker.unbindTooltip) {
marker.unbindTooltip();
}
} catch(e) {}
try {
if (marker.unbindPopup) {
marker.unbindPopup();
}
} catch(e) {}
}
function removeMarkerTitle(marker, options) {
try {
options.title = "";
} catch(e) {}
try {
if (marker._icon) {
marker._icon.removeAttribute("title");
}
} catch(e) {}
}
function getMarkerIconUrl(options) {
try {
if (options.icon &&
options.icon.options &&
options.icon.options.iconUrl) {
return options.icon.options.iconUrl;
}
} catch(e) {}
return null;
}
function buildMarkerIconHtml(meta, iconUrl) {
var isUnit = meta.markerType === "unit";
var isComplaint = meta.markerType === "complaint";
var wrapClass = isUnit
? "emi-marker-wrap emi-marker-wrap-unit"
: (isComplaint ? "emi-marker-wrap emi-marker-wrap-complaint" : "emi-marker-wrap");
var html =
'<div class="' + wrapClass + '">' +
'<img src="' + iconUrl + '" class="emi-marker-img">';
if (isUnit && meta.unitBadgeText) {
html +=
'<div class="emi-unit-badge" style="background:' + getUnitBadgeColor(iconUrl) + ';">' +
htmlEncode(meta.unitBadgeText) +
'</div>';
} else if (isComplaint && meta.complaintBadgeText) {
var complaintColors = getComplaintBadgeColors(meta.complaintStatus);
html +=
'<div class="emi-complaint-badge" style="background:' + complaintColors.background + ';color:' + complaintColors.text + ';">' +
htmlEncode(meta.complaintBadgeText) +
'</div>';
}
html += '</div>';
return html;
}
function customizeMarkerIcon(marker, meta, iconUrl) {
if (!iconUrl) {
return;
}
marker.setIcon(L.divIcon({
html: buildMarkerIconHtml(meta, iconUrl),
iconSize: [32, 40],
iconAnchor: [16, 32],
popupAnchor: [0, -24],
className: ""
}));
}
function getDefaultPickerLabel(meta) {
if (meta.pickerLabel) {
return meta.pickerLabel;
}
if (meta.markerType === "unit") {
return "Unit " + (meta.unitBadgeText || "");
}
if (meta.markerType === "complaint") {
return "Complaint";
}
return "Marker";
}
function markerIsVisible(layer) {
try {
if (!layer || !layer.getLatLng || !layer.options) {
return false;
}
if (layer.options.emiMarkerType !== "unit" &&
layer.options.emiMarkerType !== "complaint") {
return false;
}
var el = layer.getElement ? layer.getElement() : layer._icon;
if (el) {
var style = window.getComputedStyle ? window.getComputedStyle(el) : el.style;
if (style.display === "none" ||
style.visibility === "hidden" ||
style.opacity === "0") {
return false;
}
}
return true;
} catch(e) {
return false;
}
}
function findNearbyMarkers(map, marker, radius) {
var nearby = [];
try {
var basePoint = map.latLngToLayerPoint(marker.getLatLng());
map.eachLayer(function(layer) {
try {
if (!markerIsVisible(layer)) {
return;
}
var layerPoint = map.latLngToLayerPoint(layer.getLatLng());
var dx = layerPoint.x - basePoint.x;
var dy = layerPoint.y - basePoint.y;
var distance = Math.sqrt((dx * dx) + (dy * dy));
if (distance <= radius) {
nearby.push(layer);
}
} catch(e) {}
});
} catch(e) {}
nearby.sort(function(a, b) {
var at = (a.options.emiMarkerType === "unit") ? 0 : 1;
var bt = (b.options.emiMarkerType === "unit") ? 0 : 1;
if (at !== bt) {
return at - bt;
}
return String(a.options.emiPickerLabel || "")
.localeCompare(String(b.options.emiPickerLabel || ""));
});
return nearby;
}
function buildPickerHtml(nearby) {
var html =
'<div class="emi-marker-picker px-1 py-1" style="width:240px;">';
var lastType = '';
for (var i = 0; i < nearby.length; i++) {
var markerType = nearby[i].options.emiMarkerType || '';
var label = nearby[i].options.emiPickerLabel || 'Marker';
if (markerType !== lastType) {
if (i > 0) {
html += '<div class="emi-marker-picker-divider"></div>';
}
if (markerType === 'unit') {
html += '<div class="emi-marker-picker-section">Units</div>';
} else if (markerType === 'complaint') {
html += '<div class="emi-marker-picker-section">Complaints</div>';
}
lastType = markerType;
}
html +=
'<button type="button" class="btn btn-light btn-sm w-100 text-start mb-1" ' +
'onclick="window.emiOpenMapMarkerPopup(' + i + ')">' +
htmlEncode(label) +
'</button>';
}
html += '</div>';
return html;
}
function openMarkerPicker(map, marker, nearby) {
window.emiNearbyMarkerPick = nearby;
window.emiOpenMapMarkerPopup = function(index) {
var target = window.emiNearbyMarkerPick ? window.emiNearbyMarkerPick[index] : null;
if (!target) {
return;
}
try {
map.closePopup();
} catch(e) {}
try {
target.openPopup();
} catch(e) {}
};
L.popup({
className: "emi-tip emi-picker-tip",
maxWidth: 260,
closeButton: false,
autoPan: true
})
.setLatLng(marker.getLatLng())
.setContent(buildPickerHtml(nearby))
.openOn(map);
}
function bindMarkerPopup(marker, popupHtml, meta) {
marker.bindPopup(popupHtml, {
className: "emi-tip",
maxWidth: 260,
closeButton: false,
autoPan: true
});
try {
marker.off("click");
} catch(e) {}
marker.on("click", function(ev) {
try {
if (ev && ev.originalEvent) {
L.DomEvent.stop(ev.originalEvent);
}
} catch(e) {}
var map = marker._map;
if (!map) {
try {
marker.openPopup();
} catch(e) {}
return;
}
var nearby = findNearbyMarkers(map, marker, 32);
if (nearby.length <= 1) {
marker.openPopup();
return;
}
openMarkerPicker(map, marker, nearby);
});
}
window.emiCustomizeMarker = function(marker) {
var options = marker.options || {};
var rawTitle = options.title || options.tooltipHtml || "";
var dataString = options.datastring || "";
if (dataString === "device") {
clearDefaultMarkerBindings(marker);
return;
}
options.tooltipHtml = rawTitle;
var popupHtml = options.tooltipHtml || "";
var iconUrl = getMarkerIconUrl(options);
var meta = readMarkerMeta(popupHtml);
removeMarkerTitle(marker, options);
clearDefaultMarkerBindings(marker);
customizeMarkerIcon(marker, meta, iconUrl);
options.emiPopupHtml = popupHtml;
options.emiMarkerType = meta.markerType;
options.emiPickerLabel = getDefaultPickerLabel(meta);
bindMarkerPopup(marker, popupHtml, meta);
removeMarkerTitle(marker, options);
};
end;
end;
end.
......@@ -24,7 +24,8 @@ uses
View.ComplaintDetails in 'View.ComplaintDetails.pas' {FViewComplaintDetails: TWebForm} {*.html},
View.UnitDetails in 'View.UnitDetails.pas' {FViewUnitDetails: TWebForm} {*.html},
uMapFilters in 'uMapFilters.pas',
View.ComplaintArchive in 'View.ComplaintArchive.pas' {FViewComplaintArchive: TWebForm} {*.html};
View.ComplaintArchive in 'View.ComplaintArchive.pas' {FViewComplaintArchive: TWebForm} {*.html},
uMapMarkerJs in 'uMapMarkerJs.pas';
{$R *.res}
......
......@@ -193,6 +193,7 @@
<FormType>dfm</FormType>
<DesignClass>TWebForm</DesignClass>
</DCCReference>
<DCCReference Include="uMapMarkerJs.pas"/>
<None Include="index.html"/>
<None Include="css\app.css"/>
<None Include="css\spinner.css"/>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment