Commit 7481bbad by Michael Brachmann

merge master updates into websockets

parents da5ba245 62bc3ab1
object ApiDatabaseModule: TApiDatabaseModule object ApiDatabaseModule: TApiDatabaseModule
OnCreate = DataModuleCreate
Height = 491 Height = 491
Width = 640 Width = 640
object OracleUniProvider1: TOracleUniProvider
Left = 164
Top = 38
end
object uqMapUnits: TUniQuery object uqMapUnits: TUniQuery
Connection = ucENTCAD Connection = ucENTCAD
SQL.Strings = ( SQL.Strings = (
'SELECT' 'SELECT'
' uc.ENTRYID,' ' uc.ENTRYID,'
' uc.UNITID,' ' uc.UNITID,'
' COALESCE(uc.UNITNAME, uc.CAR_NUMBER) AS UNITNAME,'
' uc.UNIT_DISTRICT,' ' COALESCE(uc.UNITNAME, dua.UNITNAME, cun.CODE_DESC, dua.CARNUMB' +
'ER::text) AS UNITNAME,'
' ag.AGENCYID AS AGENCY,'
' ag.AGENCYNAME AS AGENCY_NAME,'
' ag.AGENCYTYPE AS AGENCYTYPE,'
' uc.GPS_LATITUDE,' ' uc.GPS_LATITUDE,'
' uc.GPS_LONGITUDE,' ' uc.GPS_LONGITUDE,'
' cdc.CODE_DESC AS CALL_TYPE,' ' cdc.CODE_DESC AS CALL_TYPE,'
...@@ -30,98 +31,117 @@ object ApiDatabaseModule: TApiDatabaseModule ...@@ -30,98 +31,117 @@ object ApiDatabaseModule: TApiDatabaseModule
' p2.PF_FNAME AS OFFICER2_FNAME,' ' p2.PF_FNAME AS OFFICER2_FNAME,'
' p2.PF_EMPNUM AS OFFICER2_EMPNUM' ' p2.PF_EMPNUM AS OFFICER2_EMPNUM'
'' ''
'FROM UNITS_CURRENT@AVL_LINK uc' 'FROM UNITS_CURRENT uc'
'LEFT JOIN DIS_UNIT_ACTIVE dua ON dua.UNITID = uc.UNITID'
'LEFT JOIN AGENCY ag ON ag.AGENCYID = dua.AGENCY'
'LEFT JOIN CD_UNIT_NUMBER cun ON cun.AGENCYCODE = dua.CARNUMBER'
'LEFT JOIN CFS_ACTIVE cfs ON uc.UNITID = cfs.UNITID' 'LEFT JOIN CFS_ACTIVE cfs ON uc.UNITID = cfs.UNITID'
'LEFT JOIN COMPLAINT_ACTIVE cap ON cap.COMPLAINTID = cfs.COMPLAIN' + 'LEFT JOIN COMPLAINT_ACTIVE cap ON cap.COMPLAINTID = cfs.COMPLAIN' +
'TID' 'TID'
'LEFT JOIN CD_DISPATCHCODES cdc ON cdc.CODE = cap.DISPATCHCODE' 'LEFT JOIN CD_DISPATCHCODES cdc ON cdc.CODE = cap.DISPATCHCODE'
'LEFT JOIN CD_UNITSTATUS cus ON cus.CODE = cfs.UNITSTATUS' 'LEFT JOIN CD_UNITSTATUS cus ON cus.CODE = cfs.UNITSTATUS'
'LEFT JOIN DIS_UNIT_ACTIVE dua ON dua.UNITID = uc.UNITID'
'LEFT JOIN PERSONNEL p1 ON p1.PF_NAMEID = dua.OFFICER1ID' 'LEFT JOIN PERSONNEL p1 ON p1.PF_NAMEID = dua.OFFICER1ID'
'LEFT JOIN PERSONNEL p2 ON p2.PF_NAMEID = dua.OFFICER2ID' 'LEFT JOIN PERSONNEL p2 ON p2.PF_NAMEID = dua.OFFICER2ID'
'' 'WHERE uc.GPS_LATITUDE IS NOT NULL'
'' ' AND uc.GPS_LONGITUDE IS NOT NULL')
'')
ReadOnly = True ReadOnly = True
Left = 464 Left = 466
Top = 390 Top = 390
object uqMapUnitsENTRYID: TFloatField object uqMapUnitsentryid: TFloatField
FieldName = 'ENTRYID' FieldName = 'entryid'
Required = True
end end
object uqMapUnitsUNITID: TFloatField object uqMapUnitsunitid: TFloatField
FieldName = 'UNITID' FieldName = 'unitid'
end end
object uqMapUnitsUNITNAME: TStringField object uqMapUnitsunitname: TMemoField
FieldName = 'UNITNAME' FieldName = 'unitname'
ReadOnly = True ReadOnly = True
BlobType = ftMemo
end end
object uqMapUnitsUNIT_DISTRICT: TStringField object uqMapUnitsagency: TStringField
FieldName = 'UNIT_DISTRICT' FieldName = 'agency'
Size = 8 ReadOnly = True
Size = 6
end
object uqMapUnitsagency_name: TStringField
FieldName = 'agency_name'
ReadOnly = True
Size = 40
end end
object uqMapUnitsGPS_LATITUDE: TFloatField object uqMapUnitsgps_latitude: TFloatField
FieldName = 'GPS_LATITUDE' FieldName = 'gps_latitude'
end end
object uqMapUnitsGPS_LONGITUDE: TFloatField object uqMapUnitsgps_longitude: TFloatField
FieldName = 'GPS_LONGITUDE' FieldName = 'gps_longitude'
end end
object uqMapUnitsCALL_TYPE: TStringField object uqMapUnitscall_type: TStringField
FieldName = 'CALL_TYPE' FieldName = 'call_type'
ReadOnly = True ReadOnly = True
Size = 60 Size = 60
end end
object uqMapUnitsPRIORITY: TStringField object uqMapUnitspriority: TStringField
FieldName = 'PRIORITY' FieldName = 'priority'
ReadOnly = True ReadOnly = True
Size = 6 Size = 6
end end
object uqMapUnitsUNIT_STATUS_DESC: TStringField object uqMapUnitsunit_status_desc: TStringField
FieldName = 'UNIT_STATUS_DESC' FieldName = 'unit_status_desc'
ReadOnly = True ReadOnly = True
end end
object uqMapUnitsUPDATE_TIME: TDateTimeField object uqMapUnitsupdate_time: TDateTimeField
FieldName = 'UPDATE_TIME' FieldName = 'update_time'
end end
object uqMapUnitsOFFICER1_LNAME: TStringField object uqMapUnitsdis_unitid: TFloatField
FieldName = 'OFFICER1_LNAME' FieldName = 'dis_unitid'
ReadOnly = True ReadOnly = True
Size = 45
end end
object uqMapUnitsOFFICER1_FNAME: TStringField object uqMapUnitsofficer1_lname: TStringField
FieldName = 'OFFICER1_FNAME' FieldName = 'officer1_lname'
ReadOnly = True ReadOnly = True
Size = 30 Size = 15
end
object uqMapUnitsofficer1_fname: TStringField
FieldName = 'officer1_fname'
ReadOnly = True
Size = 12
end end
object uqMapUnitsOFFICER1_EMPNUM: TStringField object uqMapUnitsofficer1_empnum: TStringField
FieldName = 'OFFICER1_EMPNUM' FieldName = 'officer1_empnum'
ReadOnly = True ReadOnly = True
Size = 10 Size = 10
end end
object uqMapUnitsOFFICER2_LNAME: TStringField object uqMapUnitsofficer2_lname: TStringField
FieldName = 'OFFICER2_LNAME' FieldName = 'officer2_lname'
ReadOnly = True ReadOnly = True
Size = 45 Size = 15
end end
object uqMapUnitsOFFICER2_FNAME: TStringField object uqMapUnitsofficer2_fname: TStringField
FieldName = 'OFFICER2_FNAME' FieldName = 'officer2_fname'
ReadOnly = True ReadOnly = True
Size = 30 Size = 12
end end
object uqMapUnitsOFFICER2_EMPNUM: TStringField object uqMapUnitsofficer2_empnum: TStringField
FieldName = 'OFFICER2_EMPNUM' FieldName = 'officer2_empnum'
ReadOnly = True ReadOnly = True
Size = 10 Size = 10
end end
object uqMapUnitsDIS_UNITID: TFloatField object uqMapUnitsagencytype: TStringField
FieldName = 'DIS_UNITID' FieldName = 'agencytype'
ReadOnly = True ReadOnly = True
Size = 10
end end
end end
object uqUnitList: TUniQuery object uqUnitList: TUniQuery
Connection = ucENTCAD Connection = ucENTCAD
SQL.Strings = ( SQL.Strings = (
'SELECT dua.UNITID, dua.UNITNAME,' 'SELECT'
' ag.AGENCYID AS AGENCY,'
' ag.AGENCYNAME AS AGENCY_NAME,'
' ag.AGENCYTYPE AS AGENCYTYPE,'
' dua.UNITID,'
' dua.UNITNAME,'
' cun.CODE_DESC AS CARNUMBER_DESC,' ' cun.CODE_DESC AS CARNUMBER_DESC,'
' cd.CODE_DESC AS DISTRICT_DESC,' ' cd.CODE_DESC AS DISTRICT_DESC,'
' cs.CODE_DESC AS SECTOR_DESC,' ' cs.CODE_DESC AS SECTOR_DESC,'
...@@ -142,6 +162,7 @@ object ApiDatabaseModule: TApiDatabaseModule ...@@ -142,6 +162,7 @@ object ApiDatabaseModule: TApiDatabaseModule
' uc.GPS_LATITUDE,' ' uc.GPS_LATITUDE,'
' uc.GPS_LONGITUDE' ' uc.GPS_LONGITUDE'
'FROM DIS_UNIT_ACTIVE dua' 'FROM DIS_UNIT_ACTIVE dua'
'JOIN AGENCY ag ON ag.AGENCYID = dua.AGENCY'
'LEFT JOIN CD_UNIT_NUMBER cun ON cun.AGENCYCODE = dua.CAR' + 'LEFT JOIN CD_UNIT_NUMBER cun ON cun.AGENCYCODE = dua.CAR' +
'NUMBER' 'NUMBER'
...@@ -170,113 +191,128 @@ object ApiDatabaseModule: TApiDatabaseModule ...@@ -170,113 +191,128 @@ object ApiDatabaseModule: TApiDatabaseModule
'LEFT JOIN CD_DISPATCHCODES cdc ON cdc.CODE = cap.DI' + 'LEFT JOIN CD_DISPATCHCODES cdc ON cdc.CODE = cap.DI' +
'SPATCHCODE' 'SPATCHCODE'
'LEFT JOIN UNITS_CURRENT@AVL_LINK uc ON dua.UNITID = uc.UNIT' + 'LEFT JOIN UNITS_CURRENT uc ON dua.UNITID = uc.UNI' +
'ID' 'TID'
'WHERE ag.AGENCYTYPE = '#39'POL'#39
'ORDER BY' 'ORDER BY'
' CASE WHEN LENGTH(cd.CODE_DESC) = 1 THEN 0 ELSE 1 END,' ' CASE WHEN ag.AGENCYID = '#39'WCSO'#39' THEN 0 ELSE 1 END,'
' cd.CODE_DESC,' ' ag.AGENCYNAME,'
' dua.UNITNAME' ' dua.UNITNAME')
'' ReadOnly = True
'') Left = 78
Top = 190
object uqUnitListagency: TStringField
FieldName = 'agency'
ReadOnly = True
Size = 6
end
object uqUnitListagency_name: TStringField
FieldName = 'agency_name'
ReadOnly = True ReadOnly = True
Left = 76 Size = 40
Top = 188 end
object uqUnitListUNITID: TFloatField object uqUnitListunitid: TFloatField
FieldName = 'UNITID' FieldName = 'unitid'
end end
object uqUnitListUNITNAME: TStringField object uqUnitListunitname: TStringField
FieldName = 'UNITNAME' FieldName = 'unitname'
Size = 10 Size = 10
end end
object uqUnitListCARNUMBER_DESC: TStringField object uqUnitListcarnumber_desc: TStringField
FieldName = 'CARNUMBER_DESC' FieldName = 'carnumber_desc'
ReadOnly = True ReadOnly = True
Size = 120 Size = 120
end end
object uqUnitListDISTRICT_DESC: TStringField object uqUnitListdistrict_desc: TStringField
FieldName = 'DISTRICT_DESC' FieldName = 'district_desc'
ReadOnly = True ReadOnly = True
Size = 120 Size = 120
end end
object uqUnitListSECTOR_DESC: TStringField object uqUnitListsector_desc: TStringField
FieldName = 'SECTOR_DESC' FieldName = 'sector_desc'
ReadOnly = True ReadOnly = True
Size = 120 Size = 120
end end
object uqUnitListOFFICER1_EMPNUM: TStringField object uqUnitListofficer1_empnum: TStringField
FieldName = 'OFFICER1_EMPNUM' FieldName = 'officer1_empnum'
ReadOnly = True ReadOnly = True
Size = 10 Size = 10
end end
object uqUnitListOFFICER1_LAST_NAME: TStringField object uqUnitListofficer1_last_name: TStringField
FieldName = 'OFFICER1_LAST_NAME' FieldName = 'officer1_last_name'
ReadOnly = True ReadOnly = True
Size = 45 Size = 15
end end
object uqUnitListOFFICER1_FIRST_NAME: TStringField object uqUnitListofficer1_first_name: TStringField
FieldName = 'OFFICER1_FIRST_NAME' FieldName = 'officer1_first_name'
ReadOnly = True ReadOnly = True
Size = 30 Size = 12
end end
object uqUnitListOFFICER1_MI: TStringField object uqUnitListofficer1_mi: TStringField
FieldName = 'OFFICER1_MI' FieldName = 'officer1_mi'
ReadOnly = True ReadOnly = True
Size = 1 Size = 1
end end
object uqUnitListOFFICER2_EMPNUM: TStringField object uqUnitListofficer2_empnum: TStringField
FieldName = 'OFFICER2_EMPNUM' FieldName = 'officer2_empnum'
ReadOnly = True ReadOnly = True
Size = 10 Size = 10
end end
object uqUnitListOFFICER2_LAST_NAME: TStringField object uqUnitListofficer2_last_name: TStringField
FieldName = 'OFFICER2_LAST_NAME' FieldName = 'officer2_last_name'
ReadOnly = True ReadOnly = True
Size = 45 Size = 15
end end
object uqUnitListOFFICER2_FIRST_NAME: TStringField object uqUnitListofficer2_first_name: TStringField
FieldName = 'OFFICER2_FIRST_NAME' FieldName = 'officer2_first_name'
ReadOnly = True ReadOnly = True
Size = 30 Size = 12
end end
object uqUnitListOFFICER2_MI: TStringField object uqUnitListofficer2_mi: TStringField
FieldName = 'OFFICER2_MI' FieldName = 'officer2_mi'
ReadOnly = True ReadOnly = True
Size = 1 Size = 1
end end
object uqUnitListLOCATION: TStringField object uqUnitListlocation: TStringField
FieldName = 'LOCATION' FieldName = 'location'
ReadOnly = True ReadOnly = True
Size = 30 Size = 100
end end
object uqUnitListCOMPLAINT: TStringField object uqUnitListcomplaint: TStringField
FieldName = 'COMPLAINT' FieldName = 'complaint'
ReadOnly = True ReadOnly = True
Size = 10 Size = 10
end end
object uqUnitListUNITSTATUS: TFloatField object uqUnitListunitstatus: TFloatField
FieldName = 'UNITSTATUS' FieldName = 'unitstatus'
ReadOnly = True ReadOnly = True
end end
object uqUnitListUNIT_STATUS_DESC: TStringField object uqUnitListunit_status_desc: TStringField
FieldName = 'UNIT_STATUS_DESC' FieldName = 'unit_status_desc'
ReadOnly = True ReadOnly = True
end end
object uqUnitListENTRYID: TFloatField object uqUnitListcall_type: TStringField
FieldName = 'ENTRYID' FieldName = 'call_type'
ReadOnly = True ReadOnly = True
Size = 60
end end
object uqUnitListGPS_LATITUDE: TFloatField object uqUnitListentryid: TFloatField
FieldName = 'GPS_LATITUDE' FieldName = 'entryid'
ReadOnly = True ReadOnly = True
Required = True
end end
object uqUnitListGPS_LONGITUDE: TFloatField object uqUnitListgps_latitude: TFloatField
FieldName = 'GPS_LONGITUDE' FieldName = 'gps_latitude'
ReadOnly = True ReadOnly = True
end end
object uqUnitListCALL_TYPE: TStringField object uqUnitListgps_longitude: TFloatField
FieldName = 'CALL_TYPE' FieldName = 'gps_longitude'
ReadOnly = True ReadOnly = True
Size = 60 end
object uqUnitListagencytype: TStringField
FieldName = 'agencytype'
ReadOnly = True
Size = 10
end end
end end
object uqComplaintUnits: TUniQuery object uqComplaintUnits: TUniQuery
...@@ -301,33 +337,35 @@ object ApiDatabaseModule: TApiDatabaseModule ...@@ -301,33 +337,35 @@ object ApiDatabaseModule: TApiDatabaseModule
item item
DataType = ftUnknown DataType = ftUnknown
Name = 'COMPLAINTID' Name = 'COMPLAINTID'
Value = Null Value = nil
end> end>
object uqComplaintUnitsCOMPLAINTID: TFloatField object uqComplaintUnitscomplaintid: TFloatField
FieldName = 'COMPLAINTID' FieldName = 'complaintid'
Required = True
end end
object uqComplaintUnitsUNITID: TFloatField object uqComplaintUnitsunitid: TFloatField
FieldName = 'UNITID' FieldName = 'unitid'
Required = True
end end
object uqComplaintUnitsUNITNAME: TStringField object uqComplaintUnitsunitname: TStringField
FieldName = 'UNITNAME' FieldName = 'unitname'
Size = 10 Size = 10
end end
object uqComplaintUnitsDATEDISPATCHED: TDateTimeField object uqComplaintUnitsdatedispatched: TDateTimeField
FieldName = 'DATEDISPATCHED' FieldName = 'datedispatched'
end end
object uqComplaintUnitsDATERESPONDED: TDateTimeField object uqComplaintUnitsdateresponded: TDateTimeField
FieldName = 'DATERESPONDED' FieldName = 'dateresponded'
end end
object uqComplaintUnitsDATEARRIVED: TDateTimeField object uqComplaintUnitsdatearrived: TDateTimeField
FieldName = 'DATEARRIVED' FieldName = 'datearrived'
end end
object uqComplaintUnitsDATECLEARED: TDateTimeField object uqComplaintUnitsdatecleared: TDateTimeField
FieldName = 'DATECLEARED' FieldName = 'datecleared'
end end
object uqComplaintUnitsLOCATION: TStringField object uqComplaintUnitslocation: TStringField
FieldName = 'LOCATION' FieldName = 'location'
Size = 30 Size = 100
end end
end end
object uqCFSMemos: TUniQuery object uqCFSMemos: TUniQuery
...@@ -344,7 +382,7 @@ object ApiDatabaseModule: TApiDatabaseModule ...@@ -344,7 +382,7 @@ object ApiDatabaseModule: TApiDatabaseModule
'WHERE cm.CFSID = :CFSID' 'WHERE cm.CFSID = :CFSID'
'ORDER BY cm.TIMESTAMP DESC') 'ORDER BY cm.TIMESTAMP DESC')
ReadOnly = True ReadOnly = True
Left = 196 Left = 198
Top = 248 Top = 248
ParamData = < ParamData = <
item item
...@@ -352,26 +390,25 @@ object ApiDatabaseModule: TApiDatabaseModule ...@@ -352,26 +390,25 @@ object ApiDatabaseModule: TApiDatabaseModule
Name = 'CFSID' Name = 'CFSID'
Value = nil Value = nil
end> end>
object uqCFSMemosMEMO_ID: TFloatField object uqCFSMemosmemo_id: TFloatField
FieldName = 'MEMO_ID' FieldName = 'memo_id'
Required = True
end end
object uqCFSMemosCFSID: TFloatField object uqCFSMemoscfsid: TFloatField
FieldName = 'CFSID' FieldName = 'cfsid'
end end
object uqCFSMemosMEMO_TYPE: TFloatField object uqCFSMemosmemo_type: TFloatField
FieldName = 'MEMO_TYPE' FieldName = 'memo_type'
end end
object uqCFSMemosTIMESTAMP: TDateTimeField object uqCFSMemostimestamp: TDateTimeField
FieldName = 'TIMESTAMP' FieldName = 'timestamp'
end end
object uqCFSMemosBADGE_NUMBER: TStringField object uqCFSMemosbadge_number: TStringField
FieldName = 'BADGE_NUMBER' FieldName = 'badge_number'
Size = 6 Size = 6
end end
object uqCFSMemosREMARKS: TStringField object uqCFSMemosremarks: TStringField
FieldName = 'REMARKS' FieldName = 'remarks'
Size = 2048 Size = 4000
end end
end end
object uqComplaintList: TUniQuery object uqComplaintList: TUniQuery
...@@ -406,154 +443,154 @@ object ApiDatabaseModule: TApiDatabaseModule ...@@ -406,154 +443,154 @@ object ApiDatabaseModule: TApiDatabaseModule
' ct.DATERESPONDED,' ' ct.DATERESPONDED,'
' ct.DATEARRIVED,' ' ct.DATEARRIVED,'
' ct.DATECLEARED,' ' ct.DATECLEARED,'
' cp.MOBILE_COLOR AS PRIORITY_COLOR,' ' cp.COLOR AS PRIORITY_COLOR,'
' cd.CODE_DESC AS DISTRICT_DESC,' ' cd.CODE_DESC AS DISTRICT_DESC,'
' cs.CODE_DESC AS SECTOR_DESC' ' cs.CODE_DESC AS SECTOR_DESC'
'FROM COMPLAINT_ACTIVE ca' 'FROM COMPLAINT_ACTIVE ca'
'JOIN COMPLAINT_TIMES ct ON ca.COMPLAINTID = ct.COMPLAINTID' 'JOIN COMPLAINT_TIMES ct ON ca.COMPLAINTID = ct.COMPLAINTID'
'LEFT JOIN CD_DISPATCHCODES cdc ON ca.DISPATCHCODE = cdc.CODE' 'LEFT JOIN CD_DISPATCHCODES cdc ON ca.DISPATCHCODE = cdc.CODE'
'LEFT JOIN CD_CALLSOURCES ccs ON ca.SOURCE = ccs.CODE' 'LEFT JOIN CD_CALLSOURCES ccs ON ca.SOURCE = ccs.CODE'
'LEFT JOIN CD_CALLPRIORITIES cp ON cp.CODE = ca.PRIORITY'
'LEFT JOIN CD_CALLPRIORITIES cp ON cp.CODE = ca.PRIORITY AND cp.A' + 'LEFT JOIN CD_DISTRICT cd ON cd.AGENCYCODE = ca.DISPATCHD' +
'GENCY = ca.AGENCY' 'ISTRICT'
'JOIN CD_DISTRICT cd ON cd.AGENCYCODE = ca.DISPATCHDI' +
'STRICT'
'LEFT JOIN CD_SECTOR cs ON cs.AGENCYCODE = ca.DISPATCHSE' + 'LEFT JOIN CD_SECTOR cs ON cs.AGENCYCODE = ca.DISPATCHS' +
'CTOR AND cs.CODE_TYPE = cd.AGENCYCODE' 'ECTOR AND cs.CODE_TYPE = cd.AGENCYCODE'
'WHERE ca.COMPLAINT IS NOT NULL' 'WHERE ca.COMPLAINT IS NOT NULL'
'ORDER BY cd.CODE_DESC, ct.DATEREPORTED DESC, ca.PRIORITY DESC;')
'ORDER BY COALESCE(ca.AGENCY, '#39#39'), ct.DATEREPORTED DESC, ca.PRIOR' +
'ITY DESC')
ReadOnly = True ReadOnly = True
OnCalcFields = uqComplaintListCalcFields OnCalcFields = uqComplaintListCalcFields
Left = 78 Left = 80
Top = 244 Top = 248
object uqComplaintListCOMPLAINTID: TFloatField object uqComplaintListcomplaintid: TFloatField
FieldName = 'COMPLAINTID' FieldName = 'complaintid'
Required = True Required = True
end end
object uqComplaintListCFSID: TFloatField object uqComplaintListcfsid: TFloatField
FieldName = 'CFSID' FieldName = 'cfsid'
end end
object uqComplaintListCOMPLAINT: TStringField object uqComplaintListcomplaint: TStringField
FieldName = 'COMPLAINT' FieldName = 'complaint'
Size = 10 Size = 10
end end
object uqComplaintListAGENCY: TStringField object uqComplaintListagency: TStringField
FieldName = 'AGENCY' FieldName = 'agency'
Size = 6 Size = 6
end end
object uqComplaintListDISPATCH_CODE_DESC: TStringField object uqComplaintListdispatch_code_desc: TStringField
FieldName = 'DISPATCH_CODE_DESC' FieldName = 'dispatch_code_desc'
ReadOnly = True ReadOnly = True
Size = 60 Size = 60
end end
object uqComplaintListSOURCE: TStringField object uqComplaintListsource: TStringField
FieldName = 'SOURCE' FieldName = 'source'
Size = 6 Size = 6
end end
object uqComplaintListSOURCE_DESC: TStringField object uqComplaintListsource_desc: TStringField
FieldName = 'SOURCE_DESC' FieldName = 'source_desc'
ReadOnly = True ReadOnly = True
Size = 120 Size = 120
end end
object uqComplaintListPRIORITY: TStringField object uqComplaintListpriority: TStringField
FieldName = 'PRIORITY' FieldName = 'priority'
Size = 6 Size = 6
end end
object uqComplaintListADDRESSID: TFloatField object uqComplaintListaddressid: TFloatField
FieldName = 'ADDRESSID' FieldName = 'addressid'
end end
object uqComplaintListADDRESS: TStringField object uqComplaintListaddress: TStringField
FieldName = 'ADDRESS' FieldName = 'address'
Size = 64 Size = 64
end end
object uqComplaintListAPARTMENT: TStringField object uqComplaintListapartment: TStringField
FieldName = 'APARTMENT' FieldName = 'apartment'
Size = 6 Size = 6
end end
object uqComplaintListCITY: TStringField object uqComplaintListcity: TStringField
FieldName = 'CITY' FieldName = 'city'
end end
object uqComplaintListBUSINESS: TStringField object uqComplaintListbusiness: TStringField
FieldName = 'BUSINESS' FieldName = 'business'
Size = 35 Size = 35
end end
object uqComplaintListDISPATCHDISTRICT: TStringField object uqComplaintListdispatchdistrict: TStringField
FieldName = 'DISPATCHDISTRICT' FieldName = 'dispatchdistrict'
Size = 6 ReadOnly = True
Size = 120
end end
object uqComplaintListDISPATCHSECTOR: TStringField object uqComplaintListdispatchsector: TStringField
FieldName = 'DISPATCHSECTOR' FieldName = 'dispatchsector'
Size = 6 Size = 6
end end
object uqComplaintListADDRESSDISTRICT: TStringField object uqComplaintListaddressdistrict: TStringField
FieldName = 'ADDRESSDISTRICT' FieldName = 'addressdistrict'
Size = 5 Size = 5
end end
object uqComplaintListADDRESSSECTOR: TStringField object uqComplaintListaddresssector: TStringField
FieldName = 'ADDRESSSECTOR' FieldName = 'addresssector'
Size = 5 Size = 5
end end
object uqComplaintListXCOORD: TFloatField object uqComplaintListxcoord: TFloatField
FieldName = 'XCOORD' FieldName = 'xcoord'
end end
object uqComplaintListYCOORD: TFloatField object uqComplaintListycoord: TFloatField
FieldName = 'YCOORD' FieldName = 'ycoord'
end end
object uqComplaintListWARNINGS: TFloatField object uqComplaintListwarnings: TFloatField
FieldName = 'WARNINGS' FieldName = 'warnings'
end end
object uqComplaintListCONTACTS: TFloatField object uqComplaintListcontacts: TFloatField
FieldName = 'CONTACTS' FieldName = 'contacts'
end end
object uqComplaintListHISTORY: TFloatField object uqComplaintListhistory: TFloatField
FieldName = 'HISTORY' FieldName = 'history'
end end
object uqComplaintListDATEREPORTED: TDateTimeField object uqComplaintListdatereported: TDateTimeField
FieldName = 'DATEREPORTED' FieldName = 'datereported'
ReadOnly = True ReadOnly = True
end end
object uqComplaintListDATERECEIVED: TDateTimeField object uqComplaintListdatereceived: TDateTimeField
FieldName = 'DATERECEIVED' FieldName = 'datereceived'
ReadOnly = True ReadOnly = True
end end
object uqComplaintListDATEDISPATCHED: TDateTimeField object uqComplaintListdatedispatched: TDateTimeField
FieldName = 'DATEDISPATCHED' FieldName = 'datedispatched'
ReadOnly = True ReadOnly = True
end end
object uqComplaintListDATERESPONDED: TDateTimeField object uqComplaintListdateresponded: TDateTimeField
FieldName = 'DATERESPONDED' FieldName = 'dateresponded'
ReadOnly = True ReadOnly = True
end end
object uqComplaintListDATEARRIVED: TDateTimeField object uqComplaintListdatearrived: TDateTimeField
FieldName = 'DATEARRIVED' FieldName = 'datearrived'
ReadOnly = True ReadOnly = True
end end
object uqComplaintListDATECLEARED: TDateTimeField object uqComplaintListdatecleared: TDateTimeField
FieldName = 'DATECLEARED' FieldName = 'datecleared'
ReadOnly = True ReadOnly = True
end end
object uqComplaintListcomplaintNumber: TStringField object uqComplaintListpriority_color: TFloatField
FieldKind = fkCalculated FieldName = 'priority_color'
FieldName = 'complaintNumber'
Size = 11
Calculated = True
end
object uqComplaintListPRIORITY_COLOR: TFloatField
FieldName = 'PRIORITY_COLOR'
ReadOnly = True ReadOnly = True
end end
object uqComplaintListDISTRICT_DESC: TStringField object uqComplaintListdistrict_desc: TStringField
FieldName = 'DISTRICT_DESC' FieldName = 'district_desc'
ReadOnly = True ReadOnly = True
Size = 120 Size = 120
end end
object uqComplaintListSECTOR_DESC: TStringField object uqComplaintListsector_desc: TStringField
FieldName = 'SECTOR_DESC' FieldName = 'sector_desc'
ReadOnly = True ReadOnly = True
Size = 120 Size = 120
end end
object uqComplaintListcomplaintNumber: TStringField
FieldKind = fkCalculated
FieldName = 'complaintNumber'
Calculated = True
end
end end
object uqComplaintDetails: TUniQuery object uqComplaintDetails: TUniQuery
Connection = ucENTCAD Connection = ucENTCAD
...@@ -651,7 +688,7 @@ object ApiDatabaseModule: TApiDatabaseModule ...@@ -651,7 +688,7 @@ object ApiDatabaseModule: TApiDatabaseModule
'WHERE ca.COMPLAINTID = :COMPLAINTID;' 'WHERE ca.COMPLAINTID = :COMPLAINTID;'
'') '')
ReadOnly = True ReadOnly = True
Left = 80 Left = 82
Top = 302 Top = 302
ParamData = < ParamData = <
item item
...@@ -659,124 +696,138 @@ object ApiDatabaseModule: TApiDatabaseModule ...@@ -659,124 +696,138 @@ object ApiDatabaseModule: TApiDatabaseModule
Name = 'COMPLAINTID' Name = 'COMPLAINTID'
Value = Null Value = Null
end> end>
object uqComplaintDetailsCOMPLAINTID: TFloatField object uqComplaintDetailscomplaintid: TFloatField
FieldName = 'COMPLAINTID' FieldName = 'complaintid'
Required = True ReadOnly = True
end end
object uqComplaintDetailsCFSID: TFloatField object uqComplaintDetailscfsid: TFloatField
FieldName = 'CFSID' FieldName = 'cfsid'
ReadOnly = True
end end
object uqComplaintDetailsCOMPLAINT: TStringField object uqComplaintDetailscomplaint: TStringField
FieldName = 'COMPLAINT' FieldName = 'complaint'
ReadOnly = True
Size = 10 Size = 10
end end
object uqComplaintDetailsPRIORITY: TStringField object uqComplaintDetailspriority: TStringField
FieldName = 'PRIORITY' FieldName = 'priority'
ReadOnly = True
Size = 6 Size = 6
end end
object uqComplaintDetailsDISPATCHCODE: TStringField object uqComplaintDetailsdispatchcode: TStringField
FieldName = 'DISPATCHCODE' FieldName = 'dispatchcode'
ReadOnly = True
Size = 6 Size = 6
end end
object uqComplaintDetailsDISPATCH_CODE_DESC: TStringField object uqComplaintDetailsdispatch_code_desc: TStringField
FieldName = 'DISPATCH_CODE_DESC' FieldName = 'dispatch_code_desc'
ReadOnly = True ReadOnly = True
Size = 60 Size = 60
end end
object uqComplaintDetailsDISPATCHDISTRICT: TStringField object uqComplaintDetailsdispatchdistrict: TStringField
FieldName = 'DISPATCHDISTRICT' FieldName = 'dispatchdistrict'
ReadOnly = True
Size = 120 Size = 120
end end
object uqComplaintDetailsADDRESS: TStringField object uqComplaintDetailsaddress: TStringField
FieldName = 'ADDRESS' FieldName = 'address'
ReadOnly = True
Size = 64 Size = 64
end end
object uqComplaintDetailsDATEREPORTED: TDateTimeField object uqComplaintDetailsbusiness: TStringField
FieldName = 'DATEREPORTED' FieldName = 'business'
ReadOnly = True
Size = 35
end
object uqComplaintDetailsdatereported: TDateTimeField
FieldName = 'datereported'
ReadOnly = True ReadOnly = True
end end
object uqComplaintDetailsDATERECEIVED: TDateTimeField object uqComplaintDetailsdatereceived: TDateTimeField
FieldName = 'DATERECEIVED' FieldName = 'datereceived'
ReadOnly = True ReadOnly = True
end end
object uqComplaintDetailsDATEDISPATCHED: TDateTimeField object uqComplaintDetailsdatedispatched: TDateTimeField
FieldName = 'DATEDISPATCHED' FieldName = 'datedispatched'
ReadOnly = True ReadOnly = True
end end
object uqComplaintDetailsDATERESPONDED: TDateTimeField object uqComplaintDetailsdateresponded: TDateTimeField
FieldName = 'DATERESPONDED' FieldName = 'dateresponded'
ReadOnly = True ReadOnly = True
end end
object uqComplaintDetailsDATEARRIVED: TDateTimeField object uqComplaintDetailsdatearrived: TDateTimeField
FieldName = 'DATEARRIVED' FieldName = 'datearrived'
ReadOnly = True ReadOnly = True
end end
object uqComplaintDetailsDATECLEARED: TDateTimeField object uqComplaintDetailsdatecleared: TDateTimeField
FieldName = 'DATECLEARED' FieldName = 'datecleared'
ReadOnly = True ReadOnly = True
end end
object uqComplaintDetailsHISTORY: TFloatField object uqComplaintDetailshistory: TFloatField
FieldName = 'HISTORY' FieldName = 'history'
ReadOnly = True
end end
object uqComplaintDetailsCONTACTS: TFloatField object uqComplaintDetailscontacts: TFloatField
FieldName = 'CONTACTS' FieldName = 'contacts'
ReadOnly = True
end end
object uqComplaintDetailsWARNINGS: TFloatField object uqComplaintDetailswarnings: TFloatField
FieldName = 'WARNINGS' FieldName = 'warnings'
ReadOnly = True
end end
object uqComplaintDetailsADDRESSID: TFloatField object uqComplaintDetailsaddressid: TFloatField
FieldName = 'ADDRESSID' FieldName = 'addressid'
ReadOnly = True
end end
object uqComplaintDetailsAGENCY: TStringField object uqComplaintDetailsagency: TStringField
FieldName = 'AGENCY' FieldName = 'agency'
ReadOnly = True
Size = 6 Size = 6
end end
object uqComplaintDetailsSTRNUMBER: TFloatField object uqComplaintDetailsstrnumber: TFloatField
FieldName = 'STRNUMBER' FieldName = 'strnumber'
ReadOnly = True ReadOnly = True
end end
object uqComplaintDetailsSTRHNUMBER: TStringField object uqComplaintDetailsstrhnumber: TStringField
FieldName = 'STRHNUMBER' FieldName = 'strhnumber'
ReadOnly = True ReadOnly = True
Size = 1 Size = 1
end end
object uqComplaintDetailsSTRPREFIX: TStringField object uqComplaintDetailsstrprefix: TStringField
FieldName = 'STRPREFIX' FieldName = 'strprefix'
ReadOnly = True ReadOnly = True
Size = 1 Size = 1
end end
object uqComplaintDetailsSTRNAME: TStringField object uqComplaintDetailsstrname: TStringField
FieldName = 'STRNAME' FieldName = 'strname'
ReadOnly = True ReadOnly = True
end end
object uqComplaintDetailsSTRSUFFIX: TStringField object uqComplaintDetailsstrsuffix: TStringField
FieldName = 'STRSUFFIX' FieldName = 'strsuffix'
ReadOnly = True ReadOnly = True
Size = 2 Size = 2
end end
object uqComplaintDetailsCITY: TStringField object uqComplaintDetailscity: TStringField
FieldName = 'CITY' FieldName = 'city'
ReadOnly = True ReadOnly = True
end end
object uqComplaintDetailsBUSINESS: TStringField object uqComplaintDetailsstatus: TIntegerField
FieldName = 'BUSINESS' FieldName = 'status'
Size = 35
end
object uqComplaintDetailsSTATUS: TFloatField
FieldName = 'STATUS'
ReadOnly = True ReadOnly = True
end end
end end
object ucENTCAD: TUniConnection object ucENTCAD: TUniConnection
ProviderName = 'Oracle' ProviderName = 'PostgreSQL'
Port = 1521 Port = 5432
Username = 'ENTCAD' Database = 'lems_ocso'
Server = 'BUFENTCAD' SpecificOptions.Strings = (
Connected = True 'PostgreSQL.Schema=lems, avl, entcad')
Username = 'postgres'
Server = '192.168.74.10'
LoginPrompt = False LoginPrompt = False
Left = 50 Left = 54
Top = 36 Top = 38
EncryptedPassword = 'BAFFB1FFABFFBCFFBEFFBBFF' EncryptedPassword = '9AFF92FF8CFF86FF8CFFCFFFCEFF'
end end
object uqMapComplaints: TUniQuery object uqMapComplaints: TUniQuery
Connection = ucENTCAD Connection = ucENTCAD
...@@ -784,23 +835,31 @@ object ApiDatabaseModule: TApiDatabaseModule ...@@ -784,23 +835,31 @@ object ApiDatabaseModule: TApiDatabaseModule
'SELECT' 'SELECT'
' ca.COMPLAINTID,' ' ca.COMPLAINTID,'
' cd.CODE_DESC AS DISPATCHDISTRICT,' ' cd.CODE_DESC AS DISPATCHDISTRICT,'
' ca.AGENCY AS AGENCY,'
' ag.AGENCYNAME AS AGENCY_NAME,'
' ca.PRIORITY AS PRIORITY,' ' ca.PRIORITY AS PRIORITY,'
' ct.DATEDISPATCHED AS DATEDISPATCHED,'
' ct.DATEARRIVED AS DATEARRIVED,'
' ca.BUSINESS,' ' ca.BUSINESS,'
' cdc.MOBILE_MAP_CATEGORY AS DISPATCHCODECATEGORY,' ' cdc.CATEGORY AS DISPATCHCODECATEGORY,'
'' ''
' SDO_CS.TRANSFORM(' ' ST_X('
' ST_Transform('
' SDO_GEOMETRY(2001, 2262, SDO_POINT_TYPE(ca.XCOORD, ca.YCOORD' + ' ST_SetSRID(ST_MakePoint(ca.XCOORD::double precision, ca.YC' +
', NULL), NULL, NULL),' 'OORD::double precision), 2262),'
' 4326' ' 4326'
' ).sdo_point.x AS LNG,' ' )'
' ) AS LNG,'
'' ''
' SDO_CS.TRANSFORM(' ' ST_Y('
' ST_Transform('
' SDO_GEOMETRY(2001, 2262, SDO_POINT_TYPE(ca.XCOORD, ca.YCOORD' + ' ST_SetSRID(ST_MakePoint(ca.XCOORD::double precision, ca.YC' +
', NULL), NULL, NULL),' 'OORD::double precision), 2262),'
' 4326' ' 4326'
' ).sdo_point.y AS LAT,' ' )'
' ) AS LAT,'
'' ''
' cdc.CODE_DESC AS DISPATCH_CODE_DESC,' ' cdc.CODE_DESC AS DISPATCH_CODE_DESC,'
' ca.ADDRESS AS ADDRESS' ' ca.ADDRESS AS ADDRESS'
...@@ -809,67 +868,91 @@ object ApiDatabaseModule: TApiDatabaseModule ...@@ -809,67 +868,91 @@ object ApiDatabaseModule: TApiDatabaseModule
' ON ct.COMPLAINTID = ca.COMPLAINTID' ' ON ct.COMPLAINTID = ca.COMPLAINTID'
'LEFT JOIN CD_DISPATCHCODES cdc' 'LEFT JOIN CD_DISPATCHCODES cdc'
' ON cdc.CODE = ca.DISPATCHCODE' ' ON cdc.CODE = ca.DISPATCHCODE'
'JOIN CD_DISTRICT cd' 'LEFT JOIN CD_DISTRICT cd'
' ON cd.AGENCYCODE = ca.DISPATCHDISTRICT' ' ON cd.AGENCYCODE = ca.DISPATCHDISTRICT'
'LEFT JOIN AGENCY ag'
' ON ag.AGENCYID = ca.AGENCY'
'WHERE ca.COMPLAINT IS NOT NULL' 'WHERE ca.COMPLAINT IS NOT NULL'
' AND ca.XCOORD IS NOT NULL' ' AND ca.XCOORD IS NOT NULL'
' AND ca.YCOORD IS NOT NULL;' ' AND ca.YCOORD IS NOT NULL')
''
''
''
'')
ReadOnly = True ReadOnly = True
OnCalcFields = uqMapComplaintsCalcFields OnCalcFields = uqMapComplaintsCalcFields
Left = 468 Left = 468
Top = 264 Top = 264
object uqMapComplaintsCOMPLAINTID: TFloatField object uqMapComplaintscomplaintid: TFloatField
FieldName = 'COMPLAINTID' FieldName = 'complaintid'
Required = True Required = True
end end
object uqMapComplaintsDISPATCHDISTRICT: TStringField object uqMapComplaintsdispatchdistrict: TStringField
FieldName = 'DISPATCHDISTRICT' FieldName = 'dispatchdistrict'
ReadOnly = True
Size = 120 Size = 120
end end
object uqMapComplaintsLNG: TFloatField object uqMapComplaintspriority: TStringField
FieldName = 'LNG' FieldName = 'priority'
Size = 6
end
object uqMapComplaintsbusiness: TStringField
FieldName = 'business'
Size = 35
end
object uqMapComplaintsdispatchcodecategory: TStringField
FieldName = 'dispatchcodecategory'
ReadOnly = True
Size = 30
end
object uqMapComplaintslng: TFloatField
FieldName = 'lng'
ReadOnly = True
end end
object uqMapComplaintsLAT: TFloatField object uqMapComplaintslat: TFloatField
FieldName = 'LAT' FieldName = 'lat'
ReadOnly = True
end end
object uqMapComplaintsDISPATCH_CODE_DESC: TStringField object uqMapComplaintsdispatch_code_desc: TStringField
FieldName = 'DISPATCH_CODE_DESC' FieldName = 'dispatch_code_desc'
ReadOnly = True ReadOnly = True
Size = 60 Size = 60
end end
object uqMapComplaintsPRIORITY: TStringField object uqMapComplaintsaddress: TStringField
FieldName = 'PRIORITY' FieldName = 'address'
Size = 64
end
object uqMapComplaintsagency: TStringField
FieldName = 'agency'
Size = 6 Size = 6
end end
object uqMapComplaintsDISPATCHCODECATEGORY: TStringField object uqMapComplaintsagency_name: TStringField
FieldName = 'DISPATCHCODECATEGORY' FieldName = 'agency_name'
ReadOnly = True ReadOnly = True
Size = 50 Size = 40
end end
object uqMapComplaintsADDRESS: TStringField object uqMapComplaintsdatedispatched: TDateTimeField
FieldName = 'ADDRESS' FieldName = 'datedispatched'
Size = 64 ReadOnly = True
end
object uqMapComplaintsdatearrived: TDateTimeField
FieldName = 'datearrived'
ReadOnly = True
end end
object uqMapComplaintspriorityKey: TStringField object uqMapComplaintspriorityBadge: TStringField
FieldKind = fkCalculated FieldKind = fkCalculated
FieldName = 'priorityKey' FieldName = 'priorityBadge'
Size = 80
Calculated = True
end
object uqMapComplaintscomplaintStatusKey: TStringField
FieldKind = fkCalculated
FieldName = 'complaintStatusKey'
Size = 80
Calculated = True Calculated = True
end end
object uqMapComplaintspngName: TStringField object uqMapComplaintspngName: TStringField
DisplayWidth = 100
FieldKind = fkCalculated FieldKind = fkCalculated
FieldName = 'pngName' FieldName = 'pngName'
Size = 100 Size = 80
Calculated = True Calculated = True
end end
object uqMapComplaintsBUSINESS: TStringField
FieldName = 'BUSINESS'
Size = 35
end
end end
object uqBadgeCounts: TUniQuery object uqBadgeCounts: TUniQuery
Connection = ucENTCAD Connection = ucENTCAD
...@@ -881,18 +964,18 @@ object ApiDatabaseModule: TApiDatabaseModule ...@@ -881,18 +964,18 @@ object ApiDatabaseModule: TApiDatabaseModule
' where ca.COMPLAINT is not null) as Complaints,' ' where ca.COMPLAINT is not null) as Complaints,'
'' ''
' (select count(distinct dua.UNITID)' ' (select count(distinct dua.UNITID)'
' from DIS_UNIT_ACTIVE dua) as Units' ' from DIS_UNIT_ACTIVE dua'
'from dual;' ' join AGENCY ag on ag.AGENCYID = dua.AGENCY'
'') ' where ag.AGENCYTYPE = '#39'POL'#39') as Units')
ReadOnly = True ReadOnly = True
Left = 198 Left = 200
Top = 190 Top = 192
object uqBadgeCountsCOMPLAINTS: TFloatField object uqBadgeCountscomplaints: TLargeintField
FieldName = 'COMPLAINTS' FieldName = 'complaints'
ReadOnly = True ReadOnly = True
end end
object uqBadgeCountsUNITS: TFloatField object uqBadgeCountsunits: TLargeintField
FieldName = 'UNITS' FieldName = 'units'
ReadOnly = True ReadOnly = True
end end
end end
...@@ -919,31 +1002,33 @@ object ApiDatabaseModule: TApiDatabaseModule ...@@ -919,31 +1002,33 @@ object ApiDatabaseModule: TApiDatabaseModule
ReadOnly = True ReadOnly = True
Left = 464 Left = 464
Top = 202 Top = 202
object uqMapComplaintUnitsListCOMPLAINTID: TFloatField object uqMapComplaintUnitsListcomplaintid: TFloatField
FieldName = 'COMPLAINTID' FieldName = 'complaintid'
Required = True
end end
object uqMapComplaintUnitsListUNITID: TFloatField object uqMapComplaintUnitsListunitid: TFloatField
FieldName = 'UNITID' FieldName = 'unitid'
Required = True
end end
object uqMapComplaintUnitsListUNITNAME: TStringField object uqMapComplaintUnitsListunitname: TStringField
FieldName = 'UNITNAME' FieldName = 'unitname'
Size = 10 Size = 10
end end
object uqMapComplaintUnitsListDATEDISPATCHED: TDateTimeField object uqMapComplaintUnitsListdatedispatched: TDateTimeField
FieldName = 'DATEDISPATCHED' FieldName = 'datedispatched'
end end
object uqMapComplaintUnitsListDATERESPONDED: TDateTimeField object uqMapComplaintUnitsListdateresponded: TDateTimeField
FieldName = 'DATERESPONDED' FieldName = 'dateresponded'
end end
object uqMapComplaintUnitsListDATEARRIVED: TDateTimeField object uqMapComplaintUnitsListdatearrived: TDateTimeField
FieldName = 'DATEARRIVED' FieldName = 'datearrived'
end end
object uqMapComplaintUnitsListDATECLEARED: TDateTimeField object uqMapComplaintUnitsListdatecleared: TDateTimeField
FieldName = 'DATECLEARED' FieldName = 'datecleared'
end end
object uqMapComplaintUnitsListLOCATION: TStringField object uqMapComplaintUnitsListlocation: TStringField
FieldName = 'LOCATION' FieldName = 'location'
Size = 30 Size = 100
end end
end end
object uqComplaintHistory: TUniQuery object uqComplaintHistory: TUniQuery
...@@ -993,28 +1078,29 @@ object ApiDatabaseModule: TApiDatabaseModule ...@@ -993,28 +1078,29 @@ object ApiDatabaseModule: TApiDatabaseModule
Name = 'COMPLAINTID' Name = 'COMPLAINTID'
Value = Null Value = Null
end> end>
object uqComplaintHistoryCOMPLAINTID: TFloatField object uqComplaintHistorycomplaintid: TFloatField
FieldName = 'COMPLAINTID' FieldName = 'complaintid'
Required = True Required = True
end end
object uqComplaintHistoryCOMPLAINT: TStringField object uqComplaintHistorycomplaint: TStringField
FieldName = 'COMPLAINT' FieldName = 'complaint'
Size = 10 Size = 10
end end
object uqComplaintHistoryAPARTMENT: TStringField object uqComplaintHistoryapartment: TStringField
FieldName = 'APARTMENT' FieldName = 'apartment'
Size = 6 Size = 6
end end
object uqComplaintHistoryDATEREPORTED: TDateTimeField object uqComplaintHistorydatereported: TDateTimeField
FieldName = 'DATEREPORTED' FieldName = 'datereported'
Required = True
end end
object uqComplaintHistoryDPRIORITY: TStringField object uqComplaintHistorydpriority: TStringField
FieldName = 'DPRIORITY' FieldName = 'dpriority'
ReadOnly = True ReadOnly = True
Size = 120 Size = 120
end end
object uqComplaintHistoryDCALLTYPE: TStringField object uqComplaintHistorydcalltype: TStringField
FieldName = 'DCALLTYPE' FieldName = 'dcalltype'
ReadOnly = True ReadOnly = True
Size = 60 Size = 60
end end
...@@ -1034,7 +1120,8 @@ object ApiDatabaseModule: TApiDatabaseModule ...@@ -1034,7 +1120,8 @@ object ApiDatabaseModule: TApiDatabaseModule
' c.remarks' ' c.remarks'
'from dis_contact c' 'from dis_contact c'
'join ctx on c.addressid = ctx.addressid' 'join ctx on c.addressid = ctx.addressid'
'left join cd_contact_type ct on c.contact_type = ct.code') 'left join cd_contacttype ct on c.contacttype = ct.code'
'order by c.notifyorder, c.name')
ReadOnly = True ReadOnly = True
Left = 330 Left = 330
Top = 130 Top = 130
...@@ -1044,75 +1131,47 @@ object ApiDatabaseModule: TApiDatabaseModule ...@@ -1044,75 +1131,47 @@ object ApiDatabaseModule: TApiDatabaseModule
Name = 'COMPLAINTID' Name = 'COMPLAINTID'
Value = nil Value = nil
end> end>
object uqComplaintContactsname: TStringField
FieldName = 'name'
Size = 30
end
object uqComplaintContactsphone: TStringField
FieldName = 'phone'
Size = 15
end
object uqComplaintContactsdcontacttype: TStringField
FieldName = 'dcontacttype'
ReadOnly = True
Size = 120
end
object uqComplaintContactsremarks: TStringField
FieldName = 'remarks'
Size = 200
end
end end
object uqComplaintWarnings: TUniQuery object uqComplaintWarnings: TUniQuery
Connection = ucENTCAD Connection = ucENTCAD
SQL.Strings = ( SQL.Strings = (
'with ctx as (' 'SELECT'
' select ca.agency, ca.addressid' ' wt.CODE_DESC,'
' from complaint_active ca' ' a.STRNUMBER,'
' where ca.complaintid = :COMPLAINTID' ' a.STRHNUMBER,'
'),' ' a.STRPREFIX,'
'sys as (' ' a.STRNAME,'
' select s.warningdistance' ' a.STRSUFFIX,'
' from system s' ' a.APARTMENT,'
' join ctx on s.agency = ctx.agency' ' a.CITY,'
'),' ' w.NOTES'
'adr as (' 'FROM COMPLAINT_ACTIVE ca'
' select a.xcoord, a.ycoord' 'JOIN DIS_WARNING w'
' from address a' ' ON w.ADDRESSID = ca.ADDRESSID'
' join ctx on a.addressid = ctx.addressid' 'JOIN CD_WARNINGTYPE wt'
')' ' ON w.CODE = wt.CODE'
'select' 'JOIN ADDRESS a'
' wt.code_desc,' ' ON w.ADDRESSID = a.ADDRESSID'
' trim(' 'WHERE ca.COMPLAINTID = :COMPLAINTID'
' nvl(a.strnumber, '#39#39') || '#39' '#39' ||' ' AND w.STATUS = '#39'1'#39
' nvl(a.strhnumber, '#39#39') || '#39' '#39' ||' 'ORDER BY wt.CODE_DESC')
' nvl(a.strprefix, '#39#39') || '#39' '#39' ||'
' nvl(a.strname, '#39#39') || '#39' '#39' ||'
' nvl(a.strsuffix, '#39#39') ||'
' case when a.apartment is not null then '#39' '#39' || a.apartment el' +
'se '#39#39' end ||'
' case when a.city is not null then '#39', '#39' || a.city else '#39#39' end'
' ) as address_text,'
' case'
' when w.addressid <> (select addressid from ctx) then '#39'[SECON' +
'DARY ADDRESS] - '#39' || nvl(w.notes, '#39#39')'
' else nvl(w.notes, '#39#39')'
' end as notes,'
' abs(sqrt(power(a.xcoord - (select xcoord from adr), 2) + power' +
'(a.ycoord - (select ycoord from adr), 2))) as distance'
'from dis_warning w'
'join cd_warningtype wt on w.code = wt.code'
'join address a on w.addressid = a.addressid'
'where w.status = 1'
'and ('
' a.addressid = (select addressid from ctx)'
' or ('
' (select xcoord from adr) > 0'
' and (select ycoord from adr) > 0'
' and nvl((select warningdistance from sys), 0) > 0'
' and a.xcoord between (select xcoord from adr) - (select warn' +
'ingdistance from sys)'
' and (select xcoord from adr) + (select warnin' +
'gdistance from sys)'
' and a.ycoord between (select ycoord from adr) - (select warn' +
'ingdistance from sys)'
' and (select ycoord from adr) + (select warnin' +
'gdistance from sys)'
' )'
')'
'order by'
' distance,'
' decode(w.code, '#39'POL'#39', 1, '#39'FIR'#39', 2, 3),'
' wt.code_desc')
ReadOnly = True ReadOnly = True
Left = 488 Left = 488
Top = 76 Top = 76
...@@ -1122,6 +1181,48 @@ object ApiDatabaseModule: TApiDatabaseModule ...@@ -1122,6 +1181,48 @@ object ApiDatabaseModule: TApiDatabaseModule
Name = 'COMPLAINTID' Name = 'COMPLAINTID'
Value = nil Value = nil
end> end>
object uqComplaintWarningscode_desc: TStringField
FieldName = 'code_desc'
ReadOnly = True
Size = 120
end
object uqComplaintWarningsstrnumber: TFloatField
FieldName = 'strnumber'
ReadOnly = True
end
object uqComplaintWarningsstrhnumber: TStringField
FieldName = 'strhnumber'
ReadOnly = True
Size = 1
end
object uqComplaintWarningsstrprefix: TStringField
FieldName = 'strprefix'
ReadOnly = True
Size = 1
end
object uqComplaintWarningsstrname: TStringField
FieldName = 'strname'
ReadOnly = True
end
object uqComplaintWarningsstrsuffix: TStringField
FieldName = 'strsuffix'
ReadOnly = True
Size = 2
end
object uqComplaintWarningsapartment: TStringField
FieldName = 'apartment'
ReadOnly = True
Size = 6
end
object uqComplaintWarningscity: TStringField
FieldName = 'city'
ReadOnly = True
end
object uqComplaintWarningsnotes: TStringField
FieldName = 'notes'
ReadOnly = True
Size = 1000
end
end end
object uqUnitDetails: TUniQuery object uqUnitDetails: TUniQuery
Connection = ucENTCAD Connection = ucENTCAD
...@@ -1140,82 +1241,82 @@ object ApiDatabaseModule: TApiDatabaseModule ...@@ -1140,82 +1241,82 @@ object ApiDatabaseModule: TApiDatabaseModule
' p2.PF_FNAME AS OFFICER2_FNAME,' ' p2.PF_FNAME AS OFFICER2_FNAME,'
' p2.PF_EMPNUM AS OFFICER2_EMPNUM,' ' p2.PF_EMPNUM AS OFFICER2_EMPNUM,'
' uc.UPDATETIME AS UPDATE_TIME' ' uc.UPDATETIME AS UPDATE_TIME'
'FROM DIS_UNITS_ACTIVE dua' 'FROM DIS_UNIT_ACTIVE dua'
'LEFT JOIN CD_UNIT_NUMBER cun ON cun.AGENCYCODE = dua.CARNUMBER' 'LEFT JOIN CD_UNIT_NUMBER cun ON cun.AGENCYCODE = dua.CARNUMBER'
'LEFT JOIN CD_DISTRICT cd ON cd.AGENCYCODE = dua.DISTRICT' 'LEFT JOIN CD_DISTRICT cd ON cd.AGENCYCODE = dua.DISTRICT'
'LEFT JOIN CFS_ACTIVE ca ON dua.UNITID = ca.UNITID' 'LEFT JOIN CFS_ACTIVE ca ON dua.UNITID = ca.UNITID'
'LEFT JOIN CD_UNITSTATUS cus ON ca.UNITSTATUS = cus.CODE' 'LEFT JOIN CD_UNITSTATUS cus ON ca.UNITSTATUS = cus.CODE'
'LEFT JOIN PERSONNEL p1 ON dua.OFFICER1ID = p1.PF_NAMEID' 'LEFT JOIN PERSONNEL p1 ON dua.OFFICER1ID = p1.PF_NAMEID'
'LEFT JOIN PERSONNEL p2 ON dua.OFFICER2ID = p2.PF_NAMEID' 'LEFT JOIN PERSONNEL p2 ON dua.OFFICER2ID = p2.PF_NAMEID'
'LEFT JOIN UNITS_CURRENT@AVL_LINK uc ON dua.UNITID = uc.UNITID' 'LEFT JOIN avl.UNITS_CURRENT uc ON dua.UNITID = uc.UNITID'
'WHERE dua.UNITID = :UNITID') 'WHERE dua.UNITID = :UNITID')
ReadOnly = True ReadOnly = True
Left = 194 Left = 198
Top = 312 Top = 304
ParamData = < ParamData = <
item item
DataType = ftUnknown DataType = ftUnknown
Name = 'UNITID' Name = 'UNITID'
Value = nil Value = nil
end> end>
object uqUnitDetailsUNITID: TFloatField object uqUnitDetailsunitid: TFloatField
FieldName = 'UNITID' FieldName = 'unitid'
end end
object uqUnitDetailsUNITNAME: TStringField object uqUnitDetailsunitname: TStringField
FieldName = 'UNITNAME' FieldName = 'unitname'
Size = 10 Size = 10
end end
object uqUnitDetailsCARNUMBER_DESC: TStringField object uqUnitDetailscarnumber_desc: TStringField
FieldName = 'CARNUMBER_DESC' FieldName = 'carnumber_desc'
ReadOnly = True ReadOnly = True
Size = 120 Size = 120
end end
object uqUnitDetailsDISTRICT_DESC: TStringField object uqUnitDetailsdistrict_desc: TStringField
FieldName = 'DISTRICT_DESC' FieldName = 'district_desc'
ReadOnly = True ReadOnly = True
Size = 120 Size = 120
end end
object uqUnitDetailsLOCATION: TStringField object uqUnitDetailslocation: TStringField
FieldName = 'LOCATION' FieldName = 'location'
ReadOnly = True ReadOnly = True
Size = 30 Size = 100
end end
object uqUnitDetailsUNIT_STATUS_DESC: TStringField object uqUnitDetailsunit_status_desc: TStringField
FieldName = 'UNIT_STATUS_DESC' FieldName = 'unit_status_desc'
ReadOnly = True ReadOnly = True
end end
object uqUnitDetailsOFFICER1_LNAME: TStringField object uqUnitDetailsofficer1_lname: TStringField
FieldName = 'OFFICER1_LNAME' FieldName = 'officer1_lname'
ReadOnly = True ReadOnly = True
Size = 45 Size = 15
end end
object uqUnitDetailsOFFICER1_FNAME: TStringField object uqUnitDetailsofficer1_fname: TStringField
FieldName = 'OFFICER1_FNAME' FieldName = 'officer1_fname'
ReadOnly = True ReadOnly = True
Size = 30 Size = 12
end end
object uqUnitDetailsOFFICER1_EMPNUM: TStringField object uqUnitDetailsofficer1_empnum: TStringField
FieldName = 'OFFICER1_EMPNUM' FieldName = 'officer1_empnum'
ReadOnly = True ReadOnly = True
Size = 10 Size = 10
end end
object uqUnitDetailsOFFICER2_LNAME: TStringField object uqUnitDetailsofficer2_lname: TStringField
FieldName = 'OFFICER2_LNAME' FieldName = 'officer2_lname'
ReadOnly = True ReadOnly = True
Size = 45 Size = 15
end end
object uqUnitDetailsOFFICER2_FNAME: TStringField object uqUnitDetailsofficer2_fname: TStringField
FieldName = 'OFFICER2_FNAME' FieldName = 'officer2_fname'
ReadOnly = True ReadOnly = True
Size = 30 Size = 12
end end
object uqUnitDetailsOFFICER2_EMPNUM: TStringField object uqUnitDetailsofficer2_empnum: TStringField
FieldName = 'OFFICER2_EMPNUM' FieldName = 'officer2_empnum'
ReadOnly = True ReadOnly = True
Size = 10 Size = 10
end end
object uqUnitDetailsUPDATE_TIME: TDateTimeField object uqUnitDetailsupdate_time: TDateTimeField
FieldName = 'UPDATE_TIME' FieldName = 'update_time'
ReadOnly = True ReadOnly = True
end end
end end
...@@ -1223,31 +1324,35 @@ object ApiDatabaseModule: TApiDatabaseModule ...@@ -1223,31 +1324,35 @@ object ApiDatabaseModule: TApiDatabaseModule
Connection = ucENTCAD Connection = ucENTCAD
SQL.Strings = ( SQL.Strings = (
'select' 'select'
' dul."TIMESTAMP" as LOG_TIME,' ' dul.timestamp as LOG_TIME,'
' dul.COMPLAINT as COMPLAINT_NUM,' ' dul.complaint as COMPLAINT_NUM,'
' dul.LOG as LOG_TEXT' ' dul.log as LOG_TEXT'
'from ENTCAD.DIS_UNIT_LOG dul' 'from DIS_UNIT_LOG dul'
'where dul.UNITID = :UNITID' 'where dul.unitid = :UNITID'
'order by dul."TIMESTAMP" desc') 'order by dul.timestamp desc')
ReadOnly = True ReadOnly = True
Left = 190 Left = 198
Top = 374 Top = 358
ParamData = < ParamData = <
item item
DataType = ftUnknown DataType = ftUnknown
Name = 'UNITID' Name = 'UNITID'
Value = nil Value = nil
end> end>
object uqUnitLogsLOG_TIME: TDateTimeField object uqUnitLogslog_time: TDateTimeField
FieldName = 'LOG_TIME' FieldName = 'log_time'
end end
object uqUnitLogsCOMPLAINT_NUM: TStringField object uqUnitLogscomplaint_num: TStringField
FieldName = 'COMPLAINT_NUM' FieldName = 'complaint_num'
Size = 10 Size = 10
end end
object uqUnitLogsLOG_TEXT: TStringField object uqUnitLogslog_text: TStringField
FieldName = 'LOG_TEXT' FieldName = 'log_text'
Size = 2000 Size = 2000
end end
end end
object PostgreSQLUniProvider1: TPostgreSQLUniProvider
Left = 162
Top = 42
end
end end
...@@ -5,11 +5,10 @@ interface ...@@ -5,11 +5,10 @@ interface
uses uses
System.SysUtils, System.Classes, Data.DB, MemDS, DBAccess, Uni, UniProvider, System.SysUtils, System.Classes, Data.DB, MemDS, DBAccess, Uni, UniProvider,
PostgreSQLUniProvider, System.Variants, System.Generics.Collections, System.IniFiles, PostgreSQLUniProvider, System.Variants, System.Generics.Collections, System.IniFiles,
Common.Logging, Vcl.Forms, OracleUniProvider, System.Character; Common.Logging, Vcl.Forms, System.Character, Common.Ini;
type type
TApiDatabaseModule = class(TDataModule) TApiDatabaseModule = class(TDataModule)
OracleUniProvider1: TOracleUniProvider;
uqMapUnits: TUniQuery; uqMapUnits: TUniQuery;
uqUnitList: TUniQuery; uqUnitList: TUniQuery;
uqComplaintUnits: TUniQuery; uqComplaintUnits: TUniQuery;
...@@ -17,174 +16,198 @@ type ...@@ -17,174 +16,198 @@ type
uqComplaintList: TUniQuery; uqComplaintList: TUniQuery;
uqComplaintDetails: TUniQuery; uqComplaintDetails: TUniQuery;
ucENTCAD: TUniConnection; ucENTCAD: TUniConnection;
uqComplaintListCOMPLAINTID: TFloatField;
uqComplaintListCFSID: TFloatField;
uqComplaintListCOMPLAINT: TStringField;
uqComplaintListAGENCY: TStringField;
uqComplaintListDISPATCH_CODE_DESC: TStringField;
uqComplaintListSOURCE: TStringField;
uqComplaintListSOURCE_DESC: TStringField;
uqComplaintListPRIORITY: TStringField;
uqComplaintListADDRESSID: TFloatField;
uqComplaintListADDRESS: TStringField;
uqComplaintListAPARTMENT: TStringField;
uqComplaintListCITY: TStringField;
uqComplaintListBUSINESS: TStringField;
uqComplaintListDISPATCHDISTRICT: TStringField;
uqComplaintListDISPATCHSECTOR: TStringField;
uqComplaintListADDRESSDISTRICT: TStringField;
uqComplaintListADDRESSSECTOR: TStringField;
uqComplaintListXCOORD: TFloatField;
uqComplaintListYCOORD: TFloatField;
uqComplaintListWARNINGS: TFloatField;
uqComplaintListCONTACTS: TFloatField;
uqComplaintListHISTORY: TFloatField;
uqComplaintListDATEREPORTED: TDateTimeField;
uqComplaintListDATERECEIVED: TDateTimeField;
uqComplaintListDATEDISPATCHED: TDateTimeField;
uqComplaintListDATERESPONDED: TDateTimeField;
uqComplaintListDATEARRIVED: TDateTimeField;
uqComplaintListDATECLEARED: TDateTimeField;
uqComplaintUnitsCOMPLAINTID: TFloatField;
uqComplaintUnitsUNITID: TFloatField;
uqComplaintUnitsUNITNAME: TStringField;
uqComplaintUnitsDATEDISPATCHED: TDateTimeField;
uqComplaintUnitsDATERESPONDED: TDateTimeField;
uqComplaintUnitsDATEARRIVED: TDateTimeField;
uqComplaintUnitsDATECLEARED: TDateTimeField;
uqComplaintUnitsLOCATION: TStringField;
uqCFSMemosMEMO_ID: TFloatField;
uqCFSMemosCFSID: TFloatField;
uqCFSMemosMEMO_TYPE: TFloatField;
uqCFSMemosTIMESTAMP: TDateTimeField;
uqCFSMemosBADGE_NUMBER: TStringField;
uqCFSMemosREMARKS: TStringField;
uqComplaintListcomplaintNumber: TStringField;
uqComplaintListPRIORITY_COLOR: TFloatField;
uqComplaintListDISTRICT_DESC: TStringField;
uqComplaintListSECTOR_DESC: TStringField;
uqUnitListUNITID: TFloatField;
uqUnitListUNITNAME: TStringField;
uqUnitListCARNUMBER_DESC: TStringField;
uqUnitListDISTRICT_DESC: TStringField;
uqUnitListSECTOR_DESC: TStringField;
uqUnitListOFFICER1_EMPNUM: TStringField;
uqUnitListOFFICER1_LAST_NAME: TStringField;
uqUnitListOFFICER1_FIRST_NAME: TStringField;
uqUnitListOFFICER1_MI: TStringField;
uqUnitListOFFICER2_EMPNUM: TStringField;
uqUnitListOFFICER2_LAST_NAME: TStringField;
uqUnitListOFFICER2_FIRST_NAME: TStringField;
uqUnitListOFFICER2_MI: TStringField;
uqUnitListLOCATION: TStringField;
uqUnitListCOMPLAINT: TStringField;
uqUnitListUNITSTATUS: TFloatField;
uqUnitListUNIT_STATUS_DESC: TStringField;
uqUnitListENTRYID: TFloatField;
uqUnitListGPS_LATITUDE: TFloatField;
uqUnitListGPS_LONGITUDE: TFloatField;
uqUnitListCALL_TYPE: TStringField;
uqMapComplaints: TUniQuery; uqMapComplaints: TUniQuery;
uqMapComplaintsCOMPLAINTID: TFloatField;
uqMapComplaintsDISPATCHDISTRICT: TStringField;
uqMapComplaintsLNG: TFloatField;
uqMapComplaintsLAT: TFloatField;
uqMapComplaintsDISPATCH_CODE_DESC: TStringField;
uqMapComplaintsPRIORITY: TStringField;
uqMapComplaintsDISPATCHCODECATEGORY: TStringField;
uqMapComplaintspriorityKey: TStringField;
uqMapComplaintspngName: TStringField;
uqBadgeCounts: TUniQuery; uqBadgeCounts: TUniQuery;
uqBadgeCountsCOMPLAINTS: TFloatField;
uqBadgeCountsUNITS: TFloatField;
uqComplaintDetailsCOMPLAINTID: TFloatField;
uqComplaintDetailsCFSID: TFloatField;
uqComplaintDetailsCOMPLAINT: TStringField;
uqComplaintDetailsPRIORITY: TStringField;
uqComplaintDetailsDISPATCHCODE: TStringField;
uqComplaintDetailsDISPATCH_CODE_DESC: TStringField;
uqComplaintDetailsDISPATCHDISTRICT: TStringField;
uqComplaintDetailsADDRESS: TStringField;
uqComplaintDetailsDATEREPORTED: TDateTimeField;
uqComplaintDetailsDATERECEIVED: TDateTimeField;
uqComplaintDetailsDATEDISPATCHED: TDateTimeField;
uqComplaintDetailsDATERESPONDED: TDateTimeField;
uqComplaintDetailsDATEARRIVED: TDateTimeField;
uqComplaintDetailsDATECLEARED: TDateTimeField;
uqMapComplaintUnitsList: TUniQuery; uqMapComplaintUnitsList: TUniQuery;
uqMapComplaintUnitsListCOMPLAINTID: TFloatField;
uqMapComplaintUnitsListUNITID: TFloatField;
uqMapComplaintUnitsListUNITNAME: TStringField;
uqMapComplaintUnitsListDATEDISPATCHED: TDateTimeField;
uqMapComplaintUnitsListDATERESPONDED: TDateTimeField;
uqMapComplaintUnitsListDATEARRIVED: TDateTimeField;
uqMapComplaintUnitsListDATECLEARED: TDateTimeField;
uqMapComplaintUnitsListLOCATION: TStringField;
uqMapComplaintsADDRESS: TStringField;
uqMapUnitsENTRYID: TFloatField;
uqMapUnitsUNITID: TFloatField;
uqMapUnitsUNITNAME: TStringField;
uqMapUnitsUNIT_DISTRICT: TStringField;
uqMapUnitsGPS_LATITUDE: TFloatField;
uqMapUnitsGPS_LONGITUDE: TFloatField;
uqMapUnitsCALL_TYPE: TStringField;
uqMapUnitsPRIORITY: TStringField;
uqMapUnitsUNIT_STATUS_DESC: TStringField;
uqComplaintDetailsHISTORY: TFloatField;
uqComplaintDetailsCONTACTS: TFloatField;
uqComplaintDetailsWARNINGS: TFloatField;
uqComplaintDetailsADDRESSID: TFloatField;
uqComplaintDetailsAGENCY: TStringField;
uqComplaintDetailsSTRNUMBER: TFloatField;
uqComplaintDetailsSTRHNUMBER: TStringField;
uqComplaintDetailsSTRPREFIX: TStringField;
uqComplaintDetailsSTRNAME: TStringField;
uqComplaintDetailsSTRSUFFIX: TStringField;
uqComplaintDetailsCITY: TStringField;
uqComplaintHistory: TUniQuery; uqComplaintHistory: TUniQuery;
uqComplaintContacts: TUniQuery; uqComplaintContacts: TUniQuery;
uqComplaintWarnings: TUniQuery; uqComplaintWarnings: TUniQuery;
uqMapUnitsUPDATE_TIME: TDateTimeField;
uqMapUnitsOFFICER1_LNAME: TStringField;
uqMapUnitsOFFICER1_FNAME: TStringField;
uqMapUnitsOFFICER1_EMPNUM: TStringField;
uqMapUnitsOFFICER2_LNAME: TStringField;
uqMapUnitsOFFICER2_FNAME: TStringField;
uqMapUnitsOFFICER2_EMPNUM: TStringField;
uqUnitDetails: TUniQuery; uqUnitDetails: TUniQuery;
uqUnitDetailsUNITID: TFloatField;
uqUnitDetailsUNITNAME: TStringField;
uqUnitDetailsCARNUMBER_DESC: TStringField;
uqUnitDetailsDISTRICT_DESC: TStringField;
uqUnitDetailsLOCATION: TStringField;
uqUnitDetailsUNIT_STATUS_DESC: TStringField;
uqUnitDetailsOFFICER1_LNAME: TStringField;
uqUnitDetailsOFFICER1_FNAME: TStringField;
uqUnitDetailsOFFICER1_EMPNUM: TStringField;
uqUnitDetailsOFFICER2_LNAME: TStringField;
uqUnitDetailsOFFICER2_FNAME: TStringField;
uqUnitDetailsOFFICER2_EMPNUM: TStringField;
uqUnitDetailsUPDATE_TIME: TDateTimeField;
uqUnitLogs: TUniQuery; uqUnitLogs: TUniQuery;
uqUnitLogsLOG_TIME: TDateTimeField; PostgreSQLUniProvider1: TPostgreSQLUniProvider;
uqUnitLogsCOMPLAINT_NUM: TStringField; uqComplaintListcomplaintid: TFloatField;
uqUnitLogsLOG_TEXT: TStringField; uqComplaintListcfsid: TFloatField;
uqMapUnitsDIS_UNITID: TFloatField; uqComplaintListcomplaint: TStringField;
uqMapComplaintsBUSINESS: TStringField; uqComplaintListagency: TStringField;
uqComplaintDetailsBUSINESS: TStringField; uqComplaintListdispatch_code_desc: TStringField;
uqComplaintDetailsSTATUS: TFloatField; uqComplaintListsource: TStringField;
uqComplaintHistoryCOMPLAINTID: TFloatField; uqComplaintListsource_desc: TStringField;
uqComplaintHistoryCOMPLAINT: TStringField; uqComplaintListpriority: TStringField;
uqComplaintHistoryAPARTMENT: TStringField; uqComplaintListaddressid: TFloatField;
uqComplaintHistoryDATEREPORTED: TDateTimeField; uqComplaintListaddress: TStringField;
uqComplaintHistoryDPRIORITY: TStringField; uqComplaintListapartment: TStringField;
uqComplaintHistoryDCALLTYPE: TStringField; uqComplaintListcity: TStringField;
uqComplaintListbusiness: TStringField;
uqComplaintListdispatchdistrict: TStringField;
uqComplaintListdispatchsector: TStringField;
uqComplaintListaddressdistrict: TStringField;
uqComplaintListaddresssector: TStringField;
uqComplaintListxcoord: TFloatField;
uqComplaintListycoord: TFloatField;
uqComplaintListwarnings: TFloatField;
uqComplaintListcontacts: TFloatField;
uqComplaintListhistory: TFloatField;
uqComplaintListdatereported: TDateTimeField;
uqComplaintListdatereceived: TDateTimeField;
uqComplaintListdatedispatched: TDateTimeField;
uqComplaintListdateresponded: TDateTimeField;
uqComplaintListdatearrived: TDateTimeField;
uqComplaintListdatecleared: TDateTimeField;
uqComplaintListpriority_color: TFloatField;
uqComplaintListdistrict_desc: TStringField;
uqComplaintListsector_desc: TStringField;
uqComplaintListcomplaintNumber: TStringField;
uqComplaintDetailscomplaintid: TFloatField;
uqComplaintDetailscfsid: TFloatField;
uqComplaintDetailscomplaint: TStringField;
uqComplaintDetailspriority: TStringField;
uqComplaintDetailsdispatchcode: TStringField;
uqComplaintDetailsdispatch_code_desc: TStringField;
uqComplaintDetailsdispatchdistrict: TStringField;
uqComplaintDetailsaddress: TStringField;
uqComplaintDetailsbusiness: TStringField;
uqComplaintDetailsdatereported: TDateTimeField;
uqComplaintDetailsdatereceived: TDateTimeField;
uqComplaintDetailsdatedispatched: TDateTimeField;
uqComplaintDetailsdateresponded: TDateTimeField;
uqComplaintDetailsdatearrived: TDateTimeField;
uqComplaintDetailsdatecleared: TDateTimeField;
uqComplaintDetailshistory: TFloatField;
uqComplaintDetailscontacts: TFloatField;
uqComplaintDetailswarnings: TFloatField;
uqComplaintDetailsaddressid: TFloatField;
uqComplaintDetailsagency: TStringField;
uqComplaintDetailsstrnumber: TFloatField;
uqComplaintDetailsstrhnumber: TStringField;
uqComplaintDetailsstrprefix: TStringField;
uqComplaintDetailsstrname: TStringField;
uqComplaintDetailsstrsuffix: TStringField;
uqComplaintDetailscity: TStringField;
uqComplaintDetailsstatus: TIntegerField;
uqCFSMemosmemo_id: TFloatField;
uqCFSMemoscfsid: TFloatField;
uqCFSMemosmemo_type: TFloatField;
uqCFSMemostimestamp: TDateTimeField;
uqCFSMemosbadge_number: TStringField;
uqCFSMemosremarks: TStringField;
uqUnitDetailsunitid: TFloatField;
uqUnitDetailsunitname: TStringField;
uqUnitDetailscarnumber_desc: TStringField;
uqUnitDetailsdistrict_desc: TStringField;
uqUnitDetailslocation: TStringField;
uqUnitDetailsunit_status_desc: TStringField;
uqUnitDetailsofficer1_lname: TStringField;
uqUnitDetailsofficer1_fname: TStringField;
uqUnitDetailsofficer1_empnum: TStringField;
uqUnitDetailsofficer2_lname: TStringField;
uqUnitDetailsofficer2_fname: TStringField;
uqUnitDetailsofficer2_empnum: TStringField;
uqUnitDetailsupdate_time: TDateTimeField;
uqUnitLogslog_time: TDateTimeField;
uqUnitLogscomplaint_num: TStringField;
uqUnitLogslog_text: TStringField;
uqComplaintHistorycomplaintid: TFloatField;
uqComplaintHistorycomplaint: TStringField;
uqComplaintHistoryapartment: TStringField;
uqComplaintHistorydatereported: TDateTimeField;
uqComplaintHistorydpriority: TStringField;
uqComplaintHistorydcalltype: TStringField;
uqComplaintContactsname: TStringField;
uqComplaintContactsphone: TStringField;
uqComplaintContactsdcontacttype: TStringField;
uqComplaintContactsremarks: TStringField;
uqComplaintWarningscode_desc: TStringField;
uqComplaintWarningsstrnumber: TFloatField;
uqComplaintWarningsstrhnumber: TStringField;
uqComplaintWarningsstrprefix: TStringField;
uqComplaintWarningsstrname: TStringField;
uqComplaintWarningsstrsuffix: TStringField;
uqComplaintWarningsapartment: TStringField;
uqComplaintWarningscity: TStringField;
uqComplaintWarningsnotes: TStringField;
uqMapComplaintUnitsListcomplaintid: TFloatField;
uqMapComplaintUnitsListunitid: TFloatField;
uqMapComplaintUnitsListunitname: TStringField;
uqMapComplaintUnitsListdatedispatched: TDateTimeField;
uqMapComplaintUnitsListdateresponded: TDateTimeField;
uqMapComplaintUnitsListdatearrived: TDateTimeField;
uqMapComplaintUnitsListdatecleared: TDateTimeField;
uqMapComplaintUnitsListlocation: TStringField;
uqMapComplaintscomplaintid: TFloatField;
uqMapComplaintsdispatchdistrict: TStringField;
uqMapComplaintspriority: TStringField;
uqMapComplaintsbusiness: TStringField;
uqMapComplaintsdispatchcodecategory: TStringField;
uqMapComplaintslng: TFloatField;
uqMapComplaintslat: TFloatField;
uqMapComplaintsdispatch_code_desc: TStringField;
uqMapComplaintsaddress: TStringField;
uqComplaintUnitscomplaintid: TFloatField;
uqComplaintUnitsunitid: TFloatField;
uqComplaintUnitsunitname: TStringField;
uqComplaintUnitsdatedispatched: TDateTimeField;
uqComplaintUnitsdateresponded: TDateTimeField;
uqComplaintUnitsdatearrived: TDateTimeField;
uqComplaintUnitsdatecleared: TDateTimeField;
uqComplaintUnitslocation: TStringField;
uqUnitListagency: TStringField;
uqUnitListagency_name: TStringField;
uqUnitListunitid: TFloatField;
uqUnitListunitname: TStringField;
uqUnitListcarnumber_desc: TStringField;
uqUnitListdistrict_desc: TStringField;
uqUnitListsector_desc: TStringField;
uqUnitListofficer1_empnum: TStringField;
uqUnitListofficer1_last_name: TStringField;
uqUnitListofficer1_first_name: TStringField;
uqUnitListofficer1_mi: TStringField;
uqUnitListofficer2_empnum: TStringField;
uqUnitListofficer2_last_name: TStringField;
uqUnitListofficer2_first_name: TStringField;
uqUnitListofficer2_mi: TStringField;
uqUnitListlocation: TStringField;
uqUnitListcomplaint: TStringField;
uqUnitListunitstatus: TFloatField;
uqUnitListunit_status_desc: TStringField;
uqUnitListcall_type: TStringField;
uqUnitListentryid: TFloatField;
uqUnitListgps_latitude: TFloatField;
uqUnitListgps_longitude: TFloatField;
uqBadgeCountscomplaints: TLargeintField;
uqBadgeCountsunits: TLargeintField;
uqMapUnitsentryid: TFloatField;
uqMapUnitsunitid: TFloatField;
uqMapUnitsunitname: TMemoField;
uqMapUnitsagency: TStringField;
uqMapUnitsagency_name: TStringField;
uqMapUnitsgps_latitude: TFloatField;
uqMapUnitsgps_longitude: TFloatField;
uqMapUnitscall_type: TStringField;
uqMapUnitspriority: TStringField;
uqMapUnitsunit_status_desc: TStringField;
uqMapUnitsupdate_time: TDateTimeField;
uqMapUnitsdis_unitid: TFloatField;
uqMapUnitsofficer1_lname: TStringField;
uqMapUnitsofficer1_fname: TStringField;
uqMapUnitsofficer1_empnum: TStringField;
uqMapUnitsofficer2_lname: TStringField;
uqMapUnitsofficer2_fname: TStringField;
uqMapUnitsofficer2_empnum: TStringField;
uqMapComplaintsagency: TStringField;
uqMapComplaintsagency_name: TStringField;
uqMapComplaintsdatedispatched: TDateTimeField;
uqMapComplaintsdatearrived: TDateTimeField;
uqMapComplaintspriorityBadge: TStringField;
uqMapComplaintscomplaintStatusKey: TStringField;
uqMapComplaintspngName: TStringField;
uqMapUnitsagencytype: TStringField;
uqUnitListagencytype: TStringField;
procedure uqComplaintListCalcFields(DataSet: TDataSet); procedure uqComplaintListCalcFields(DataSet: TDataSet);
procedure uqMapComplaintsCalcFields(DataSet: TDataSet); procedure uqMapComplaintsCalcFields(DataSet: TDataSet);
procedure DataModuleCreate(Sender: TObject);
private private
{ Private declarations } { Private declarations }
public public
function DerivePriorityKeyFromPriorityString(const priorityString: string): string;
function HandleUniqueFilenames(const category: string): string; function HandleUniqueFilenames(const category: string): string;
function BadgeCounts(const BaseQuery: TUniQuery): Integer; function BadgeCounts(const BaseQuery: TUniQuery): Integer;
end; end;
...@@ -198,6 +221,24 @@ implementation ...@@ -198,6 +221,24 @@ implementation
{$R *.dfm} {$R *.dfm}
procedure TApiDatabaseModule.DataModuleCreate(Sender: TObject);
begin
ucENTCAD.ProviderName := 'PostgreSQL';
ucENTCAD.Server := IniEntries.DatabaseServer;
ucENTCAD.Port := IniEntries.DatabasePort;
ucENTCAD.Database := IniEntries.DatabaseName;
ucENTCAD.Username := IniEntries.DatabaseUsername;
ucENTCAD.Password := IniEntries.DatabasePassword;
ucENTCAD.LoginPrompt := False;
if not ucENTCAD.Connected then
ucENTCAD.Connect;
ucENTCAD.ExecSQL('set search_path to lems, avl, entcad, public');
Logger.Log(2, 'PostgreSQL API search_path set to lems, avl, entcad, public');
end;
procedure TApiDatabaseModule.uqComplaintListCalcFields(DataSet: TDataSet); procedure TApiDatabaseModule.uqComplaintListCalcFields(DataSet: TDataSet);
var var
raw: string; raw: string;
...@@ -217,21 +258,32 @@ procedure TApiDatabaseModule.uqMapComplaintsCalcFields(DataSet: TDataSet); ...@@ -217,21 +258,32 @@ procedure TApiDatabaseModule.uqMapComplaintsCalcFields(DataSet: TDataSet);
var var
rawCategory: string; rawCategory: string;
rawPriority: string; rawPriority: string;
derivedPriorityKey: string; statusKey: string;
computedPngName: string; priorityBadge: string;
begin begin
rawCategory := DataSet.FieldByName('DISPATCHCODECATEGORY').AsString; rawCategory := DataSet.FieldByName('DISPATCHCODECATEGORY').AsString;
rawPriority := DataSet.FieldByName('PRIORITY').AsString; rawPriority := Trim(DataSet.FieldByName('PRIORITY').AsString);
derivedPriorityKey := DerivePriorityKeyFromPriorityString(rawPriority); if not DataSet.FieldByName('DATEARRIVED').IsNull then
DataSet.FieldByName('priorityKey').AsString := derivedPriorityKey; statusKey := 'onscene'
else if not DataSet.FieldByName('DATEDISPATCHED').IsNull then
statusKey := 'attached'
else
statusKey := 'notattached';
if Trim(rawCategory) = '' then if rawPriority = '' then
computedPngName := Format('default_%s.png', [derivedPriorityKey]) priorityBadge := '?'
else else
computedPngName := Format('%s_%s.png', [HandleUniqueFilenames(rawCategory), derivedPriorityKey]); priorityBadge := UpperCase(Copy(rawPriority, 1, 1));
DataSet.FieldByName('complaintStatusKey').AsString := statusKey;
DataSet.FieldByName('priorityBadge').AsString := priorityBadge;
DataSet.FieldByName('pngName').AsString := computedPngName; if Trim(rawCategory) = '' then
DataSet.FieldByName('pngName').AsString := Format('default_%s.png', [statusKey])
else
DataSet.FieldByName('pngName').AsString :=
Format('%s_%s.png', [HandleUniqueFilenames(rawCategory), statusKey]);
end; end;
function TApiDatabaseModule.HandleUniqueFilenames(const category: string): string; function TApiDatabaseModule.HandleUniqueFilenames(const category: string): string;
...@@ -253,21 +305,6 @@ begin ...@@ -253,21 +305,6 @@ begin
Result := resultBuilder; Result := resultBuilder;
end; end;
function TApiDatabaseModule.DerivePriorityKeyFromPriorityString(const priorityString: string): string;
var
firstChar: Char;
begin
if priorityString <> '' then
begin
firstChar := priorityString[1]; // handles "3J", "3P", etc.
if (firstChar >= '1') and (firstChar <= '4') then
Exit(string(firstChar));
if (firstChar >= '5') and (firstChar <= '9') then
Exit('5-9');
end;
Result := '5-9';
end;
function TApiDatabaseModule.BadgeCounts(const BaseQuery: TUniQuery): Integer; function TApiDatabaseModule.BadgeCounts(const BaseQuery: TUniQuery): Integer;
var var
......
...@@ -16,7 +16,6 @@ type ...@@ -16,7 +16,6 @@ type
private private
procedure AfterConstruction; override; procedure AfterConstruction; override;
procedure BeforeDestruction; override; procedure BeforeDestruction; override;
function GetComplaintMemos(const CfsId: string): TJSONObject;
public public
function GetBadgeCounts: TJSONObject; function GetBadgeCounts: TJSONObject;
function GetComplaintList: TJSONObject; function GetComplaintList: TJSONObject;
...@@ -30,6 +29,7 @@ type ...@@ -30,6 +29,7 @@ type
function GetComplaintWarnings(const ComplaintId: string): TJSONObject; function GetComplaintWarnings(const ComplaintId: string): TJSONObject;
function GetUnitDetails(const UnitId: string): TJSONObject; function GetUnitDetails(const UnitId: string): TJSONObject;
function GetUnitLogs(const UnitId: string): TJSONObject; function GetUnitLogs(const UnitId: string): TJSONObject;
function GetComplaintMemos(const CfsId: string): TJSONObject;
end; end;
implementation implementation
...@@ -73,7 +73,7 @@ begin ...@@ -73,7 +73,7 @@ begin
except except
on E: Exception do on E: Exception do
begin begin
Logger.Log(2, '---TApiService.GetBadgeCounts End (error): ' + E.Message); Logger.Log(2, '---TApiService.GetBadgeCounts error: ' + E.ClassName + ': ' + E.Message);
raise EXDataHttpException.Create(500, 'Failed to load badge counts'); raise EXDataHttpException.Create(500, 'Failed to load badge counts');
end; end;
end; end;
...@@ -86,106 +86,134 @@ var ...@@ -86,106 +86,134 @@ var
data: TJSONArray; data: TJSONArray;
emitted: Integer; emitted: Integer;
item: TJSONObject; item: TJSONObject;
UnitsByComplaintMap: TObjectDictionary<string,TJSONArray>; UnitsByComplaintMap: TObjectDictionary<string, TJSONArray>;
complaintId: string; complaintId: string;
unitArray: TJSONArray; unitArray: TJSONArray;
unitStatus: string; unitStatus: string;
latestUpdate: TDateTime; latestUpdate: TDateTime;
unitObj: TJSONObject; unitObj: TJSONObject;
begin begin
Logger.Log(3,'---TApiService.GetComplaintMap initiated'); Logger.Log(3, '---TApiService.GetComplaintMap initiated');
Result:=TJSONObject.Create; Result := TJSONObject.Create;
TXDataOperationContext.Current.Handler.ManagedObjects.Add(Result); TXDataOperationContext.Current.Handler.ManagedObjects.Add(Result);
data:=TJSONArray.Create; data := TJSONArray.Create;
UnitsByComplaintMap:=TObjectDictionary<string,TJSONArray>.Create([doOwnsValues]); Result.AddPair('data', data);
UnitsByComplaintMap := TObjectDictionary<string, TJSONArray>.Create([doOwnsValues]);
try
try try
ApiDB.uqMapComplaintUnitsList.Close;
ApiDB.uqMapComplaintUnitsList.Open; ApiDB.uqMapComplaintUnitsList.Open;
try
while not ApiDB.uqMapComplaintUnitsList.Eof do while not ApiDB.uqMapComplaintUnitsList.Eof do
begin begin
complaintId:=ApiDB.uqMapComplaintUnitsListCOMPLAINTID.AsString; complaintId := ApiDB.uqMapComplaintUnitsListCOMPLAINTID.AsString;
if not UnitsByComplaintMap.TryGetValue(complaintId,unitArray) then
if not UnitsByComplaintMap.TryGetValue(complaintId, unitArray) then
begin begin
unitArray:=TJSONArray.Create; unitArray := TJSONArray.Create;
UnitsByComplaintMap.Add(complaintId,unitArray); UnitsByComplaintMap.Add(complaintId, unitArray);
end; end;
unitStatus:='Dispatched'; unitStatus := 'Dispatched';
if not ApiDB.uqMapComplaintUnitsListDATECLEARED.IsNull then if not ApiDB.uqMapComplaintUnitsListDATECLEARED.IsNull then
unitStatus:='Cleared' unitStatus := 'Cleared'
else if not ApiDB.uqMapComplaintUnitsListDATEARRIVED.IsNull then else if not ApiDB.uqMapComplaintUnitsListDATEARRIVED.IsNull then
unitStatus:='On Scene' unitStatus := 'On Scene'
else if not ApiDB.uqMapComplaintUnitsListDATERESPONDED.IsNull then else if not ApiDB.uqMapComplaintUnitsListDATERESPONDED.IsNull then
unitStatus:='Enroute'; unitStatus := 'Enroute';
latestUpdate := 0;
latestUpdate:=0;
if not ApiDB.uqMapComplaintUnitsListDATEDISPATCHED.IsNull then if not ApiDB.uqMapComplaintUnitsListDATEDISPATCHED.IsNull then
latestUpdate:=ApiDB.uqMapComplaintUnitsListDATEDISPATCHED.AsDateTime; latestUpdate := ApiDB.uqMapComplaintUnitsListDATEDISPATCHED.AsDateTime;
if (not ApiDB.uqMapComplaintUnitsListDATERESPONDED.IsNull) and if (not ApiDB.uqMapComplaintUnitsListDATERESPONDED.IsNull) and
(ApiDB.uqMapComplaintUnitsListDATERESPONDED.AsDateTime>latestUpdate) then (ApiDB.uqMapComplaintUnitsListDATERESPONDED.AsDateTime > latestUpdate) then
latestUpdate:=ApiDB.uqMapComplaintUnitsListDATERESPONDED.AsDateTime; latestUpdate := ApiDB.uqMapComplaintUnitsListDATERESPONDED.AsDateTime;
if (not ApiDB.uqMapComplaintUnitsListDATEARRIVED.IsNull) and if (not ApiDB.uqMapComplaintUnitsListDATEARRIVED.IsNull) and
(ApiDB.uqMapComplaintUnitsListDATEARRIVED.AsDateTime>latestUpdate) then (ApiDB.uqMapComplaintUnitsListDATEARRIVED.AsDateTime > latestUpdate) then
latestUpdate:=ApiDB.uqMapComplaintUnitsListDATEARRIVED.AsDateTime; latestUpdate := ApiDB.uqMapComplaintUnitsListDATEARRIVED.AsDateTime;
if (not ApiDB.uqMapComplaintUnitsListDATECLEARED.IsNull) and if (not ApiDB.uqMapComplaintUnitsListDATECLEARED.IsNull) and
(ApiDB.uqMapComplaintUnitsListDATECLEARED.AsDateTime>latestUpdate) then (ApiDB.uqMapComplaintUnitsListDATECLEARED.AsDateTime > latestUpdate) then
latestUpdate:=ApiDB.uqMapComplaintUnitsListDATECLEARED.AsDateTime; latestUpdate := ApiDB.uqMapComplaintUnitsListDATECLEARED.AsDateTime;
unitObj:=TJSONObject.Create;
unitObj.AddPair('Unit',ApiDB.uqMapComplaintUnitsListUNITNAME.AsString);
unitObj.AddPair('Status',unitStatus);
if latestUpdate<>0 then
unitObj.AddPair('Updated',FormatDateTime('yyyy-mm-dd hh:nn:ss',latestUpdate))
else
unitObj.AddPair('Updated','');
unitObj := TJSONObject.Create;
unitArray.AddElement(unitObj); unitArray.AddElement(unitObj);
unitObj.AddPair('Unit', ApiDB.uqMapComplaintUnitsListUNITNAME.AsString);
unitObj.AddPair('Status', unitStatus);
if latestUpdate <> 0 then
unitObj.AddPair('Updated', FormatDateTime('yyyy-mm-dd hh:nn:ss', latestUpdate))
else
unitObj.AddPair('Updated', '');
ApiDB.uqMapComplaintUnitsList.Next; ApiDB.uqMapComplaintUnitsList.Next;
end; end;
finally
ApiDB.uqMapComplaintUnitsList.Close;
end;
try emitted := 0;
emitted:=0;
ApiDB.uqMapComplaints.Close;
ApiDB.uqMapComplaints.Open; ApiDB.uqMapComplaints.Open;
try
while not ApiDB.uqMapComplaints.Eof do while not ApiDB.uqMapComplaints.Eof do
begin begin
item:=TJSONObject.Create; item := TJSONObject.Create;
data.AddElement(item);
item.AddPair('ComplaintId',ApiDB.uqMapComplaintsCOMPLAINTID.AsString); item.AddPair('ComplaintId', ApiDB.uqMapComplaintsCOMPLAINTID.AsString);
item.AddPair('DispatchDistrict', ApiDB.uqMapComplaintsDISPATCHDISTRICT.AsString); item.AddPair('DispatchDistrict', ApiDB.uqMapComplaintsDISPATCHDISTRICT.AsString);
item.AddPair('DispatchCodeDesc',ApiDB.uqMapComplaintsDISPATCH_CODE_DESC.AsString); item.AddPair('Agency', ApiDB.uqMapComplaintsAGENCY.AsString);
item.AddPair('DispatchCodeCategory',ApiDB.uqMapComplaintsDISPATCHCODECATEGORY.AsString); item.AddPair('AgencyName', ApiDB.uqMapComplaintsAGENCY_NAME.AsString);
item.AddPair('Priority',ApiDB.uqMapComplaintsPRIORITY.AsString); item.AddPair('DispatchCodeDesc', ApiDB.uqMapComplaintsDISPATCH_CODE_DESC.AsString);
item.AddPair('priorityKey',ApiDB.uqMapComplaintspriorityKey.AsString); item.AddPair('DispatchCodeCategory', ApiDB.uqMapComplaintsDISPATCHCODECATEGORY.AsString);
item.AddPair('pngName',ApiDB.uqMapComplaintspngName.AsString); item.AddPair('Priority', ApiDB.uqMapComplaintsPRIORITY.AsString);
item.AddPair('Address',ApiDB.uqMapComplaintsADDRESS.AsString); item.AddPair('PriorityBadge', ApiDB.uqMapComplaintspriorityBadge.AsString);
item.AddPair('Business',ApiDB.uqMapComplaintsBUSINESS.AsString); item.AddPair('ComplaintStatusKey', ApiDB.uqMapComplaintscomplaintStatusKey.AsString);
item.AddPair('pngName', ApiDB.uqMapComplaintspngName.AsString);
complaintId:=ApiDB.uqMapComplaintsCOMPLAINTID.AsString; item.AddPair('Address', ApiDB.uqMapComplaintsADDRESS.AsString);
if UnitsByComplaintMap.TryGetValue(complaintId,unitArray) then item.AddPair('Business', ApiDB.uqMapComplaintsBUSINESS.AsString);
item.AddPair('Units',TJSONArray(unitArray.Clone))
complaintId := ApiDB.uqMapComplaintsCOMPLAINTID.AsString;
if UnitsByComplaintMap.TryGetValue(complaintId, unitArray) then
item.AddPair('Units', TJSONArray(unitArray.Clone))
else else
item.AddPair('Units',TJSONArray.Create); item.AddPair('Units', TJSONArray.Create);
item.AddPair('Lat',TJSONNumber.Create(ApiDB.uqMapComplaintsLAT.AsFloat)); item.AddPair('Lat', TJSONNumber.Create(ApiDB.uqMapComplaintsLAT.AsFloat));
item.AddPair('Lng',TJSONNumber.Create(ApiDB.uqMapComplaintsLNG.AsFloat)); item.AddPair('Lng', TJSONNumber.Create(ApiDB.uqMapComplaintsLNG.AsFloat));
data.AddElement(item);
Inc(emitted); Inc(emitted);
ApiDB.uqMapComplaints.Next; ApiDB.uqMapComplaints.Next;
end; end;
finally
ApiDB.uqMapComplaints.Close;
end;
Result.AddPair('count', TJSONNumber.Create(data.Count));
Result.AddPair('returned', TJSONNumber.Create(emitted));
Result.AddPair('count',TJSONNumber.Create(data.Count)); Logger.Log(3, '---TApiService.GetComplaintMap End (returned=' + IntToStr(emitted) + ')');
Result.AddPair('returned',TJSONNumber.Create(emitted));
Result.AddPair('data',data);
Logger.Log(3,'---TApiService.GetComplaintMap End (returned='+emitted.ToString+')');
except except
on E: EXDataHttpException do
begin
Logger.Log(2, '---TApiService.GetComplaintMap http error: ' + E.ClassName + ': ' + E.Message);
raise;
end;
on E: Exception do on E: Exception do
begin begin
FreeAndNil(data); Logger.Log(2, '---TApiService.GetComplaintMap error: ' + E.ClassName + ': ' + E.Message);
Logger.Log(2,'GetComplaintMap error: '+E.Message); raise EXDataHttpException.Create(500, 'Failed to load complaint map');
raise EXDataHttpException.Create(500,'Failed to load complaint map');
end; end;
end; end;
finally finally
...@@ -203,17 +231,16 @@ var ...@@ -203,17 +231,16 @@ var
begin begin
Logger.Log(4, '---TApiService.GetUnitMap initiated'); Logger.Log(4, '---TApiService.GetUnitMap initiated');
// Note: GetUnitMap is AVL-anchored (shows all AVL units).
// Note: DIS_UNITID is null when the unit is not dispatch-active; client should disable/hide Details in that case.
// Note: To restrict map to dispatch-active only, change uqMapUnits join to DIS_UNIT_ACTIVE from LEFT JOIN to INNER JOIN.
Result := TJSONObject.Create; Result := TJSONObject.Create;
TXDataOperationContext.Current.Handler.ManagedObjects.Add(Result); TXDataOperationContext.Current.Handler.ManagedObjects.Add(Result);
data := TJSONArray.Create; data := TJSONArray.Create;
Result.AddPair('data', data);
try try
with ApiDB.uqMapUnits do with ApiDB.uqMapUnits do
begin begin
Close;
Open; Open;
try try
First; First;
...@@ -222,10 +249,14 @@ begin ...@@ -222,10 +249,14 @@ begin
if (not ApiDB.uqMapUnitsGPS_LATITUDE.IsNull) and (not ApiDB.uqMapUnitsGPS_LONGITUDE.IsNull) then if (not ApiDB.uqMapUnitsGPS_LATITUDE.IsNull) and (not ApiDB.uqMapUnitsGPS_LONGITUDE.IsNull) then
begin begin
item := TJSONObject.Create; item := TJSONObject.Create;
data.AddElement(item);
item.AddPair('UnitId', ApiDB.uqMapUnitsUNITID.AsString); item.AddPair('UnitId', ApiDB.uqMapUnitsUNITID.AsString);
item.AddPair('UnitName', ApiDB.uqMapUnitsUNITNAME.AsString); item.AddPair('UnitName', ApiDB.uqMapUnitsUNITNAME.AsString);
item.AddPair('District', ApiDB.uqMapUnitsUNIT_DISTRICT.AsString); item.AddPair('UnitBadge', ApiDB.uqMapUnitsUNITNAME.AsString);
item.AddPair('Agency', ApiDB.uqMapUnitsAGENCY.AsString);
item.AddPair('AgencyName', ApiDB.uqMapUnitsAGENCY_NAME.AsString);
item.AddPair('AgencyType', ApiDB.uqMapUnitsAGENCYTYPE.AsString);
item.AddPair('Lat', TJSONNumber.Create(ApiDB.uqMapUnitsGPS_LATITUDE.AsFloat)); item.AddPair('Lat', TJSONNumber.Create(ApiDB.uqMapUnitsGPS_LATITUDE.AsFloat));
item.AddPair('Lng', TJSONNumber.Create(ApiDB.uqMapUnitsGPS_LONGITUDE.AsFloat)); item.AddPair('Lng', TJSONNumber.Create(ApiDB.uqMapUnitsGPS_LONGITUDE.AsFloat));
...@@ -253,7 +284,6 @@ begin ...@@ -253,7 +284,6 @@ begin
item.AddPair('CanShowDetails', TJSONBool.Create(not ApiDB.uqMapUnitsDIS_UNITID.IsNull)); item.AddPair('CanShowDetails', TJSONBool.Create(not ApiDB.uqMapUnitsDIS_UNITID.IsNull));
data.AddElement(item);
end; end;
Next; Next;
...@@ -265,13 +295,18 @@ begin ...@@ -265,13 +295,18 @@ begin
Result.AddPair('count', TJSONNumber.Create(data.Count)); Result.AddPair('count', TJSONNumber.Create(data.Count));
Result.AddPair('returned', TJSONNumber.Create(data.Count)); Result.AddPair('returned', TJSONNumber.Create(data.Count));
Result.AddPair('data', data);
except except
data.Free; on E: EXDataHttpException do
Logger.Log(2, '---TApiService.GetUnitMap error'); begin
Logger.Log(2, '---TApiService.GetUnitMap http error: ' + E.ClassName + ': ' + E.Message);
raise;
end;
on E: Exception do
begin
Logger.Log(2, '---TApiService.GetUnitMap error: ' + E.ClassName + ': ' + E.Message);
raise EXDataHttpException.Create(500, 'Failed to load unit map'); raise EXDataHttpException.Create(500, 'Failed to load unit map');
end; end;
end;
Logger.Log(4, '---TApiService.GetUnitMap End'); Logger.Log(4, '---TApiService.GetUnitMap End');
end; end;
...@@ -299,33 +334,49 @@ begin ...@@ -299,33 +334,49 @@ begin
while not Eof do while not Eof do
begin begin
var status: string; var status: string;
if not FieldByName('DATECLEARED').IsNull then var statusColor: string;
status := 'Cleared' var statusTextColor: string;
else if not FieldByName('DATEARRIVED').IsNull then
status := 'AtScene' if not FieldByName('DATEARRIVED').IsNull then
begin
status := 'On Scene';
statusColor := '#22C55E';
statusTextColor := '#000000';
end
else if not FieldByName('DATERESPONDED').IsNull then
begin
status := 'Enroute';
statusColor := '#FFFF00';
statusTextColor := '#000000';
end
else if not FieldByName('DATEDISPATCHED').IsNull then else if not FieldByName('DATEDISPATCHED').IsNull then
status := 'Dispatched' begin
status := 'Dispatched';
statusColor := '#FFFF00';
statusTextColor := '#000000';
end
else else
begin
status := 'Pending'; status := 'Pending';
statusColor := '#FF8080';
statusTextColor := '#000000';
end;
var item := TJSONObject.Create; var item := TJSONObject.Create;
var curDistrict := ApiDB.uqComplaintListDISPATCHDISTRICT.AsString; var curAgency := Trim(ApiDB.uqComplaintListAGENCY.AsString);
if not SameText(curDistrict, lastDistrict) then if curAgency = '' then
item.AddPair('DistrictHeader', curDistrict); curAgency := 'Unknown Agency';
lastDistrict := curDistrict;
var districtSector := ApiDB.uqComplaintListDISTRICT_DESC.AsString + ApiDB.uqComplaintListSECTOR_DESC.AsString; if not SameText(curAgency, lastDistrict) then
item.AddPair('DistrictSector', districtSector); item.AddPair('DistrictHeader', curAgency);
lastDistrict := curAgency;
item.AddPair('AgencyLine', 'Agency: ' + curAgency);
var colorVal := ApiDB.uqComplaintListPRIORITY_COLOR.AsInteger; var colorVal := ApiDB.uqComplaintListPRIORITY_COLOR.AsInteger;
item.AddPair('PriorityColor', '#' + IntToHex(colorVal and $FFFFFF, 6)); item.AddPair('PriorityColor', '#' + IntToHex(colorVal and $FFFFFF, 6));
if (colorVal and $FFFFFF) = $0000FF then
item.AddPair('PriorityTextColor', '#FFFFFF')
else
item.AddPair('PriorityTextColor', '#000000'); item.AddPair('PriorityTextColor', '#000000');
item.AddPair('ComplaintId', ApiDB.uqComplaintListCOMPLAINTID.AsString); item.AddPair('ComplaintId', ApiDB.uqComplaintListCOMPLAINTID.AsString);
item.AddPair('Complaint', ApiDB.uqComplaintListcomplaintNumber.AsString); item.AddPair('Complaint', ApiDB.uqComplaintListcomplaintNumber.AsString);
item.AddPair('Agency', ApiDB.uqComplaintListAGENCY.AsString); item.AddPair('Agency', ApiDB.uqComplaintListAGENCY.AsString);
...@@ -335,7 +386,9 @@ begin ...@@ -335,7 +386,9 @@ begin
item.AddPair('Business', ApiDB.uqComplaintListBUSINESS.AsString); item.AddPair('Business', ApiDB.uqComplaintListBUSINESS.AsString);
item.AddPair('CFSId', ApiDB.uqComplaintListCFSID.AsString); item.AddPair('CFSId', ApiDB.uqComplaintListCFSID.AsString);
item.AddPair('Status', status); item.AddPair('Status', status);
item.AddPair('DispatchDistrict', curDistrict); item.AddPair('StatusColor', statusColor);
item.AddPair('StatusTextColor', statusTextColor);
item.AddPair('DispatchDistrict', ApiDB.uqComplaintListDISPATCHDISTRICT.AsString);
item.AddPair('DateReported', ApiDB.uqComplaintListDATEREPORTED.AsString); item.AddPair('DateReported', ApiDB.uqComplaintListDATEREPORTED.AsString);
data.AddElement(item); data.AddElement(item);
...@@ -359,7 +412,7 @@ end; ...@@ -359,7 +412,7 @@ end;
function TApiService.GetUnitList: TJSONObject; function TApiService.GetUnitList: TJSONObject;
var var
data: TJSONArray; data: TJSONArray;
lastDistrict: string; lastAgency: string;
begin begin
Logger.Log(3, '---TApiService.GetUnitList initiated'); Logger.Log(3, '---TApiService.GetUnitList initiated');
...@@ -368,61 +421,140 @@ begin ...@@ -368,61 +421,140 @@ begin
data := TJSONArray.Create; data := TJSONArray.Create;
try try
lastDistrict := ''; lastAgency := '';
with ApiDB.uqUnitList do with ApiDB.uqUnitList do
begin begin
Open; Open;
First; First;
while not Eof do while not Eof do
begin begin
var item := TJSONObject.Create; var item := TJSONObject.Create;
// Section: Group header shown once when the agency changes.
var curAgency := Trim(ApiDB.uqUnitListAGENCY_NAME.AsString);
if curAgency = '' then
curAgency := Trim(ApiDB.uqUnitListAGENCY.AsString);
if curAgency = '' then
curAgency := 'Unknown Agency';
// Group header: show once when district changes if not SameText(curAgency, lastAgency) then
var curDistrict := ApiDB.uqUnitListDISTRICT_DESC.AsString; begin
var header := IfThen(curDistrict <> '', curDistrict + ' District', ''); item.AddPair('AgencyHeader', curAgency);
if (header <> '') and not SameText(header, lastDistrict) then lastAgency := curAgency;
item.AddPair('DistrictHeader', header); end
lastDistrict := header; else
item.AddPair('AgencyHeader', '');
// Core unit identity // Section: Core unit identity.
item.AddPair('UnitId', ApiDB.uqUnitListUNITID.AsString); item.AddPair('UnitId', ApiDB.uqUnitListUNITID.AsString);
item.AddPair('UnitName', ApiDB.uqUnitListUNITNAME.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('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('Sector', ApiDB.uqUnitListSECTOR_DESC.AsString);
item.AddPair('CallType', ApiDB.uqUnitListCALL_TYPE.AsString); item.AddPair('CallType', ApiDB.uqUnitListCALL_TYPE.AsString);
// Section: Map button availability.
var canShowMap := False;
// Current assignment (if any) if (not ApiDB.uqUnitListGPS_LATITUDE.IsNull) and
item.AddPair('Location', ApiDB.uqUnitListLOCATION.AsString); (not ApiDB.uqUnitListGPS_LONGITUDE.IsNull) then
item.AddPair('Complaint', ApiDB.uqUnitListCOMPLAINT.AsString); 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'
)
);
// Section: Current complaint assignment.
var complaintNumber :=
Trim(ApiDB.uqUnitListCOMPLAINT.AsString);
item.AddPair(
'Location',
ApiDB.uqUnitListLOCATION.AsString
);
item.AddPair('Complaint', complaintNumber);
if complaintNumber <> '' then
item.AddPair(
'ComplaintHeader',
' - ' + complaintNumber
)
else
item.AddPair('ComplaintHeader', '');
// Section: Default to Available when there is no active CFS row.
var statusDesc :=
ApiDB.uqUnitListUNIT_STATUS_DESC.AsString;
// Status: default to "Available" when no active CFS row
var statusDesc := ApiDB.uqUnitListUNIT_STATUS_DESC.AsString;
if statusDesc = '' then if statusDesc = '' then
statusDesc := 'Available'; statusDesc := 'Available';
item.AddPair('Status', statusDesc); item.AddPair('Status', statusDesc);
// Officers (LAST, FIRST [MI]) // Section: Primary officer.
var o1 := Trim(ApiDB.uqUnitListOFFICER1_LAST_NAME.AsString); var o1 :=
var f1 := Trim(ApiDB.uqUnitListOFFICER1_FIRST_NAME.AsString); Trim(ApiDB.uqUnitListOFFICER1_LAST_NAME.AsString);
var m1 := Trim(ApiDB.uqUnitListOFFICER1_MI.AsString); var f1 :=
Trim(ApiDB.uqUnitListOFFICER1_FIRST_NAME.AsString);
var m1 :=
Trim(ApiDB.uqUnitListOFFICER1_MI.AsString);
if o1 <> '' then if o1 <> '' then
begin begin
if f1 <> '' then o1 := o1 + ', ' + f1; if f1 <> '' then
if m1 <> '' then o1 := o1 + ' ' + m1; o1 := o1 + ', ' + f1;
if m1 <> '' then
o1 := o1 + ' ' + m1;
item.AddPair('Officer1', o1); item.AddPair('Officer1', o1);
end; end;
var o2 := Trim(ApiDB.uqUnitListOFFICER2_LAST_NAME.AsString); // Section: Secondary officer.
var f2 := Trim(ApiDB.uqUnitListOFFICER2_FIRST_NAME.AsString); var o2 :=
var m2 := Trim(ApiDB.uqUnitListOFFICER2_MI.AsString); Trim(ApiDB.uqUnitListOFFICER2_LAST_NAME.AsString);
var f2 :=
Trim(ApiDB.uqUnitListOFFICER2_FIRST_NAME.AsString);
var m2 :=
Trim(ApiDB.uqUnitListOFFICER2_MI.AsString);
if o2 <> '' then if o2 <> '' then
begin begin
if f2 <> '' then o2 := o2 + ', ' + f2; if f2 <> '' then
if m2 <> '' then o2 := o2 + ' ' + m2; o2 := o2 + ', ' + f2;
if m2 <> '' then
o2 := o2 + ' ' + m2;
item.AddPair('Officer2', o2); item.AddPair('Officer2', o2);
end; end;
...@@ -431,13 +563,25 @@ begin ...@@ -431,13 +563,25 @@ begin
end; end;
end; end;
Result.AddPair('count', TJSONNumber.Create(data.Count)); Result.AddPair(
Result.AddPair('returned', TJSONNumber.Create(data.Count)); 'count',
TJSONNumber.Create(data.Count)
);
Result.AddPair(
'returned',
TJSONNumber.Create(data.Count)
);
Result.AddPair('data', data); Result.AddPair('data', data);
except except
data.Free; data.Free;
Logger.Log(3, '---TApiService.GetUnitList End (error)'); Logger.Log(
raise EXDataHttpException.Create(500, 'Failed to load units list'); 3,
'---TApiService.GetUnitList End (error)'
);
raise EXDataHttpException.Create(
500,
'Failed to load units list'
);
end; end;
Logger.Log(3, '---TApiService.GetUnitList End'); Logger.Log(3, '---TApiService.GetUnitList End');
...@@ -544,7 +688,6 @@ begin ...@@ -544,7 +688,6 @@ begin
raise EXDataHttpException.Create(404,'Complaint archive not found'); raise EXDataHttpException.Create(404,'Complaint archive not found');
obj := TJSONObject.Create; obj := TJSONObject.Create;
TXDataOperationContext.Current.Handler.ManagedObjects.Add(obj);
obj.AddPair('ComplaintId', ApiDB.uqComplaintDetailsCOMPLAINTID.AsString); obj.AddPair('ComplaintId', ApiDB.uqComplaintDetailsCOMPLAINTID.AsString);
obj.AddPair('CFSId', ApiDB.uqComplaintDetailsCFSID.AsString); obj.AddPair('CFSId', ApiDB.uqComplaintDetailsCFSID.AsString);
...@@ -622,6 +765,8 @@ begin ...@@ -622,6 +765,8 @@ begin
TXDataOperationContext.Current.Handler.ManagedObjects.Add(Result); TXDataOperationContext.Current.Handler.ManagedObjects.Add(Result);
data := TJSONArray.Create; data := TJSONArray.Create;
Result.AddPair('data', data);
try try
with ApiDB.uqCFSMemos do with ApiDB.uqCFSMemos do
begin begin
...@@ -632,6 +777,7 @@ begin ...@@ -632,6 +777,7 @@ begin
while not Eof do while not Eof do
begin begin
item := TJSONObject.Create; item := TJSONObject.Create;
data.AddElement(item);
item.AddPair('MemoId', ApiDB.uqCFSMemosMEMO_ID.AsString); item.AddPair('MemoId', ApiDB.uqCFSMemosMEMO_ID.AsString);
item.AddPair('CFSId', ApiDB.uqCFSMemosCFSID.AsString); item.AddPair('CFSId', ApiDB.uqCFSMemosCFSID.AsString);
...@@ -646,7 +792,6 @@ begin ...@@ -646,7 +792,6 @@ begin
item.AddPair('BadgeNumber', ApiDB.uqCFSMemosBADGE_NUMBER.AsString); item.AddPair('BadgeNumber', ApiDB.uqCFSMemosBADGE_NUMBER.AsString);
item.AddPair('Remarks', ApiDB.uqCFSMemosREMARKS.AsString); item.AddPair('Remarks', ApiDB.uqCFSMemosREMARKS.AsString);
data.AddElement(item);
Next; Next;
end; end;
finally finally
...@@ -656,13 +801,18 @@ begin ...@@ -656,13 +801,18 @@ begin
Result.AddPair('count', TJSONNumber.Create(data.Count)); Result.AddPair('count', TJSONNumber.Create(data.Count));
Result.AddPair('returned', TJSONNumber.Create(data.Count)); Result.AddPair('returned', TJSONNumber.Create(data.Count));
Result.AddPair('data', data);
except except
data.Free; on E: EXDataHttpException do
Logger.Log(3, '---TApiService.GetComplaintMemos End (error)'); begin
Logger.Log(2, '---TApiService.GetComplaintMemos http error: ' + E.ClassName + ': ' + E.Message);
raise;
end;
on E: Exception do
begin
Logger.Log(2, '---TApiService.GetComplaintMemos error: ' + E.ClassName + ': ' + E.Message);
raise EXDataHttpException.Create(500, 'Failed to load complaint memos'); raise EXDataHttpException.Create(500, 'Failed to load complaint memos');
end; end;
end;
Logger.Log(3, '---TApiService.GetComplaintMemos End'); Logger.Log(3, '---TApiService.GetComplaintMemos End');
end; end;
...@@ -729,7 +879,7 @@ begin ...@@ -729,7 +879,7 @@ begin
end; end;
on E: Exception do on E: Exception do
begin begin
Logger.Log(2, '---TApiService.GetComplaintHistory error: ' + E.Message); Logger.Log(2, '---TApiService.GetComplaintHistory error: ' + E.ClassName + ': ' + E.Message);
raise EXDataHttpException.Create(500, 'Failed to load complaint history'); raise EXDataHttpException.Create(500, 'Failed to load complaint history');
end; end;
end; end;
...@@ -787,7 +937,7 @@ begin ...@@ -787,7 +937,7 @@ begin
end; end;
on E: Exception do on E: Exception do
begin begin
Logger.Log(2, '---TApiService.GetComplaintContacts error: ' + E.Message); Logger.Log(2, '---TApiService.GetComplaintContacts error: ' + E.ClassName + ': ' + E.Message);
raise EXDataHttpException.Create(500, 'Failed to load complaint contacts'); raise EXDataHttpException.Create(500, 'Failed to load complaint contacts');
end; end;
end; end;
...@@ -798,6 +948,9 @@ var ...@@ -798,6 +948,9 @@ var
dataArr: TJSONArray; dataArr: TJSONArray;
rowObj: TJSONObject; rowObj: TJSONObject;
returnedCount: Integer; returnedCount: Integer;
addressText: string;
apartmentText: string;
cityText: string;
begin begin
Logger.Log(3, '---TApiService.GetComplaintWarnings initiated: ' + ComplaintId); Logger.Log(3, '---TApiService.GetComplaintWarnings initiated: ' + ComplaintId);
...@@ -820,9 +973,30 @@ begin ...@@ -820,9 +973,30 @@ begin
rowObj := TJSONObject.Create; rowObj := TJSONObject.Create;
dataArr.AddElement(rowObj); dataArr.AddElement(rowObj);
rowObj.AddPair('CodeDesc', FieldByName('CODE_DESC').AsString); addressText := Trim(
rowObj.AddPair('Address', FieldByName('ADDRESS_TEXT').AsString); ApiDB.uqComplaintWarningsSTRNUMBER.AsString + ' ' +
rowObj.AddPair('Notes', FieldByName('NOTES').AsString); ApiDB.uqComplaintWarningsSTRHNUMBER.AsString + ' ' +
ApiDB.uqComplaintWarningsSTRPREFIX.AsString + ' ' +
ApiDB.uqComplaintWarningsSTRNAME.AsString + ' ' +
ApiDB.uqComplaintWarningsSTRSUFFIX.AsString
);
apartmentText := Trim(ApiDB.uqComplaintWarningsAPARTMENT.AsString);
if apartmentText <> '' then
addressText := Trim(addressText + ' ' + apartmentText);
cityText := Trim(ApiDB.uqComplaintWarningsCITY.AsString);
if cityText <> '' then
begin
if addressText <> '' then
addressText := addressText + ', ' + cityText
else
addressText := cityText;
end;
rowObj.AddPair('CodeDesc', ApiDB.uqComplaintWarningsCODE_DESC.AsString);
rowObj.AddPair('Address', addressText);
rowObj.AddPair('Notes', ApiDB.uqComplaintWarningsNOTES.AsString);
Inc(returnedCount); Inc(returnedCount);
Next; Next;
...@@ -844,7 +1018,7 @@ begin ...@@ -844,7 +1018,7 @@ begin
end; end;
on E: Exception do on E: Exception do
begin begin
Logger.Log(2, '---TApiService.GetComplaintWarnings error: ' + E.Message); Logger.Log(2, '---TApiService.GetComplaintWarnings error: ' + E.ClassName + ': ' + E.Message);
raise EXDataHttpException.Create(500, 'Failed to load complaint warnings'); raise EXDataHttpException.Create(500, 'Failed to load complaint warnings');
end; end;
end; end;
...@@ -864,6 +1038,8 @@ begin ...@@ -864,6 +1038,8 @@ begin
TXDataOperationContext.Current.Handler.ManagedObjects.Add(Result); TXDataOperationContext.Current.Handler.ManagedObjects.Add(Result);
dataArr := TJSONArray.Create; dataArr := TJSONArray.Create;
Result.AddPair('data', dataArr);
returnedCount := 0; returnedCount := 0;
try try
...@@ -897,7 +1073,6 @@ begin ...@@ -897,7 +1073,6 @@ begin
Result.AddPair('count', TJSONNumber.Create(returnedCount)); Result.AddPair('count', TJSONNumber.Create(returnedCount));
Result.AddPair('returned', TJSONNumber.Create(returnedCount)); Result.AddPair('returned', TJSONNumber.Create(returnedCount));
Result.AddPair('data', dataArr);
Logger.Log(4, '---TApiService.GetUnitLogs End (returned=' + IntToStr(returnedCount) + ')'); Logger.Log(4, '---TApiService.GetUnitLogs End (returned=' + IntToStr(returnedCount) + ')');
except except
...@@ -908,7 +1083,7 @@ begin ...@@ -908,7 +1083,7 @@ begin
end; end;
on E: Exception do on E: Exception do
begin begin
Logger.Log(2, '---TApiService.GetUnitLogs error: ' + E.Message); Logger.Log(2, '---TApiService.GetUnitLogs error: ' + E.ClassName + ': ' + E.Message);
raise EXDataHttpException.Create(500, 'Failed to load unit logs'); raise EXDataHttpException.Create(500, 'Failed to load unit logs');
end; end;
end; end;
...@@ -930,6 +1105,7 @@ begin ...@@ -930,6 +1105,7 @@ begin
try try
with ApiDB.uqUnitDetails do with ApiDB.uqUnitDetails do
begin begin
Close;
ParamByName('UNITID').AsString := UnitId; ParamByName('UNITID').AsString := UnitId;
Open; Open;
try try
...@@ -937,6 +1113,7 @@ begin ...@@ -937,6 +1113,7 @@ begin
raise EXDataHttpException.Create(404, 'Unit not found'); raise EXDataHttpException.Create(404, 'Unit not found');
obj := TJSONObject.Create; obj := TJSONObject.Create;
Result.AddPair('data', obj);
obj.AddPair('UnitId', ApiDB.uqUnitDetailsUNITID.AsString); obj.AddPair('UnitId', ApiDB.uqUnitDetailsUNITID.AsString);
obj.AddPair('UnitName', ApiDB.uqUnitDetailsUNITNAME.AsString); obj.AddPair('UnitName', ApiDB.uqUnitDetailsUNITNAME.AsString);
...@@ -962,7 +1139,6 @@ begin ...@@ -962,7 +1139,6 @@ begin
updateTimeText := FormatDateTime('yyyy-mm-dd hh:nn:ss', ApiDB.uqUnitDetailsUPDATE_TIME.AsDateTime); updateTimeText := FormatDateTime('yyyy-mm-dd hh:nn:ss', ApiDB.uqUnitDetailsUPDATE_TIME.AsDateTime);
obj.AddPair('UpdateTime', updateTimeText); obj.AddPair('UpdateTime', updateTimeText);
Result.AddPair('data', obj);
finally finally
Close; Close;
end; end;
...@@ -970,12 +1146,12 @@ begin ...@@ -970,12 +1146,12 @@ begin
except except
on E: EXDataHttpException do on E: EXDataHttpException do
begin begin
Logger.Log(2, '---TApiService.GetUnitDetails http error: ' + E.Message); Logger.Log(2, '---TApiService.GetUnitDetails http error: ' + E.ClassName + ': ' + E.Message);
raise; raise;
end; end;
on E: Exception do on E: Exception do
begin begin
Logger.Log(2, '---TApiService.GetUnitDetails error: ' + E.Message); Logger.Log(2, '---TApiService.GetUnitDetails error: ' + E.ClassName + ': ' + E.Message);
raise EXDataHttpException.Create(500, 'Failed to load unit details'); raise EXDataHttpException.Create(500, 'Failed to load unit details');
end; end;
end; end;
......
...@@ -3,78 +3,35 @@ object AuthDatabase: TAuthDatabase ...@@ -3,78 +3,35 @@ object AuthDatabase: TAuthDatabase
OnDestroy = DataModuleDestroy OnDestroy = DataModuleDestroy
Height = 249 Height = 249
Width = 433 Width = 433
object uq: TUniQuery
SQL.Strings = (
'select * from users')
FetchRows = 100
Left = 162
Top = 45
object uquser_id: TLargeintField
FieldName = 'user_id'
end
object uqusername: TStringField
FieldName = 'username'
Required = True
Size = 64
end
object uqpassword: TMemoField
FieldName = 'password'
Required = True
BlobType = ftMemo
end
object uqdate_created: TStringField
FieldName = 'date_created'
Required = True
Size = 21
end
object uqadmin: TBooleanField
FieldName = 'admin'
end
object uqemail: TMemoField
FieldName = 'email'
BlobType = ftMemo
end
object uqphone_number: TStringField
FieldName = 'phone_number'
Size = 14
end
object uqfull_name: TStringField
FieldName = 'full_name'
Size = 30
end
object uqactive: TBooleanField
FieldName = 'active'
end
end
object uqMisc: TUniQuery object uqMisc: TUniQuery
FetchRows = 100 FetchRows = 100
Left = 249 Left = 237
Top = 45 Top = 53
end end
object OracleUniProvider1: TOracleUniProvider object ucLemsOCSO: TUniConnection
Left = 94 ProviderName = 'PostgreSQL'
Top = 152 Database = 'lems_ocso'
end Username = 'postgres'
object ucBooking: TUniConnection Server = '192.168.102.10'
ProviderName = 'Oracle'
Database = 'EMBOOKING'
Username = 'emBooking'
Server = 'EMBOOK-CPS'
LoginPrompt = False LoginPrompt = False
Left = 339 Left = 221
Top = 75 Top = 145
EncryptedPassword = '9AFF92FF9DFF90FF90FF94FFCFFFCEFF' EncryptedPassword = '9AFF92FF8CFF86FF8CFFCFFFCEFF'
end end
object uqUserPref: TUniQuery object uqUserPref: TUniQuery
Connection = ucBooking Connection = ucLemsOCSO
SQL.Strings = ( SQL.Strings = (
'SELECT * FROM USER_PREFERENCES') 'SELECT * FROM USER_PREFERENCES')
Left = 204 Left = 160
Top = 114 Top = 52
end end
object uqBooking: TUniQuery object uqAuth: TUniQuery
Connection = ucBooking Connection = ucLemsOCSO
Left = 88 Left = 88
Top = 48 Top = 48
end end
object PostgreSQLUniProvider1: TPostgreSQLUniProvider
Left = 90
Top = 148
end
end end
...@@ -3,26 +3,16 @@ unit Auth.Database; ...@@ -3,26 +3,16 @@ unit Auth.Database;
interface interface
uses uses
System.SysUtils, System.Classes, IniFiles, Vcl.Forms, MemDS, System.SysUtils, System.Classes, MemDS,
Data.DB, DBAccess, Uni, UniProvider, PostgreSQLUniProvider, OracleUniProvider; Data.DB, DBAccess, Uni, UniProvider, PostgreSQLUniProvider;
type type
TAuthDatabase = class(TDataModule) TAuthDatabase = class(TDataModule)
uq: TUniQuery;
uqMisc: TUniQuery; uqMisc: TUniQuery;
uquser_id: TLargeintField; ucLemsOCSO: TUniConnection;
uqusername: TStringField;
uqpassword: TMemoField;
uqdate_created: TStringField;
uqadmin: TBooleanField;
uqemail: TMemoField;
uqphone_number: TStringField;
uqfull_name: TStringField;
uqactive: TBooleanField;
OracleUniProvider1: TOracleUniProvider;
ucBooking: TUniConnection;
uqUserPref: TUniQuery; uqUserPref: TUniQuery;
uqBooking: TUniQuery; uqAuth: TUniQuery;
PostgreSQLUniProvider1: TPostgreSQLUniProvider;
procedure DataModuleCreate(Sender: TObject); procedure DataModuleCreate(Sender: TObject);
procedure DataModuleDestroy(Sender: TObject); procedure DataModuleDestroy(Sender: TObject);
private private
...@@ -41,6 +31,7 @@ uses ...@@ -41,6 +31,7 @@ uses
System.JSON, System.JSON,
Common.Config, Common.Config,
Common.Logging, Common.Logging,
Common.Ini,
uLibrary; uLibrary;
{%CLASSGROUP 'Vcl.Controls.TControl'} {%CLASSGROUP 'Vcl.Controls.TControl'}
...@@ -48,50 +39,45 @@ uses ...@@ -48,50 +39,45 @@ uses
{$R *.dfm} {$R *.dfm}
procedure TAuthDatabase.DataModuleCreate(Sender: TObject); procedure TAuthDatabase.DataModuleCreate(Sender: TObject);
var
iniFile: TIniFile;
begin begin
iniFile := TIniFile.Create(ChangeFileExt(Application.ExeName, '.ini')); ucLemsOCSO.ProviderName := 'PostgreSQL';
try ucLemsOCSO.Server := IniEntries.DatabaseServer;
ucBooking.Server := IniFile.ReadString('EMB Database', 'Server', 'EMBOOKING'); ucLemsOCSO.Port := IniEntries.DatabasePort;
ucBooking.Username := IniFile.ReadString('EMB Database', 'Username', 'emBooking'); ucLemsOCSO.Database := IniEntries.DatabaseName;
ucBooking.Password := IniFile.ReadString('EMB Database', 'Password', 'embook01'); ucLemsOCSO.Username := IniEntries.DatabaseUsername;
ucLemsOCSO.Password := IniEntries.DatabasePassword;
ucLemsOCSO.LoginPrompt := False;
try try
Logger.Log(2, ''); Logger.Log(2, '');
Logger.Log(2, 'Connecting to emBooking Database...'); Logger.Log(2, 'Connecting to PostgreSQL auth database...');
if not ucBooking.Connected then if not ucLemsOCSO.Connected then
ucBooking.Connect; ucLemsOCSO.Connect;
except except
on E: Exception do on E: Exception do
begin begin
Logger.Log(2, Format('Failed to connect to database: %s', [E.Message])); Logger.Log(2, Format('Failed to connect to auth database: %s', [E.Message]));
end; raise;
end; end;
finally
iniFile.Free;
end; end;
end; end;
procedure TAuthDatabase.DataModuleDestroy(Sender: TObject); procedure TAuthDatabase.DataModuleDestroy(Sender: TObject);
begin begin
ucBooking.Connected := false; if ucLemsOCSO.Connected then
ucLemsOCSO.Disconnect;
end; end;
procedure TAuthDatabase.SetLoginAuditEntry( userStr: string ); procedure TAuthDatabase.SetLoginAuditEntry( userStr: string );
var var
auditMasterId: string; auditMasterId: string;
userInfo: TStringList; userInfo: TStringList;
entry: string;
username: string; username: string;
fullname: string; fullname: string;
agency: string; agency: string;
userid: string; userid: string;
personnelid: string; personnelid: string;
admin: boolean;
i: Integer;
begin begin
Logger.Log( 3, 'TAuthDatabase.SetLoginAuditEntry - start' ); Logger.Log( 3, 'TAuthDatabase.SetLoginAuditEntry - start' );
...@@ -108,8 +94,8 @@ begin ...@@ -108,8 +94,8 @@ begin
if ServerConfig.auditEnabled then if ServerConfig.auditEnabled then
begin begin
auditMasterId := GetNextSeqVal( uqMisc, 'SEQ_AUDIT_MASTERID' ); auditMasterId := GetNextSeqVal(uqMisc, 'SEQ_AUDIT_MASTERID');
SetMasterAuditEntry( uq, auditMasterId, 'BKG', '', agency, personnelid, fullname, 'Login', '', 'webCharms' ); SetMasterAuditEntry(uqMisc, auditMasterId, 'AUTH', '', agency, personnelid, fullname, 'Login', '', 'emiMobile');
end end
else else
begin begin
......
...@@ -27,7 +27,7 @@ type ...@@ -27,7 +27,7 @@ type
TAgencyConfigItem = class TAgencyConfigItem = class
public public
id: integer; id: string;
agency: String; agency: String;
name: String; name: String;
end; end;
...@@ -58,9 +58,3 @@ begin ...@@ -58,9 +58,3 @@ begin
end; end;
end. end.
constructor TAgencyItem.Create(AAgency: String);
begin
agency := AAgency;
end;
end.
...@@ -14,7 +14,6 @@ type ...@@ -14,7 +14,6 @@ type
TAuthService = class(TInterfacedObject, IAuthService) TAuthService = class(TInterfacedObject, IAuthService)
strict private strict private
authDB: TAuthDatabase; authDB: TAuthDatabase;
function GetQuery: TUniQuery;
private private
userName: string; userName: string;
userFullName: string; userFullName: string;
...@@ -25,7 +24,6 @@ type ...@@ -25,7 +24,6 @@ type
procedure AfterConstruction; override; procedure AfterConstruction; override;
procedure BeforeDestruction; override; procedure BeforeDestruction; override;
function VerifyVersion(ClientVersion: string): TJSONObject; function VerifyVersion(ClientVersion: string): TJSONObject;
property Query: TUniQuery read GetQuery;
function CheckUser(const User, Password, Agency: string): Integer; function CheckUser(const User, Password, Agency: string): Integer;
function Decrypt(inStr, keyStr: AnsiString): AnsiString; function Decrypt(inStr, keyStr: AnsiString): AnsiString;
public public
...@@ -52,17 +50,14 @@ procedure TAuthService.AfterConstruction; ...@@ -52,17 +50,14 @@ procedure TAuthService.AfterConstruction;
begin begin
inherited; inherited;
authDB := TAuthDatabase.Create(nil); authDB := TAuthDatabase.Create(nil);
Logger.Log(3, 'AuthDatabase created');
end; end;
procedure TAuthService.BeforeDestruction; procedure TAuthService.BeforeDestruction;
begin begin
authDB.Free; authDB.Free;
inherited; inherited;
end; Logger.Log(3, 'AuthDatabase destroyed');
function TAuthService.GetQuery: TUniQuery;
begin
Result := authDB.uq;
end; end;
function TAuthService.GetAgenciesList: TAgenciesList; function TAuthService.GetAgenciesList: TAgenciesList;
...@@ -71,31 +66,42 @@ var ...@@ -71,31 +66,42 @@ var
begin begin
Logger.Log(2, 'TAuthService.GetAgenciesList - call'); Logger.Log(2, 'TAuthService.GetAgenciesList - call');
if authDB.ucBooking.Connected then if authDB.ucLemsOCSO.Connected then
begin begin
authDB.uqBooking.Close; authDB.uqAuth.Close;
authDB.uqBooking.SQL.Text := 'select * from agencies order by agency'; authDB.uqAuth.SQL.Text :=
authDB.uqBooking.Open; 'select agencyid as agency ' +
'from lems.agency ' +
'where login = ''T'' ' +
'order by agencyid';
authDB.uqAuth.Open;
Result := TAgenciesList.Create; Result := TAgenciesList.Create;
TXDataOperationContext.Current.Handler.ManagedObjects.Add(Result);
Result.data := TList<TAgencyItem>.Create; Result.data := TList<TAgencyItem>.Create;
TXDataOperationContext.Current.Handler.ManagedObjects.Add(Result.data); TXDataOperationContext.Current.Handler.ManagedObjects.Add(Result.data);
while not authDB.uqBooking.Eof do
try
while not authDB.uqAuth.Eof do
begin begin
agency := TAgencyItem.Create(authDB.uqBooking.FieldByName('agency').AsString); agency := TAgencyItem.Create(authDB.uqAuth.FieldByName('agency').AsString);
TXDataOperationContext.Current.Handler.ManagedObjects.Add(agency); TXDataOperationContext.Current.Handler.ManagedObjects.Add(agency);
Result.Data.Add(agency); Result.Data.Add(agency);
authDB.uqBooking.Next; authDB.uqAuth.Next;
end; end;
authDB.uqBooking.Close; finally
authDB.uqAuth.Close;
end;
Result.count := Result.data.count; Result.count := Result.data.count;
Result.returned := Result.data.count; Result.returned := Result.data.count;
Logger.Log( 2, 'GetAgenciesList - Count: ' + IntToStr(Result.Count) + ' Returned: ' + IntToStr(Result.Returned) ); Logger.Log(2, 'GetAgenciesList - Count: ' + IntToStr(Result.Count) + ' Returned: ' + IntToStr(Result.Returned));
end end
else else
begin begin
Logger.Log( 2, 'TAuthService.GetAgenciesList - Error: connecting to CPS database!' ); Logger.Log(2, 'TAuthService.GetAgenciesList - Error: connecting to auth database!');
raise EXDataHttpException.Create('Error connecting to CPS database!'); raise EXDataHttpException.Create(500, 'Failed to load agencies');
end; end;
end; end;
...@@ -106,26 +112,38 @@ var ...@@ -106,26 +112,38 @@ var
begin begin
Logger.Log(2, 'AuthService.GetAgencyConfigList - call'); Logger.Log(2, 'AuthService.GetAgencyConfigList - call');
authDB.uqBooking.Close; authDB.uqAuth.Close;
sql := 'select agency_id, agency, agency_name from agencyconfig ' +
'where active = ''Y'' order by agency'; sql := 'select agencyid, agencyname ' +
authDB.uqBooking.SQL.Text := sql; 'from lems.agency ' +
authDB.uqBooking.Open; 'where login = ''T'' ' +
'order by agencyname';
authDB.uqAuth.SQL.Text := sql;
authDB.uqAuth.Open;
Result := TAgencyConfigList.Create; Result := TAgencyConfigList.Create;
TXDataOperationContext.Current.Handler.ManagedObjects.Add(Result);
Result.data := TList<TAgencyConfigItem>.Create; Result.data := TList<TAgencyConfigItem>.Create;
TXDataOperationContext.Current.Handler.ManagedObjects.Add(Result.data); TXDataOperationContext.Current.Handler.ManagedObjects.Add(Result.data);
while not authDB.uqBooking.Eof do
try
while not authDB.uqAuth.Eof do
begin begin
agencyConfig := TAgencyConfigItem.Create; agencyConfig := TAgencyConfigItem.Create;
TXDataOperationContext.Current.Handler.ManagedObjects.Add(agencyConfig); TXDataOperationContext.Current.Handler.ManagedObjects.Add(agencyConfig);
Result.data.Add(agencyConfig); Result.data.Add(agencyConfig);
agencyConfig.id := authDB.uqBooking.FieldByName('agency_id').AsInteger;
agencyConfig.agency := authDB.uqBooking.FieldByName('agency').AsString; agencyConfig.id := authDB.uqAuth.FieldByName('agencyid').AsString;
agencyConfig.name := authDB.uqBooking.FieldByName('agency_name').AsString; agencyConfig.agency := authDB.uqAuth.FieldByName('agencyid').AsString;
authDB.uqBooking.Next; agencyConfig.name := authDB.uqAuth.FieldByName('agencyname').AsString;
authDB.uqAuth.Next;
end;
finally
authDB.uqAuth.Close;
end; end;
authDB.uqBooking.Close;
Result.count := Result.data.Count; Result.count := Result.data.Count;
Result.returned := Result.data.Count; Result.returned := Result.data.Count;
...@@ -178,10 +196,27 @@ begin ...@@ -178,10 +196,27 @@ begin
Logger.Log(1, Format('AuthService.Login - User: "%s" Agency: "%s"', [User, Agency])); Logger.Log(1, Format('AuthService.Login - User: "%s" Agency: "%s"', [User, Agency]));
userState := CheckUser(User, Password, Agency); userState := CheckUser(User, Password, Agency);
try
userState := CheckUser(User, Password, Agency);
except
on E: Exception do
begin
Logger.Log(2, 'AuthService.Login - CheckUser error: ' + E.ClassName + ': ' + E.Message);
raise EXDataHttpException.Create(500, 'Login failed');
end;
end;
if userState = 0 then if userState = 0 then
begin
Logger.Log(2, Format('AuthService.Login - invalid login for User: "%s" Agency: "%s"', [User, Agency]));
raise EXDataHttpUnauthorized.Create('Invalid user or password'); raise EXDataHttpUnauthorized.Create('Invalid user or password');
end;
if userState = 1 then if userState = 1 then
raise EXDataHttpUnauthorized.Create('User not active!'); begin
Logger.Log(2, Format('AuthService.Login - inactive user: "%s" Agency: "%s"', [User, Agency]));
raise EXDataHttpUnauthorized.Create('User not active');
end;
JWT := TJWT.Create; JWT := TJWT.Create;
try try
...@@ -203,37 +238,41 @@ end; ...@@ -203,37 +238,41 @@ end;
function TAuthService.CheckUser(const User, Password, Agency: string): Integer; function TAuthService.CheckUser(const User, Password, Agency: string): Integer;
var var
userStr: string; userStr: string;
sqlStr: string;
decryptedPassword: AnsiString; decryptedPassword: AnsiString;
passwordKey: AnsiString; passwordKey: AnsiString;
begin begin
Logger.Log(3, Format('LoginService.CheckUser - User: "%s" Agency: "%s"', [User, Agency])); Logger.Log(3, Format('LoginService.CheckUser - User: "%s" Agency: "%s"', [User, Agency]));
passwordKey := 'wx3cFo$kIf2jrk(gOmvi7uvPfk*iorE8@kfm+nvR6jfh=swDqalpokSjf'; passwordKey := 'wx3cFo$kIf2jrk(gOmvi7uvPfk*iorE8@kfm+nvR6jfh=swDqalpokSjf';
sqlStr := 'select u.* from users@cps_link u '; authDB.uqAuth.Close;
sqlStr := sqlStr + 'where upper(user_name) = ' + QuotedStr(UpperCase(Trim(User))) + ' '; authDB.uqAuth.SQL.Text :=
sqlStr := sqlStr + 'and u.dept = ' + QuotedStr(UpperCase(Trim(Agency))); 'select u.* from lems.users u ' +
authDB.uqBooking.SQL.Text := sqlStr; 'where upper(user_name) = :USER_NAME ' +
Logger.Log(4, Format('LoginService.CheckUser - Query: "%s"', [sqlStr])); 'and u.dept = :AGENCY';
authDB.uqBooking.Open;
if authDB.uqBooking.IsEmpty then authDB.uqAuth.ParamByName('USER_NAME').AsString := UpperCase(Trim(User));
authDB.uqAuth.ParamByName('AGENCY').AsString := UpperCase(Trim(Agency));
Logger.Log(4, 'LoginService.CheckUser - opening user lookup query');
authDB.uqAuth.Open;
try
if authDB.uqAuth.IsEmpty then
Result := 0 Result := 0
else else
begin begin
if authDB.uqBooking.FieldByName('active').AsString = 'F' then if authDB.uqAuth.FieldByName('active').AsString = 'F' then
Result := 1 Result := 1
else else
begin begin
decryptedPassword := Uppercase(Trim(Decrypt(authDB.uqBooking.FieldByName('password').AsString, passwordKey))); decryptedPassword := Uppercase(Trim(Decrypt(authDB.uqAuth.FieldByName('password').AsString, passwordKey)));
if decryptedPassword = Uppercase(Trim(Password)) then if decryptedPassword = Uppercase(Trim(Password)) then
begin begin
userName := authDB.uqBooking.FieldByName('user_name').AsString; userName := authDB.uqAuth.FieldByName('user_name').AsString;
userFullName := authDB.uqBooking.FieldByName('firstname').AsString + ' ' + authDB.uqBooking.FieldByName('lastname').AsString; userFullName := authDB.uqAuth.FieldByName('firstname').AsString + ' ' + authDB.uqAuth.FieldByName('lastname').AsString;
userAgency := authDB.uqBooking.FieldByName('dept').AsString; userAgency := authDB.uqAuth.FieldByName('dept').AsString;
userBadge := authDB.uqBooking.FieldByName('badgenum').AsString; userBadge := authDB.uqAuth.FieldByName('badgenum').AsString;
userId := authDB.uqBooking.FieldByName('userid').AsString; userId := authDB.uqAuth.FieldByName('userid').AsString;
userPersonnelId := authDB.uqBooking.FieldByName('personnelid').AsString; userPersonnelId := authDB.uqAuth.FieldByName('personnelid').AsString;
userStr := '?username=' + userName; userStr := '?username=' + userName;
userStr := userStr + '&fullname=' + userFullName; userStr := userStr + '&fullname=' + userFullName;
...@@ -248,6 +287,9 @@ begin ...@@ -248,6 +287,9 @@ begin
Result := 0; Result := 0;
end; end;
end; end;
finally
authDB.uqAuth.Close;
end;
end; end;
function TAuthService.Decrypt(inStr, keyStr: AnsiString): AnsiString; function TAuthService.Decrypt(inStr, keyStr: AnsiString): AnsiString;
...@@ -256,6 +298,12 @@ var ...@@ -256,6 +298,12 @@ var
k, i: integer; k, i: integer;
tempKeyStr: AnsiString; tempKeyStr: AnsiString;
begin begin
if inStr = '' then
Exit('');
if keyStr = '' then
Exit('');
k := Integer(inStr[1]); k := Integer(inStr[1]);
tempKeyStr := keyStr; tempKeyStr := keyStr;
while Length(tempKeyStr) < 256 do while Length(tempKeyStr) < 256 do
......
...@@ -19,6 +19,16 @@ type ...@@ -19,6 +19,16 @@ type
FWebClientVersionFromIni: Boolean; FWebClientVersionFromIni: Boolean;
// [Database] // [Database]
FDatabaseServer: string;
FDatabaseServerFromIni: Boolean;
FDatabasePort: Integer;
FDatabasePortFromIni: Boolean;
FDatabaseName: string;
FDatabaseNameFromIni: Boolean;
FDatabaseUsername: string;
FDatabaseUsernameFromIni: Boolean;
FDatabasePassword: string;
FDatabasePasswordFromIni: Boolean;
public public
constructor Create; constructor Create;
...@@ -35,6 +45,16 @@ type ...@@ -35,6 +45,16 @@ type
property WebClientVersionFromIni: Boolean read FWebClientVersionFromIni; property WebClientVersionFromIni: Boolean read FWebClientVersionFromIni;
// [Database] // [Database]
property DatabaseServer: string read FDatabaseServer;
property DatabaseServerFromIni: Boolean read FDatabaseServerFromIni;
property DatabasePort: Integer read FDatabasePort;
property DatabasePortFromIni: Boolean read FDatabasePortFromIni;
property DatabaseName: string read FDatabaseName;
property DatabaseNameFromIni: Boolean read FDatabaseNameFromIni;
property DatabaseUsername: string read FDatabaseUsername;
property DatabaseUsernameFromIni: Boolean read FDatabaseUsernameFromIni;
property DatabasePassword: string read FDatabasePassword;
property DatabasePasswordFromIni: Boolean read FDatabasePasswordFromIni;
end; end;
...@@ -74,6 +94,20 @@ begin ...@@ -74,6 +94,20 @@ begin
FWebClientVersionFromIni := iniFile.ValueExists('Settings', 'webClientVersion'); FWebClientVersionFromIni := iniFile.ValueExists('Settings', 'webClientVersion');
// [Database] // [Database]
FDatabaseServer := iniFile.ReadString('Database', 'Server', '192.168.74.10');
FDatabaseServerFromIni := iniFile.ValueExists('Database', 'Server');
FDatabasePort := iniFile.ReadInteger('Database', 'Port', 5433);
FDatabasePortFromIni := iniFile.ValueExists('Database', 'Port');
FDatabaseName := iniFile.ReadString('Database', 'Database', 'lems_wcso');
FDatabaseNameFromIni := iniFile.ValueExists('Database', 'Database');
FDatabaseUsername := iniFile.ReadString('Database', 'Username', 'postgres');
FDatabaseUsernameFromIni := iniFile.ValueExists('Database', 'Username');
FDatabasePassword := iniFile.ReadString('Database', 'Password', '');
FDatabasePasswordFromIni := iniFile.ValueExists('Database', 'Password');
finally finally
iniFile.Free; iniFile.Free;
...@@ -81,4 +115,3 @@ begin ...@@ -81,4 +115,3 @@ begin
end; end;
end. end.
...@@ -2,8 +2,8 @@ object FMain: TFMain ...@@ -2,8 +2,8 @@ object FMain: TFMain
Left = 0 Left = 0
Top = 0 Top = 0
Caption = 'emiMobileServer' Caption = 'emiMobileServer'
ClientHeight = 597 ClientHeight = 583
ClientWidth = 764 ClientWidth = 761
Color = clBtnFace Color = clBtnFace
Font.Charset = DEFAULT_CHARSET Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText Font.Color = clWindowText
...@@ -12,20 +12,20 @@ object FMain: TFMain ...@@ -12,20 +12,20 @@ object FMain: TFMain
Font.Style = [] Font.Style = []
OnClose = FormClose OnClose = FormClose
DesignSize = ( DesignSize = (
764 761
597) 583)
TextHeight = 13 TextHeight = 13
object memoInfo: TMemo object memoInfo: TMemo
Left = 8 Left = 8
Top = 39 Top = 39
Width = 744 Width = 741
Height = 549 Height = 535
Anchors = [akLeft, akTop, akRight, akBottom] Anchors = [akLeft, akTop, akRight, akBottom]
ReadOnly = True ReadOnly = True
TabOrder = 0 TabOrder = 0
end end
object btnApiSwaggerUI: TButton object btnApiSwaggerUI: TButton
Left = 297 Left = 141
Top = 8 Top = 8
Width = 100 Width = 100
Height = 25 Height = 25
...@@ -43,7 +43,7 @@ object FMain: TFMain ...@@ -43,7 +43,7 @@ object FMain: TFMain
OnClick = btnExitClick OnClick = btnExitClick
end end
object btnAuthSwaggerUI: TButton object btnAuthSwaggerUI: TButton
Left = 169 Left = 31
Top = 8 Top = 8
Width = 100 Width = 100
Height = 25 Height = 25
......
...@@ -26,6 +26,8 @@ type ...@@ -26,6 +26,8 @@ type
strict private strict private
procedure StartServers; procedure StartServers;
function LogValue(const LabelName: string; const Value: string; FromIni: Boolean): string; function LogValue(const LabelName: string; const Value: string; FromIni: Boolean): string;
private
function LocalBrowserUrl(const Url: string): string;
end; end;
var var
...@@ -50,14 +52,25 @@ begin ...@@ -50,14 +52,25 @@ begin
Close; Close;
end; end;
function TFMain.LocalBrowserUrl(const Url: string): string;
begin
Result := StringReplace(Url, '://0.0.0.0:', '://localhost:', [rfIgnoreCase]);
end;
procedure TFMain.btnApiSwaggerUIClick(Sender: TObject); procedure TFMain.btnApiSwaggerUIClick(Sender: TObject);
var
url: string;
begin begin
ShellExecute(Handle, 'open', PChar(TSparkleUtils.CombineUrlFast(ApiServerModule.XDataServer1.BaseUrl, 'swaggerui')), nil, nil, SW_SHOWNORMAL); url := TSparkleUtils.CombineUrlFast(ApiServerModule.XDataServer1.BaseUrl, 'swaggerui');
ShellExecute(Handle, 'open', PChar(LocalBrowserUrl(url)), nil, nil, SW_SHOWNORMAL);
end; end;
procedure TFMain.btnAuthSwaggerUIClick(Sender: TObject); procedure TFMain.btnAuthSwaggerUIClick(Sender: TObject);
var
url: string;
begin begin
ShellExecute(Handle, 'open', PChar(TSparkleUtils.CombineUrlFast(AuthServerModule.XDataServer.BaseUrl, 'swaggerui')), nil, nil, SW_SHOWNORMAL); url := TSparkleUtils.CombineUrlFast(AuthServerModule.XDataServer.BaseUrl, 'swaggerui');
ShellExecute(Handle, 'open', PChar(LocalBrowserUrl(url)), nil, nil, SW_SHOWNORMAL);
end; end;
procedure TFMain.ContactFormData(AText: String); procedure TFMain.ContactFormData(AText: String);
......
...@@ -39,7 +39,7 @@ var ...@@ -39,7 +39,7 @@ var
sql: string; sql: string;
serverDateTime: TDateTime; serverDateTime: TDateTime;
begin begin
sql := 'select sysdate as currentdatetime from dual'; sql := 'select current_timestamp as currentdatetime';
DoQuery( uq, sql ); DoQuery( uq, sql );
serverDateTime := uq.FieldByName('CURRENTDATETIME').AsDateTime; serverDateTime := uq.FieldByName('CURRENTDATETIME').AsDateTime;
...@@ -86,7 +86,7 @@ function GetNextSeqVal(uq: TUniQuery; sequence: string ): string; ...@@ -86,7 +86,7 @@ function GetNextSeqVal(uq: TUniQuery; sequence: string ): string;
var var
sql: string; sql: string;
begin begin
sql := 'select ' + sequence + '.NEXTVAL as nextseqval from dual'; sql := 'select nextval(' + QuotedStr(sequence) + ') as nextseqval';
uq.Close; uq.Close;
uq.SQL.Text := sql; uq.SQL.Text := sql;
uq.Open; uq.Open;
......
[Settings] [Settings]
LogFileNum=677 LogFileNum=723
webClientVersion=0.1.0 webClientVersion=0.1.0
[Database]
--Server=192.168.102.10
Server=192.168.74.10
Port=5433
Database=lems_wcso
Username=postgres
--Password=postgreSQL
Password=emsys01
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup> <PropertyGroup>
<ProjectGuid>{2A3028D9-BC39-4625-9BA5-0338012E2824}</ProjectGuid> <ProjectGuid>{2A3028D9-BC39-4625-9BA5-0338012E2824}</ProjectGuid>
<ProjectVersion>20.3</ProjectVersion> <ProjectVersion>20.4</ProjectVersion>
<FrameworkType>VCL</FrameworkType> <FrameworkType>VCL</FrameworkType>
<Base>True</Base> <Base>True</Base>
<Config Condition="'$(Config)'==''">Debug</Config> <Config Condition="'$(Config)'==''">Debug</Config>
...@@ -863,6 +863,9 @@ ...@@ -863,6 +863,9 @@
<Platform Name="Win64x"> <Platform Name="Win64x">
<Operation>1</Operation> <Operation>1</Operation>
</Platform> </Platform>
<Platform Name="WinARM64EC">
<Operation>1</Operation>
</Platform>
</DeployClass> </DeployClass>
<DeployClass Name="ProjectiOSDeviceDebug"> <DeployClass Name="ProjectiOSDeviceDebug">
<Platform Name="iOSDevice32"> <Platform Name="iOSDevice32">
...@@ -933,6 +936,10 @@ ...@@ -933,6 +936,10 @@
<RemoteDir>Assets</RemoteDir> <RemoteDir>Assets</RemoteDir>
<Operation>1</Operation> <Operation>1</Operation>
</Platform> </Platform>
<Platform Name="WinARM64EC">
<RemoteDir>Assets</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass> </DeployClass>
<DeployClass Name="UWP_DelphiLogo44"> <DeployClass Name="UWP_DelphiLogo44">
<Platform Name="Win32"> <Platform Name="Win32">
...@@ -943,6 +950,10 @@ ...@@ -943,6 +950,10 @@
<RemoteDir>Assets</RemoteDir> <RemoteDir>Assets</RemoteDir>
<Operation>1</Operation> <Operation>1</Operation>
</Platform> </Platform>
<Platform Name="WinARM64EC">
<RemoteDir>Assets</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass> </DeployClass>
<DeployClass Name="iOS_AppStore1024"> <DeployClass Name="iOS_AppStore1024">
<Platform Name="iOSDevice64"> <Platform Name="iOSDevice64">
...@@ -1156,6 +1167,7 @@ ...@@ -1156,6 +1167,7 @@
<ProjectRoot Platform="Win32" Name="$(PROJECTNAME)"/> <ProjectRoot Platform="Win32" Name="$(PROJECTNAME)"/>
<ProjectRoot Platform="Win64" Name="$(PROJECTNAME)"/> <ProjectRoot Platform="Win64" Name="$(PROJECTNAME)"/>
<ProjectRoot Platform="Win64x" Name="$(PROJECTNAME)"/> <ProjectRoot Platform="Win64x" Name="$(PROJECTNAME)"/>
<ProjectRoot Platform="WinARM64EC" Name="$(PROJECTNAME)"/>
</Deployment> </Deployment>
<Platforms> <Platforms>
<Platform value="Win32">True</Platform> <Platform value="Win32">True</Platform>
......
...@@ -110,20 +110,6 @@ object FViewComplaintArchive: TFViewComplaintArchive ...@@ -110,20 +110,6 @@ object FViewComplaintArchive: TFViewComplaintArchive
WidthPercent = 100.000000000000000000 WidthPercent = 100.000000000000000000
OnClick = btnCmpArcClick OnClick = btnCmpArcClick
end end
object btnComplaintViewOnMapArc: TWebButton
Left = 342
Top = 259
Width = 96
Height = 25
Caption = 'Map'
ChildOrder = 1
ElementID = 'btn_complaint_view_on_map_arc'
ElementFont = efCSS
HeightStyle = ssAuto
HeightPercent = 100.000000000000000000
WidthPercent = 100.000000000000000000
OnClick = btnComplaintViewOnMapArcClick
end
object xdwcComplaintArchive: TXDataWebClient object xdwcComplaintArchive: TXDataWebClient
Connection = DMConnection.ApiConnection Connection = DMConnection.ApiConnection
Left = 292 Left = 292
......
...@@ -81,11 +81,4 @@ ...@@ -81,11 +81,4 @@
</div> </div>
</div> </div>
<button id="btn_complaint_view_on_map_arc"
type="button"
class="btn btn-primary btn-sm shadow position-fixed"
style="right: 15px; bottom: 25px; z-index: 1040;">
Map
</button>
</div> </div>
...@@ -26,13 +26,10 @@ type ...@@ -26,13 +26,10 @@ type
btnREMArc: TWebButton; btnREMArc: TWebButton;
btnE911Arc: TWebButton; btnE911Arc: TWebButton;
btnCmpArc: TWebButton; btnCmpArc: TWebButton;
btnComplaintViewOnMapArc: TWebButton;
procedure WebFormCreate(Sender: TObject);
procedure btnCmpArcClick(Sender: TObject); procedure btnCmpArcClick(Sender: TObject);
procedure btnE911ArcClick(Sender: TObject); procedure btnE911ArcClick(Sender: TObject);
procedure btnREMArcClick(Sender: TObject); procedure btnREMArcClick(Sender: TObject);
procedure btnUntArcClick(Sender: TObject); procedure btnUntArcClick(Sender: TObject);
procedure btnComplaintViewOnMapArcClick(Sender: TObject);
private private
FComplaintId: string; FComplaintId: string;
FCfsId: string; FCfsId: string;
...@@ -44,6 +41,8 @@ type ...@@ -44,6 +41,8 @@ type
FShowREM: Boolean; FShowREM: Boolean;
FShowUNT: Boolean; FShowUNT: Boolean;
FInitialized: Boolean;
procedure WireUi; procedure WireUi;
procedure SetTextById(const Id, Value: string); procedure SetTextById(const Id, Value: string);
procedure SetHiddenById(const Id: string; Hidden: Boolean); procedure SetHiddenById(const Id: string; Hidden: Boolean);
...@@ -56,8 +55,13 @@ type ...@@ -56,8 +55,13 @@ type
function GetMemoTypeCode(const MemoType: string): string; function GetMemoTypeCode(const MemoType: string): string;
function MemoTypeLabel(const MemoType: string): string; function MemoTypeLabel(const MemoType: string): string;
function MemoTypeInList(const memoType: string; const list: array of string): Boolean;
procedure SetRemarksData(dataArr: TJSArray);
function DeriveStatusFromDates(const dateDispatched, dateArrived, dateCleared: string): string;
public public
class function CreateForm(const AHostId, AComplaintId: string): TFViewComplaintArchive; class function CreateForm(const AHostId, AComplaintId: string): TWebForm;
procedure InitializeForm;
end; end;
implementation implementation
...@@ -67,14 +71,32 @@ uses ...@@ -67,14 +71,32 @@ uses
{$R *.dfm} {$R *.dfm}
class function TFViewComplaintArchive.CreateForm(const AHostId, AComplaintId: string): TFViewComplaintArchive; class function TFViewComplaintArchive.CreateForm(const AHostId, AComplaintId: string): TWebForm;
procedure AfterCreate(AForm: TObject);
begin
with TFViewComplaintArchive(AForm) do
begin
FComplaintId := AComplaintId;
InitializeForm;
end;
end;
begin begin
Result := TFViewComplaintArchive(inherited CreateNew(AHostId)); Application.CreateForm(TFViewComplaintArchive, AHostId, Result, @AfterCreate);
Result.FComplaintId := AComplaintId;
end; end;
procedure TFViewComplaintArchive.WebFormCreate(Sender: TObject);
procedure TFViewComplaintArchive.InitializeForm;
begin begin
if FInitialized then
Exit;
FInitialized := True;
xdwcComplaintArchive.Connection := DMConnection.ApiConnection;
xdwdsRemarksArc.Connection := DMConnection.ApiConnection;
FAllMemos := nil; FAllMemos := nil;
FShowCMP := True; FShowCMP := True;
...@@ -142,21 +164,27 @@ var ...@@ -142,21 +164,27 @@ var
rootObj: TJSObject; rootObj: TJSObject;
dataObj: TJSObject; dataObj: TJSObject;
business: string; business: string;
dateDispatchedText: string;
dateArrivedText: string;
dateClearedText: string;
statusText: string;
begin begin
resp := await(xdwcComplaintArchive.RawInvokeAsync('IApiService.GetComplaintArchiveDetails', [FComplaintId])); resp := await(xdwcComplaintArchive.RawInvokeAsync('IApiService.GetComplaintArchiveDetails', [FComplaintId]));
rootObj := TJSObject(resp.Result); rootObj := TJSObject(resp.Result);
dataObj := TJSObject(rootObj['data']); dataObj := TJSObject(rootObj['data']);
// Summary title (optional) SetTextById('lbl_summary_title_arc', 'Summary for Complaint ' + string(dataObj['Complaint']));
SetTextById('lbl_summary_title_arc', 'Summary');
SetTextById('lbl_priority_arc', string(dataObj['Priority'])); SetTextById('lbl_priority_arc', string(dataObj['Priority']));
SetTextById('lbl_dispatch_code_arc', string(dataObj['DispatchCodeDesc'])); SetTextById('lbl_dispatch_code_arc', string(dataObj['DispatchCodeDesc']));
SetTextById('lbl_dispatch_district_arc', string(dataObj['DispatchDistrict'])); SetTextById('lbl_dispatch_district_arc', string(dataObj['DispatchDistrict']));
SetTextById('lbl_address_arc', string(dataObj['Address'])); SetTextById('lbl_address_arc', string(dataObj['Address']));
business := '';
if dataObj.hasOwnProperty('Business') then
business := string(dataObj['Business']); business := string(dataObj['Business']);
if business <> '' then
if Trim(business) <> '' then
begin begin
SetTextById('lbl_business_arc', business); SetTextById('lbl_business_arc', business);
SetHiddenById('row_business_arc', False); SetHiddenById('row_business_arc', False);
...@@ -164,15 +192,30 @@ begin ...@@ -164,15 +192,30 @@ begin
else else
SetHiddenById('row_business_arc', True); SetHiddenById('row_business_arc', True);
// Status: same logic style as details; simplest is derive from timestamps if you want, dateDispatchedText := '';
// but archive view can just show blank unless you prefer the full logic. dateArrivedText := '';
// If your archive endpoint includes a computed Status, you can display it directly. dateClearedText := '';
if dataObj.hasOwnProperty('DateDispatched') then
dateDispatchedText := string(dataObj['DateDispatched']);
if dataObj.hasOwnProperty('DateArrived') then
dateArrivedText := string(dataObj['DateArrived']);
if dataObj.hasOwnProperty('DateCleared') then
dateClearedText := string(dataObj['DateCleared']);
statusText := '';
if dataObj.hasOwnProperty('Status') then if dataObj.hasOwnProperty('Status') then
SetTextById('lbl_status_arc', string(dataObj['Status'])) statusText := string(dataObj['Status']);
else
SetTextById('lbl_status_arc', ''); if Trim(statusText) = '' then
statusText := DeriveStatusFromDates(dateDispatchedText, dateArrivedText, dateClearedText);
// CFSId is needed for memos SetTextById('lbl_status_arc', statusText);
FCfsId := '';
if dataObj.hasOwnProperty('CFSId') then
FCfsId := string(dataObj['CFSId']); FCfsId := string(dataObj['CFSId']);
await(LoadMemosAsync); await(LoadMemosAsync);
...@@ -180,30 +223,41 @@ end; ...@@ -180,30 +223,41 @@ end;
function TFViewComplaintArchive.MemoTypeLabel(const MemoType: string): string; function TFViewComplaintArchive.MemoTypeLabel(const MemoType: string): string;
var var
c: string; code: string;
begin begin
c := GetMemoTypeCode(MemoType); code := GetMemoTypeCode(MemoType);
if c = 'CMP' then Exit('CMP');
if c = 'E911' then Exit('E-911'); if code = 'CMP' then
if c = 'REM' then Exit('REM'); Exit('CMP');
if c = 'UNT' then Exit('UNT'); if code = 'E911' then
Exit('E-911');
if code = 'REM' then
Exit('REM');
if code = 'UNT' then
Exit('UNT');
Result := MemoType; Result := MemoType;
end; end;
function TFViewComplaintArchive.GetMemoTypeCode(const MemoType: string): string; function TFViewComplaintArchive.GetMemoTypeCode(const MemoType: string): string;
var var
mt: string; memoTypeCode: string;
begin begin
mt := LowerCase(Trim(MemoType)); memoTypeCode := Trim(MemoType);
if MemoTypeInList(memoTypeCode, ['30', '31', '32', '33', '34']) then
Exit('UNT');
// Match whatever your server returns in GetComplaintMemos: if MemoTypeInList(memoTypeCode, ['3', '4']) then
// Keep these mappings aligned with ComplaintDetails. Exit('CMP');
if (mt = 'cmp') or (mt = 'complaint') then Exit('CMP');
if (mt = 'e-911') or (mt = 'e911') then Exit('E911');
if (mt = 'rem') or (mt = 'remarks') then Exit('REM');
if (mt = 'unt') or (mt = 'unit') then Exit('UNT');
Result := UpperCase(mt); if MemoTypeInList(memoTypeCode, ['2']) then
Exit('E911');
if MemoTypeInList(memoTypeCode, ['1']) then
Exit('REM');
Result := memoTypeCode;
end; end;
[async] procedure TFViewComplaintArchive.LoadMemosAsync; [async] procedure TFViewComplaintArchive.LoadMemosAsync;
...@@ -218,6 +272,9 @@ begin ...@@ -218,6 +272,9 @@ begin
// Reset dataset // Reset dataset
xdwdsRemarksArc.Close; xdwdsRemarksArc.Close;
if Trim(FCfsId) = '' then
Exit;
resp := await(xdwcComplaintArchive.RawInvokeAsync('IApiService.GetComplaintMemos', [FCfsId])); resp := await(xdwcComplaintArchive.RawInvokeAsync('IApiService.GetComplaintMemos', [FCfsId]));
rootObj := TJSObject(resp.Result); rootObj := TJSObject(resp.Result);
dataArr := TJSArray(rootObj['data']); dataArr := TJSArray(rootObj['data']);
...@@ -265,8 +322,7 @@ begin ...@@ -265,8 +322,7 @@ begin
filtered.push(memoObj); filtered.push(memoObj);
end; end;
xdwdsRemarksArc.SetJsonData(filtered); SetRemarksData(filtered);
xdwdsRemarksArc.Open;
end; end;
procedure TFViewComplaintArchive.btnCmpArcClick(Sender: TObject); procedure TFViewComplaintArchive.btnCmpArcClick(Sender: TObject);
...@@ -297,10 +353,36 @@ begin ...@@ -297,10 +353,36 @@ begin
ApplyMemoFilters; ApplyMemoFilters;
end; end;
procedure TFViewComplaintArchive.btnComplaintViewOnMapArcClick(Sender: TObject); function TFViewComplaintArchive.MemoTypeInList(const memoType: string; const list: array of string): Boolean;
var
i: Integer;
begin
Result := False;
for i := Low(list) to High(list) do
begin
if memoType = list[i] then
Exit(True);
end;
end;
procedure TFViewComplaintArchive.SetRemarksData(dataArr: TJSArray);
begin
xdwdsRemarksArc.Close;
xdwdsRemarksArc.SetJsonData(dataArr);
xdwdsRemarksArc.Open;
end;
function TFViewComplaintArchive.DeriveStatusFromDates(const dateDispatched, dateArrived, dateCleared: string): string;
begin begin
if Assigned(FViewMain) then if Trim(dateCleared) <> '' then
FViewMain.ShowMapFocusComplaint(FComplaintId); Exit('Cleared');
if Trim(dateArrived) <> '' then
Exit('On Scene');
if Trim(dateDispatched) <> '' then
Exit('Dispatched');
Result := 'Pending';
end; end;
end. end.
...@@ -373,6 +373,20 @@ object FViewComplaintDetails: TFViewComplaintDetails ...@@ -373,6 +373,20 @@ object FViewComplaintDetails: TFViewComplaintDetails
WidthPercent = 100.000000000000000000 WidthPercent = 100.000000000000000000
OnClick = btnComplaintViewOnMapClick OnClick = btnComplaintViewOnMapClick
end end
object btnArchive: TWebButton
Left = 612
Top = 165
Width = 96
Height = 25
Caption = 'Archive Details'
ChildOrder = 14
ElementID = 'btn_archive'
ElementFont = efCSS
HeightStyle = ssAuto
HeightPercent = 100.000000000000000000
WidthPercent = 100.000000000000000000
OnClick = btnArchiveClick
end
object xdwcComplaintDetails: TXDataWebClient object xdwcComplaintDetails: TXDataWebClient
Connection = DMConnection.ApiConnection Connection = DMConnection.ApiConnection
Left = 384 Left = 384
......
...@@ -78,6 +78,11 @@ ...@@ -78,6 +78,11 @@
<button type="button" class="btn btn-success btn-sm px-3 active" id="btn_rem" aria-pressed="true">REM</button> <button type="button" class="btn btn-success btn-sm px-3 active" id="btn_rem" aria-pressed="true">REM</button>
<button type="button" class="btn btn-success btn-sm px-3 active" id="btn_unt" aria-pressed="true">UNT</button> <button type="button" class="btn btn-success btn-sm px-3 active" id="btn_unt" aria-pressed="true">UNT</button>
</div> </div>
<!-- History action row (only visible on History tab) -->
<div class="d-flex flex-wrap gap-2 justify-content-center mt-2 tab-hidden" id="row_history_actions">
<button type="button" class="btn btn-secondary btn-sm px-3 disabled" id="btn_archive" disabled>Archive</button>
</div>
</div> </div>
</div> </div>
......
...@@ -55,6 +55,7 @@ type ...@@ -55,6 +55,7 @@ type
lstWarnings: TWebDBListControl; lstWarnings: TWebDBListControl;
btnComplaintViewOnMap: TWebButton; btnComplaintViewOnMap: TWebButton;
xdwdsHistoryComplaintId: TStringField; xdwdsHistoryComplaintId: TStringField;
btnArchive: TWebButton;
procedure btnRemarksClick(Sender: TObject); procedure btnRemarksClick(Sender: TObject);
procedure btnHistoryClick(Sender: TObject); procedure btnHistoryClick(Sender: TObject);
...@@ -65,6 +66,7 @@ type ...@@ -65,6 +66,7 @@ type
procedure btnE911Click(Sender: TObject); procedure btnE911Click(Sender: TObject);
procedure btnREMClick(Sender: TObject); procedure btnREMClick(Sender: TObject);
procedure btnUntClick(Sender: TObject); procedure btnUntClick(Sender: TObject);
procedure btnArchiveClick(Sender: TObject);
private private
FComplaintId: string; FComplaintId: string;
FCfsId: string; FCfsId: string;
...@@ -85,6 +87,9 @@ type ...@@ -85,6 +87,9 @@ type
FContactsLoaded: Boolean; FContactsLoaded: Boolean;
FWarningsLoaded: Boolean; FWarningsLoaded: Boolean;
FSelectedArchiveComplaintId: string;
FHistorySelectionBound: Boolean;
procedure WireUi; procedure WireUi;
procedure CloseClick(Event: TJSEvent); procedure CloseClick(Event: TJSEvent);
...@@ -101,6 +106,11 @@ type ...@@ -101,6 +106,11 @@ type
procedure UpdateTabButtonCss(const activeTabName: string); procedure UpdateTabButtonCss(const activeTabName: string);
procedure SetButtonEnabledById(const buttonId: string; enabled: Boolean); procedure SetButtonEnabledById(const buttonId: string; enabled: Boolean);
procedure BindHistoryRowSelection;
procedure HistoryTableClick(Event: TJSEvent);
procedure ClearHistorySelection;
procedure SelectHistoryRowByIndex(rowIndex: Integer);
procedure ApplyRemarksFilters; procedure ApplyRemarksFilters;
[async] procedure ApplyTabAsync(const tabName: string); [async] procedure ApplyTabAsync(const tabName: string);
...@@ -163,6 +173,9 @@ begin ...@@ -163,6 +173,9 @@ begin
FContactsLoaded := False; FContactsLoaded := False;
FWarningsLoaded := False; FWarningsLoaded := False;
FSelectedArchiveComplaintId := '';
FHistorySelectionBound := False;
WireUi; WireUi;
LoadComplaintAsync; LoadComplaintAsync;
end; end;
...@@ -174,6 +187,8 @@ begin ...@@ -174,6 +187,8 @@ begin
btnClose := Document.getElementById('btn_close_complaint_details'); btnClose := Document.getElementById('btn_close_complaint_details');
if btnClose <> nil then if btnClose <> nil then
btnClose.addEventListener('click', @CloseClick); btnClose.addEventListener('click', @CloseClick);
BindHistoryRowSelection;
end; end;
procedure TFViewComplaintDetails.CloseClick(Event: TJSEvent); procedure TFViewComplaintDetails.CloseClick(Event: TJSEvent);
...@@ -348,16 +363,121 @@ begin ...@@ -348,16 +363,121 @@ begin
end; end;
end; end;
procedure TFViewComplaintDetails.BindHistoryRowSelection;
var
el: TJSElement;
begin
if FHistorySelectionBound then
Exit;
el := Document.getElementById('tbl_history');
if el = nil then
Exit;
el.addEventListener('click', @HistoryTableClick);
FHistorySelectionBound := True;
end;
procedure TFViewComplaintDetails.HistoryTableClick(Event: TJSEvent);
var
rowIndex: Integer;
begin
rowIndex := -1;
asm
var target = Event.target;
var row = target.closest('tbody tr');
if (!row) {
return;
}
var rows = Array.prototype.slice.call(row.parentNode.children);
rows.forEach(function(r) {
r.classList.remove('table-primary');
});
row.classList.add('table-primary');
rowIndex = rows.indexOf(row);
end;
SelectHistoryRowByIndex(rowIndex);
end;
procedure TFViewComplaintDetails.ClearHistorySelection;
begin
FSelectedArchiveComplaintId := '';
SetButtonEnabledById('btn_archive', False);
asm
var host = document.getElementById('tbl_history');
if (host) {
var rows = host.querySelectorAll('tbody tr');
rows.forEach(function(row) {
row.classList.remove('table-primary');
});
}
end;
end;
procedure TFViewComplaintDetails.SelectHistoryRowByIndex(rowIndex: Integer);
var
i: Integer;
begin
FSelectedArchiveComplaintId := '';
if rowIndex < 0 then
begin
SetButtonEnabledById('btn_archive', False);
Exit;
end;
if not xdwdsHistory.Active then
begin
SetButtonEnabledById('btn_archive', False);
Exit;
end;
xdwdsHistory.First;
for i := 0 to rowIndex - 1 do
begin
if xdwdsHistory.Eof then
Break;
xdwdsHistory.Next;
end;
if xdwdsHistory.Eof then
begin
SetButtonEnabledById('btn_archive', False);
Exit;
end;
FSelectedArchiveComplaintId := xdwdsHistory.FieldByName('ComplaintId').AsString;
SetButtonEnabledById('btn_archive', Trim(FSelectedArchiveComplaintId) <> '');
end;
procedure TFViewComplaintDetails.SetActiveTab(const tabName: string); procedure TFViewComplaintDetails.SetActiveTab(const tabName: string);
begin begin
SetHiddenById('tbl_remarks', tabName <> 'remarks'); SetHiddenById('tbl_remarks', tabName <> 'remarks');
SetHiddenById('tbl_history', tabName <> 'history'); SetHiddenById('tbl_history', tabName <> 'history');
if tabName <> 'history' then
ClearHistorySelection;
SetHiddenById('tbl_contacts', tabName <> 'contacts'); SetHiddenById('tbl_contacts', tabName <> 'contacts');
SetHiddenById('tbl_warnings', tabName <> 'warnings'); SetHiddenById('tbl_warnings', tabName <> 'warnings');
SetHiddenById('lst_warnings', tabName <> 'warnings'); SetHiddenById('lst_warnings', tabName <> 'warnings');
SetHiddenById('row_remarks_toggles', tabName <> 'remarks'); SetHiddenById('row_remarks_toggles', tabName <> 'remarks');
SetHiddenById('row_history_actions', tabName <> 'history');
UpdateTabButtonCss(tabName); UpdateTabButtonCss(tabName);
end; end;
...@@ -497,6 +617,15 @@ begin ...@@ -497,6 +617,15 @@ begin
ApplyTabAsync('warnings'); ApplyTabAsync('warnings');
end; end;
procedure TFViewComplaintDetails.btnArchiveClick(Sender: TObject);
begin
if Trim(FSelectedArchiveComplaintId) = '' then
Exit;
if Assigned(FViewMain) then
FViewMain.ShowComplaintArchive(FSelectedArchiveComplaintId);
end;
procedure TFViewComplaintDetails.btnCmpClick(Sender: TObject); procedure TFViewComplaintDetails.btnCmpClick(Sender: TObject);
begin begin
FShowCmp := not FShowCmp; FShowCmp := not FShowCmp;
...@@ -684,6 +813,9 @@ begin ...@@ -684,6 +813,9 @@ begin
dataArr := TJSArray(rootObj['data']); dataArr := TJSArray(rootObj['data']);
SetDataSetJsonData(xdwdsHistory, dataArr); SetDataSetJsonData(xdwdsHistory, dataArr);
BindHistoryRowSelection;
ClearHistorySelection;
FHistoryLoaded := True; FHistoryLoaded := True;
end; end;
......
...@@ -44,20 +44,18 @@ object FViewComplaints: TFViewComplaints ...@@ -44,20 +44,18 @@ object FViewComplaints: TFViewComplaints
ItemTemplate = ItemTemplate =
'<div class="list-section-header small fw-semibold bg-secondary t' + '<div class="list-section-header small fw-semibold bg-secondary t' +
'ext-white rounded-1 px-2 mb-1">(%DistrictHeader%)</div><div clas' + 'ext-white rounded-1 px-2 mb-1">(%DistrictHeader%)</div><div clas' +
's="card border shadow-sm" style="--bs-card-bg:(%PriorityColor%);' + 's="card border shadow-sm"><div class="card-body py-2 px-3 d-flex' +
'--bs-card-color:(%PriorityTextColor%);"><div class="card-body py' + ' gap-2"><div class="flex-grow-1"><div class="fw-bold text-upperc' +
'-2 px-3 d-flex gap-2"><div class="flex-grow-1"><div class="fw-bo' + 'ase small">(%Priority%): (%DispatchCodeDesc%)</div><div class="s' +
'ld text-uppercase small">(%Priority%): (%DispatchCodeDesc%)</div' + 'mall">(%Address%)</div><div class="small d-none complaint-busine' +
'><div class="small">(%Address%)</div><div class="small d-none co' + 'ss" data-business="(%Business%)">(%Business%)</div><div class="s' +
'mplaint-business" data-business="(%Business%)">(%Business%)</div' + 'mall text-opacity-75">(%Complaint%): (%Status%)&nbsp;&nbsp;(%Age' +
'><div class="small text-opacity-75">(%Complaint%): (%Status%)&nb' + 'ncyLine%)</div><div class="small text-opacity-75">(%DateReported' +
'sp;&nbsp;(%DistrictSector%)</div><div class="small text-opacity-' + '%)</div></div><div class="d-flex flex-column justify-content-cen' +
'75">(%DateReported%)</div></div><div class="d-flex flex-column j' + 'ter gap-1"><button type="button" class="btn btn-primary btn-sm c' +
'ustify-content-center gap-1"><button type="button" class="btn bt' + 'omplaint-details-btn" data-id="(%ComplaintId%)">Details</button>' +
'n-primary btn-sm complaint-details-btn" data-id="(%ComplaintId%)' + '<button type="button" class="btn btn-primary btn-sm btn-complain' +
'">Details</button><button type="button" class="btn btn-primary b' + 't-map" data-id="(%ComplaintId%)">Map</button></div></div></div>'
'tn-sm btn-complaint-map" data-id="(%ComplaintId%)">Map</button><' +
'/div></div></div>'
ListSource = wdsComplaints ListSource = wdsComplaints
end end
object xdwcComplaints: TXDataWebClient object xdwcComplaints: TXDataWebClient
...@@ -111,8 +109,13 @@ object FViewComplaints: TFViewComplaints ...@@ -111,8 +109,13 @@ object FViewComplaints: TFViewComplaints
object xdwdsComplaintsPriorityTextColor: TStringField object xdwdsComplaintsPriorityTextColor: TStringField
FieldName = 'PriorityTextColor' FieldName = 'PriorityTextColor'
end end
object xdwdsComplaintsDistrictSector: TStringField object xdwdsComplaintsStatusTextColor: TStringField
FieldName = 'DistrictSector' FieldName = 'StatusTextColor'
Size = 80
end
object xdwdsComplaintsAgencyLine: TStringField
FieldName = 'AgencyLine'
Size = 80
end end
end end
object wdsComplaints: TWebDataSource object wdsComplaints: TWebDataSource
......
<div class="d-flex flex-column h-100"> <div class="d-flex flex-column h-100">
<!-- Header / controls (non-scrolling) -->
<div class="flex-shrink-0">
<!-- Search bar under local navbar -->
<div class="bg-light border-bottom py-2">
<div class="container-fluid">
<div class="input-group">
<span class="input-group-text bg-white"><i class="fa fa-search"></i></span>
<input id="complaints_search" class="form-control" placeholder="Search...">
</div>
</div>
</div>
</div>
<!-- Scrolling list area --> <!-- Scrolling list area -->
<div class="flex-grow-1 overflow-auto" style="min-height:0;"> <div class="flex-grow-1 overflow-auto" style="min-height:0;">
<div class="container-fluid mt-2"> <div class="container-fluid mt-2">
......
...@@ -30,8 +30,9 @@ type ...@@ -30,8 +30,9 @@ type
xdwdsComplaintsStatusColor: TStringField; xdwdsComplaintsStatusColor: TStringField;
xdwdsComplaintsPriorityColor: TStringField; xdwdsComplaintsPriorityColor: TStringField;
xdwdsComplaintsPriorityTextColor: TStringField; xdwdsComplaintsPriorityTextColor: TStringField;
xdwdsComplaintsDistrictSector: TStringField;
xdwdsComplaintsBusiness: TStringField; xdwdsComplaintsBusiness: TStringField;
xdwdsComplaintsStatusTextColor: TStringField;
xdwdsComplaintsAgencyLine: TStringField;
procedure WebFormCreate(Sender: TObject); procedure WebFormCreate(Sender: TObject);
procedure WebFormDestroy(Sender: TObject); procedure WebFormDestroy(Sender: TObject);
private private
......
...@@ -53,10 +53,7 @@ ...@@ -53,10 +53,7 @@
</button> </button>
</div> </div>
<div class="card-footer text-muted small"> <div class="card-footer text-muted small">
Please use your CHARMS username &amp; password to login.<br><br> Please use your lems username &amp; password to login.
For non-CHARMS users, please call the CPS Help Desk at
(716) 858-7773 during business hours<br>
or email: <a href="mailto:CPSHelpdesk@erie.gov">CPSHelpdesk@erie.gov</a>
</div> </div>
</div> </div>
</div> </div>
......
...@@ -16,18 +16,6 @@ object FViewMain: TFViewMain ...@@ -16,18 +16,6 @@ object FViewMain: TFViewMain
Visible = False Visible = False
WidthPercent = 100.000000000000000000 WidthPercent = 100.000000000000000000
end end
object wllblLogout: TWebLinkLabel
Left = 551
Top = 85
Width = 41
Height = 15
ElementID = 'dropdown.menu.logout'
Visible = False
HeightPercent = 100.000000000000000000
WidthPercent = 100.000000000000000000
OnClick = wllblLogoutClick
Caption = ' Logout'
end
object lblAppTitle: TWebLabel object lblAppTitle: TWebLabel
Left = 57 Left = 57
Top = 31 Top = 31
...@@ -82,6 +70,18 @@ object FViewMain: TFViewMain ...@@ -82,6 +70,18 @@ object FViewMain: TFViewMain
ElementID = 'pnl_main' ElementID = 'pnl_main'
ChildOrder = 3 ChildOrder = 3
TabOrder = 0 TabOrder = 0
object WebButton1: TWebButton
Left = 456
Top = 20
Width = 96
Height = 25
Caption = 'WebButton1'
ElementClassName = 'btn btn-light'
ElementFont = efCSS
HeightStyle = ssAuto
HeightPercent = 100.000000000000000000
WidthPercent = 100.000000000000000000
end
end end
object WebMessageDlg1: TWebMessageDlg object WebMessageDlg1: TWebMessageDlg
Left = 47 Left = 47
...@@ -159,7 +159,7 @@ object FViewMain: TFViewMain ...@@ -159,7 +159,7 @@ object FViewMain: TFViewMain
end end
object pnlUnits: TWebPanel object pnlUnits: TWebPanel
Left = 740 Left = 740
Top = 180 Top = 182
Width = 227 Width = 227
Height = 139 Height = 139
ElementID = 'pnl_units' ElementID = 'pnl_units'
...@@ -177,7 +177,7 @@ object FViewMain: TFViewMain ...@@ -177,7 +177,7 @@ object FViewMain: TFViewMain
end end
object pnlDetails: TWebPanel object pnlDetails: TWebPanel
Left = 992 Left = 992
Top = 277 Top = 275
Width = 163 Width = 163
Height = 114 Height = 114
ElementID = 'pnl_details' ElementID = 'pnl_details'
...@@ -197,6 +197,42 @@ object FViewMain: TFViewMain ...@@ -197,6 +197,42 @@ object FViewMain: TFViewMain
OnClick = btnDetailsModalCloseClick OnClick = btnDetailsModalCloseClick
end end
end end
object pnlArchive: TWebPanel
Left = 992
Top = 395
Width = 163
Height = 114
ElementID = 'pnl_archive'
ChildOrder = 15
ElementFont = efCSS
TabOrder = 10
object btnArchiveModalClose: TWebButton
Left = 127
Top = 8
Width = 28
Height = 23
ElementID = 'btn_archive_modal_close'
ElementFont = efCSS
HeightStyle = ssAuto
HeightPercent = 100.000000000000000000
WidthPercent = 100.000000000000000000
OnClick = btnArchiveModalCloseClick
end
end
object btnLogout: TWebButton
Left = 438
Top = 66
Width = 96
Height = 25
Caption = 'Logout'
ChildOrder = 16
ElementID = 'btn_logout'
ElementFont = efCSS
HeightStyle = ssAuto
HeightPercent = 100.000000000000000000
WidthPercent = 100.000000000000000000
OnClick = btnLogoutClick
end
object xdwcBadgeCounts: TXDataWebClient object xdwcBadgeCounts: TXDataWebClient
Connection = DMConnection.ApiConnection Connection = DMConnection.ApiConnection
Left = 44 Left = 44
......
...@@ -11,8 +11,12 @@ ...@@ -11,8 +11,12 @@
<span id="lbl_main_title" class="navbar-brand text-light mb-0 ms-1"></span> <span id="lbl_main_title" class="navbar-brand text-light mb-0 ms-1"></span>
</div> </div>
<!-- Right: Connection label --> <!-- Right: Connection / Logout -->
<span id="view.main.lblconnection" class="navbar-text text-light ms-auto"></span>
<div class="d-flex align-items-center gap-2 ms-auto">
<span id="view.main.lblconnection" class="navbar-text text-light small"></span>
<button id="btn_logout" type="button" class="btn btn-outline-light btn-sm">Logout</button>
</div>
</div> </div>
</nav> </nav>
...@@ -22,7 +26,6 @@ ...@@ -22,7 +26,6 @@
<div id="pnl_units" class="flex-grow-1 position-relative p-0 overflow-hidden d-none" style="min-height:0;"></div> <div id="pnl_units" class="flex-grow-1 position-relative p-0 overflow-hidden d-none" style="min-height:0;"></div>
<div id="pnl_complaints" class="flex-grow-1 position-relative p-0 overflow-hidden d-none" style="min-height:0;"></div> <div id="pnl_complaints" class="flex-grow-1 position-relative p-0 overflow-hidden d-none" style="min-height:0;"></div>
<!-- Details modal (new panel pnl_details) --> <!-- Details modal (new panel pnl_details) -->
<div id="pnl_details" <div id="pnl_details"
class="position-fixed top-0 start-0 w-100 h-100 d-none d-flex justify-content-center align-items-center" class="position-fixed top-0 start-0 w-100 h-100 d-none d-flex justify-content-center align-items-center"
...@@ -38,6 +41,21 @@ ...@@ -38,6 +41,21 @@
</div> </div>
</div> </div>
<!-- Archive details modal -->
<div id="pnl_archive"
class="position-fixed top-0 start-0 w-100 h-100 d-none d-flex justify-content-center align-items-center"
style="background: rgba(0,0,0,0.35); z-index:1070;">
<div class="card shadow-lg w-100 mx-2 d-flex flex-column" style="max-width: 96vw; height: 92%;">
<div class="card-header d-flex align-items-center justify-content-between">
<h5 class="card-title mb-0" id="lbl_archive_title">Archived Complaint Details</h5>
<button id="btn_archive_modal_close" type="button" class="btn-close" aria-label="Close"></button>
</div>
<div class="card-body p-0 flex-grow-1 overflow-hidden" style="min-height:0;">
<div id="pnl_archive_host" class="h-100"></div>
</div>
</div>
</div>
<!-- Bottom Nav --> <!-- Bottom Nav -->
<nav id="bottom_nav" class="navbar navbar-dark bg-primary py-2 flex-shrink-0"> <nav id="bottom_nav" class="navbar navbar-dark bg-primary py-2 flex-shrink-0">
<div class="container-fluid"> <div class="container-fluid">
...@@ -58,7 +76,6 @@ ...@@ -58,7 +76,6 @@
</div> </div>
</div> </div>
</nav> </nav>
</div> </div>
<!-- Spinner --> <!-- Spinner -->
...@@ -81,7 +98,7 @@ ...@@ -81,7 +98,7 @@
Please contact EMSystems to solve the issue. Please contact EMSystems to solve the issue.
</div> </div>
<div class="modal-footer justify-content-center"> <div class="modal-footer justify-content-center">
<button type="button" id="btn_modal_restart" class="btn btn-primary">Back to Orders</button> <button type="button" id="btn_modal_restart" class="btn btn-primary">Restart Application</button>
</div> </div>
</div> </div>
</div> </div>
......
...@@ -12,7 +12,6 @@ uses ...@@ -12,7 +12,6 @@ uses
type type
TFViewMain = class(TWebForm) TFViewMain = class(TWebForm)
lblUsername: TWebLabel; lblUsername: TWebLabel;
wllblLogout: TWebLinkLabel;
pnlMain: TWebPanel; pnlMain: TWebPanel;
WebMessageDlg1: TWebMessageDlg; WebMessageDlg1: TWebMessageDlg;
lblAppTitle: TWebLabel; lblAppTitle: TWebLabel;
...@@ -31,15 +30,21 @@ type ...@@ -31,15 +30,21 @@ type
tmrGlobalRefresh: TWebTimer; tmrGlobalRefresh: TWebTimer;
pnlDetails: TWebPanel; pnlDetails: TWebPanel;
btnDetailsModalClose: TWebButton; btnDetailsModalClose: TWebButton;
pnlArchive: TWebPanel;
btnArchiveModalClose: TWebButton;
WebButton1: TWebButton;
btnLogout: TWebButton;
procedure WebFormCreate(Sender: TObject); procedure WebFormCreate(Sender: TObject);
procedure mnuLogoutClick(Sender: TObject); procedure mnuLogoutClick(Sender: TObject);
procedure wllblLogoutClick(Sender: TObject); procedure lblLogoutClick(Sender: TObject);
procedure btnUnitsClick(Sender: TObject); procedure btnUnitsClick(Sender: TObject);
procedure btnComplaintsClick(Sender: TObject); procedure btnComplaintsClick(Sender: TObject);
procedure btnMapClick(Sender: TObject); procedure btnMapClick(Sender: TObject);
procedure tmrBadgeCountsTimer(Sender: TObject); procedure tmrBadgeCountsTimer(Sender: TObject);
procedure tmrGlobalRefreshTimer(Sender: TObject); procedure tmrGlobalRefreshTimer(Sender: TObject);
procedure btnDetailsModalCloseClick(Sender: TObject); procedure btnDetailsModalCloseClick(Sender: TObject);
procedure btnArchiveModalCloseClick(Sender: TObject);
procedure btnLogoutClick(Sender: TObject);
private private
{ Private declarations } { Private declarations }
FUserInfo: string; FUserInfo: string;
...@@ -48,6 +53,7 @@ type ...@@ -48,6 +53,7 @@ type
FUnitsForm: TFViewUnits; FUnitsForm: TFViewUnits;
FComplaintsForm: TFViewComplaints; FComplaintsForm: TFViewComplaints;
FDetailsForm: TWebForm; FDetailsForm: TWebForm;
FArchiveForm: TWebForm;
FLogoutProc: TLogoutProc; FLogoutProc: TLogoutProc;
//WebSocketModule: Module.Websocket; //WebSocketModule: Module.Websocket;
[async] procedure RefreshBadgesAsync; [async] procedure RefreshBadgesAsync;
...@@ -55,6 +61,8 @@ type ...@@ -55,6 +61,8 @@ type
procedure SetHeaderTitle(const title: string); procedure SetHeaderTitle(const title: string);
procedure HideDetailsModal; procedure HideDetailsModal;
procedure ShowDetailsModal(const titleText: string); procedure ShowDetailsModal(const titleText: string);
procedure HideArchiveModal;
procedure ShowArchiveModal(const titleText: string);
type TActivePanel = (apNone, apMap, apUnits, apComplaints); type TActivePanel = (apNone, apMap, apUnits, apComplaints);
var var
...@@ -74,6 +82,7 @@ type ...@@ -74,6 +82,7 @@ type
class procedure Display(LogoutProc: TLogoutProc); class procedure Display(LogoutProc: TLogoutProc);
procedure ShowForm( AFormClass: TWebFormClass ); procedure ShowForm( AFormClass: TWebFormClass );
procedure ShowComplaintDetails(ComplaintId: string); procedure ShowComplaintDetails(ComplaintId: string);
procedure ShowComplaintArchive(ComplaintId: string);
procedure SetActiveNavButton(const BtnId: string); procedure SetActiveNavButton(const BtnId: string);
procedure ShowMapFocusUnit(const unitId: string); procedure ShowMapFocusUnit(const unitId: string);
procedure ShowMapFocusComplaint(const complaintId: string); procedure ShowMapFocusComplaint(const complaintId: string);
...@@ -95,12 +104,14 @@ uses ...@@ -95,12 +104,14 @@ uses
View.Users, View.Users,
View.EditUser, View.EditUser,
View.UnitDetails, View.UnitDetails,
View.ComplaintArchive,
Utils; Utils;
{$R *.dfm} {$R *.dfm}
const const
DETAILS_HOST_ID = 'pnl_details_host'; DETAILS_HOST_ID = 'pnl_details_host';
ARCHIVE_HOST_ID = 'pnl_archive_host';
procedure TFViewMain.WebFormCreate(Sender: TObject); procedure TFViewMain.WebFormCreate(Sender: TObject);
var var
...@@ -110,6 +121,7 @@ begin ...@@ -110,6 +121,7 @@ begin
lblUsername.Caption := ' ' + userName.ToLower + ' '; lblUsername.Caption := ' ' + userName.ToLower + ' ';
FChildForm := nil; FChildForm := nil;
FDetailsForm := nil; FDetailsForm := nil;
FArchiveForm := nil;
FActivePanel := apNone; FActivePanel := apNone;
FGlobalRefreshTick := 0; FGlobalRefreshTick := 0;
...@@ -127,6 +139,7 @@ begin ...@@ -127,6 +139,7 @@ begin
HidePanel(pnlUnits); HidePanel(pnlUnits);
HidePanel(pnlComplaints); HidePanel(pnlComplaints);
HidePanel(pnlDetails); HidePanel(pnlDetails);
HidePanel(pnlArchive);
if not Assigned(FMapForm) then if not Assigned(FMapForm) then
begin begin
...@@ -223,12 +236,17 @@ begin ...@@ -223,12 +236,17 @@ begin
end; end;
procedure TFViewMain.wllblLogoutClick(Sender: TObject); procedure TFViewMain.lblLogoutClick(Sender: TObject);
begin begin
FLogoutProc; FLogoutProc;
end; end;
procedure TFViewMain.btnArchiveModalCloseClick(Sender: TObject);
begin
HideArchiveModal;
end;
procedure TFViewMain.btnComplaintsClick(Sender: TObject); procedure TFViewMain.btnComplaintsClick(Sender: TObject);
begin begin
ShowForm(TFViewComplaints); ShowForm(TFViewComplaints);
...@@ -237,9 +255,15 @@ end; ...@@ -237,9 +255,15 @@ end;
procedure TFViewMain.btnDetailsModalCloseClick(Sender: TObject); procedure TFViewMain.btnDetailsModalCloseClick(Sender: TObject);
begin begin
HideArchiveModal;
HideDetailsModal; HideDetailsModal;
end; end;
procedure TFViewMain.btnLogoutClick(Sender: TObject);
begin
FLogoutProc;
end;
procedure TFViewMain.btnMapClick(Sender: TObject); procedure TFViewMain.btnMapClick(Sender: TObject);
begin begin
ShowForm(TFViewMap); ShowForm(TFViewMap);
...@@ -263,6 +287,7 @@ end; ...@@ -263,6 +287,7 @@ end;
procedure TFViewMain.ShowForm(AFormClass: TWebFormClass); procedure TFViewMain.ShowForm(AFormClass: TWebFormClass);
begin begin
HideArchiveModal;
HideDetailsModal; HideDetailsModal;
if AFormClass = TFViewMap then if AFormClass = TFViewMap then
...@@ -377,6 +402,16 @@ begin ...@@ -377,6 +402,16 @@ begin
FDetailsForm := TFViewComplaintDetails.CreateForm(DETAILS_HOST_ID, ComplaintId); FDetailsForm := TFViewComplaintDetails.CreateForm(DETAILS_HOST_ID, ComplaintId);
end; end;
procedure TFViewMain.ShowComplaintArchive(ComplaintId: string);
begin
ShowArchiveModal('Archived Complaint Details');
if Assigned(FArchiveForm) then
FArchiveForm.Free;
FArchiveForm := TFViewComplaintArchive.CreateForm(ARCHIVE_HOST_ID, ComplaintId);
end;
procedure TFViewMain.ShowUnitDetails(UnitId: string); procedure TFViewMain.ShowUnitDetails(UnitId: string);
begin begin
ShowDetailsModal('Unit Details'); ShowDetailsModal('Unit Details');
...@@ -513,4 +548,27 @@ begin ...@@ -513,4 +548,27 @@ begin
end; end;
procedure TFViewMain.HideArchiveModal;
begin
HidePanel(pnlArchive);
if Assigned(FArchiveForm) then
begin
FArchiveForm.Free;
FArchiveForm := nil;
end;
end;
procedure TFViewMain.ShowArchiveModal(const titleText: string);
var
el: TJSElement;
begin
ShowPanel(pnlArchive);
el := Document.getElementById('lbl_archive_title');
if el <> nil then
el.innerHTML := titleText;
end;
end. end.
...@@ -62,10 +62,10 @@ object FViewMap: TFViewMap ...@@ -62,10 +62,10 @@ object FViewMap: TFViewMap
end end
object httpReqGeoJson: TWebHttpRequest object httpReqGeoJson: TWebHttpRequest
ResponseType = rtText ResponseType = rtText
URL = 'assets/bpddistricts-updated.geojson' URL = 'assets/orleanscounty.geojson'
OnResponse = httpReqGeoJsonResponse OnResponse = httpReqGeoJsonResponse
Left = 114 Left = 116
Top = 696 Top = 698
end end
object xdwcMap: TXDataWebClient object xdwcMap: TXDataWebClient
Connection = DMConnection.ApiConnection Connection = DMConnection.ApiConnection
......
...@@ -8,7 +8,7 @@ uses ...@@ -8,7 +8,7 @@ uses
WEBLib.ExtCtrls, DB, WEBLib.WebCtrls, WEBLib.REST, VCL.TMSFNCTypes, VCL.TMSFNCUtils, WEBLib.ExtCtrls, DB, WEBLib.WebCtrls, WEBLib.REST, VCL.TMSFNCTypes, VCL.TMSFNCUtils,
VCL.TMSFNCGraphics, VCL.TMSFNCGraphicsTypes, VCL.TMSFNCCustomControl, VCL.TMSFNCWebBrowser, VCL.TMSFNCGraphics, VCL.TMSFNCGraphicsTypes, VCL.TMSFNCCustomControl, VCL.TMSFNCWebBrowser,
VCL.TMSFNCMaps, VCL.TMSFNCLeaflet, VCL.TMSFNCMapsCommonTypes, System.StrUtils, XData.Web.Client, VCL.TMSFNCMaps, VCL.TMSFNCLeaflet, VCL.TMSFNCMapsCommonTypes, System.StrUtils, XData.Web.Client,
XData.Web.Connection, ConnectionModule, Utils, uMapFilters; XData.Web.Connection, ConnectionModule, Utils, uMapFilters, JS, Web, uMapMarkerJs;
type type
TFViewMap = class(TWebForm) TFViewMap = class(TWebForm)
...@@ -21,8 +21,7 @@ type ...@@ -21,8 +21,7 @@ type
procedure lfMapMapInitialized(Sender: TObject); procedure lfMapMapInitialized(Sender: TObject);
[async] procedure httpReqGeoJsonResponse(Sender: TObject; AResponse: string); [async] procedure httpReqGeoJsonResponse(Sender: TObject; AResponse: string);
procedure lfMapCustomizeMarker(Sender: TObject; procedure lfMapCustomizeMarker(Sender: TObject; var ACustomizeMarker: string);
var ACustomizeMarker: string);
procedure lfMapCustomizeCSS(Sender: TObject; var ACustomizeCSS: string); procedure lfMapCustomizeCSS(Sender: TObject; var ACustomizeCSS: string);
procedure btnFindLocationClick(Sender: TObject); procedure btnFindLocationClick(Sender: TObject);
procedure tmrLocateTimer(Sender: TObject); procedure tmrLocateTimer(Sender: TObject);
...@@ -38,13 +37,17 @@ type ...@@ -38,13 +37,17 @@ type
FPendingFocusCoord: TTMSFNCMapsCoordinateRec; FPendingFocusCoord: TTMSFNCMapsCoordinateRec;
FPendingFocusZoom: Integer; FPendingFocusZoom: Integer;
FDoFocusZoom: Boolean; FDoFocusZoom: Boolean;
FGeoJsonLoadStep: Integer;
[async] procedure LoadPointsAsync(showBusy: Boolean); [async] procedure LoadPointsAsync(showBusy: Boolean);
function CarIconForDistrict(const DistrictCode: string): string;
procedure UpdateDeviceLocation(lat, lng: Double); procedure UpdateDeviceLocation(lat, lng: Double);
procedure StartDeviceLocation; procedure StartDeviceLocation;
procedure ApplyPendingUnitFocus; procedure ApplyPendingUnitFocus;
procedure ApplyPendingComplaintFocus; procedure ApplyPendingComplaintFocus;
procedure StartGeoJsonLoad;
procedure LoadGeoJsonLayer(const AResponse, ALayerName: string; FitLayer: Boolean);
function GetJsonString(obj: TJSObject; name: string): string;
function HtmlAttrEncode(const value: string): string;
public public
procedure FocusUnit(const unitId: string); procedure FocusUnit(const unitId: string);
procedure FocusComplaint(const complaintId: string); procedure FocusComplaint(const complaintId: string);
...@@ -56,17 +59,15 @@ var ...@@ -56,17 +59,15 @@ var
implementation implementation
uses
JS, Web;
{$R *.dfm} {$R *.dfm}
procedure TFViewMap.lfMapMapInitialized(Sender: TObject); procedure TFViewMap.lfMapMapInitialized(Sender: TObject);
begin begin
RegisterMapMarkerJs;
ShowSpinner('spinner'); ShowSpinner('spinner');
FUnitsLoaded := False; FUnitsLoaded := False;
FComplaintsLoaded := False; FComplaintsLoaded := False;
httpReqGeoJson.Execute; StartGeoJsonLoad;
asm asm
window.showComplaintDetails = function (id) { window.showComplaintDetails = function (id) {
try { try {
...@@ -140,58 +141,85 @@ begin ...@@ -140,58 +141,85 @@ begin
end; end;
end; end;
procedure TFViewMap.StartGeoJsonLoad;
begin
lfMap.BeginUpdate;
try
lfMap.Polygons.Clear;
finally
lfMap.EndUpdate;
end;
[async] procedure TFViewMap.httpReqGeoJsonResponse(Sender: TObject; AResponse: string); FGeoJsonLoadStep := 1;
httpReqGeoJson.URL := 'assets/wyomingtowns.geojson';
httpReqGeoJson.Execute;
end;
procedure TFViewMap.LoadGeoJsonLayer(const AResponse, ALayerName: string; FitLayer: Boolean);
var var
startIndex: Integer;
i: Integer; i: Integer;
P: TTMSFNCMapsPolygon; P: TTMSFNCMapsPolygon;
begin begin
startIndex := lfMap.Polygons.Count;
lfMap.BeginUpdate; lfMap.BeginUpdate;
try try
lfMap.Polygons.Clear; lfMap.LoadGeoJSONFromText(AResponse, True, FitLayer);
lfMap.LoadGeoJSONFromText(AResponse, True, Trim(FPendingUnitId) = '');
for i := 0 to lfMap.Polygons.Count - 1 do for i := startIndex to lfMap.Polygons.Count - 1 do
begin begin
P := lfMap.Polygons[i]; P := lfMap.Polygons[i];
case i of if SameText(ALayerName, 'Towns') then
0: begin begin
P.DisplayName := 'District A'; P.FillColor := HTMLToColor('#ffffff');
P.FillColor := HTMLToColor('#d3ffbe'); // light green P.FillOpacity := 0.0;
P.StrokeColor := HTMLToColor('#6ea85c'); // darker green P.StrokeColor := HTMLToColor('#000000');
end;
1: begin
P.DisplayName := 'District B';
P.FillColor := HTMLToColor('#ffbebe'); // light red
P.StrokeColor := HTMLToColor('#b34a4a'); // darker red
end;
2: begin
P.DisplayName := 'District C';
P.FillColor := HTMLToColor('#ffd37f'); // light orange
P.StrokeColor := HTMLToColor('#b36e00'); // darker orange
end;
3: begin
P.DisplayName := 'District D';
P.FillColor := HTMLToColor('#bed2ff'); // light blue
P.StrokeColor := HTMLToColor('#3c5ca8'); // darker blue
end;
4: begin
P.DisplayName := 'District E';
P.FillColor := HTMLToColor('#ffffbe'); // light yellow
P.StrokeColor := HTMLToColor('#a8a85c'); // darker yellow/olive
end;
end;
P.FillOpacity := 0.60;
P.StrokeOpacity := 1.0; P.StrokeOpacity := 1.0;
P.StrokeWidth := 2; P.StrokeWidth := 2;
end
else if SameText(ALayerName, 'Villages') then
begin
P.FillColor := HTMLToColor('#0d6efd');
P.FillOpacity := 0.0;
P.StrokeColor := HTMLToColor('#0d6efd');
P.StrokeOpacity := 1.0;
P.StrokeWidth := 2;
end;
end; end;
finally finally
lfMap.EndUpdate; lfMap.EndUpdate;
end; end;
end;
[async] procedure TFViewMap.httpReqGeoJsonResponse(Sender: TObject; AResponse: string);
var
layerName: string;
fitLayer: Boolean;
begin
if FGeoJsonLoadStep = 1 then
layerName := 'Towns'
else
layerName := 'Villages';
fitLayer :=
(FGeoJsonLoadStep = 1) and
(Trim(FPendingUnitId) = '') and
(Trim(FPendingComplaintId) = '');
LoadGeoJsonLayer(AResponse, layerName, fitLayer);
if FGeoJsonLoadStep = 1 then
begin
FGeoJsonLoadStep := 2;
httpReqGeoJson.URL := 'assets/wyomingvillages.geojson';
httpReqGeoJson.Execute;
Exit;
end;
FGeoJsonLoadStep := 0;
await(LoadPointsAsync(True)); await(LoadPointsAsync(True));
...@@ -202,23 +230,32 @@ begin ...@@ -202,23 +230,32 @@ begin
end; end;
end; end;
function TFViewMap.CarIconForDistrict(const DistrictCode: string): string;
var function TFViewMap.GetJsonString(obj: TJSObject; name: string): string;
U: string;
L: Char;
begin begin
U := UpperCase(Trim(DistrictCode)); Result := '';
if U = '' then if obj = nil then
Exit('assets/markers/car_default.png'); Exit;
L := U[1]; if obj[name] = nil then
case L of Exit;
'A','B','C','D','E','X':
Result := 'assets/markers/car_' + L + '.png'; Result := string(obj[name]);
else
Result := 'assets/markers/car_default.png'; if SameText(Trim(Result), 'undefined') or SameText(Trim(Result), 'null') then
end; Result := '';
end;
function TFViewMap.HtmlAttrEncode(const value: string): string;
begin
Result := value;
Result := StringReplace(Result, '&', '&amp;', [rfReplaceAll]);
Result := StringReplace(Result, '"', '&quot;', [rfReplaceAll]);
Result := StringReplace(Result, #39, '&#39;', [rfReplaceAll]);
Result := StringReplace(Result, '<', '&lt;', [rfReplaceAll]);
Result := StringReplace(Result, '>', '&gt;', [rfReplaceAll]);
end; end;
...@@ -230,9 +267,9 @@ var ...@@ -230,9 +267,9 @@ var
i, ui: Integer; i, ui: Integer;
m: TTMSFNCMapsMarker; m: TTMSFNCMapsMarker;
lat, lng: Double; lat, lng: Double;
uName, dist: string; uName, unitBadge, agencyType, agencyId, agencyName, agency: string;
unitId, callType, priorityText, statusText: string; unitId, callType, priorityText, statusText: string;
complaintId, codeDesc, dispatchDist, priority, business: string; complaintId, codeDesc, priority, priorityBadge, complaintStatusKey, business, address: string;
pngName, iconUrl, rowsHtml: string; pngName, iconUrl, rowsHtml: string;
officer1Lname, officer1Fname, officer1Empnum: string; officer1Lname, officer1Fname, officer1Empnum: string;
officer2Lname, officer2Fname, officer2Empnum: string; officer2Lname, officer2Fname, officer2Empnum: string;
...@@ -300,21 +337,44 @@ begin ...@@ -300,21 +337,44 @@ begin
lat := Double(item['Lat']); lat := Double(item['Lat']);
lng := Double(item['Lng']); lng := Double(item['Lng']);
uName := string(item['UnitName']); uName := GetJsonString(item, 'UnitName');
dist := string(item['District']); unitId := GetJsonString(item, 'UnitId');
unitId := string(item['UnitId']);
callType := string(item['CallType']); unitBadge := GetJsonString(item, 'UnitBadge');
priorityText := string(item['Priority']); if Trim(unitBadge) = '' then
statusText := string(item['Status']); unitBadge := uName;
updateTimeText := string(item['UpdateTime']); if Trim(unitBadge) = '' then
unitBadge := unitId;
officer1Lname := string(item['Officer1Lname']);
officer1Fname := string(item['Officer1Fname']); agencyId := GetJsonString(item, 'Agency');
officer1Empnum := string(item['Officer1Empnum']); agencyName := GetJsonString(item, 'AgencyName');
agency := agencyName;
officer2Lname := string(item['Officer2Lname']); if Trim(agency) = '' then
officer2Fname := string(item['Officer2Fname']); agency := agencyId;
officer2Empnum := string(item['Officer2Empnum']);
agencyType := UpperCase(Trim(GetJsonString(item, 'AgencyType')));
if agencyType = 'FIR' then
iconUrl := 'assets/markers/car_fire.png'
else if agencyType = 'EMS' then
iconUrl := 'assets/markers/car_ems.png'
else if agencyType = 'POL' then
iconUrl := 'assets/markers/car_police.png'
else
iconUrl := 'assets/markers/car_police.png';
callType := GetJsonString(item, 'CallType');
priorityText := GetJsonString(item, 'Priority');
statusText := GetJsonString(item, 'Status');
updateTimeText := GetJsonString(item, 'UpdateTime');
officer1Lname := GetJsonString(item, 'Officer1Lname');
officer1Fname := GetJsonString(item, 'Officer1Fname');
officer1Empnum := GetJsonString(item, 'Officer1Empnum');
officer2Lname := GetJsonString(item, 'Officer2Lname');
officer2Fname := GetJsonString(item, 'Officer2Fname');
officer2Empnum := GetJsonString(item, 'Officer2Empnum');
canShowDetailsText := ''; canShowDetailsText := '';
if item['CanShowDetails'] <> nil then if item['CanShowDetails'] <> nil then
...@@ -357,12 +417,17 @@ begin ...@@ -357,12 +417,17 @@ begin
m.Longitude := lng; m.Longitude := lng;
m.Title := 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="d-flex flex-column gap-1 px-1 py-1" style="width:260px;">' +
'<div class="fw-semibold small">' + '<div class="fw-semibold small">' +
'<span class="fw-bold">Unit:</span> ' + uName + '<span class="fw-bold">Unit:</span> ' + uName +
'</div>' + '</div>' +
IfThen(dist <> '', IfThen(agency <> '',
'<div class="small"><span class="fw-bold">District:</span> ' + dist + '</div>', '<div class="small"><span class="fw-bold">Agency:</span> ' + agency + '</div>',
'' ''
) + ) +
IfThen(Trim(callType) <> '', IfThen(Trim(callType) <> '',
...@@ -395,8 +460,9 @@ begin ...@@ -395,8 +460,9 @@ begin
) + ) +
'</div>'; '</div>';
m.DataString := 'unit|' + unitId; m.DataString := 'unit|' + unitId + '|' + StringReplace(unitBadge, '|', '/', [rfReplaceAll]);
m.IconURL := CarIconForDistrict(dist); m.IconURL := iconUrl;
end; end;
end; end;
...@@ -407,11 +473,27 @@ begin ...@@ -407,11 +473,27 @@ begin
begin begin
item := TJSObject(complaintsData[i]); item := TJSObject(complaintsData[i]);
complaintId := string(item['ComplaintId']); complaintId := GetJsonString(item, 'ComplaintId');
codeDesc := string(item['DispatchCodeDesc']); codeDesc := GetJsonString(item, 'DispatchCodeDesc');
dispatchDist := string(item['DispatchDistrict']);
priority := string(item['Priority']); agencyId := GetJsonString(item, 'Agency');
business := string(item['Business']); agencyName := GetJsonString(item, 'AgencyName');
agency := agencyName;
if Trim(agency) = '' then
agency := agencyId;
priority := GetJsonString(item, 'Priority');
priorityBadge := GetJsonString(item, 'PriorityBadge');
if Trim(priorityBadge) = '' then
priorityBadge := '?';
complaintStatusKey := LowerCase(Trim(GetJsonString(item, 'ComplaintStatusKey')));
if complaintStatusKey = '' then
complaintStatusKey := 'notattached';
business := GetJsonString(item, 'Business');
address := GetJsonString(item, 'Address');
lat := Double(item['Lat']); lat := Double(item['Lat']);
lng := Double(item['Lng']); lng := Double(item['Lng']);
...@@ -419,11 +501,11 @@ begin ...@@ -419,11 +501,11 @@ begin
if ((lat = 0) and (lng = 0)) or (Abs(lat) > 90) or (Abs(lng) > 180) then if ((lat = 0) and (lng = 0)) or (Abs(lat) > 90) or (Abs(lng) > 180) then
Continue; Continue;
pngName := string(item['pngName']); pngName := GetJsonString(item, 'pngName');
if Trim(pngName) <> '' then if Trim(pngName) <> '' then
iconUrl := 'assets/markers/' + pngName iconUrl := 'assets/markers/' + pngName
else else
iconUrl := 'assets/markers/default_5-9.png'; iconUrl := 'assets/markers/default_notattached.png';
rowsHtml := ''; rowsHtml := '';
units := TJSArray(item['Units']); units := TJSArray(item['Units']);
...@@ -436,9 +518,9 @@ begin ...@@ -436,9 +518,9 @@ begin
rowsHtml := rowsHtml + rowsHtml := rowsHtml +
'<tr>' + '<tr>' +
'<td>' + string(uo['Unit']) + '</td>' + '<td>' + GetJsonString(uo, 'Unit') + '</td>' +
'<td>' + string(uo['Status']) + '</td>' + '<td>' + GetJsonString(uo, 'Status') + '</td>' +
'<td>' + string(uo['Updated']) + '</td>' + '<td>' + GetJsonString(uo, 'Updated') + '</td>' +
'</tr>'; '</tr>';
end; end;
end end
...@@ -453,6 +535,13 @@ begin ...@@ -453,6 +535,13 @@ begin
m.IconURL := iconUrl; m.IconURL := iconUrl;
m.Title := m.Title :=
'<span class="emi-marker-meta" data-marker-type="complaint" data-priority-badge="' +
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="d-flex flex-column gap-1 px-1 py-1" style="width:260px;">' +
'<div class="fw-semibold small">' + '<div class="fw-semibold small">' +
'<span class="fw-bold">Complaint:</span> ' + complaintId + '<span class="fw-bold">Complaint:</span> ' + complaintId +
...@@ -464,7 +553,7 @@ begin ...@@ -464,7 +553,7 @@ begin
'<span class="fw-bold">Dispatch Code:</span> ' + codeDesc + '<span class="fw-bold">Dispatch Code:</span> ' + codeDesc +
'</div>' + '</div>' +
'<div class="small">' + '<div class="small">' +
'<span class="fw-bold">Dispatch District:</span> ' + dispatchDist + '<span class="fw-bold">Agency:</span> ' + agency +
'</div>' + '</div>' +
IfThen(Trim(business) <> '', IfThen(Trim(business) <> '',
'<div class="small">' + '<div class="small">' +
...@@ -473,7 +562,7 @@ begin ...@@ -473,7 +562,7 @@ begin
'' ''
) + ) +
'<div class="small mb-1">' + '<div class="small mb-1">' +
'<span class="fw-bold">Address:</span> ' + string(item['Address']) + '<span class="fw-bold">Address:</span> ' + address +
'</div>' + '</div>' +
'<table class="table table-sm table-bordered mb-1 emi-tip-table">' + '<table class="table table-sm table-bordered mb-1 emi-tip-table">' +
'<colgroup>' + '<colgroup>' +
...@@ -519,67 +608,7 @@ end; ...@@ -519,67 +608,7 @@ end;
procedure TFViewMap.lfMapCustomizeMarker(Sender: TObject; var ACustomizeMarker: string); procedure TFViewMap.lfMapCustomizeMarker(Sender: TObject; var ACustomizeMarker: string);
begin begin
ACustomizeMarker := ACustomizeMarker :=
'var m=' + MARKERVAR + ', o=m.options||{};' + #13#10 + 'if (window.emiCustomizeMarker) window.emiCustomizeMarker(' + MARKERVAR + ');';
'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 +
'try { if (m.unbindTooltip) m.unbindTooltip(); } catch(e) {}' + #13#10 +
'try { if (m.unbindPopup) m.unbindPopup(); } catch(e) {}' + #13#10 +
// Note: we derive badgeText from icon filename suffix: *_2.png, *_3.png, *_4.png, *_5-9.png, etc.
'var badgeText = "";' + #13#10 +
'if (u) {' + #13#10 +
' try {' + #13#10 +
' var file = u.split("/").pop();' + #13#10 +
' var base = file.replace(/\.[^/.]+$/, "");' + #13#10 +
' var idx = base.lastIndexOf("_");' + #13#10 +
' if (idx >= 0 && idx < base.length - 1) {' + #13#10 +
' var suffix = base.substring(idx + 1);' + #13#10 +
' if (suffix && suffix.length > 0) {' + #13#10 +
' var ch = suffix.charAt(0);' + #13#10 +
' if (ch >= "1" && ch <= "9") badgeText = ch;' + #13#10 +
' }' + #13#10 +
' }' + #13#10 +
' } catch(e) {}' + #13#10 +
'}' + #13#10 +
'var badgeColor = "#4b5563";' + #13#10 +
'if (badgeText === "1") badgeColor = "#bc28d9";' + #13#10 +
'else if (badgeText === "2") badgeColor = "#b91c1c";' + #13#10 +
'else if (badgeText === "3") badgeColor = "#b45309";' + #13#10 +
'else if (badgeText === "4") badgeColor = "#047857";' + #13#10 +
'if (u) {' + #13#10 +
' var html = ''<div class="emi-marker-wrap"><img src="'' + u + ''" class="emi-marker-img">'';' + #13#10 +
' if (badgeText) {' + #13#10 +
' html += ''<div class="emi-marker-badge" style="background:'' + badgeColor + '';">'' + badgeText + ''</div>'';' + #13#10 +
' }' + #13#10 +
' html += ''</div>'';' + #13#10 +
' m.setIcon(L.divIcon({' + #13#10 +
' html: html,' + #13#10 +
' iconSize: [32,32],' + #13#10 +
' iconAnchor: [16,32],' + #13#10 +
' popupAnchor: [0,-20],' + #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 +
'});';
end; end;
...@@ -598,6 +627,10 @@ begin ...@@ -598,6 +627,10 @@ begin
'width:auto;' + 'width:auto;' +
'max-width:260px;' + 'max-width:260px;' +
'}' + #13#10 + '}' + #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 Query: on very small screens, lets it grow almost full width ------
'@media (max-width:480px){' + '@media (max-width:480px){' +
'.emi-tip .leaflet-popup-content{' + '.emi-tip .leaflet-popup-content{' +
...@@ -619,8 +652,12 @@ begin ...@@ -619,8 +652,12 @@ begin
'}' + #13#10 + '}' + #13#10 +
// --- Marker Badge ------------------------------------------------------------ // --- Marker Badge ------------------------------------------------------------
'.emi-marker-wrap{position:relative;display:inline-block;}'+#13#10+ '.emi-marker-wrap{position:relative;display:inline-block;}'+#13#10+
'.emi-marker-wrap-unit{padding-top:13px;}'+#13#10+
'.emi-marker-img{display:block;}'+#13#10+ '.emi-marker-img{display:block;}'+#13#10+
'.emi-marker-badge{position:absolute;top:-4px;right:-4px;min-width:16px;height:16px;padding:0 4px;border-radius:999px;background:var(--bs-danger);color:#fff;font:700 11px/16px system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;text-align:center;box-shadow:0 0 0 2px #fff;}' + #13#10; '.emi-marker-badge{position:absolute;top:-4px;right:-4px;min-width:16px;height:16px;padding:0 4px;border-radius:999px;background:var(--bs-danger);color:#fff;font:700 11px/16px system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;text-align:center;box-shadow:0 0 0 2px #fff;}'+#13#10+
'.emi-marker-wrap-complaint{padding-top:0;}'+#13#10+
'.emi-complaint-badge{position:absolute;top:-4px;right:-4px;min-width:16px;height:16px;padding:0 4px;border-radius:999px;color:#111827;font:700 11px/16px system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;text-align:center;box-shadow:0 0 0 2px #fff;}'+#13#10+
'.emi-unit-badge{position:absolute;top:0;left:50%;transform:translateX(-50%);min-width:34px;max-width:58px;height:15px;padding:0 5px;border-radius:4px;background:#111827;color:#fff;font:700 10px/15px system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;text-align:center;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;box-shadow:0 0 0 2px #fff;z-index:2;}'+#13#10;
end; end;
procedure TFViewMap.tmrLocateTimer(Sender: TObject); procedure TFViewMap.tmrLocateTimer(Sender: TObject);
...@@ -688,7 +725,7 @@ begin ...@@ -688,7 +725,7 @@ begin
for i := 0 to lfMap.Markers.Count - 1 do for i := 0 to lfMap.Markers.Count - 1 do
begin begin
m := lfMap.Markers[i]; m := lfMap.Markers[i];
if SameText(m.DataString, targetDs) then if SameText(m.DataString, targetDs) or StartsText(targetDs + '|', m.DataString) then
begin begin
coord := CreateCoordinate(m.Latitude, m.Longitude); coord := CreateCoordinate(m.Latitude, m.Longitude);
lfmap.SetCenterCoordinate(coord); lfmap.SetCenterCoordinate(coord);
......
...@@ -40,20 +40,22 @@ object FViewUnits: TFViewUnits ...@@ -40,20 +40,22 @@ object FViewUnits: TFViewUnits
DataSource = wdsUnits DataSource = wdsUnits
ItemTemplate = ItemTemplate =
'<div class="list-section-header small fw-semibold bg-body-second' + '<div class="list-section-header small fw-semibold bg-body-second' +
'ary text-dark rounded-1 px-2 mb-1">(%DistrictHeader%)</div><div ' + 'ary text-dark rounded-1 px-2 mb-1">(%AgencyHeader%)</div><div cl' +
'class="card border shadow-sm"> <div class="card-body py-2 px-3 ' + 'ass="card border shadow-sm"> <div class="card-body py-2 px-3 d-' +
'd-flex gap-2"> <div class="flex-grow-1"> <div class="fw-' + 'flex gap-2"> <div class="flex-grow-1"> <div class="fw-bo' +
'bold text-uppercase small">(%UnitName%)&nbsp;-&nbsp;(%Status%)</' + 'ld text-uppercase small">(%UnitName%)(%ComplaintHeader%)&nbsp;-&' +
'div> <div class="small text-body-secondary mb-1">(%Location' + 'nbsp;(%Status%)</div> <div class="small text-body-secondary' +
'%)</div> <div class="small">(%CallType%)</div> <hr cla' + ' mb-1">(%Location%)</div> <div class="small">(%CallType%)</' +
'ss="unit-divider my-1" style="width: 80px; margin-left: 0" /> ' + 'div> <hr class="unit-divider my-1" style="width: 80px; marg' +
' <div class="small officer1">(%Officer1%)</div> <div clas' + 'in-left: 0" /> <div class="small officer1">(%Officer1%)</di' +
's="small officer2">(%Officer2%)</div> </div> <div class="d' + 'v> <div class="small officer2">(%Officer2%)</div> </div>' +
'-flex flex-column justify-content-center gap-1"> <button ty' + ' <div class="d-flex flex-column justify-content-center gap-1"' +
'pe="button" class="btn btn-primary btn-sm btn-unit-details" data' + '> <button type="button" class="btn btn-primary btn-sm btn-u' +
'-unitid="(%UnitId%)">Details</button> <button type="button"' + 'nit-details" data-unitid="(%UnitId%)">Details</button> <but' +
' class="btn btn-primary btn-sm btn-unit-map" data-unitid="(%Unit' + 'ton type="button" class="btn btn-sm btn-unit-map (%MapBut' +
'Id%)">Map</button> </div> </div></div>' 'tonClass%)" data-unitid="(%UnitId%)" data-canmap="' +
'(%CanShowMap%)" title="(%MapButtonTitle%)" (%MapBu' +
'ttonDisabled%)> Map</button> </div> </div></div>'
ListSource = wdsUnits ListSource = wdsUnits
end end
object wdsUnits: TWebDataSource object wdsUnits: TWebDataSource
...@@ -64,10 +66,10 @@ object FViewUnits: TFViewUnits ...@@ -64,10 +66,10 @@ object FViewUnits: TFViewUnits
end end
object xdwdsUnits: TXDataWebDataSet object xdwdsUnits: TXDataWebDataSet
Connection = DMConnection.ApiConnection Connection = DMConnection.ApiConnection
Left = 260 Left = 262
Top = 412 Top = 412
object xdwdsUnitsDistrictHeader: TStringField object xdwdsUnitsAgencyHeader: TStringField
FieldName = 'DistrictHeader' FieldName = 'AgencyHeader'
end end
object xdwdsUnitsUnitId: TStringField object xdwdsUnitsUnitId: TStringField
FieldName = 'UnitId' FieldName = 'UnitId'
...@@ -90,6 +92,33 @@ object FViewUnits: TFViewUnits ...@@ -90,6 +92,33 @@ object FViewUnits: TFViewUnits
object xdwdsUnitsCallType: TStringField object xdwdsUnitsCallType: TStringField
FieldName = 'CallType' FieldName = 'CallType'
end 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
object xdwdsUnitsComplaintHeader: TStringField
FieldName = 'ComplaintHeader'
Size = 40
end
end end
object xdwcUnits: TXDataWebClient object xdwcUnits: TXDataWebClient
Connection = DMConnection.ApiConnection Connection = DMConnection.ApiConnection
......
<div class="d-flex flex-column h-100"> <div class="d-flex flex-column h-100">
<!-- Header / controls (non-scrolling) -->
<div class="flex-shrink-0">
<!-- Search bar under local navbar -->
<div class="bg-light border-bottom py-2">
<div class="container-fluid">
<div class="input-group">
<span class="input-group-text bg-white"><i class="fa fa-search"></i></span>
<input id="units_search" class="form-control" placeholder="Search...">
</div>
</div>
</div>
</div>
<!-- Scrolling list area --> <!-- Scrolling list area -->
<div class="flex-grow-1 overflow-auto" style="min-height:0;"> <div class="flex-grow-1 overflow-auto" style="min-height:0;">
<div class="container-fluid mt-2"> <div class="container-fluid mt-2">
......
...@@ -17,7 +17,7 @@ type ...@@ -17,7 +17,7 @@ type
xdwdsUnits: TXDataWebDataSet; xdwdsUnits: TXDataWebDataSet;
xdwcUnits: TXDataWebClient; xdwcUnits: TXDataWebClient;
lblEntries: TWebLabel; lblEntries: TWebLabel;
xdwdsUnitsDistrictHeader: TStringField; xdwdsUnitsAgencyHeader: TStringField;
xdwdsUnitsUnitId: TStringField; xdwdsUnitsUnitId: TStringField;
xdwdsUnitsUnitName: TStringField; xdwdsUnitsUnitName: TStringField;
xdwdsUnitsLocation: TStringField; xdwdsUnitsLocation: TStringField;
...@@ -25,6 +25,14 @@ type ...@@ -25,6 +25,14 @@ type
xdwdsUnitsOfficer1: TStringField; xdwdsUnitsOfficer1: TStringField;
xdwdsUnitsOfficer2: TStringField; xdwdsUnitsOfficer2: TStringField;
xdwdsUnitsCallType: TStringField; xdwdsUnitsCallType: TStringField;
xdwdsUnitsAgency: TStringField;
xdwdsUnitsAgencyName: TStringField;
xdwdsUnitsAgencyType: TStringField;
xdwdsUnitsCanShowMap: TStringField;
xdwdsUnitsMapButtonClass: TStringField;
xdwdsUnitsMapButtonDisabled: TStringField;
xdwdsUnitsMapButtonTitle: TStringField;
xdwdsUnitsComplaintHeader: TStringField;
procedure WebFormCreate(Sender: TObject); procedure WebFormCreate(Sender: TObject);
private private
FLoading: Boolean; FLoading: Boolean;
...@@ -65,6 +73,7 @@ var ...@@ -65,6 +73,7 @@ var
el: TJSElement; el: TJSElement;
btn: TJSElement; btn: TJSElement;
unitId: string; unitId: string;
canShowMap: string;
begin begin
btn := nil; btn := nil;
el := TJSElement(e.target); el := TJSElement(e.target);
...@@ -93,10 +102,16 @@ begin ...@@ -93,10 +102,16 @@ begin
if (btn <> nil) and (btn is TJSHtmlElement) then if (btn <> nil) and (btn is TJSHtmlElement) then
begin begin
unitId := string(TJSHtmlElement(btn).getAttribute('data-unitid')); canShowMap := string(TJSHtmlElement(btn).getAttribute('data-canmap'));
e.preventDefault; e.preventDefault;
e.stopPropagation; e.stopPropagation;
if not SameText(canShowMap, 'true') then
Exit;
unitId := string(TJSHtmlElement(btn).getAttribute('data-unitid'));
asm asm
pas['View.Main'].FViewMain.ShowMapFocusUnit(unitId); pas['View.Main'].FViewMain.ShowMapFocusUnit(unitId);
end; end;
......
{
"type": "FeatureCollection",
"name": "orleanscounty",
"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } },
"features": [
{"type":"Feature","properties":{"Municipali":"Town of Yates"},"geometry":{"type":"MultiPolygon","coordinates":[[[[-78.44185200002066,43.372158],[-78.441476000020643,43.372204],[-78.438029000020492,43.372273],[-78.436086000020424,43.3728440000001],[-78.433642000020313,43.373049],[-78.430665000020184,43.373598],[-78.429982000020161,43.373644000000105],[-78.42596500002,43.373917],[-78.424398000019934,43.374168],[-78.422267000019829,43.374305],[-78.422110000019842,43.374374],[-78.420355000019768,43.374465],[-78.418497000019698,43.374592000000106],[-78.416940000019622,43.374699],[-78.416104000019587,43.374735],[-78.41125700001939,43.374944],[-78.409952000019345,43.375],[-78.409056000019305,43.374965],[-78.402243000019041,43.374697],[-78.400398000018967,43.374844],[-78.398439000018882,43.375],[-78.395012000018752,43.375212],[-78.393876000018707,43.375282],[-78.393321000018688,43.375316],[-78.392817000018667,43.375347],[-78.389133000018546,43.375743],[-78.388523000018509,43.375809],[-78.38545200001839,43.375922],[-78.38460800001836,43.375926],[-78.382509000018274,43.375936],[-78.376960000018059,43.375963],[-78.375000000017991,43.376147],[-78.373607000017941,43.376309],[-78.370474000017822,43.376448],[-78.368750000017755,43.376312],[-78.367996000017726,43.376399],[-78.367685000017715,43.376381],[-78.367340000017705,43.376267],[-78.366556000017695,43.375415],[-78.366114000017674,43.375],[-78.365734000017639,43.374614],[-78.364668000017616,43.374386],[-78.361597000017511,43.37409],[-78.360689000017473,43.37409],[-78.358965000017406,43.373931],[-78.35805600001737,43.373886],[-78.357080000017334,43.373977],[-78.356834000017329,43.374],[-78.353977000017224,43.374061],[-78.353544000017223,43.37407],[-78.352479000017183,43.373887],[-78.351661000017131,43.373925],[-78.350319000017095,43.373986],[-78.349251000017063,43.374035],[-78.347967000017007,43.374094],[-78.346212000016948,43.373958],[-78.343893000016863,43.373935],[-78.341637000016775,43.373433],[-78.340540000016745,43.37325],[-78.338973000016694,43.373251],[-78.338261000016672,43.373142],[-78.337939000016647,43.372999],[-78.337219000016617,43.372908],[-78.333364000016502,43.372794],[-78.330043000016389,43.373023],[-78.327630000016299,43.372772],[-78.326471000016255,43.372795],[-78.325186000016217,43.372978],[-78.321865000016118,43.373001],[-78.319014000015997,43.373367],[-78.317666000015961,43.37339],[-78.312809000015804,43.373984],[-78.311955000015757,43.374002],[-78.310710000015717,43.374029],[-78.30899900001566,43.373866],[-78.308998000015677,43.372898],[-78.31025800001585,43.306047],[-78.328899000016492,43.306071],[-78.466829000021932,43.303512000000104],[-78.465729000021696,43.371402],[-78.462345000021543,43.371517000000104],[-78.459870000021439,43.371974000000108],[-78.457153000021322,43.372292000000101],[-78.456548000021272,43.372363],[-78.455765000021259,43.372546000000106],[-78.454198000021165,43.372729000000106],[-78.450751000021043,43.372707],[-78.449686000020989,43.372364],[-78.448759000020956,43.372250000000101],[-78.447273000020886,43.372067000000101],[-78.445409000020803,43.372098000000101],[-78.44185200002066,43.372158]]]]}},
{"type":"Feature","properties":{"Municipali":"Town of Shelby"},"geometry":{"type":"MultiPolygon","coordinates":[[[[-78.415487382094412,43.214831830085288],[-78.405983558936768,43.216068686026468],[-78.405954141943582,43.206728069757105],[-78.402940571583258,43.206868183980568],[-78.403022797073334,43.203942746082944],[-78.398483266306769,43.20411498423298],[-78.398499077281159,43.207004680637894],[-78.397713806849453,43.207039305787319],[-78.397642974451657,43.206123829432755],[-78.397103129048247,43.20614090021396],[-78.397198489710021,43.20705641680734],[-78.392499000019072,43.207189143111535],[-78.391456486059582,43.207208465031478],[-78.391417861448943,43.206491078576086],[-78.390894237093491,43.206543434994096],[-78.390870265259352,43.206363912040487],[-78.390158663693398,43.206380660981232],[-78.390106985668083,43.20720619375291],[-78.389138560845836,43.20725594107553],[-78.38932409713253,43.206486938353088],[-78.388557324478612,43.206443538253957],[-78.388070180946926,43.207292476862989],[-78.386080000018808,43.207330999981373],[-78.38267074402296,43.207366307920495],[-78.382674150488739,43.206327026441947],[-78.381890320575096,43.206325648930566],[-78.381886782876535,43.207400767646376],[-78.375176590863475,43.207517883281248],[-78.370182273755518,43.207607446342791],[-78.370190453615777,43.216532116843808],[-78.370246762372034,43.217130105197448],[-78.370477356111479,43.217984653585972],[-78.370596729910602,43.218552876967074],[-78.311017710684908,43.219007926234447],[-78.311075000016103,43.212318143111546],[-78.311278000016245,43.138874143111522],[-78.311258000016252,43.130561143111521],[-78.339473000017236,43.130219143111574],[-78.396097000019395,43.129727143111545],[-78.420264000020396,43.129732143111582],[-78.466736000022422,43.128366143111542],[-78.465394000022329,43.150634143111574],[-78.461997000021967,43.215899999995557],[-78.445762930062145,43.216356388400506],[-78.438346629367231,43.216564882860197],[-78.423067078774949,43.216994436862379],[-78.415463456491764,43.217200352099027],[-78.415487382094412,43.214831830085288]]]]}},
{"type":"Feature","properties":{"Municipali":"Town of Ridgeway"},"geometry":{"type":"MultiPolygon","coordinates":[[[[-78.297542000015454,43.306104999995632],[-78.299301000015603,43.251997999995588],[-78.299942000015605,43.219068999995613],[-78.311017710684851,43.219007926234447],[-78.370596729910588,43.218552876967195],[-78.370764568804361,43.219351777934122],[-78.370765018564953,43.220011377854014],[-78.370774149234734,43.233402033578585],[-78.380511356043712,43.233420094566974],[-78.380509008698098,43.234127915968692],[-78.384878772864965,43.234135579849202],[-78.384894610604817,43.23339828901409],[-78.397951805456628,43.233350245289181],[-78.398086665250517,43.231551396937988],[-78.402786415885515,43.231492668681064],[-78.405979407682807,43.231450570459494],[-78.405972329144561,43.227853587342757],[-78.405968857164069,43.220927571941203],[-78.405975328854737,43.218694840848947],[-78.407459057977547,43.218640780811796],[-78.407492123903552,43.219097851091163],[-78.408254095056805,43.219073645690486],[-78.408080287465324,43.219361039129332],[-78.408271984329758,43.21936231293936],[-78.409380195006847,43.219374358182705],[-78.409477317725234,43.220143159803499],[-78.411645239075398,43.22020538568588],[-78.412191003127703,43.217291687520664],[-78.415463456491764,43.217200352099134],[-78.423067078774906,43.216994436862457],[-78.438346629367231,43.216564882860304],[-78.445762930062116,43.21635638840057],[-78.461997000021896,43.215899999995635],[-78.466829000021889,43.303511999995571],[-78.328899000016477,43.306070999995491],[-78.310258000015878,43.306046999995445],[-78.297542000015454,43.306104999995632]]]]}},
{"type":"Feature","properties":{"Municipali":"Town of Murray"},"geometry":{"type":"MultiPolygon","coordinates":[[[[-78.066591000009396,43.286446999986339],[-77.995615000007987,43.286545999986345],[-77.995789000008003,43.278643999986294],[-77.996549000008073,43.234476999986342],[-77.996540000008068,43.218190999986319],[-78.017036475463556,43.217327121785289],[-78.016655063203771,43.229749815146988],[-78.016649366859866,43.230356155388229],[-78.016591601979954,43.231148035669541],[-78.016568659559098,43.231469122055579],[-78.016688555956463,43.231468726780221],[-78.018430159080694,43.231453126967779],[-78.018430903539894,43.231268111245406],[-78.019981739206159,43.231255400540483],[-78.020972653256607,43.23124821740322],[-78.022912151004917,43.231225804457424],[-78.024128595536595,43.231211730365402],[-78.026416321143117,43.231194586373931],[-78.027287547574844,43.231199562476249],[-78.027271625655999,43.2324824086379],[-78.027453720386987,43.232485524255971],[-78.027436487571336,43.235386705216392],[-78.03179222312373,43.235367807168068],[-78.031824294690168,43.231180961053305],[-78.041595548350244,43.231150991528217],[-78.041566505024107,43.227912321278289],[-78.042293623477377,43.228174338206912],[-78.042260081374522,43.227005870747249],[-78.042224704648007,43.225647731427159],[-78.041553996357962,43.225731423798209],[-78.04109696899144,43.225733909194965],[-78.041004017587213,43.217143751741659],[-78.116500000010603,43.217097999986379],[-78.115898000010546,43.254298999986325],[-78.11650000001049,43.299597999986332],[-78.066703000009383,43.300097999986328],[-78.066591000009396,43.286446999986339]]]]}},
{"type":"Feature","properties":{"Municipali":"Village of Medina"},"geometry":{"type":"MultiPolygon","coordinates":[[[[-78.405980243293911,43.231446888016563],[-78.405979400715168,43.231447029877273],[-78.405979400467785,43.231446904175904],[-78.405980243293911,43.231446888016563]]],[[[-78.405979400715168,43.231447029877273],[-78.40597940768275,43.231450570459536],[-78.405956584204645,43.231450871377405],[-78.405979400715168,43.231447029877273]]],[[[-78.405979400467785,43.231446904175904],[-78.405367129211086,43.23145864308762],[-78.402786415885515,43.23149266868117],[-78.39808666525046,43.231551396938045],[-78.397951805456628,43.233350245289238],[-78.389414524086263,43.233381658069867],[-78.386920631030279,43.233390834306718],[-78.384894610604817,43.233398289014154],[-78.384878772864965,43.234135579849259],[-78.380509008698027,43.234127915968749],[-78.380511356043712,43.23342009456703],[-78.370774149234677,43.233402033578642],[-78.37076501856491,43.220011377854078],[-78.370764568804304,43.219351777934172],[-78.370596729910588,43.218552876967259],[-78.370477356111408,43.217984653586051],[-78.370246762372005,43.217130105197519],[-78.370190453615749,43.216532116843787],[-78.370182273755532,43.207607446342841],[-78.375176590863404,43.207517883281312],[-78.381886782876578,43.207400767646476],[-78.381890320575053,43.206325648930623],[-78.382674150488739,43.206327026442089],[-78.382670744022874,43.207366307920566],[-78.386080000018751,43.207330999981387],[-78.388070180946926,43.207292476863039],[-78.38855732447864,43.206443538254028],[-78.389324097132501,43.206486938353144],[-78.389138560845709,43.207255941075651],[-78.390106985668112,43.20720619375296],[-78.390158663693398,43.206380660981175],[-78.390870265259338,43.206363912040473],[-78.390894237093477,43.206543434994124],[-78.391417861448957,43.206491078576086],[-78.391456486059582,43.207208465031584],[-78.392499000019001,43.207189143111485],[-78.397198489710021,43.207056416807433],[-78.397103129048148,43.206140900214017],[-78.397642974451642,43.206123829432805],[-78.397713806849481,43.207039305787447],[-78.398499077281173,43.20700468063793],[-78.398491302689749,43.205583755663795],[-78.398483266306798,43.204114984232909],[-78.403022797073206,43.203942746082944],[-78.402940571583301,43.20686818398061],[-78.405954141943539,43.206728069757084],[-78.405983558936697,43.216068686026475],[-78.415487382094355,43.214831830085274],[-78.415463456491764,43.217200352099191],[-78.412191003127703,43.217291687520721],[-78.411645239075341,43.220205385685937],[-78.409477317725234,43.220143159803541],[-78.409380195006861,43.219374358182769],[-78.408271984329701,43.21936231293941],[-78.408080287465268,43.219361039129375],[-78.408254095056805,43.219073645690543],[-78.407492123903481,43.21909785109122],[-78.407459057977476,43.218640780811846],[-78.40597532885468,43.218694840849004],[-78.405968857164012,43.220927571941303],[-78.405972329144561,43.227853587342821],[-78.405979400467785,43.231446904175904]]]]}},
{"type":"Feature","properties":{"Municipali":"Village of Lyndonville"},"geometry":{"type":"MultiPolygon","coordinates":[[[[-78.398655212772368,43.326519419029367],[-78.398677691640913,43.327664222447154],[-78.398688832521117,43.328224588792672],[-78.398689349053214,43.328536712471021],[-78.397632356844227,43.328537078219554],[-78.391730165995298,43.328538932617562],[-78.391734731372907,43.329992002294013],[-78.391739912510957,43.331640196967875],[-78.388862416154126,43.331621186107277],[-78.388614223358303,43.331665674984265],[-78.385729397896199,43.33163964780244],[-78.385730714842524,43.331223004555035],[-78.385735247637612,43.329787707740373],[-78.385739171530659,43.328544787535101],[-78.37877816563153,43.32853102477236],[-78.378789722105097,43.327345013741535],[-78.378803800903739,43.325899910984681],[-78.378809165134953,43.325686676837648],[-78.378838169601522,43.323236685912363],[-78.378859017496907,43.321433061757027],[-78.378852576336172,43.32126614700698],[-78.378873445050928,43.318940406208839],[-78.378880363812527,43.318169110806238],[-78.378910142967655,43.316477788741167],[-78.378912142681457,43.313996865472895],[-78.387051983954109,43.314010462010557],[-78.388675090724405,43.314006066545907],[-78.388932768917485,43.314005670701768],[-78.391028188199243,43.313992754645],[-78.39873725750283,43.314015238603737],[-78.398734864373651,43.316096874353384],[-78.398726841294646,43.318609389175855],[-78.398725212703852,43.320803054238219],[-78.39870215475699,43.325235661635375],[-78.398701004906997,43.325456495294041],[-78.401274027340719,43.325399616925658],[-78.400364052811128,43.325643630348303],[-78.400358474284701,43.326366579241586],[-78.400353452572546,43.326482715320132],[-78.399129630044627,43.326509165457402],[-78.398655212772368,43.326519419029367]]]]}},
{"type":"Feature","properties":{"Municipali":"Town of Kendall"},"geometry":{"type":"MultiPolygon","coordinates":[[[[-78.055134000008948,43.369816],[-78.04595500000876,43.370592],[-78.040973000008663,43.370774],[-78.040472000008648,43.370888],[-78.039313000008619,43.370979],[-78.038717000008603,43.370819],[-78.036400000008584,43.369013],[-78.036306000008551,43.36883],[-78.035956000008554,43.36865],[-78.034521000008525,43.367915],[-78.033676000008526,43.367458],[-78.033268000008505,43.367412],[-78.032109000008475,43.367137],[-78.03038700000846,43.366908],[-78.030355000008456,43.366839],[-78.029979000008453,43.366862],[-78.028413000008428,43.366679],[-78.023871000008327,43.366517],[-78.019360000008234,43.366812],[-78.016634000008182,43.367177],[-78.016446000008173,43.367193],[-78.015036000008152,43.367313],[-78.013250000008128,43.367564],[-78.011778000008078,43.367952],[-78.009428000008057,43.368225],[-78.005512000007968,43.368223],[-78.003633000007937,43.36781],[-78.002506000007926,43.367352],[-78.001097000007903,43.366437],[-78.000000000007873,43.365836],[-77.998862000007861,43.3656],[-77.997136000007814,43.365367],[-77.996341000007817,43.365322],[-77.995710000007804,43.36518],[-77.995101000007779,43.365201],[-77.994481000007767,43.365129],[-77.994499000007764,43.364898],[-77.995615000007874,43.286546],[-78.066591000009296,43.286447],[-78.066703000009269,43.300098],[-78.11650000001039,43.299598],[-78.116500000010376,43.3087],[-78.115898000010276,43.374599],[-78.11589700001025,43.375346],[-78.115619000010255,43.375348],[-78.114544000010227,43.375327],[-78.112727000010182,43.375373],[-78.111474000010162,43.375442],[-78.104769000010009,43.37557],[-78.101009000009924,43.375433],[-78.095873000009803,43.375015],[-78.095695000009798,43.375],[-78.095112000009792,43.374694],[-78.094329000009793,43.374477],[-78.091947000009725,43.373815],[-78.089064000009657,43.373472],[-78.08295500000952,43.373176],[-78.081294000009493,43.373039],[-78.080135000009463,43.373016],[-78.079195000009449,43.372857],[-78.077252000009423,43.372742],[-78.075809000009372,43.372529],[-78.074934000009364,43.3724],[-78.074057000009347,43.372103],[-78.072929000009324,43.371508],[-78.072114000009307,43.37128],[-78.063311000009122,43.370023],[-78.062308000009111,43.369931],[-78.058016000009005,43.369954],[-78.055134000008948,43.369816]]]]}},
{"type":"Feature","properties":{"Municipali":"Village of Holley"},"geometry":{"type":"MultiPolygon","coordinates":[[[[-78.04155313406747,43.231161528461854],[-78.035547852687046,43.231169540485588],[-78.031824294690111,43.231180961053347],[-78.031792223123645,43.235367807168167],[-78.027436487571336,43.235386705216413],[-78.027453720386944,43.232485524256084],[-78.027271625656013,43.232482408637928],[-78.027287547574829,43.231199562476348],[-78.026416321143074,43.231194586373967],[-78.024128595536581,43.231211730365445],[-78.022912151004959,43.231225804457416],[-78.020972653256479,43.231248217403362],[-78.019981739206159,43.231255400540512],[-78.01843090353988,43.231268111245498],[-78.018430159080609,43.231453126967921],[-78.016688555956463,43.2314687267802],[-78.016568659559113,43.231469122055636],[-78.016591601979968,43.23114803566952],[-78.016649366859866,43.230356155388378],[-78.016655063203785,43.229749815147073],[-78.017036256034217,43.217334268653381],[-78.041004017587255,43.217143751741638],[-78.041096968991454,43.2257339091951],[-78.041553996357962,43.22573142379813],[-78.042224704648035,43.225647731427237],[-78.042260081374394,43.227005870747369],[-78.042293623477377,43.228174338207026],[-78.041566505024065,43.227912321278353],[-78.041595548350216,43.231150991528217],[-78.041553102148811,43.231151121715435],[-78.04155313406747,43.231161528461854]]]]}},
{"type":"Feature","properties":{"Municipali":"Town of Gaines"},"geometry":{"type":"MultiPolygon","coordinates":[[[[-78.116474830543723,43.299517531658815],[-78.115872830543793,43.254218531659113],[-78.168601000011975,43.25319999995439],[-78.168776519108889,43.253208272673461],[-78.16877553090481,43.253297873416486],[-78.16876934464176,43.253858620254121],[-78.168680188797182,43.253906923783646],[-78.16857604850432,43.253925828420194],[-78.168552190975987,43.253929526061818],[-78.168494272377544,43.253929316039631],[-78.168455418438242,43.253926133295032],[-78.168426739075358,43.253923783998516],[-78.168371329803904,43.253909483116239],[-78.168278230986004,43.253887010233207],[-78.168196228445879,43.253858811271371],[-78.168174318155067,43.253850110742007],[-78.168118495289079,43.253827489225287],[-78.168063149811331,43.253804375652386],[-78.167990945233853,43.253775913719437],[-78.167954960884046,43.253744057611613],[-78.167926173154044,43.253719276201984],[-78.167894980070074,43.253692727330673],[-78.167849237437807,43.253676697103309],[-78.167813097443741,43.253667753057897],[-78.167764941256692,43.253651716769056],[-78.167723917962917,43.253651567732717],[-78.167670706914947,43.253668999951813],[-78.167624704362154,43.253691745459825],[-78.167620556599019,43.253695801368046],[-78.167616710701424,43.253699562095143],[-78.167583401528461,43.253732133584606],[-78.16756082034037,43.253766723701645],[-78.167538886029703,43.253798369427216],[-78.167526570078451,43.253835334479596],[-78.167535989047806,43.25386886111049],[-78.167562118837154,43.253930642779792],[-78.167593108051406,43.25398715772949],[-78.167609663753396,43.254036566154973],[-78.167613154127409,43.254055968590279],[-78.167605721529625,43.254084141367507],[-78.167583859058851,43.254105213126302],[-78.167530635591547,43.254124406686856],[-78.167486074782886,43.254129290711248],[-78.16743636907556,43.254134738613466],[-78.167319831319261,43.254148926227579],[-78.167160472486401,43.254160685015179],[-78.167102473402494,43.254172812427029],[-78.167042104974385,43.254177879872827],[-78.166969636284577,43.254188190362825],[-78.166914134803775,43.254187988333626],[-78.166875569028477,43.254180799359183],[-78.166825171016896,43.254140077646831],[-78.166753194897112,43.254078126117228],[-78.166694834517912,43.254036980257041],[-78.166632224585015,43.254017365189334],[-78.16659354147211,43.254015561601861],[-78.166570157168337,43.254014471305481],[-78.166550197808192,43.254013540702182],[-78.166342662815822,43.254012784378965],[-78.166178528861806,43.254017473073652],[-78.166048181399717,43.254022284784028],[-78.165965953121514,43.254023235931349],[-78.165855303891334,43.254056696397733],[-78.16585356045988,43.254311918087218],[-78.165830317622792,43.255669646305748],[-78.168403438651339,43.255219598121151],[-78.169522136162826,43.255029677116241],[-78.170375173750543,43.254879622928179],[-78.171256210416942,43.254719454115012],[-78.17194177106235,43.254548366563462],[-78.172403510847232,43.254427516712269],[-78.172781240483488,43.254336990888802],[-78.173347320570286,43.25433791467244],[-78.173620267684583,43.254338359092628],[-78.177525935643501,43.25434907357625],[-78.177788898861976,43.254351798462828],[-78.178147116545375,43.254346482363758],[-78.178280271973264,43.254344473942062],[-78.178265361328116,43.25464556817569],[-78.178382236703257,43.254648428197228],[-78.178822753104939,43.254645602328509],[-78.178830748030165,43.254914516313164],[-78.178369448911781,43.254916179390406],[-78.178246127465044,43.254916162390678],[-78.178236640718509,43.25525450055521],[-78.178217342925322,43.255574564618627],[-78.178197087825495,43.256038998693207],[-78.1781861278631,43.256396754865357],[-78.178171504420078,43.256769704571603],[-78.178165456033454,43.256923958434612],[-78.179678436725638,43.256940521040939],[-78.18037729395013,43.256935557542249],[-78.180956005284145,43.256933155149937],[-78.181329734776043,43.256929530363209],[-78.182301918961343,43.256925528955854],[-78.182658170189441,43.256924240111715],[-78.183060110610725,43.256922784641404],[-78.185745050624377,43.256910633883955],[-78.18749353033833,43.256898133211607],[-78.189081747734107,43.256886757471229],[-78.191534602552181,43.256871821529771],[-78.191545003814127,43.256161655059472],[-78.193238670139451,43.256155537797468],[-78.193244794644798,43.25595015870784],[-78.196391223511142,43.255895358889425],[-78.196363386210706,43.25754443569388],[-78.19832351168607,43.25750369634018],[-78.20147467043553,43.257448986273154],[-78.208571365808154,43.257325950142928],[-78.208694968237666,43.253546556940968],[-78.208726370991897,43.252346952702254],[-78.208745657149379,43.251751642695339],[-78.208762789530965,43.251115004032926],[-78.250282830546965,43.250975531659172],[-78.285275830548031,43.251158531659001],[-78.297094346940298,43.251422148087379],[-78.299356726189856,43.251457825060918],[-78.297586756735399,43.306127999989691],[-78.116468651730116,43.308722690745206],[-78.116474830543723,43.299517531658815]]]]}},
{"type":"Feature","properties":{"Municipali":"Town of Clarendon"},"geometry":{"type":"MultiPolygon","coordinates":[[[[-78.016941096779632,43.217335025063015],[-77.996540000008011,43.218190999995457],[-77.997299000008113,43.133398999995478],[-78.01740200000846,43.132300999995486],[-78.063201000009443,43.13109899999548],[-78.11580100001062,43.130869999995511],[-78.116343000010545,43.214081999995486],[-78.116500000010518,43.217097999995474],[-78.041004017587213,43.217143751741659],[-78.016941096779632,43.217335025063015]]]]}},
{"type":"Feature","properties":{"Municipali":"Town of Carlton"},"geometry":{"type":"MultiPolygon","coordinates":[[[[-78.280819000014759,43.372035],[-78.278939000014688,43.371485],[-78.276401000014616,43.371325],[-78.273268000014511,43.371598],[-78.270416000014421,43.372122],[-78.269667000014394,43.372408],[-78.26910000001439,43.372625],[-78.26875500001438,43.37267],[-78.268254000014366,43.372761],[-78.267321000014334,43.372749],[-78.264713000014254,43.372714],[-78.259762000014106,43.372849],[-78.254311000013942,43.372297],[-78.254137000013927,43.372282],[-78.252651000013898,43.372159],[-78.252149000013887,43.372022],[-78.25089700001385,43.370809],[-78.250000000013827,43.370377],[-78.24851800001376,43.369924],[-78.248435000013771,43.369899],[-78.246962000013724,43.369579],[-78.244236000013643,43.369284],[-78.241666000013552,43.369125],[-78.233865000013338,43.369013],[-78.22747400001316,43.369564],[-78.226190000013119,43.369747],[-78.224435000013059,43.369976],[-78.22243000001302,43.370365],[-78.219454000012931,43.370572],[-78.214942000012798,43.37103],[-78.21143300001269,43.371739],[-78.208645000012609,43.372174],[-78.207924000012596,43.372128],[-78.203475000012475,43.371237],[-78.200373000012391,43.370849],[-78.197647000012338,43.370918],[-78.194295000012218,43.371192],[-78.194016000012226,43.371273],[-78.192885000012183,43.371603],[-78.191663000012156,43.372106],[-78.191538000012159,43.372175],[-78.190410000012122,43.371855],[-78.189909000012122,43.371558],[-78.188718000012074,43.371192],[-78.188555000012073,43.371055],[-78.188499000012058,43.371009],[-78.187465000012054,43.370666],[-78.186337000012017,43.370483],[-78.183173000011934,43.37046],[-78.181606000011897,43.370551],[-78.177502000011785,43.371099],[-78.175966000011741,43.371511],[-78.173930000011694,43.371739],[-78.17079600001162,43.372355],[-78.169167000011569,43.372949],[-78.164498000011449,43.374068],[-78.1623670000114,43.374501],[-78.160143000011331,43.374666],[-78.159140000011305,43.37474],[-78.158169000011284,43.374603],[-78.156508000011257,43.374534],[-78.154753000011212,43.374614],[-78.150072000011079,43.375],[-78.146801000010996,43.375351],[-78.145454000010972,43.375453],[-78.140942000010867,43.375542],[-78.137245000010779,43.375486],[-78.130351000010606,43.375583],[-78.129443000010582,43.375674],[-78.125000000010488,43.375582],[-78.120184000010354,43.375541],[-78.117866000010309,43.375395],[-78.117040000010292,43.375378],[-78.11589700001025,43.375346],[-78.115898000010276,43.374599],[-78.116500000010376,43.3087],[-78.297542000015412,43.306105],[-78.31025800001585,43.306047],[-78.308998000015677,43.372898],[-78.30899900001566,43.373866],[-78.308893000015658,43.373855],[-78.308199000015634,43.373789],[-78.308078000015627,43.373778],[-78.307577000015627,43.373618],[-78.307272000015615,43.373417],[-78.306605000015566,43.372977],[-78.305478000015555,43.372474],[-78.304224000015495,43.372246],[-78.303316000015485,43.372177],[-78.301874000015431,43.371811],[-78.300778000015413,43.371651],[-78.29908600001535,43.371834],[-78.296893000015274,43.37149],[-78.292537000015116,43.371535],[-78.291535000015102,43.371809],[-78.290093000015048,43.371832],[-78.28805600001499,43.372105],[-78.286114000014919,43.372128],[-78.285330000014895,43.372242],[-78.282072000014793,43.372149],[-78.280819000014759,43.372035]]]]}},
{"type":"Feature","properties":{"Municipali":"Town of Barre"},"geometry":{"type":"MultiPolygon","coordinates":[[[[-78.115801000010578,43.13087],[-78.115943000010603,43.132108],[-78.127774000010874,43.132102],[-78.141188000011212,43.132064],[-78.181020000012239,43.131974],[-78.214017000013172,43.131688],[-78.294820000015676,43.130705],[-78.311258000016238,43.130611],[-78.31127800001623,43.138924],[-78.311075000016075,43.212368],[-78.306783000015912,43.21243],[-78.249556000014096,43.212673],[-78.201937000012705,43.212733],[-78.116343000010502,43.214082],[-78.115801000010578,43.13087]]]]}},
{"type":"Feature","properties":{"Municipali":"Village of Albion"},"geometry":{"type":"MultiPolygon","coordinates":[[[[-78.208507391370702,43.257258527885547],[-78.201950766465529,43.257440732147913],[-78.201474670435573,43.257448986273275],[-78.19832351168607,43.257503696340144],[-78.196363386210649,43.257544435693937],[-78.196391223511142,43.255895358889568],[-78.19324479464477,43.255950158707925],[-78.193238670139451,43.25615553779749],[-78.191545003814156,43.256161655059522],[-78.191534602552167,43.256871821529884],[-78.189081747734122,43.2568867574713],[-78.18749353033833,43.256898133211607],[-78.185745050624362,43.256910633883919],[-78.183060110610612,43.25692278464151],[-78.18265817018937,43.256924240111729],[-78.182301918961258,43.256925528955769],[-78.181329734776014,43.256929530363145],[-78.180956005284145,43.256933155150016],[-78.180377293950059,43.256935557542292],[-78.179678436725595,43.256940521041017],[-78.178165456033454,43.256923958434633],[-78.178171504420035,43.256769704571632],[-78.1781861278631,43.256396754865293],[-78.178197087825481,43.256038998693263],[-78.178217342925208,43.255574564618577],[-78.178236640718509,43.255254500555324],[-78.178246127465044,43.254916162390757],[-78.178369448911766,43.254916179390463],[-78.178830748030066,43.254914516313121],[-78.178822753104896,43.25464560232858],[-78.1783822367032,43.254648428197349],[-78.178265361327973,43.25464556817581],[-78.178280271973222,43.254344473942176],[-78.178147116545389,43.254346482363857],[-78.177788898861962,43.254351798462885],[-78.177525935643445,43.254349073576222],[-78.173620267684598,43.254338359092735],[-78.173347320570301,43.254337914672583],[-78.172781240483374,43.254336990888859],[-78.172403510847246,43.254427516712312],[-78.171941771062336,43.254548366563519],[-78.1712562104169,43.25471945411509],[-78.170375173750429,43.254879622928271],[-78.169522136162811,43.255029677116319],[-78.168403438651254,43.255219598121116],[-78.165830317622763,43.255669646305797],[-78.165853560459837,43.254311918087218],[-78.16585530389132,43.254056696397789],[-78.165965953121443,43.254023235931399],[-78.166048181399745,43.254022284784114],[-78.166178528861806,43.254017473073638],[-78.166342662815822,43.254012784379071],[-78.166550197808107,43.254013540702246],[-78.166570157168252,43.254014471305581],[-78.166593541472054,43.254015561601904],[-78.166632224584959,43.254017365189426],[-78.166694834517926,43.254036980257055],[-78.166753194897041,43.254078126117278],[-78.166825171016896,43.254140077646902],[-78.16687556902842,43.254180799359261],[-78.166914134803775,43.254187988333669],[-78.16696963628452,43.254188190362839],[-78.167042104974357,43.254177879872934],[-78.16710247340238,43.254172812427093],[-78.167160472486316,43.254160685015314],[-78.167319831319261,43.254148926227607],[-78.16743636907546,43.254134738613502],[-78.167486074782914,43.254129290711248],[-78.167530635591547,43.254124406686898],[-78.167583859058865,43.254105213126358],[-78.16760572152964,43.254084141367606],[-78.167613154127366,43.254055968590293],[-78.167609663753353,43.254036566155079],[-78.167593108051392,43.253987157729604],[-78.167562118837168,43.253930642779927],[-78.167535989047792,43.253868861110561],[-78.167526570078394,43.253835334479668],[-78.167538886029661,43.253798369427301],[-78.16756082034037,43.25376672370178],[-78.167583401528404,43.253732133584698],[-78.167616710701438,43.253699562095193],[-78.16762055659899,43.253695801367975],[-78.167624704362197,43.253691745459875],[-78.167670706914976,43.253668999951778],[-78.167723917962917,43.253651567732767],[-78.16776494125655,43.253651716769106],[-78.167813097443698,43.253667753058025],[-78.167849237437807,43.253676697103337],[-78.167894980070074,43.253692727330801],[-78.167926173154001,43.253719276201984],[-78.16795496088406,43.253744057611613],[-78.167990945233797,43.253775913719437],[-78.168063149811317,43.253804375652521],[-78.168118495289036,43.253827489225436],[-78.168174318155081,43.253850110742007],[-78.168196228445879,43.25385881127135],[-78.168278230985962,43.253887010233299],[-78.168371329803875,43.253909483116374],[-78.168426739075272,43.253923783998445],[-78.168455418438228,43.253926133295053],[-78.168494272377501,43.253929316039574],[-78.168552190975873,43.253929526061803],[-78.168576048504249,43.253925828420158],[-78.168680188797182,43.253906923783724],[-78.168769344641774,43.253858620254206],[-78.16877553090481,43.253297873416592],[-78.168776519108846,43.253208272668971],[-78.168497035960655,43.244493915129951],[-78.167073802893583,43.244601635060505],[-78.166045932272567,43.244686384282602],[-78.166084902257495,43.243240013249292],[-78.166111063731918,43.241538327988721],[-78.172509708738559,43.241404593115234],[-78.172563961566752,43.239209595426743],[-78.173538660415488,43.239203244367147],[-78.178923521106952,43.239102119757639],[-78.179028038865951,43.234179615329914],[-78.184255430294868,43.234096764791438],[-78.185352495752042,43.234080616773532],[-78.193178180008346,43.234032149438683],[-78.193176664523008,43.234269705999786],[-78.19392714856167,43.234266416212307],[-78.194075045505414,43.234267033444837],[-78.194072927268721,43.234345699038812],[-78.195728265480156,43.234310495201811],[-78.199286711148332,43.234227387887394],[-78.201926805631075,43.234209034125158],[-78.20581634967013,43.234173000227713],[-78.20633681425447,43.234147429009496],[-78.209059648868234,43.234111974053171],[-78.209022873827166,43.237036630548289],[-78.208963292635175,43.241774260538627],[-78.208959204078027,43.242264493570573],[-78.208965784025395,43.242801102225364],[-78.210378821855656,43.242772927712153],[-78.210172657941172,43.245931049633441],[-78.208903632399256,43.245966192104447],[-78.208783735474057,43.25111493367244],[-78.208782922017107,43.251149866008966],[-78.208507391370702,43.257258527885547]]]]}},
{"type":"Feature","properties":{"Municipali":"Town of Albion"},"geometry":{"type":"MultiPolygon","coordinates":[[[[-78.299332629873462,43.251450069433709],[-78.298827660419192,43.251438244367087],[-78.285321660418731,43.251254244367104],[-78.250328660417608,43.251071244367097],[-78.20878292201715,43.251149866008902],[-78.208903632399299,43.24596619210439],[-78.210172657941229,43.245931049633384],[-78.210378821855713,43.242772927712117],[-78.208965784025438,43.2428011022253],[-78.208959204078027,43.242264493570509],[-78.208963292635246,43.241774260538577],[-78.209022873827223,43.237036630548218],[-78.209059648868234,43.234111974053107],[-78.20633681425447,43.23414742900939],[-78.205816349670201,43.234173000227649],[-78.201926805631075,43.234209034125094],[-78.199286711148332,43.234227387887344],[-78.195728265480142,43.234310495201754],[-78.194072927268792,43.234345699038769],[-78.194075045505414,43.234267033444773],[-78.19392714856167,43.234266416212265],[-78.193176664523051,43.234269705999722],[-78.193178180008402,43.234032149438619],[-78.185352495752042,43.234080616773468],[-78.184255430294911,43.234096764791317],[-78.179028038866008,43.234179615329857],[-78.178923521106952,43.239102119757582],[-78.173538660415488,43.239203244367097],[-78.172563961566794,43.239209595426637],[-78.172509708738602,43.241404593115178],[-78.166111063731918,43.241538327988664],[-78.166084902257552,43.243240013249242],[-78.166045932272624,43.244686384282538],[-78.16707380289364,43.244601635060441],[-78.168497035960726,43.244493915129908],[-78.168776519108889,43.253208272664473],[-78.115872830543793,43.254218531659177],[-78.116520660414139,43.217113244367191],[-78.11636366041418,43.214097244367039],[-78.201957660416241,43.212748244367148],[-78.249576660417674,43.2126882443672],[-78.306783000015912,43.212430000000154],[-78.31107455301678,43.21237034006186],[-78.311017710684851,43.219007926234447],[-78.299962660419212,43.219084244367252],[-78.299332629873462,43.251450069433709]]]]}}
]
}
{
"type": "FeatureCollection",
"name": "wyomingtowns",
"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } },
"features": [
{"type":"Feature","properties":{"AREA":143374578.704,"PERIMETER":48985.43252,"TOWN_":350.0,"TOWN_ID":350.0,"TOWN":"Bennington","FIPS_MCD":"3612105936","POP1990":3046,"GEOCODE":"0062","Shape_Leng":160615.73910100001,"Shape_Area":1541400009.1},"geometry":{"type":"MultiPolygon","coordinates":[[[[-78.463198310409183,42.780498388698987],[-78.487970948759482,42.780534732774228],[-78.487959984212679,42.7815578088302],[-78.487992701258165,42.783620544223503],[-78.487577079000815,42.804140307373693],[-78.487267992297546,42.812929740577943],[-78.487031102877623,42.824191589540753],[-78.486723630025935,42.84115924189981],[-78.486549170449379,42.849805746604986],[-78.486278638676012,42.867655251140057],[-78.480300256836443,42.867560575028698],[-78.472015669110121,42.867493755415865],[-78.464386631947619,42.867449616167974],[-78.448240289078598,42.867710754642168],[-78.387948744617447,42.868630613696673],[-78.374748501895084,42.868761582924769],[-78.374734755027021,42.868761746019658],[-78.369078645982896,42.868826697588418],[-78.333755248194819,42.869539719512495],[-78.320041287788243,42.869705670590236],[-78.307226747807206,42.86978954287391],[-78.307173832107225,42.864500554104985],[-78.307225894111426,42.855653951535224],[-78.307166390436137,42.839821780946586],[-78.306821835657388,42.825381611569377],[-78.306852079786125,42.811968709489939],[-78.307141373878025,42.795514809443119],[-78.307547056783534,42.780859238262067],[-78.319142989756344,42.78103422292925],[-78.34875347089536,42.781333313766169],[-78.368819286552764,42.781319378376615],[-78.374850486933298,42.781260560564803],[-78.391445629461813,42.78114102497743],[-78.396211410500612,42.781106245325319],[-78.407528030577652,42.781022887512776],[-78.410341239360392,42.780982372109783],[-78.410887717808052,42.780977545120805],[-78.442745447140211,42.780510690981551],[-78.463198310409183,42.780498388698987]]]]}},
{"type":"Feature","properties":{"AREA":93493311.117899999,"PERIMETER":38757.56762,"TOWN_":353.0,"TOWN_ID":353.0,"TOWN":"Attica","FIPS_MCD":"3612103012","POP1990":7383,"GEOCODE":"0037","Shape_Leng":127091.403019,"Shape_Area":1005309817.22},"geometry":{"type":"MultiPolygon","coordinates":[[[[-78.265962748960277,42.86945234712082],[-78.249747339136221,42.869911051881651],[-78.226360231668124,42.870152395805704],[-78.191382253516934,42.870413353097646],[-78.190783031001118,42.849082528120185],[-78.190596795305254,42.836936417312145],[-78.190368994501384,42.827565740220422],[-78.190092055154238,42.809237144510874],[-78.189858232192023,42.798740845767036],[-78.189614287130667,42.790480619531849],[-78.189629892891077,42.782762485244533],[-78.208939585222041,42.782789279032734],[-78.242198695593899,42.781997620796176],[-78.249719766015161,42.781868962253121],[-78.278021793126527,42.780849379898257],[-78.299208219547396,42.780799494004569],[-78.307547056783534,42.780859238262067],[-78.307141373878025,42.795514809443119],[-78.306852079786125,42.811968709489939],[-78.306821835657388,42.825381611569377],[-78.307166390436137,42.839821780946586],[-78.307225894111426,42.855653951535224],[-78.307173832107225,42.864500554104985],[-78.307226747807206,42.86978954287391],[-78.295381871068642,42.869707256088368],[-78.284869197961783,42.869509114775859],[-78.279216489267114,42.869402176065869],[-78.278961022685039,42.86939732723642],[-78.278912014539259,42.869396518146146],[-78.273623100859865,42.869375860832157],[-78.273574142035045,42.869375975021413],[-78.265962748960277,42.86945234712082]]]]}},
{"type":"Feature","properties":{"AREA":67842798.222100005,"PERIMETER":33594.78496,"TOWN_":356.0,"TOWN_ID":356.0,"TOWN":"Covington","FIPS_MCD":"3612118696","POP1990":1266,"GEOCODE":"0207","Shape_Leng":110177.05560199999,"Shape_Area":729697903.61699998},"geometry":{"type":"MultiPolygon","coordinates":[[[[-78.074248806532466,42.800771810389953],[-78.074156373283657,42.823146617585053],[-78.074154152233618,42.824465529683145],[-78.074239446438796,42.837865607325895],[-78.074271403899473,42.857749631517692],[-78.074201825160245,42.863756693092299],[-78.019475282859716,42.864683920786149],[-77.99972474225838,42.865076374305346],[-77.99541565515419,42.864999318244465],[-77.995320653399347,42.863315320745919],[-77.979937422717768,42.863139794096931],[-77.954818271306806,42.862741818414165],[-77.954813449097898,42.861834798602224],[-77.954927060211688,42.853787959547873],[-77.955179122741754,42.825214575155869],[-77.955199322288678,42.815968005015392],[-77.955310177800783,42.810550064233503],[-77.955449671790149,42.801500983508788],[-77.955466783806457,42.800391050867312],[-77.995901818663668,42.80151301473029],[-77.999750727122162,42.801674321508273],[-78.019672264549087,42.801812649961974],[-78.031809939666388,42.801679843923878],[-78.048697405418494,42.801425285366832],[-78.058098621223124,42.801127061043019],[-78.074248806532466,42.800771810389953]]]]}},
{"type":"Feature","properties":{"AREA":92451286.35269998,"PERIMETER":38493.90169,"TOWN_":380.0,"TOWN_ID":380.0,"TOWN":"Middlebury","FIPS_MCD":"3612146877","POP1990":1532,"GEOCODE":"0527","Shape_Leng":126235.53666100001,"Shape_Area":994242348.82599998},"geometry":{"type":"MultiPolygon","coordinates":[[[[-78.191382253516934,42.870413353097646],[-78.187359380921905,42.870411270551209],[-78.180410671155556,42.870407337305807],[-78.124737269717684,42.869922601955878],[-78.109991465977927,42.869944199387234],[-78.074176529096107,42.870207404770767],[-78.074201825160245,42.863756693092299],[-78.074271403899473,42.857749631517692],[-78.074239446438796,42.837865607325895],[-78.074154152233618,42.824465529683145],[-78.074156373283657,42.823146617585053],[-78.074248806532466,42.800771810389953],[-78.074197717839553,42.782746044705284],[-78.107843263732462,42.782434817362237],[-78.12475156270699,42.782305322802173],[-78.149430169756954,42.782671645342447],[-78.164231634764718,42.782798070173705],[-78.189629892891077,42.782762485244533],[-78.189614287130667,42.790480619531849],[-78.189858232192023,42.798740845767036],[-78.190092055154238,42.809237144510874],[-78.190368994501384,42.827565740220422],[-78.190596795305254,42.836936417312145],[-78.190783031001118,42.849082528120185],[-78.191382253516934,42.870413353097646]]]]}},
{"type":"Feature","properties":{"AREA":122865356.04099999,"PERIMETER":44666.40156,"TOWN_":386.0,"TOWN_ID":386.0,"TOWN":"Sheldon","FIPS_MCD":"3612166773","POP1990":2487,"GEOCODE":"0772","Shape_Leng":146454.88559300001,"Shape_Area":1320917375.49},"geometry":{"type":"MultiPolygon","coordinates":[[[[-78.30810009803939,42.695224802884077],[-78.32673197284592,42.695066016487253],[-78.373329600526347,42.694075514740689],[-78.374802783230606,42.694005054934408],[-78.381445032437298,42.693807932672861],[-78.383877466581097,42.693770298804679],[-78.388715023241616,42.693761423186977],[-78.398608484357069,42.693742645486388],[-78.410098774526617,42.693826705115747],[-78.414368323893143,42.693770800386716],[-78.41767975258486,42.693813435045882],[-78.422312130509681,42.693806883750113],[-78.422866514285687,42.693872984670371],[-78.427152640196979,42.693719523554421],[-78.429078166971024,42.693816653270829],[-78.429515674220511,42.693787300549452],[-78.430127688354531,42.693818712897567],[-78.447170404925288,42.693840321481623],[-78.457442634215184,42.693873455334504],[-78.463897760820018,42.693906708859501],[-78.463863202506886,42.698944514768669],[-78.463861471234665,42.717875895008618],[-78.463678870803619,42.72928666634354],[-78.463512163935363,42.736984157944669],[-78.463419964327372,42.75007199943083],[-78.463198310409183,42.780498388698987],[-78.442745447140211,42.780510690981551],[-78.410887717808052,42.780977545120805],[-78.410341239360392,42.780982372109783],[-78.407528030577652,42.781022887512776],[-78.396211410500612,42.781106245325319],[-78.391445629461813,42.78114102497743],[-78.374850486933298,42.781260560564803],[-78.368819286552764,42.781319378376615],[-78.34875347089536,42.781333313766169],[-78.319142989756344,42.78103422292925],[-78.307547056783534,42.780859238262067],[-78.308062459091445,42.760589212199889],[-78.308271832168387,42.750050826264612],[-78.3083390984593,42.738598350345463],[-78.308240033609451,42.725724266471097],[-78.308265236818116,42.722070196130637],[-78.30820726421895,42.712116789537276],[-78.308122383684008,42.710064383329438],[-78.308114117687595,42.700773809797091],[-78.30810009803939,42.695224802884077]]]]}},
{"type":"Feature","properties":{"AREA":92391124.875100002,"PERIMETER":38424.90083,"TOWN_":389.0,"TOWN_ID":389.0,"TOWN":"Orangeville","FIPS_MCD":"3612155222","POP1990":1115,"GEOCODE":"0616","Shape_Leng":126000.098341,"Shape_Area":993449944.0819999},"geometry":{"type":"MultiPolygon","coordinates":[[[[-78.190951838015835,42.695990914310336],[-78.249975328344078,42.695870882840296],[-78.256004284010046,42.695760982670329],[-78.258086256205232,42.695722953479958],[-78.276468248842136,42.695553192413939],[-78.295891304773619,42.695370605911712],[-78.30810009803939,42.695224802884077],[-78.308114117687595,42.700773809797091],[-78.308122383684008,42.710064383329438],[-78.30820726421895,42.712116789537276],[-78.308265236818116,42.722070196130637],[-78.308240033609451,42.725724266471097],[-78.3083390984593,42.738598350345463],[-78.308271832168387,42.750050826264612],[-78.308062459091445,42.760589212199889],[-78.307547056783534,42.780859238262067],[-78.299208219547396,42.780799494004569],[-78.278021793126527,42.780849379898257],[-78.249719766015161,42.781868962253121],[-78.242198695593899,42.781997620796176],[-78.208939585222041,42.782789279032734],[-78.189629892891077,42.782762485244533],[-78.189919641388059,42.750075629011647],[-78.190951838015835,42.695990914310336]]]]}},
{"type":"Feature","properties":{"AREA":91897286.367400005,"PERIMETER":38363.38021000001,"TOWN_":396.0,"TOWN_ID":396.0,"TOWN":"Warsaw","FIPS_MCD":"3612178344","POP1990":5342,"GEOCODE":"0872","Shape_Leng":125807.024351,"Shape_Area":988278377.29399991},"geometry":{"type":"MultiPolygon","coordinates":[[[[-78.074122252148072,42.713502306666456],[-78.074109494053289,42.695549305418695],[-78.103198442774527,42.695349621516009],[-78.12471937017132,42.695414588480745],[-78.13570170410469,42.695436890370807],[-78.157770605889908,42.695816323820118],[-78.190951838015835,42.695990914310336],[-78.189919641388059,42.750075629011647],[-78.189629892891077,42.782762485244533],[-78.164231634764718,42.782798070173705],[-78.149430169756954,42.782671645342447],[-78.12475156270699,42.782305322802173],[-78.107843263732462,42.782434817362237],[-78.074197717839553,42.782746044705284],[-78.074232050734551,42.764023820472161],[-78.074177241570283,42.750092111049163],[-78.07411756785207,42.717035682008373],[-78.074122252148072,42.713502306666456]]]]}},
{"type":"Feature","properties":{"AREA":122613614.86300001,"PERIMETER":44674.26174,"TOWN_":426.0,"TOWN_ID":426.0,"TOWN":"Java","FIPS_MCD":"3612138341","POP1990":2197,"GEOCODE":"0426","Shape_Leng":146480.27594600001,"Shape_Area":1318202893.8599999},"geometry":{"type":"MultiPolygon","coordinates":[[[[-78.461765182959056,42.606581603655883],[-78.461950012505085,42.617303788921696],[-78.461964515939016,42.618198294352887],[-78.462075606928281,42.625074080660283],[-78.462219108711963,42.629201093953306],[-78.462450077895383,42.635841402888069],[-78.462776290349225,42.645215231804187],[-78.462988191568385,42.651301294660101],[-78.463234551030681,42.658381876744528],[-78.463406491600111,42.666337829838653],[-78.463559556003347,42.672201249630525],[-78.463786112786863,42.683831992383269],[-78.463897760820018,42.693906708859501],[-78.457442634215184,42.693873455334504],[-78.447170404925288,42.693840321481623],[-78.430127688354531,42.693818712897567],[-78.429515674220511,42.693787300549452],[-78.429078166971024,42.693816653270829],[-78.427152640196979,42.693719523554421],[-78.422866514285687,42.693872984670371],[-78.422312130509681,42.693806883750113],[-78.41767975258486,42.693813435045882],[-78.414368323893143,42.693770800386716],[-78.410098774526617,42.693826705115747],[-78.398608484357069,42.693742645486388],[-78.388715023241616,42.693761423186977],[-78.383877466581097,42.693770298804679],[-78.381445032437298,42.693807932672861],[-78.374802783230606,42.694005054934408],[-78.373329600526347,42.694075514740689],[-78.32673197284592,42.695066016487253],[-78.30810009803939,42.695224802884077],[-78.308199354694779,42.687133882409789],[-78.308369663043081,42.680117663301473],[-78.308515755781087,42.675920867727719],[-78.308533425236277,42.6736893676132],[-78.308676347444205,42.668574830014705],[-78.308681608437936,42.661392274151687],[-78.308932973474469,42.655356626525538],[-78.308872260084186,42.651621293532813],[-78.309025797949587,42.644565885009825],[-78.30911586435839,42.640007219664362],[-78.309214240656445,42.629462857898503],[-78.30927604194855,42.625073117351505],[-78.309525282313018,42.608085387992567],[-78.356947993424086,42.607172410760697],[-78.374742594102045,42.606715795890359],[-78.388058650674921,42.606376529474211],[-78.461765182959056,42.606581603655883]]]]}},
{"type":"Feature","properties":{"AREA":94806889.737200007,"PERIMETER":38942.53533,"TOWN_":427.0,"TOWN_ID":427.0,"TOWN":"Perry","FIPS_MCD":"3612157254","POP1990":5353,"GEOCODE":"0651","Shape_Leng":127715.160176,"Shape_Area":1019709567.73},"geometry":{"type":"MultiPolygon","coordinates":[[[[-78.01640469667916,42.713469721051169],[-78.021765092542864,42.713372494284066],[-78.022449593760911,42.713383336415845],[-78.025411583602249,42.713430189822049],[-78.029044087547206,42.71350202628507],[-78.035882753786368,42.713636947796566],[-78.054670921791171,42.7136158174428],[-78.074122252148072,42.713502306666456],[-78.07411756785207,42.717035682008373],[-78.074177241570283,42.750092111049163],[-78.074232050734551,42.764023820472161],[-78.074197717839553,42.782746044705284],[-78.074248806532466,42.800771810389953],[-78.058098621223124,42.801127061043019],[-78.048697405418494,42.801425285366832],[-78.031809939666388,42.801679843923878],[-78.019672264549087,42.801812649961974],[-77.999750727122162,42.801674321508273],[-77.995901818663668,42.80151301473029],[-77.955466783806457,42.800391050867312],[-77.955618339058688,42.781706729000298],[-77.95550971578885,42.770500231060979],[-77.955364549499407,42.759623147508933],[-77.955139842002851,42.750073879200897],[-77.955081947693714,42.745567318017429],[-77.955405685601491,42.715943274319031],[-77.955430028671032,42.713717531747953],[-77.996989491328634,42.71368137551881],[-77.999724092381683,42.713700002006966],[-78.01640469667916,42.713469721051169]]],[[[-78.029902679118251,42.705086063690267],[-78.030586804820715,42.705120882024481],[-78.030555714126947,42.705384169695172],[-78.03004569573163,42.705360291510509],[-78.030035445734413,42.705305911125066],[-78.030005975881025,42.705250962538919],[-78.029961791299158,42.705163047779763],[-78.029902679118251,42.705086063690267]]]]}},
{"type":"Feature","properties":{"AREA":93599733.534799993,"PERIMETER":38696.64141,"TOWN_":429.0,"TOWN_ID":429.0,"TOWN":"Wethersfield","FIPS_MCD":"3612181325","POP1990":794,"GEOCODE":"0902","Shape_Leng":126890.722757,"Shape_Area":1006439880.26},"geometry":{"type":"MultiPolygon","coordinates":[[[[-78.190930749528192,42.608997062842619],[-78.239471134682091,42.608653523221278],[-78.244321768961868,42.608712997684613],[-78.249743190451667,42.608707997057003],[-78.275241736840499,42.608430035877184],[-78.309525282313018,42.608085387992567],[-78.30927604194855,42.625073117351505],[-78.309214240656445,42.629462857898503],[-78.30911586435839,42.640007219664362],[-78.309025797949587,42.644565885009825],[-78.308872260084186,42.651621293532813],[-78.308932973474469,42.655356626525538],[-78.308681608437936,42.661392274151687],[-78.308676347444205,42.668574830014705],[-78.308533425236277,42.6736893676132],[-78.308515755781087,42.675920867727719],[-78.308369663043081,42.680117663301473],[-78.308199354694779,42.687133882409789],[-78.30810009803939,42.695224802884077],[-78.295891304773619,42.695370605911712],[-78.276468248842136,42.695553192413939],[-78.258086256205232,42.695722953479958],[-78.256004284010046,42.695760982670329],[-78.249975328344078,42.695870882840296],[-78.190951838015835,42.695990914310336],[-78.191041030288801,42.681052604811455],[-78.190611608837727,42.669292394351061],[-78.190786564504492,42.6656328048783],[-78.191209529575914,42.653353500674186],[-78.191250067105841,42.64116040655945],[-78.191202869817047,42.635724961573651],[-78.191232296487257,42.626444396322483],[-78.191185254419267,42.625033336185879],[-78.190930749528192,42.608997062842619]]]]}},
{"type":"Feature","properties":{"AREA":92554789.512999997,"PERIMETER":38534.61578,"TOWN_":459.0,"TOWN_ID":459.0,"TOWN":"Gainesville","FIPS_MCD":"3612127980","POP1990":2288,"GEOCODE":"0311","Shape_Leng":126368.146165,"Shape_Area":995340969.4230001},"geometry":{"type":"MultiPolygon","coordinates":[[[[-78.088500413383429,42.608353918732739],[-78.097604758095358,42.608500907047535],[-78.103852669754218,42.608590186777469],[-78.107245335033767,42.608655121314627],[-78.111892739780941,42.608687047206587],[-78.118742730295637,42.60875974665224],[-78.124735172682023,42.608800284929622],[-78.133948360644112,42.608873577590693],[-78.178214156415251,42.608834642448734],[-78.190930749528192,42.608997062842619],[-78.191185254419267,42.625033336185879],[-78.191232296487257,42.626444396322483],[-78.191202869817047,42.635724961573651],[-78.191250067105841,42.64116040655945],[-78.191209529575914,42.653353500674186],[-78.190786564504492,42.6656328048783],[-78.190611608837727,42.669292394351061],[-78.191041030288801,42.681052604811455],[-78.190951838015835,42.695990914310336],[-78.157770605889908,42.695816323820118],[-78.13570170410469,42.695436890370807],[-78.12471937017132,42.695414588480745],[-78.103198442774527,42.695349621516009],[-78.074109494053289,42.695549305418695],[-78.074220856311314,42.663310450910927],[-78.074260204203654,42.659417527415343],[-78.074212492970105,42.634668842804793],[-78.074264190545776,42.629812636776315],[-78.074195786529259,42.625087327325765],[-78.074158272887544,42.622194576181258],[-78.074167481516298,42.616242714486553],[-78.074180621191658,42.614057159536088],[-78.07415341950302,42.610730596532754],[-78.074131835078902,42.6082878373591],[-78.076670418146293,42.608227526099263],[-78.078728996358109,42.608229033960512],[-78.082810731017503,42.608265798601792],[-78.088500413383429,42.608353918732739]]]]}},
{"type":"Feature","properties":{"AREA":99688914.428000003,"PERIMETER":41923.99624,"TOWN_":462.0,"TOWN_ID":462.0,"TOWN":"Castile","FIPS_MCD":"3612112782","POP1990":3042,"GEOCODE":"0134","Shape_Leng":137492.181614,"Shape_Area":1072203310.6},"geometry":{"type":"MultiPolygon","coordinates":[[[[-78.074220856311314,42.663310450910927],[-78.074109494053289,42.695549305418695],[-78.074122252148072,42.713502306666456],[-78.054670921791171,42.7136158174428],[-78.035882753786368,42.713636947796566],[-78.029044087547206,42.71350202628507],[-78.025411583602249,42.713430189822049],[-78.022449593760911,42.713383336415845],[-78.021765092542864,42.713372494284066],[-78.01640469667916,42.713469721051169],[-77.999724092381683,42.713700002006966],[-77.996989491328634,42.71368137551881],[-77.955430028671032,42.713717531747953],[-77.955678015563421,42.700520701088088],[-77.95570104208673,42.699295377453801],[-77.95584912092599,42.691416577591951],[-77.95638838974439,42.667893763638361],[-77.957085666832512,42.667641706763433],[-77.957927845803127,42.667295264927198],[-77.958928673998358,42.666612968355757],[-77.959550776877208,42.665949939573622],[-77.960323926345495,42.664940126842914],[-77.961167872267438,42.663688371152297],[-77.961974958069561,42.662458832500221],[-77.962383191366214,42.661912607388302],[-77.96263469123781,42.661833913778288],[-77.962871884917504,42.661793733297543],[-77.963162196140914,42.661829965431039],[-77.963566406489974,42.6620354262813],[-77.964223861877201,42.662282880671647],[-77.964642067931521,42.662427878501411],[-77.965290433649358,42.662560177507771],[-77.966281287856717,42.662739277228773],[-77.967189068846139,42.66282572020642],[-77.96775988705069,42.662733639089822],[-77.968492959022228,42.662558033774552],[-77.968912150897737,42.662258600186313],[-77.969322472579307,42.661860473065396],[-77.97016339389792,42.660926899095038],[-77.97056938992533,42.660232522800619],[-77.970864897565477,42.659604826590986],[-77.970998230987803,42.659066132310627],[-77.971172782324032,42.65829669783831],[-77.971244200958935,42.657588394622842],[-77.971380645963379,42.656753404681567],[-77.971643215659995,42.6564167400145],[-77.971833572790132,42.656223270848521],[-77.97234081406981,42.655851835591776],[-77.972886310077456,42.655556919276158],[-77.973594038828963,42.655178470628833],[-77.974034799726169,42.654834965265877],[-77.974267313252795,42.654476575246662],[-77.974468026333838,42.653975775355647],[-77.974536063418142,42.653547309424752],[-77.974363423210349,42.652917664992565],[-77.97406730571133,42.652480986808051],[-77.973749306820778,42.65207190663746],[-77.973482989264113,42.651640485432964],[-77.973245031981563,42.651115579057702],[-77.973001728292274,42.650733354895081],[-77.972610262665327,42.650379697595014],[-77.972012885985421,42.649659990760902],[-77.971777183672486,42.649288686776046],[-77.971749471117931,42.648915810320837],[-77.97175202880841,42.648581110586072],[-77.971943243372223,42.647427487226039],[-77.972153139740627,42.646537077339595],[-77.972408000434299,42.646184011492871],[-77.972686898961911,42.645951462715573],[-77.973144537930409,42.645750482455497],[-77.973632284921294,42.645576707942489],[-77.974395893460809,42.64547216063432],[-77.974974450826082,42.64541290856539],[-77.975414282071043,42.645519302762089],[-77.9758075460431,42.645488877550463],[-77.976118142791165,42.645393251894248],[-77.976565613976149,42.645005793286956],[-77.977112806440488,42.644326782355293],[-77.97732535340171,42.643622890806043],[-77.977343297913833,42.643326479832972],[-77.977270270835945,42.64290457061594],[-77.977013849176217,42.642132914829475],[-77.976593847390049,42.640841281092335],[-77.976607350159341,42.639732893588814],[-77.976761518649624,42.639100768783081],[-77.977008173469756,42.638698374945754],[-77.977351294387219,42.638289759735549],[-77.977674218951179,42.638023948992142],[-77.978219698648061,42.637739981192169],[-77.97884582303476,42.637384070092068],[-77.979472265705098,42.637050107309562],[-77.980553585292341,42.636334090593124],[-77.981171853905522,42.6359507997945],[-77.982067964963548,42.635269107395466],[-77.982651825427922,42.634567848404771],[-77.98294015462497,42.6339730956078],[-77.983015063397048,42.633511647538555],[-77.983024750179951,42.633160439027293],[-77.98286153295669,42.632667895519774],[-77.982651830233621,42.632044035690143],[-77.982570469196531,42.631561839083602],[-77.982636823147928,42.631023650440873],[-77.982744822665666,42.630792386615198],[-77.98293272980986,42.630439812154144],[-77.983231615739683,42.630058951159263],[-77.983569729896757,42.629820440289237],[-77.983790402935568,42.629676101118839],[-77.984264597624374,42.629601145578818],[-77.98582100025726,42.62938620649868],[-77.986339420526804,42.629288953981089],[-77.986863318972382,42.62905998010303],[-77.987142738138161,42.62887128929826],[-77.987464435902723,42.628528642946549],[-77.987770629325368,42.628142236266342],[-77.988445292525981,42.627061679508266],[-77.98876259102444,42.626422798605532],[-77.989033465605786,42.625658082437859],[-77.989081054248516,42.625361441657439],[-77.989099337585387,42.625064533575539],[-77.989026282594921,42.624543066979093],[-77.988273908719052,42.623110037415813],[-77.987859028843687,42.622111188720581],[-77.987485004828798,42.621014350484892],[-77.987461277282094,42.620193564804211],[-77.987631127080604,42.618786890582186],[-77.988061681471564,42.617919219442577],[-77.989139258659662,42.616289979913162],[-77.989684317334948,42.615581706827257],[-77.990625941254876,42.614878455563748],[-77.991400057426887,42.614730959969627],[-77.992331136848776,42.614905223174851],[-77.993952824024404,42.615666259839223],[-77.994831746059234,42.615952294353789],[-77.995739319127438,42.616140163259239],[-77.996578303531891,42.616125400213669],[-77.997539488546252,42.615678114174386],[-77.998251536042076,42.61499528523526],[-77.999162188251887,42.613445049852622],[-77.999728685536454,42.612786557530789],[-78.001300164726615,42.611746241493826],[-78.003235928442081,42.610848568079724],[-78.004218594551759,42.610519029927936],[-78.005396481812895,42.610194222556942],[-78.006156410687908,42.610069076320414],[-78.00681314406286,42.610148145181753],[-78.007521731889398,42.610376427479075],[-78.008803600618094,42.611443767501029],[-78.00972054254531,42.612064443918328],[-78.010887717024289,42.612286426818351],[-78.011828079537352,42.612368822293163],[-78.012779531975923,42.612404105056243],[-78.013279715490654,42.612366010676681],[-78.013664164949915,42.612212632579968],[-78.014795235729267,42.61068814466055],[-78.015350918822406,42.60937565107443],[-78.018742883711909,42.609352735590328],[-78.027604748421467,42.609198188334354],[-78.027653464132271,42.609197259732007],[-78.033282074779407,42.609075456612878],[-78.039086082185364,42.608959934307329],[-78.044368787993889,42.608813568153977],[-78.049045555510446,42.608731944158194],[-78.055256497856377,42.608566716993678],[-78.057497379018201,42.608503505089281],[-78.062946917351766,42.608412383518306],[-78.06849473286529,42.608351402542517],[-78.070826734018439,42.60832401415351],[-78.074131835078902,42.6082878373591],[-78.07415341950302,42.610730596532754],[-78.074180621191658,42.614057159536088],[-78.074167481516298,42.616242714486553],[-78.074158272887544,42.622194576181258],[-78.074195786529259,42.625087327325765],[-78.074264190545776,42.629812636776315],[-78.074212492970105,42.634668842804793],[-78.074260204203654,42.659417527415343],[-78.074220856311314,42.663310450910927]]]]}},
{"type":"Feature","properties":{"AREA":94489017.194499999,"PERIMETER":38872.58723,"TOWN_":476.0,"TOWN_ID":476.0,"TOWN":"Eagle","FIPS_MCD":"3612121369","POP1990":1155,"GEOCODE":"0245","Shape_Leng":127467.450627,"Shape_Area":1015998491.58},"geometry":{"type":"MultiPolygon","coordinates":[[[[-78.234235345143603,42.521728906107249],[-78.24851902349441,42.521646798638876],[-78.250089024120726,42.521492557076456],[-78.259574619355902,42.521430582074181],[-78.270349554506979,42.521359217725134],[-78.308843896571943,42.521095993692803],[-78.309065554525034,42.553356786753788],[-78.309468565557438,42.583502722458796],[-78.309470771981978,42.606943427663474],[-78.309525282313018,42.608085387992567],[-78.275241736840499,42.608430035877184],[-78.249743190451667,42.608707997057003],[-78.244321768961868,42.608712997684613],[-78.239471134682091,42.608653523221278],[-78.190930749528192,42.608997062842619],[-78.190443798095117,42.578033670194465],[-78.19003250010141,42.545791322980996],[-78.190123273614077,42.521684124168694],[-78.19091699969438,42.521684232941659],[-78.199487204542066,42.521685061003495],[-78.204665177427501,42.521884702095278],[-78.216366667152187,42.521859542876349],[-78.217235434146275,42.52174719195348],[-78.217873145914822,42.521820768638321],[-78.223774236072799,42.521787907893028],[-78.234235345143603,42.521728906107249]]]]}},
{"type":"Feature","properties":{"AREA":40688279.265199997,"PERIMETER":42614.02878,"TOWN_":481.0,"TOWN_ID":481.0,"TOWN":"Genesee Falls","FIPS_MCD":"3612128596","POP1990":488,"GEOCODE":"0319","Shape_Leng":139752.27184,"Shape_Area":437600612.89899999},"geometry":{"type":"MultiPolygon","coordinates":[[[[-78.088944545957546,42.521103873918079],[-78.088945415356164,42.523226525405612],[-78.088988861008616,42.529179149613249],[-78.089053914806655,42.535729746264622],[-78.089077364268192,42.541880376592225],[-78.089064620333318,42.544374128395567],[-78.089059275213089,42.551920300115846],[-78.089047332388745,42.55810448191442],[-78.088836599701096,42.564936279419534],[-78.08882856285156,42.570587408054898],[-78.08876077459135,42.576690299054739],[-78.08869995552169,42.581864833063214],[-78.088674905843149,42.588081689410579],[-78.088612068478653,42.592448878034986],[-78.08855426496963,42.597837215736689],[-78.088511853221434,42.604306783933723],[-78.088500413383429,42.608353918732739],[-78.082810731017503,42.608265798601792],[-78.078728996358109,42.608229033960512],[-78.076670418146293,42.608227526099263],[-78.074131835078902,42.6082878373591],[-78.070826734018439,42.60832401415351],[-78.06849473286529,42.608351402542517],[-78.062946917351766,42.608412383518306],[-78.057497379018201,42.608503505089281],[-78.055256497856377,42.608566716993678],[-78.049045555510446,42.608731944158194],[-78.044368787993889,42.608813568153977],[-78.039086082185364,42.608959934307329],[-78.033282074779407,42.609075456612878],[-78.027653464132271,42.609197259732007],[-78.027604748421467,42.609198188334354],[-78.018742883711909,42.609352735590328],[-78.015350918822406,42.60937565107443],[-78.015576928292873,42.608456874938341],[-78.016493321906822,42.606925339748493],[-78.018829251497579,42.606108785387022],[-78.020958743177843,42.605098796203713],[-78.022729233794692,42.605221603002093],[-78.024078155701005,42.605470116483751],[-78.026163797146353,42.605441849799874],[-78.027117230670811,42.605029496684985],[-78.028703019079742,42.604032072210096],[-78.029004057031699,42.60272198908195],[-78.028313490550872,42.600919243296715],[-78.027654781847701,42.599429711626478],[-78.027061262077666,42.598594150208584],[-78.026072475902353,42.597874489140359],[-78.023832140236152,42.597195479370441],[-78.021629805647393,42.596031016462113],[-78.019728406214682,42.596055585567548],[-78.018760493599331,42.596602975906563],[-78.017653124857418,42.59784651830536],[-78.016607347712494,42.598928986546973],[-78.014886686966932,42.599739768774057],[-78.013326054162533,42.599677648422585],[-78.011207290081856,42.599232085251401],[-78.007769725400209,42.598778528994643],[-78.00658305564582,42.599108405710318],[-78.005906962892098,42.598612032071884],[-78.004129362329749,42.59667209510075],[-78.004079395542249,42.595425015574925],[-78.004586245299279,42.594256858700163],[-78.006790634858888,42.591653471519535],[-78.00799063055581,42.590116512820337],[-78.009900900939385,42.588409023374211],[-78.012114836120858,42.586877917090121],[-78.013830400288668,42.586894655755216],[-78.01495111721654,42.586840934868285],[-78.016488170155299,42.586501746788343],[-78.018021015233316,42.585958212637266],[-78.019361321044528,42.585815590000877],[-78.021072691338915,42.585473442458607],[-78.022666821090581,42.585030140284253],[-78.02448778578318,42.585166651002837],[-78.026931340623193,42.586316098162484],[-78.029065592357369,42.586475224447767],[-78.031012518894983,42.586224382668171],[-78.032328158034503,42.586305316356253],[-78.032887201186327,42.586499296232013],[-78.034142354887166,42.587036721744802],[-78.035616425382216,42.587783123924758],[-78.036421777174681,42.587690019370079],[-78.03723041461555,42.587356780549314],[-78.038617627476796,42.585793934419556],[-78.039699458976472,42.584801456412386],[-78.040807951847952,42.583923872900982],[-78.042167885289501,42.583263088690956],[-78.042998835403878,42.582616760768936],[-78.044063419136847,42.581655759820471],[-78.045433400467545,42.580692184218165],[-78.046619743402218,42.580115802104274],[-78.048223719416427,42.579268664505491],[-78.04907179241431,42.578277866414666],[-78.049957248287427,42.577326100489323],[-78.050664931637229,42.576226599777485],[-78.050849035970103,42.574775900616729],[-78.050521194105357,42.573934228469597],[-78.048694952799437,42.572197107508131],[-78.047620172194783,42.57125705720027],[-78.046710088882904,42.570524198219566],[-78.045828456914748,42.570099258492974],[-78.044922996738293,42.570011649707737],[-78.044292715014592,42.570104698553742],[-78.043259948945803,42.570394813865256],[-78.041875813965547,42.571014046417041],[-78.040977976834313,42.57123581724121],[-78.0393074775765,42.571374630348387],[-78.038876919065061,42.571410412366134],[-78.038111903670341,42.57190377461113],[-78.037354795201779,42.57260454718616],[-78.036721728940421,42.572957743895152],[-78.03542367417927,42.573499523803058],[-78.034530580213598,42.573648780616466],[-78.033322519429717,42.573521611792962],[-78.032524364613693,42.572999216851201],[-78.031832746354539,42.572175106341611],[-78.030481381392434,42.570291432219157],[-78.029762525662832,42.569204865111935],[-78.02897221423116,42.568574179725772],[-78.028371393977096,42.568158465778488],[-78.028090771328422,42.567904049313583],[-78.028298449992306,42.567232915456408],[-78.028738312233543,42.566901632703733],[-78.029339950650638,42.566772724432113],[-78.030487102434876,42.566752311332415],[-78.032110074130827,42.567131036731588],[-78.032828681939435,42.567264661232201],[-78.033661965549456,42.567313389631288],[-78.034559791440373,42.567290296070929],[-78.036357271074152,42.56679929496805],[-78.041066708643314,42.563228326347257],[-78.044067172100895,42.560921485987876],[-78.046164547364754,42.559438163903643],[-78.047364845530609,42.558414863217592],[-78.048175676179312,42.557577588886673],[-78.048822078566687,42.556752640579496],[-78.04939285739492,42.555916797483547],[-78.050719252009401,42.55229095647681],[-78.051518884550603,42.549134943003608],[-78.051642133835784,42.54855582950271],[-78.051726218818288,42.547564302765636],[-78.051428229832538,42.546747314720157],[-78.051077179256737,42.54637795613408],[-78.050497019950967,42.545900877576322],[-78.049662563089996,42.545483508054986],[-78.049207086646604,42.545038272820975],[-78.048989044713821,42.544645784865409],[-78.048990294598752,42.544092453909933],[-78.0493635607843,42.543211340174473],[-78.049716456296409,42.542616454122275],[-78.050479298685559,42.542398955036127],[-78.051054541984797,42.542473720242953],[-78.05194524533276,42.54249839738781],[-78.052546550864605,42.542373435038591],[-78.053191318432042,42.542137950487074],[-78.054627572189005,42.541362136095479],[-78.055510555028974,42.540974076938234],[-78.056238065365491,42.54046963576328],[-78.056709354064196,42.539839266879362],[-78.057141886825576,42.539613539784362],[-78.058006255444056,42.539399161982473],[-78.058955868301894,42.539066242506749],[-78.059616770105791,42.538322158446363],[-78.060250501844379,42.537289466043518],[-78.060647491878342,42.536183985043429],[-78.060826269417618,42.534775726313114],[-78.060825560060152,42.533652826203635],[-78.060558577795788,42.532843421351252],[-78.059309418267205,42.532289846856457],[-78.058233616455837,42.532330644525658],[-78.05728746779748,42.532898362583047],[-78.05667668858753,42.533399249766049],[-78.055402807122718,42.534648951011611],[-78.054567496127376,42.535355746203415],[-78.053229016106599,42.536277617328686],[-78.052028370598819,42.537261041583292],[-78.050345557146784,42.538785521990917],[-78.049961263505978,42.538896916051087],[-78.048957530266918,42.53908323739855],[-78.048319903880937,42.538876309901006],[-78.048166220976967,42.538697462087136],[-78.047736297541149,42.537764234585588],[-78.046932701857202,42.536654543801653],[-78.043908692488671,42.53574485371994],[-78.043138036618529,42.535413963894136],[-78.042028573989512,42.534803559249951],[-78.041571779939318,42.534351439251097],[-78.041684499146839,42.533780059115152],[-78.042268467223906,42.533457513061059],[-78.042846088095303,42.533193871466253],[-78.043195154169439,42.532874480471257],[-78.044494464392841,42.531309533781311],[-78.045314941957443,42.530500048407809],[-78.046320141568401,42.529956154407827],[-78.047257030028049,42.529505925196752],[-78.047774002044903,42.529156010116616],[-78.0481824634943,42.528793225109894],[-78.048331107816082,42.527971970199708],[-78.048249288211224,42.527549638703881],[-78.048002141979111,42.527099527392863],[-78.04761387431553,42.526836737895479],[-78.046993232227351,42.526508533000992],[-78.045720962151336,42.526442375977631],[-78.045100309552922,42.526670551187102],[-78.044366124504393,42.527097674858815],[-78.043819906082291,42.527714972851875],[-78.043475391406602,42.528376097980782],[-78.042989136709707,42.529027789429421],[-78.042146056801315,42.529255532179398],[-78.041068968049871,42.528909443001098],[-78.04058870840683,42.528342227996568],[-78.040433200897837,42.527591296854283],[-78.040559584032081,42.526470283306487],[-78.040489605710775,42.525557950519698],[-78.040027878860968,42.524799648812788],[-78.039558021969256,42.524205090245587],[-78.039116177283802,42.523406047928802],[-78.038781407831834,42.522655935037548],[-78.03850508610067,42.521832101680545],[-78.038399286749197,42.521105756716608],[-78.066706348539171,42.521037454515813],[-78.088944545957546,42.521103873918079]]]]}},
{"type":"Feature","properties":{"AREA":80935655.6426,"PERIMETER":36126.9391,"TOWN_":496.0,"TOWN_ID":496.0,"TOWN":"Pike","FIPS_MCD":"3612157826","POP1990":1081,"GEOCODE":"0663","Shape_Leng":118471.858421,"Shape_Area":870378885.171},"geometry":{"type":"MultiPolygon","coordinates":[[[[-78.088944545957546,42.521103873918079],[-78.118174451893893,42.521270854676885],[-78.124727112838784,42.521354666047444],[-78.148788268840221,42.521569302193797],[-78.170223721236667,42.521604075886074],[-78.188489537146424,42.521677692460017],[-78.190123273614077,42.521684124168694],[-78.19003250010141,42.545791322980996],[-78.190443798095117,42.578033670194465],[-78.190930749528192,42.608997062842619],[-78.178214156415251,42.608834642448734],[-78.133948360644112,42.608873577590693],[-78.124735172682023,42.608800284929622],[-78.118742730295637,42.60875974665224],[-78.111892739780941,42.608687047206587],[-78.107245335033767,42.608655121314627],[-78.103852669754218,42.608590186777469],[-78.097604758095358,42.608500907047535],[-78.088500413383429,42.608353918732739],[-78.088511853221434,42.604306783933723],[-78.08855426496963,42.597837215736689],[-78.088612068478653,42.592448878034986],[-78.088674905843149,42.588081689410579],[-78.08869995552169,42.581864833063214],[-78.08876077459135,42.576690299054739],[-78.08882856285156,42.570587408054898],[-78.088836599701096,42.564936279419534],[-78.089047332388745,42.55810448191442],[-78.089059275213089,42.551920300115846],[-78.089064620333318,42.544374128395567],[-78.089077364268192,42.541880376592225],[-78.089053914806655,42.535729746264622],[-78.088988861008616,42.529179149613249],[-78.088945415356164,42.523226525405612],[-78.088944545957546,42.521103873918079]]]]}},
{"type":"Feature","properties":{"AREA":122205321.39700001,"PERIMETER":44692.19294,"TOWN_":510.0,"TOWN_ID":510.0,"TOWN":"Arcade","FIPS_MCD":"3612102418","POP1990":3938,"GEOCODE":"0028","Shape_Leng":146538.74010200001,"Shape_Area":1313809448.6300001},"geometry":{"type":"MultiPolygon","coordinates":[[[[-78.447148990668396,42.519140945126715],[-78.447826606465483,42.519140740683575],[-78.450069141679364,42.519140032442692],[-78.461417618784793,42.519131726560957],[-78.464477069415793,42.519129294083939],[-78.464261400771164,42.522340996428134],[-78.46384403185651,42.536382497963217],[-78.461835578471366,42.604442488727159],[-78.461765182959056,42.606581603655883],[-78.388058650674921,42.606376529474211],[-78.374742594102045,42.606715795890359],[-78.356947993424086,42.607172410760697],[-78.309525282313018,42.608085387992567],[-78.309470771981978,42.606943427663474],[-78.309468565557438,42.583502722458796],[-78.309065554525034,42.553356786753788],[-78.308843896571943,42.521095993692803],[-78.318390420255824,42.520920237288259],[-78.324499860403876,42.520889913448563],[-78.32666026657536,42.520845230355839],[-78.329347791169269,42.520796146960365],[-78.345317806190295,42.520554915132053],[-78.34875557422572,42.52049590099346],[-78.357858769051219,42.520392802124526],[-78.362224888246601,42.520357111367879],[-78.364333755174115,42.520333570974785],[-78.374746041506796,42.520116227238169],[-78.408158559505011,42.519418890393055],[-78.41512771222871,42.51929699030179],[-78.417764620531798,42.51922827736],[-78.417813240893352,42.519227005320971],[-78.424802498722599,42.519197101978939],[-78.442978745673273,42.519108300755242],[-78.447148990668396,42.519140945126715]]]]}}
]
}
{
"type": "FeatureCollection",
"name": "wyomingvillages",
"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } },
"features": [
{"type":"Feature","properties":{"FID_Villag":0,"Id":0,"Village_Na":"Perry village","Label":"Perry","Shape_Leng":41269.00961690001,"Shape_Area":66430957.342500001},"geometry":{"type":"MultiPolygon","coordinates":[[[[-77.992301294322573,42.730392974423985],[-77.992141674554702,42.730202032566517],[-77.991757275685458,42.729612810466797],[-77.991246196737251,42.72892345968382],[-77.991083390704347,42.728709427486201],[-77.991019100020537,42.728614642297487],[-77.990723640462576,42.728212247747649],[-77.990642982507794,42.728208101513097],[-77.990558131658261,42.728143664894255],[-77.989513418683458,42.726719314857306],[-77.989512907605345,42.72671861811898],[-77.987706403444761,42.724255475766284],[-77.98579669470044,42.721710190721822],[-77.985684796628334,42.721585077737032],[-77.985481657223971,42.721584994173632],[-77.984971277009052,42.721518100580475],[-77.983770924016653,42.721340553505115],[-77.982663650893159,42.721146354049672],[-77.982420222875618,42.721097977052032],[-77.982406737370127,42.721021094696127],[-77.982347395975012,42.720321427122968],[-77.979903419522429,42.720042507770671],[-77.979340168970353,42.717462324624826],[-77.982727844777216,42.717498624678711],[-77.983803353505905,42.717504136910712],[-77.983894047124252,42.717773469966644],[-77.984197595188832,42.717981951955018],[-77.984567983442133,42.718049538636123],[-77.984656910081185,42.718325428215849],[-77.984843208390203,42.718434927459981],[-77.984941157119252,42.718716239316556],[-77.985476856251211,42.718883597575704],[-77.985583000581073,42.718808220480369],[-77.986159110079697,42.718988455620625],[-77.986460171207057,42.719509724096383],[-77.98643876546943,42.719777622701699],[-77.986162554419252,42.719835561961204],[-77.985358206538379,42.719734862983529],[-77.985400457937345,42.71997597207644],[-77.985607935204897,42.720107263931176],[-77.986155514557396,42.720169192981125],[-77.987296092466536,42.720241143740409],[-77.987447287164585,42.720212509661742],[-77.987673020158226,42.720039292151334],[-77.989962725787862,42.718285198587729],[-77.990831925161814,42.717643997059646],[-77.99085864838429,42.717624283104975],[-77.990989351308571,42.71756574738982],[-77.991072442518501,42.717472303146572],[-77.992241384942602,42.716573675054434],[-77.994020374704277,42.715200260470297],[-77.995523857882162,42.714026168857551],[-77.995789539839208,42.713821686478738],[-77.995837367722288,42.713788350869571],[-77.995837748861703,42.713721422225653],[-77.995933213179342,42.713723684511521],[-77.996151081774954,42.713556197407961],[-77.996711290829737,42.713139000947557],[-77.999752961441743,42.710894775193609],[-78.000068442797215,42.710667092318936],[-78.000498495188836,42.710349635872653],[-78.000561300949755,42.710303272323706],[-78.000633454607424,42.710249899272995],[-78.000707941590491,42.710207288794415],[-78.000726241427699,42.710198422644432],[-78.00540755285239,42.707492180129336],[-78.005388866939597,42.706239177415704],[-78.005369933238455,42.705001750161266],[-78.00536523389124,42.702899238269971],[-78.010141099802098,42.702878406071228],[-78.011805471016032,42.702867761982453],[-78.013419535065452,42.702857953356393],[-78.013664589301172,42.702856453922756],[-78.015914093826908,42.702846013622285],[-78.022555078577767,42.702808319565655],[-78.022835158458648,42.702807339326654],[-78.022830858017301,42.702842377543391],[-78.022824544590975,42.70287093042171],[-78.022821873939606,42.702898607183364],[-78.022837103265175,42.702955846583798],[-78.022839163170971,42.702996052746769],[-78.022849742306676,42.703031833575174],[-78.022872309847642,42.703081069669857],[-78.022828511494623,42.70310318505711],[-78.022800369586705,42.703135204173464],[-78.022784352657752,42.703165495642644],[-78.022775386975923,42.703219941974226],[-78.022746811031041,42.70330018614407],[-78.022730456446453,42.703367991991229],[-78.022721446884532,42.703426898831118],[-78.022713611545896,42.703490278449237],[-78.022706922637056,42.703560812548652],[-78.022700062495389,42.703650100676349],[-78.022688753094229,42.703695597859507],[-78.022671228985644,42.703758930036543],[-78.022651251116258,42.703825823280596],[-78.022649338488918,42.703903526026387],[-78.022655097428029,42.703936602327865],[-78.0226354373888,42.703967768080268],[-78.022606965380646,42.704036405815792],[-78.022610951962349,42.704132000164314],[-78.022603349552327,42.704169478972695],[-78.022605461968226,42.704203431365769],[-78.022619671125412,42.704239227281896],[-78.022625366955509,42.704279452142977],[-78.022616441202686,42.704329428015498],[-78.022589378357196,42.704375741991861],[-78.022598575495635,42.704430274533998],[-78.022596908871407,42.704481179895012],[-78.022580899537587,42.704510577419214],[-78.02257953282799,42.704528434346251],[-78.022598245096461,42.704601770145658],[-78.022588195699925,42.704641914305142],[-78.022579106625187,42.704709752990759],[-78.022562766337373,42.70477577187485],[-78.022555989896475,42.704856129025522],[-78.022552332336645,42.704897141711349],[-78.022546689048596,42.704920637552355],[-78.022538685044836,42.704981151727104],[-78.022527490170617,42.705018631837049],[-78.02251795833179,42.705041000427642],[-78.022503558492517,42.705090771226814],[-78.022527950875585,42.705161750992517],[-78.022535444798692,42.705226100495871],[-78.022530936622744,42.705278480042708],[-78.022520034663884,42.705323685710503],[-78.022510633777216,42.705381396868333],[-78.022519085841637,42.705429076673362],[-78.02254111616287,42.705492901398294],[-78.022544611185452,42.705553064067168],[-78.022540986085886,42.705597108941014],[-78.022528549704589,42.705633369433123],[-78.022508798875336,42.705674958678024],[-78.022495593629102,42.705706454890503],[-78.022491374882378,42.705726679900181],[-78.022480519866619,42.705766523915045],[-78.022477438785657,42.705839752979408],[-78.02246815569336,42.705884367901753],[-78.022453938142334,42.70593908193986],[-78.022434718106254,42.706011040924515],[-78.022438486508193,42.706040834174416],[-78.022451946438139,42.706070672192332],[-78.022445025462275,42.706122447547216],[-78.022441341760683,42.706172446364477],[-78.022448254068749,42.706211784133622],[-78.02243826764655,42.706244487129787],[-78.022426734389072,42.706270034581891],[-78.022428646684133,42.706326613710026],[-78.02243776450328,42.706390376097183],[-78.022449504103008,42.706431521799921],[-78.022453839321571,42.706488111893222],[-78.022464873995261,42.706517940729327],[-78.022454982357942,42.706540519104607],[-78.022426148041873,42.706604095597207],[-78.022424976615667,42.706644582095784],[-78.022417308210919,42.706689205816922],[-78.022420232298415,42.706723162174313],[-78.022425319485052,42.706785711086788],[-78.02243117838799,42.706852434625276],[-78.022434494737865,42.706932841174925],[-78.022458167185036,42.70699369412354],[-78.022475398717688,42.70705273032997],[-78.022480690927821,42.707092653419529],[-78.022472722263259,42.707170621738342],[-78.022473511453271,42.70726232931522],[-78.022472675051986,42.707355219056041],[-78.022472112069437,42.707417742986927],[-78.022483396158094,42.707509501060962],[-78.022483473816578,42.707590486801493],[-78.022480494514511,42.707652400230941],[-78.022485015767472,42.707688747358453],[-78.022502586666334,42.707799593554391],[-78.022491912055543,42.707909107685083],[-78.022489793165505,42.70796507378985],[-78.022491594195216,42.708034155891212],[-78.022491282372528,42.708075700780142],[-78.022444360204929,42.708073017788898],[-78.022446234472199,42.70812637022631],[-78.02244928899276,42.708158999348562],[-78.022449652009513,42.708208425606344],[-78.022432994822339,42.708264916461353],[-78.022437448093271,42.708308407616727],[-78.02244201813042,42.708338797143995],[-78.022448027301252,42.708388849719768],[-78.022454965480009,42.708425205080999],[-78.022463509043646,42.708462762032504],[-78.022467989147344,42.708503277099091],[-78.022461127381973,42.708547903859106],[-78.022444519613884,42.70859903308164],[-78.022450457890201,42.708656825633881],[-78.02245019516991,42.708686000460951],[-78.022444055621179,42.708740160773445],[-78.022421486339937,42.708736478044514],[-78.02243083310843,42.708774038930727],[-78.022428066961297,42.708812133748133],[-78.022418671959599,42.708869255229779],[-78.022420482430761,42.708937146340546],[-78.022424613982807,42.70901636582748],[-78.02242978713106,42.709069387819881],[-78.022437550713889,42.709103964731433],[-78.022432175067337,42.709162891988917],[-78.022420458920379,42.709208685990689],[-78.022406054561159,42.709283643834034],[-78.022413509576097,42.709352756316818],[-78.022417759107455,42.70941887687988],[-78.022430130377657,42.709479674451821],[-78.022427331993271,42.709521344888238],[-78.022435714827168,42.709576763751123],[-78.022428494839417,42.709661286798273],[-78.022430273134177,42.709732753523468],[-78.022429297179599,42.709841124089714],[-78.022429589226761,42.709898293010596],[-78.022428935019306,42.709970936144124],[-78.022441113338033,42.710053172147774],[-78.02244642102157,42.710130575279457],[-78.022439417150693,42.710203971424107],[-78.022451041834941,42.710275305420978],[-78.022470054455297,42.710343108822393],[-78.02249200266219,42.710413386972476],[-78.022479932252864,42.710438174672262],[-78.022434854092751,42.710510581307105],[-78.022417611322538,42.71061643251069],[-78.022426139936556,42.710697020282112],[-78.022444023543756,42.710725777143388],[-78.022449562748392,42.710768395858132],[-78.022458976398383,42.710791922206873],[-78.02252692089165,42.710811915468632],[-78.022545955989855,42.710836034479051],[-78.022535960892796,42.710876942264861],[-78.022520206512596,42.710899798900599],[-78.022492061420678,42.710980017998999],[-78.022477549798239,42.711031739029892],[-78.022457705763102,42.711097441782236],[-78.022446969605966,42.711138616902957],[-78.022438410835179,42.711184446160928],[-78.022424367991974,42.711222600958422],[-78.022429641822384,42.711253753726709],[-78.02242095641077,42.711313776554384],[-78.022410977128544,42.711353045953942],[-78.02240787479144,42.711395109195486],[-78.022412598807549,42.711419673964762],[-78.022399643396824,42.711460018083073],[-78.022392468113864,42.711516775006238],[-78.022386119408793,42.711646162488833],[-78.022377684718933,42.711678338302711],[-78.022402970701833,42.711748359129523],[-78.022394221209538,42.711815485483385],[-78.022379545187874,42.711882851977627],[-78.022363832062823,42.711942293917879],[-78.022358934949921,42.7119927809728],[-78.02236055805902,42.712017909409482],[-78.022349331564612,42.712031231178663],[-78.022317638130772,42.712014968695613],[-78.022302073987944,42.712016530891731],[-78.022297289955191,42.712054459155055],[-78.022295507675025,42.712128715377219],[-78.022281093127333,42.712208096028313],[-78.022271925298497,42.712321635237153],[-78.02228315006262,42.712390766162429],[-78.022299046970119,42.712434802193819],[-78.022274313051412,42.712467446388615],[-78.022275480171814,42.712502399682911],[-78.022305604521264,42.712528759757902],[-78.022358394381499,42.71258689973925],[-78.022388290124312,42.712638377354381],[-78.022428623602067,42.712682258493111],[-78.022456900990278,42.712749020652055],[-78.02246143503605,42.712821123806911],[-78.022549906823173,42.712864148671962],[-78.022567503461175,42.712924849349463],[-78.022624017151998,42.713022052936047],[-78.022653233795495,42.713066700583411],[-78.022701771426242,42.713104069798099],[-78.02275108939979,42.713137076616228],[-78.022779672110943,42.713169979211131],[-78.02278310059026,42.713200030920518],[-78.022770334210051,42.713220172660066],[-78.02270027586566,42.713228838451606],[-78.022665987617216,42.713254062929821],[-78.022615261235515,42.71325436280322],[-78.022578446623584,42.713230700498762],[-78.022549286515513,42.713179772090108],[-78.022522275649138,42.713136773668566],[-78.022461426190162,42.713150947109895],[-78.0223937516474,42.713183378295767],[-78.022268122559183,42.71323682443218],[-78.022193867478748,42.713259940814538],[-78.022155883932314,42.71328378315804],[-78.022138714072057,42.713340488365979],[-78.022124902639021,42.713393663959081],[-78.022081985020506,42.713431677183074],[-78.022012685275811,42.713479664538177],[-78.021881162972889,42.713530079036396],[-78.021771937397702,42.713530089896935],[-78.021671299139015,42.713522220604055],[-78.021593741649099,42.713541774326742],[-78.021546324301042,42.713544272156568],[-78.021466860699775,42.713570092960431],[-78.02137684700368,42.713615793392748],[-78.021350317854939,42.713662203241775],[-78.02063457190448,42.713665064803514],[-78.020430714096733,42.713666659191333],[-78.020141975765341,42.713665135947103],[-78.020008821511723,42.713664433274182],[-78.019798986955834,42.713662489775203],[-78.016482835352988,42.713670950154679],[-78.016464312884082,42.71590354925231],[-78.016463828611592,42.715956343585546],[-78.016456454162181,42.716754597511226],[-78.016453780387991,42.716989020013166],[-78.016453454248065,42.717057564920957],[-78.016407842391502,42.717165159759055],[-78.016410735443273,42.71727529215525],[-78.016449754750894,42.718759983337073],[-78.016431244476891,42.719169934657906],[-78.016435743956905,42.719319129419873],[-78.016413050387598,42.721177872543393],[-78.016348680375003,42.726449461351045],[-78.016344005360367,42.726832406664293],[-78.016330208630492,42.728055719880146],[-78.016304352327566,42.730348938182736],[-78.013776824007095,42.7303507556434],[-78.013609810115099,42.730350874545508],[-78.009747729894997,42.730336951914076],[-78.007124113363403,42.730327418379829],[-78.005720282135783,42.730327677103418],[-78.005436027872989,42.730323521426186],[-78.004359729118804,42.730322899835443],[-78.001059274622818,42.730325686988635],[-77.999185951489295,42.73034526798417],[-77.996416618936806,42.730374165825722],[-77.995874990826223,42.730361716374389],[-77.992414592235662,42.730397625513945],[-77.992301294322573,42.730392974423985]]]]}},
{"type":"Feature","properties":{"FID_Villag":2,"Id":0,"Village_Na":"Attica village","Label":"Attica","Shape_Leng":29313.659190499999,"Shape_Area":42537832.037},"geometry":{"type":"MultiPolygon","coordinates":[[[[-78.285307783865264,42.869577869756974],[-78.284811995904789,42.86958040834584],[-78.283930179051822,42.8702474817172],[-78.283008878669932,42.870701541547213],[-78.28194279952092,42.871039649217856],[-78.281022163880394,42.87135212996148],[-78.280973263306592,42.871278326443239],[-78.279772629418758,42.871175058266289],[-78.278926663999059,42.871463801180539],[-78.279138955347037,42.871951187027442],[-78.277138437564687,42.872527671885749],[-78.277140638579439,42.872818454678516],[-78.27777379823668,42.874357416788087],[-78.27840158365548,42.874259464856003],[-78.278586535894718,42.874759971439623],[-78.279321396983818,42.875196024175146],[-78.280946006837979,42.875879592923489],[-78.280986484225267,42.878005290462568],[-78.273992235086155,42.877997926061781],[-78.272150737312288,42.876132966949598],[-78.274380564777857,42.875152347346791],[-78.273969633109687,42.874506864510955],[-78.274455026227457,42.874208959769888],[-78.27486850429419,42.874035510497109],[-78.274346251658628,42.87343406308937],[-78.27409440204606,42.873034247057355],[-78.273909969029532,42.869591839821581],[-78.272075230252355,42.86956316727759],[-78.270542682040301,42.869542732717711],[-78.270097936170643,42.86953661307026],[-78.268623460626756,42.869543865789332],[-78.267279224448473,42.869548609971453],[-78.2671004524842,42.869549877984639],[-78.266924036541781,42.869551129725295],[-78.266032136808434,42.869556593741187],[-78.265817448618265,42.869557729582297],[-78.265817447611326,42.869557687268944],[-78.265816184096053,42.869514093987547],[-78.265808982624321,42.869265627312252],[-78.265698798467668,42.865205405948096],[-78.265694711470687,42.865054727496414],[-78.265692478749671,42.864589450621494],[-78.265666532744092,42.863459168919611],[-78.26196731622646,42.86274983462306],[-78.262074488926928,42.862354221576688],[-78.260597896513715,42.862078142308455],[-78.260270948380494,42.861646716777301],[-78.260018964595119,42.861289499703169],[-78.262108107685592,42.861697301423952],[-78.262173913365672,42.861534791844875],[-78.262816273911284,42.861658517013893],[-78.262964215131632,42.861295165168173],[-78.262912387035954,42.859091491694159],[-78.265550969135035,42.85971371586367],[-78.265532453635302,42.858972280448718],[-78.265491966859358,42.857615634889633],[-78.265486479324437,42.857415175216353],[-78.265439004070856,42.85548584229393],[-78.269497532821717,42.855424715876104],[-78.269960130793905,42.855413472737588],[-78.273533748744185,42.855337189507409],[-78.277199690281691,42.855258822614886],[-78.277311195282536,42.855254933803948],[-78.277418784132124,42.85525117935552],[-78.280985609622064,42.855199543857346],[-78.281558426601222,42.855200904134357],[-78.284365112025824,42.855191789271977],[-78.284551846088036,42.855191456710997],[-78.288709726229641,42.855202699991416],[-78.295062421280349,42.855185481172889],[-78.295046660271709,42.859094067131252],[-78.295045657087144,42.859203946230423],[-78.29505039230007,42.859323682853585],[-78.2952099804413,42.864552593014892],[-78.295359824938714,42.869653938835839],[-78.295359827181329,42.869653982952507],[-78.293222979691052,42.869632702831993],[-78.292843191506265,42.869628912966647],[-78.291876367575114,42.869619269778774],[-78.289390801610281,42.869591591458466],[-78.289083904112019,42.869586464795447],[-78.288842345049758,42.869582429951933],[-78.287774831895845,42.86956519402495],[-78.285307783865264,42.869577869756974]]]]}},
{"type":"Feature","properties":{"FID_Villag":3,"Id":0,"Village_Na":"Silver Springs village","Label":"Silver Springs","Shape_Leng":25225.642882200002,"Shape_Area":27854958.174800001},"geometry":{"type":"MultiPolygon","coordinates":[[[[-78.074155259758584,42.66353119170207],[-78.074156181107114,42.663291728863513],[-78.074158329264037,42.662802123887836],[-78.074157479057931,42.662731733661325],[-78.074158036360586,42.662663452687617],[-78.074160125677537,42.662182092834314],[-78.074162340911016,42.660670192275816],[-78.074157935612902,42.660467472070302],[-78.074163165842933,42.65968027946856],[-78.075281590619909,42.659685246357334],[-78.075282164501843,42.656276302165516],[-78.075268122410478,42.653462475985911],[-78.075267832910654,42.652781222591713],[-78.075270161277388,42.651778845615382],[-78.076618131790482,42.651784817392823],[-78.076808711985223,42.651782793103031],[-78.077258544543511,42.65178538214375],[-78.079272914193169,42.651794005202959],[-78.083221846529227,42.65181981716642],[-78.083465027026804,42.651820878796535],[-78.086918239509174,42.651842344285228],[-78.088740850487341,42.651851179479422],[-78.089907375508858,42.651852954034162],[-78.090286508136344,42.651850347515229],[-78.0909405311276,42.651854162299557],[-78.090954400463005,42.653350129333084],[-78.091515327771873,42.654091311783638],[-78.091852226449916,42.654590833128282],[-78.092293996991486,42.655390438209068],[-78.092550278957845,42.655916041452656],[-78.092779255514145,42.65650555245481],[-78.09296957882988,42.6572796831421],[-78.092991300154281,42.657417527044771],[-78.09350471041077,42.660630366222243],[-78.09387422016836,42.662695806142779],[-78.094170667330914,42.664362371173574],[-78.094220253513612,42.664650328792263],[-78.094442303115031,42.666060860120503],[-78.094491987944906,42.666518823875684],[-78.094489294121445,42.666862547647455],[-78.094452233508619,42.66722184854612],[-78.094404498171684,42.667485623732588],[-78.094318600614173,42.66776383987041],[-78.094237083991715,42.667968495035026],[-78.094039840710565,42.668372326804551],[-78.093872663209382,42.668823746318445],[-78.093801643427099,42.669144990225199],[-78.093593886096073,42.672288063913513],[-78.093571793351771,42.672620657341042],[-78.093562767582725,42.672790167140711],[-78.093528169493908,42.67343993304975],[-78.09342539888155,42.67344174049019],[-78.093444042684126,42.673975395176967],[-78.093483342469582,42.674301322740526],[-78.093556479060837,42.675518842140022],[-78.093562823063905,42.675630557403174],[-78.091340331817818,42.673372434466621],[-78.090279570915257,42.672301131187055],[-78.089074390593652,42.67106938342932],[-78.088798442883657,42.670787798138029],[-78.087169900765119,42.669100585507394],[-78.086566978665132,42.668478512204359],[-78.086249432077551,42.668149730729162],[-78.086047763637396,42.667940710992525],[-78.084932428412813,42.666789715193495],[-78.082999845523034,42.66479344850827],[-78.082340321383583,42.664111621818044],[-78.082341039754795,42.664022173821493],[-78.082340263680138,42.663850271228249],[-78.082343442842557,42.663722950358817],[-78.082342468656606,42.663575714779938],[-78.082340401566881,42.663564565080627],[-78.081578095415807,42.663562273922103],[-78.081152995057991,42.663559817429039],[-78.080727977447665,42.663560115402333],[-78.080467115854304,42.663558970290801],[-78.077917057397244,42.663552072464562],[-78.074155259758584,42.66353119170207]]]]}},
{"type":"Feature","properties":{"FID_Villag":4,"Id":0,"Village_Na":"Gainesville village","Label":"Gainesville","Shape_Leng":22411.247064700001,"Shape_Area":23792147.971000001},"geometry":{"type":"MultiPolygon","coordinates":[[[[-78.124371477350508,42.64754612614027],[-78.124366089214618,42.646585945891495],[-78.124369217152449,42.645002432749138],[-78.124341567557209,42.641122739446864],[-78.124342028366101,42.641045467552438],[-78.124342732137237,42.640964339020265],[-78.124327877260285,42.638629202176226],[-78.124329193930123,42.637632755139343],[-78.124328544697406,42.637335278831415],[-78.124332106681635,42.636464443922229],[-78.124330519292485,42.636294748481326],[-78.124331672625161,42.635365249121193],[-78.125175753626976,42.635364438035417],[-78.125337468502622,42.635365084817344],[-78.129388274404945,42.63537383578948],[-78.129609090429,42.635372518637588],[-78.130306749662722,42.635380658462601],[-78.130335133894562,42.635378677626441],[-78.13400952744108,42.635383720495255],[-78.134134125276589,42.635385259842629],[-78.134257826888259,42.635385791750416],[-78.135752724513438,42.635390339834522],[-78.14005096592885,42.63541124879935],[-78.140044892222178,42.636266779388492],[-78.140045002769128,42.638249389765697],[-78.140042777337825,42.638398994415525],[-78.140042942785144,42.638539526495528],[-78.140042116915353,42.638824314143172],[-78.141028623504113,42.638820422841754],[-78.141313213912966,42.638819297001547],[-78.14198349483155,42.638816643311081],[-78.142203820938875,42.638815771740163],[-78.144263375955333,42.638807594617397],[-78.147294881052744,42.638808639584177],[-78.147647527003244,42.638808754575152],[-78.147837879119805,42.638810019592512],[-78.148873756783871,42.638805993678538],[-78.14887363185008,42.638823947084667],[-78.14887265089385,42.638964913234346],[-78.148856643525093,42.641265073251823],[-78.148853493559457,42.642295492671003],[-78.148851512689362,42.642580100294175],[-78.144316923544537,42.641670158670699],[-78.144320101401888,42.641974622316503],[-78.144324510138276,42.642904104548542],[-78.144321213251615,42.644818783251878],[-78.144316232582142,42.647476927855422],[-78.141130370019937,42.64749262695905],[-78.140732729042682,42.647494584841631],[-78.135492491837184,42.647505665162292],[-78.134238033027742,42.647511234675655],[-78.134107918791358,42.647510990866188],[-78.133989281325611,42.647510064529179],[-78.128915319884754,42.647527851149391],[-78.124371477350508,42.64754612614027]]]]}},
{"type":"Feature","properties":{"FID_Villag":5,"Id":0,"Village_Na":"Arcade village","Label":"Arcade","Shape_Leng":64527.684678999998,"Shape_Area":72026630.229100004},"geometry":{"type":"MultiPolygon","coordinates":[[[[-78.410410201047824,42.543501908518465],[-78.410456340754436,42.540479539461181],[-78.41045398354558,42.540380376667279],[-78.410698989484928,42.540291814173344],[-78.410710572809563,42.540389160607489],[-78.41074362675856,42.540855448686699],[-78.411391202587581,42.540734545964142],[-78.411256325879194,42.540194912567017],[-78.411232942704956,42.540101508772295],[-78.411660246605024,42.539950683864404],[-78.411659504716482,42.5398504530772],[-78.411646943317123,42.538155672720592],[-78.411613949699188,42.537104848852266],[-78.41029899488899,42.537100965524232],[-78.410273175059444,42.535157580306944],[-78.410272329421844,42.535052975719729],[-78.411579084865167,42.535045890660435],[-78.411578282337331,42.534961180490299],[-78.411560138111639,42.534281815984976],[-78.411539696994495,42.533487980262507],[-78.406900821489799,42.533544800676111],[-78.406863374845798,42.532239978490473],[-78.41150659787894,42.532177867031876],[-78.411504357865454,42.530404800822218],[-78.411494233103994,42.52939390566852],[-78.41232632618086,42.5288461394032],[-78.412407140800354,42.528792937416561],[-78.412478435277293,42.528745667513476],[-78.413552215670649,42.528033774279521],[-78.413827535925265,42.527852610192568],[-78.414107659986939,42.527668287154391],[-78.414383216016887,42.527486965419619],[-78.414481243385495,42.527420871941153],[-78.414564169768653,42.527364957190301],[-78.415557069141471,42.526711786034348],[-78.415641406790243,42.526656306498623],[-78.415907059780722,42.526478610895175],[-78.415739694076223,42.526340886219501],[-78.41561062202301,42.526428631161615],[-78.413955298152516,42.525236453928919],[-78.413313498042228,42.525685094473253],[-78.412015469935568,42.524727204493701],[-78.411664898326876,42.524957932590702],[-78.411573588524718,42.525018029798531],[-78.409041434774807,42.523046350146416],[-78.409135802882929,42.5229883581396],[-78.414777930770001,42.519516929549354],[-78.414778749957165,42.519516425697574],[-78.414805299532674,42.519516261305171],[-78.417772420528522,42.519497813738049],[-78.417773777411199,42.519498307252761],[-78.421156842529626,42.519379535767897],[-78.422249254297341,42.519340563980755],[-78.422991948916348,42.519313427429346],[-78.423657548014845,42.519292438250147],[-78.423846742383262,42.519285956978244],[-78.42471744735181,42.519255170251149],[-78.424728461011611,42.519511695258018],[-78.424740296552017,42.519748372905092],[-78.424759487163598,42.520206668372452],[-78.424769064143206,42.520443342860503],[-78.424782039096016,42.520676672098951],[-78.424790486492213,42.520912511342289],[-78.424817326721893,42.521475349741799],[-78.424818391495236,42.52150043752571],[-78.424695521954249,42.521807173830616],[-78.424574914299342,42.52211224220256],[-78.424361015769321,42.522649657601981],[-78.424289351899318,42.522824334874592],[-78.424172167177886,42.523115188498096],[-78.424117133266535,42.523255543977733],[-78.424302395564837,42.523591864204271],[-78.424455152546003,42.523869044648556],[-78.424601892457943,42.524136069482033],[-78.424878281825542,42.52465176160581],[-78.424908988153376,42.524649916201355],[-78.424919506189951,42.524790648981359],[-78.424942048831028,42.525064070812007],[-78.42496886900841,42.525389379857494],[-78.424437731684677,42.525417306348075],[-78.424454783812692,42.525636212295765],[-78.424475935594202,42.52590882019534],[-78.424495855628408,42.526186302283655],[-78.424517570941717,42.526463857966867],[-78.424527932986621,42.526604436573855],[-78.424323131573217,42.526613562113546],[-78.424347473370744,42.526914440492597],[-78.424366080772629,42.527175231353034],[-78.424922545438079,42.527157770881729],[-78.424938433346824,42.527436048504242],[-78.424950952044142,42.527627707379573],[-78.424963820369271,42.527835656384006],[-78.427430225963008,42.527827304602546],[-78.427407708694659,42.530059176618579],[-78.428762223328945,42.530056312547622],[-78.432230999903226,42.530049793038316],[-78.432412360067914,42.530049438915526],[-78.436099327182077,42.530039217564862],[-78.440019398745562,42.53002047467443],[-78.445929916758274,42.529979421313186],[-78.447867054403943,42.529970862160823],[-78.447869738599337,42.52982207171614],[-78.447874207322144,42.529175912472056],[-78.447904467807206,42.525166045840287],[-78.447962036032138,42.522034099958773],[-78.448007732306195,42.519164937457489],[-78.449749178323444,42.519159644055172],[-78.461349445800323,42.519130468282036],[-78.460014183566955,42.521200472048172],[-78.458346278133277,42.523783652634684],[-78.457443151151296,42.525167611577764],[-78.456968467191189,42.52588192163401],[-78.456975151147773,42.525989076700156],[-78.456146577927555,42.527245840017116],[-78.455788469946455,42.527804126291919],[-78.455685902721868,42.527904957622248],[-78.455603580482148,42.52798249510915],[-78.455524327642834,42.528091730297078],[-78.454738672391571,42.529272372640591],[-78.454718426130412,42.529314156164268],[-78.45470461839092,42.529357350452415],[-78.454697421919576,42.529401415533911],[-78.454696926718427,42.529445799639994],[-78.454625259704997,42.529543193225507],[-78.454500271428756,42.529619246883748],[-78.454289229990295,42.52975192013681],[-78.454011772189304,42.529924412070628],[-78.453989913327533,42.529952779481285],[-78.454597835051359,42.529951395752803],[-78.455218993217002,42.529949189051763],[-78.45676771069067,42.529939888434015],[-78.45673686569117,42.530859900764298],[-78.456716435402228,42.531408332573626],[-78.45665529394887,42.532976805255849],[-78.456645152796227,42.533092442922609],[-78.456643599030471,42.533208316958465],[-78.456650634617162,42.533324080758668],[-78.456666238398824,42.533439384990182],[-78.456690364866091,42.53355388299201],[-78.456719920788146,42.533628483946998],[-78.456757633081864,42.533701044876658],[-78.456803248340449,42.533771079342593],[-78.456856461992174,42.533838117953984],[-78.457885954424697,42.534812889910697],[-78.457984711571001,42.53492283022382],[-78.458075223869542,42.535036586712664],[-78.45815722294337,42.535153819698188],[-78.45823046598511,42.535274183226335],[-78.458280057091727,42.535355335364692],[-78.458322750404008,42.535438606406565],[-78.458358380985814,42.535523673879872],[-78.458386811903665,42.53561021174103],[-78.458402651353396,42.535677576135285],[-78.458410044654144,42.53574573165875],[-78.458408930843547,42.535814102068507],[-78.458399317139865,42.53588210579246],[-78.458381287441057,42.535949167642336],[-78.45835499258736,42.536014718803067],[-78.458320656434651,42.536078204041779],[-78.457957270136347,42.536597675872827],[-78.457867950036771,42.536567206249863],[-78.457267894247636,42.536372389565294],[-78.457111093956101,42.53606514636197],[-78.456581199131534,42.535681224341197],[-78.456572680286712,42.535974296860175],[-78.456569280266763,42.536086835062655],[-78.456564776276281,42.536218547063505],[-78.456537842008643,42.537436569052588],[-78.456534127763717,42.53759061598911],[-78.456489805445912,42.539366742834503],[-78.456388839219599,42.539368573733789],[-78.455149190637215,42.539392761377471],[-78.454607168480294,42.539404399752144],[-78.452990756597302,42.539435519415697],[-78.451582429435419,42.539462728132236],[-78.449970827715603,42.539494630885173],[-78.449327062673007,42.539507591388031],[-78.449221995189149,42.539509710064834],[-78.448787705010091,42.539518453623209],[-78.447719877046993,42.539540419245732],[-78.445769673739264,42.539581866165967],[-78.442441133430108,42.53964493011393],[-78.439841270736537,42.539693492801668],[-78.438994457685155,42.539708124843166],[-78.438003326180066,42.539727272403283],[-78.43747790527442,42.539736532610462],[-78.437252646260404,42.539741233901808],[-78.435454730871754,42.539785341765352],[-78.435270611416883,42.539791563334461],[-78.431899017964099,42.539854562482191],[-78.430207799513084,42.539877056694735],[-78.427362071458148,42.539922803341717],[-78.427288205300883,42.5399232320731],[-78.4272322669716,42.539923556457161],[-78.424768801774277,42.539963884843111],[-78.422917233856637,42.539992118986142],[-78.421842691800535,42.540011279979325],[-78.42002123056298,42.540037509365199],[-78.417829627945636,42.540072175535357],[-78.417644556657422,42.540075510828117],[-78.415272077324445,42.540096344078947],[-78.415074608351517,42.540098077039801],[-78.41422247850592,42.540105544661742],[-78.414218377639784,42.540768298410988],[-78.414211295183733,42.541046809403042],[-78.414123516460009,42.549920596731013],[-78.412903825210904,42.549947706679262],[-78.412899100723735,42.550707056982425],[-78.412062231730488,42.550713574199627],[-78.411227611281916,42.550721060081749],[-78.41039281652742,42.550728542322467],[-78.410392615973706,42.550696666861178],[-78.410366144048496,42.546519584162155],[-78.410394953945698,42.544048532611555],[-78.41040395365043,42.543774207587852],[-78.410410201047824,42.543501908518465]]]]}},
{"type":"Feature","properties":{"FID_Villag":6,"Id":0,"Village_Na":"Castile village","Label":"Castile","Shape_Leng":27423.028456100001,"Shape_Area":38043994.161899999},"geometry":{"type":"MultiPolygon","coordinates":[[[[-78.040567919370091,42.638971600358907],[-78.040552185542495,42.638318617851596],[-78.040522181903029,42.637042518428267],[-78.040465851879688,42.635167224720547],[-78.040453318804282,42.634695884211901],[-78.040442807697616,42.634230538170208],[-78.040415571134858,42.633058805394036],[-78.040395620447171,42.632096128260798],[-78.040392167062237,42.631937165675801],[-78.040378734646382,42.631371926258581],[-78.040340123962665,42.629974700410756],[-78.04031505276626,42.629063831842394],[-78.040276517866644,42.627366772316734],[-78.04022014954468,42.625711914755996],[-78.040216821150707,42.625654043733945],[-78.040204646512919,42.62529185261203],[-78.040185193361836,42.624605741087635],[-78.040186269426343,42.624532313729262],[-78.040185265620607,42.62445848489385],[-78.040150634451592,42.623132232945991],[-78.041834473779403,42.623114741907152],[-78.044089159513163,42.623092842569761],[-78.044363074057188,42.623092719291428],[-78.04445574676329,42.623091157645902],[-78.044552519349551,42.623087298839266],[-78.044647913561604,42.623088488433552],[-78.046109839470674,42.62310799872138],[-78.051153551273728,42.623072131223594],[-78.054132773486799,42.62305212260182],[-78.057833268578591,42.623024789644283],[-78.05869219546139,42.623018297579499],[-78.058779582440479,42.622959051012366],[-78.058868457358997,42.622895050609891],[-78.059207662562784,42.622650732952728],[-78.059305459950679,42.622579446709253],[-78.059387610272026,42.622519739988164],[-78.05953540699079,42.622417540412258],[-78.059929204224645,42.622132290480216],[-78.060439870855092,42.621766043293398],[-78.060961502870342,42.621404842962257],[-78.061683917013056,42.621944620882225],[-78.062407411569211,42.622488185619815],[-78.062854505662315,42.622825238642129],[-78.062940031068536,42.62288945306414],[-78.064295304511035,42.621887680742958],[-78.064402234731133,42.62336232778285],[-78.064475164123522,42.624368035540414],[-78.063118074593319,42.624360574573615],[-78.063011370697126,42.624442305330234],[-78.062982356060388,42.626652853045087],[-78.06297894257257,42.627062610053535],[-78.062974452858754,42.627339746567955],[-78.062969806972603,42.627611916522532],[-78.062969588579747,42.627661837674445],[-78.062953847429739,42.628854566145947],[-78.062953380290196,42.628921755849746],[-78.062952676109603,42.62899515447112],[-78.06295713923916,42.629814897668972],[-78.062951158803727,42.630864384663589],[-78.063102676612004,42.630918119879091],[-78.064353098665464,42.632729018730743],[-78.064283749376699,42.63276134969604],[-78.065261723816548,42.634149918466306],[-78.068541385021319,42.638929231375741],[-78.068116656153663,42.638929189616796],[-78.058651629014904,42.638883094000398],[-78.058421532847575,42.638883336975184],[-78.058327221706847,42.638882109780724],[-78.057593135307854,42.638876840265269],[-78.05532861804943,42.638868501038303],[-78.053908003042991,42.638898898821374],[-78.053804128508915,42.638899856338277],[-78.05369929921909,42.638900815647801],[-78.051429963264013,42.638926788551416],[-78.050851978938681,42.638932041398959],[-78.048177997997314,42.63896336723549],[-78.046437320495741,42.638984123642182],[-78.044613832090988,42.639005575042844],[-78.043647216657433,42.639014055702191],[-78.043246383821113,42.639017969355734],[-78.042570385249718,42.639024090185487],[-78.04057069605372,42.639041283614674],[-78.040567919370091,42.638971600358907]]]]}},
{"type":"Feature","properties":{"FID_Villag":7,"Id":0,"Village_Na":"Warsaw village","Label":"Warsaw","Shape_Leng":43948.629071800002,"Shape_Area":115540486.292},"geometry":{"type":"MultiPolygon","coordinates":[[[[-78.12450450155869,42.725007472501332],[-78.124501439149483,42.724504086777337],[-78.131167259460355,42.724554961785785],[-78.132579598697646,42.724565628445539],[-78.132705483578619,42.724567150219222],[-78.13837988557664,42.724635712218777],[-78.140190130118782,42.724652887276868],[-78.140742140879141,42.724658057058406],[-78.141301461048698,42.724663351424546],[-78.141862631388221,42.724669473017769],[-78.142057435708651,42.724670797392321],[-78.142415165373279,42.72467528493047],[-78.144005493495143,42.724692941709485],[-78.145471518284623,42.724707510220554],[-78.14658798268249,42.724720464292517],[-78.146775132378878,42.724722884855453],[-78.148423386700088,42.72474246291231],[-78.148807144510698,42.724746933019183],[-78.150612395868748,42.724772283186084],[-78.153379715040657,42.724806552783292],[-78.153907935175994,42.724823116332395],[-78.157132264537978,42.724859099250232],[-78.15712767347145,42.725215925392988],[-78.1571268436563,42.725351199610088],[-78.157098836242199,42.727323708312028],[-78.157099223006952,42.728174242254582],[-78.157099238541264,42.728206742086442],[-78.157101390946011,42.732950754572393],[-78.157105890755645,42.737679927746093],[-78.157101818911613,42.739124067244155],[-78.157099985100004,42.739474265262835],[-78.157100065386786,42.739524046356252],[-78.157100995374918,42.739581718550539],[-78.157093916742483,42.740470314904101],[-78.157084118564541,42.742914170881001],[-78.157110722517402,42.743912545351805],[-78.157177453559925,42.748347479879016],[-78.157177428959045,42.75022514461147],[-78.157124890012668,42.75569760894728],[-78.157121113749412,42.756844091286503],[-78.157114216109861,42.757523915892627],[-78.15709634045065,42.761008365936895],[-78.157097214475257,42.761076372538362],[-78.155949465032165,42.761064187962035],[-78.154833710808433,42.761056231138362],[-78.151617474072253,42.761012105627941],[-78.151065044529815,42.76100886263648],[-78.146476193303968,42.760966815654193],[-78.145387121140161,42.76095605624328],[-78.143665895597465,42.760940212153088],[-78.132496431705931,42.760814533601447],[-78.128375334763987,42.76079562302202],[-78.127232569929021,42.760788625905867],[-78.125901977062426,42.760772459763452],[-78.12548384219501,42.760769943117396],[-78.124453258258953,42.760758410983328],[-78.124462830499169,42.758912726142469],[-78.124497154749761,42.755560517010451],[-78.124500312177815,42.754931230518515],[-78.124554470543472,42.750432196965349],[-78.124564910038728,42.749615555752477],[-78.124580147442387,42.748580019605043],[-78.124568454506502,42.748264850141005],[-78.12458741849062,42.746681085485008],[-78.12458820101412,42.746487215078702],[-78.1245901680673,42.746421892354377],[-78.124608735389828,42.744319814350057],[-78.124610255463764,42.744244863512002],[-78.124609674669642,42.744178202695586],[-78.12461058355477,42.742378476341322],[-78.124616817164409,42.738451882021245],[-78.124605917211071,42.736193159380917],[-78.124591368351943,42.733266540554204],[-78.124583518031088,42.732106234944496],[-78.124577029886538,42.731454415435856],[-78.124554536827048,42.729520922917274],[-78.124527906239905,42.727522523465261],[-78.124525812763054,42.727329953446564],[-78.124514333422724,42.726276305815304],[-78.12450450155869,42.725007472501332]]]]}},
{"type":"Feature","properties":{"FID_Villag":8,"Id":0,"Village_Na":"Wyoming village","Label":"Wyoming","Shape_Leng":18916.6498125,"Shape_Area":19373901.5616},"geometry":{"type":"MultiPolygon","coordinates":[[[[-78.074138367872507,42.824530278844868],[-78.074147092295803,42.824405594186508],[-78.074150376006983,42.823913714224396],[-78.074141650929306,42.823142806245421],[-78.074140690036955,42.823057978548199],[-78.074140918848329,42.82291327633309],[-78.074960907796651,42.822180627946587],[-78.075780529662239,42.821447721697915],[-78.076152918015993,42.821114537880383],[-78.076927569529516,42.820421433069548],[-78.078408972849388,42.819061737655893],[-78.078409521268782,42.819061216139332],[-78.080190828219997,42.817428686162827],[-78.082111031477496,42.815672243842933],[-78.083634155962827,42.815680155699759],[-78.083877080899668,42.815682006096367],[-78.08386412960968,42.817122513661147],[-78.085687709122098,42.818088376641008],[-78.086209004505605,42.818362936410885],[-78.08635231233724,42.818439526479857],[-78.086609289368582,42.818576827927082],[-78.087630305600584,42.819114878409913],[-78.088354305913654,42.819499792969083],[-78.089567632334592,42.820145025178995],[-78.090379926510209,42.820581501114646],[-78.090452364995031,42.82049834616717],[-78.091807278800459,42.818942919928837],[-78.092194960018261,42.819145759458443],[-78.094662468098363,42.820493376950502],[-78.09476989100763,42.820552066916363],[-78.093338836899989,42.822171346165355],[-78.093452865720081,42.822232611477254],[-78.093974388645179,42.822514520384125],[-78.094942097076057,42.823030927567793],[-78.094026540066977,42.824104461443667],[-78.093205419154359,42.825065985618039],[-78.092313412882945,42.826111736862323],[-78.09204397328098,42.826428056514878],[-78.091984696778823,42.826502116219388],[-78.091685331857718,42.826837511203692],[-78.091512569496572,42.827032001404064],[-78.090824153317925,42.827813050548464],[-78.090060340770407,42.828686334160174],[-78.089378688168495,42.829471514874754],[-78.089368739169444,42.829483100230036],[-78.089109508886125,42.829779832705384],[-78.08900529655422,42.829896924110997],[-78.088576380865803,42.830382960355919],[-78.088219811963143,42.830800676969929],[-78.087924379657736,42.831146478329373],[-78.086289804743089,42.833057961708796],[-78.08525284131386,42.832563445505464],[-78.084768076460804,42.832328888008917],[-78.084657537440734,42.832275646803843],[-78.084571301185619,42.832211876046379],[-78.08332603962026,42.831291008362875],[-78.081000876660369,42.829586354237364],[-78.080747594307311,42.829398522229774],[-78.080425537687006,42.829162756374913],[-78.079788638644672,42.828694913987754],[-78.079703635771068,42.828632001070602],[-78.079618631832219,42.828569089885434],[-78.075901503098265,42.825826184715559],[-78.075777324888449,42.825735559847047],[-78.074754237360011,42.82498032411069],[-78.074138367872507,42.824530278844868]]]]}}
]
}
...@@ -85,6 +85,39 @@ html, body { ...@@ -85,6 +85,39 @@ html, body {
transform: rotate(180deg); transform: rotate(180deg);
} }
.time-vscroll {
height: 100%;
overflow: auto;
}
.time-vscroll thead th {
position: sticky;
top: 0;
z-index: 2;
background: var(--bs-body-bg);
}
.time-vscroll thead th.th-resize {
z-index: 3;
}
.time-table {
table-layout: fixed;
}
.time-table th,
.time-table td {
overflow: hidden;
}
.time-table .dropdown,
.time-dd-toggle,
.time-dd-label,
.cell-input,
.cell-textarea {
min-width: 0;
}
...@@ -183,9 +183,9 @@ begin ...@@ -183,9 +183,9 @@ begin
// - complaints: m.DataString := 'complaint' // - complaints: m.DataString := 'complaint'
showMarker := True; showMarker := True;
if SameText(ds, 'unit') then if SameText(ds, 'unit') or StartsText('unit|', ds) then
showMarker := FShowUnits showMarker := FShowUnits
else if StartsText('complaint', LowerCase(ds)) then else if SameText(ds, 'complaint') or StartsText('complaint|', ds) then
showMarker := FShowComplaints showMarker := FShowComplaints
else if SameText(ds, 'device') then else if SameText(ds, 'device') then
showMarker := FShowLocation; showMarker := FShowLocation;
......
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 "#B05454";
}
if (iconUrl && iconUrl.indexOf("car_ems") >= 0) {
return "#5D9270";
}
if (iconUrl && iconUrl.indexOf("car_police") >= 0) {
return "#637AAE";
}
return "#637AAE";
}
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" onerror="this.onerror=null;this.src=\'' + getFallbackIconUrl(meta) + '\';">';
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 getFallbackIconUrl(meta) {
if (meta.markerType === 'complaint') {
if (meta.complaintStatus === 'attached') {
return 'assets/markers/default_attached.png';
}
if (meta.complaintStatus === 'onscene') {
return 'assets/markers/default_onscene.png';
}
return 'assets/markers/default_notattached.png';
}
if (meta.markerType === 'unit') {
return 'assets/markers/car_default.png';
}
return 'assets/markers/default_notattached.png';
}
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.
...@@ -25,6 +25,7 @@ uses ...@@ -25,6 +25,7 @@ uses
View.UnitDetails in 'View.UnitDetails.pas' {FViewUnitDetails: TWebForm} {*.html}, View.UnitDetails in 'View.UnitDetails.pas' {FViewUnitDetails: TWebForm} {*.html},
uMapFilters in 'uMapFilters.pas', 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';
Module.Websocket in 'Module.Websocket.pas' {dmWebsocket: TDataModule}; Module.Websocket in 'Module.Websocket.pas' {dmWebsocket: TDataModule};
{$R *.res} {$R *.res}
...@@ -83,7 +84,7 @@ begin ...@@ -83,7 +84,7 @@ begin
dlg.style.fontFamily = "system-ui, sans-serif"; dlg.style.fontFamily = "system-ui, sans-serif";
dlg.innerHTML = dlg.innerHTML =
"<h5 class='fw-bold mb-3 text-danger'>kgOrders web app</h5>" + "<h5 class='fw-bold mb-3 text-danger'>emiMobile</h5>" +
"<p class='mb-3' style='white-space: pre-wrap;'>" + ErrorMessage + "</p>" + "<p class='mb-3' style='white-space: pre-wrap;'>" + ErrorMessage + "</p>" +
"<div class='text-end'>" + "<div class='text-end'>" +
"<button id='refreshBtn' class='btn btn-primary'>Reload</button></div>"; "<button id='refreshBtn' class='btn btn-primary'>Reload</button></div>";
......
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup> <PropertyGroup>
<ProjectGuid>{DB6F5DBF-7E4B-45DA-AFFA-6C8DF15BA740}</ProjectGuid> <ProjectGuid>{DB6F5DBF-7E4B-45DA-AFFA-6C8DF15BA740}</ProjectGuid>
<ProjectVersion>20.3</ProjectVersion> <ProjectVersion>20.4</ProjectVersion>
<FrameworkType>VCL</FrameworkType> <FrameworkType>VCL</FrameworkType>
<MainSource>webEmiMobile.dpr</MainSource> <MainSource>webEmiMobile.dpr</MainSource>
<Base>True</Base> <Base>True</Base>
...@@ -194,6 +194,7 @@ ...@@ -194,6 +194,7 @@
<FormType>dfm</FormType> <FormType>dfm</FormType>
<DesignClass>TWebForm</DesignClass> <DesignClass>TWebForm</DesignClass>
</DCCReference> </DCCReference>
<DCCReference Include="uMapMarkerJs.pas"/>
<DCCReference Include="Module.Websocket.pas"> <DCCReference Include="Module.Websocket.pas">
<Form>dmWebsocket</Form> <Form>dmWebsocket</Form>
<FormType>dfm</FormType> <FormType>dfm</FormType>
...@@ -517,7 +518,12 @@ ...@@ -517,7 +518,12 @@
<Source> <Source>
<Source Name="MainSource">webEmiMobile.dpr</Source> <Source Name="MainSource">webEmiMobile.dpr</Source>
</Source> </Source>
<Excluded_Packages/> <Excluded_Packages>
<Excluded_Packages Name="$(BDSBIN)\bcboffice2k370.bpl">Embarcadero C++Builder Office 2000 Servers Package</Excluded_Packages>
<Excluded_Packages Name="$(BDSBIN)\bcbofficexp370.bpl">Embarcadero C++Builder Office XP Servers Package</Excluded_Packages>
<Excluded_Packages Name="$(BDSBIN)\dcloffice2k370.bpl">Microsoft Office 2000 Sample Automation Server Wrapper Components</Excluded_Packages>
<Excluded_Packages Name="$(BDSBIN)\dclofficexp370.bpl">Microsoft Office XP Sample Automation Server Wrapper Components</Excluded_Packages>
</Excluded_Packages>
</Delphi.Personality> </Delphi.Personality>
<Deployment Version="5"> <Deployment Version="5">
<DeployFile Condition="'$(SKIADIR)'==''" Required="true" LocalName="$(BDS)\bin64\sk4d.dll" Configuration="Debug" Class="Skia"> <DeployFile Condition="'$(SKIADIR)'==''" Required="true" LocalName="$(BDS)\bin64\sk4d.dll" Configuration="Debug" Class="Skia">
...@@ -1737,6 +1743,9 @@ ...@@ -1737,6 +1743,9 @@
<Platform Name="Win64x"> <Platform Name="Win64x">
<Operation>1</Operation> <Operation>1</Operation>
</Platform> </Platform>
<Platform Name="WinARM64EC">
<Operation>1</Operation>
</Platform>
</DeployClass> </DeployClass>
<DeployClass Name="ProjectiOSDeviceDebug"> <DeployClass Name="ProjectiOSDeviceDebug">
<Platform Name="iOSDevice32"> <Platform Name="iOSDevice32">
...@@ -1807,6 +1816,10 @@ ...@@ -1807,6 +1816,10 @@
<RemoteDir>Assets</RemoteDir> <RemoteDir>Assets</RemoteDir>
<Operation>1</Operation> <Operation>1</Operation>
</Platform> </Platform>
<Platform Name="WinARM64EC">
<RemoteDir>Assets</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass> </DeployClass>
<DeployClass Name="UWP_DelphiLogo44"> <DeployClass Name="UWP_DelphiLogo44">
<Platform Name="Win32"> <Platform Name="Win32">
...@@ -1817,6 +1830,10 @@ ...@@ -1817,6 +1830,10 @@
<RemoteDir>Assets</RemoteDir> <RemoteDir>Assets</RemoteDir>
<Operation>1</Operation> <Operation>1</Operation>
</Platform> </Platform>
<Platform Name="WinARM64EC">
<RemoteDir>Assets</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass> </DeployClass>
<DeployClass Name="iOS_AppStore1024"> <DeployClass Name="iOS_AppStore1024">
<Platform Name="iOSDevice64"> <Platform Name="iOSDevice64">
...@@ -2031,6 +2048,7 @@ ...@@ -2031,6 +2048,7 @@
<ProjectRoot Platform="Win32" Name="$(PROJECTNAME)"/> <ProjectRoot Platform="Win32" Name="$(PROJECTNAME)"/>
<ProjectRoot Platform="Win64" Name="$(PROJECTNAME)"/> <ProjectRoot Platform="Win64" Name="$(PROJECTNAME)"/>
<ProjectRoot Platform="Win64x" Name="$(PROJECTNAME)"/> <ProjectRoot Platform="Win64x" Name="$(PROJECTNAME)"/>
<ProjectRoot Platform="WinARM64EC" Name="$(PROJECTNAME)"/>
</Deployment> </Deployment>
<Platforms> <Platforms>
<Platform value="Win32">True</Platform> <Platform value="Win32">True</Platform>
......
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