From 26d4f6235f8c6f7409e247ab189cd3c761e5142c Mon Sep 17 00:00:00 2001 From: ekke Date: Fri, 19 Jun 2026 11:02:41 +0200 Subject: [PATCH] UPN-grid layer, external imports/staged upload, GIS export, SW v10 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit UPN-grid layer: - src/database.js — new upn_grid SQLocal table (id, districtid, upn_prefix, geometry_wkt) + saveUpnGrid / getLocalUpnGrid; cache-once-per-district. - src/remotedb.js — getUpnGrid → get_upn_grid_per_district.php. - main.js loadUpnGrid + upnGridToGeoJSON in the Administration group, with a zoom-aware style: white casing under a bolder violet dashed stroke (visible against parcels) and upn_prefix labels rendered only when resolution ≤ 7 m/px (≈ scale ≤ 1:25,000). - main.js click handler: single click on a UPN-grid cell opens an info popup showing the upn_prefix. External-dataset import → staging → upload (client-side complete): - src/database.js — external_imports + external_import_features tables, plus createExternalImport / addExternalImportFeatures / updateExternalImport / getExternalImport / getExternalImportFeatures / listExternalImports / remapImportedFeatureProperties / deleteExternalImport. Status enum: imported/mapped/other/uploading/ submitted/migrated/failed (aligned with the database team's staged- upload model — lu_parcels_upload_tmp + supervisor review). - src/import-detect.js — pure helpers: detectTargetType(), autoMapFields(), applyFieldMapping(), listSourceFields() + TARGET_TYPES / TARGET_FIELDS registries. - src/import-modal.js — Bootstrap mapping modal: target dropdown, field-rename table, three actions (Cancel / Save / Save + Upload now). - main.js — stageImport hooked into addImportedGeoJSON (the single convergence point for shp/GeoJSON/KML drops); handleImportModalResult applies the mapping in one transaction; runUpload builds the real payload (district_id + api_token from remotePost, user_id_upload from SSO session, per-feature client_uuid/geom/props) and currently logs + toasts — the upload_.php endpoints are not yet live. - index.html — #importMappingModal markup. - MapView._decorateLayerListItem — import-state chip (Upload N / spinner / ✓ submitted / ✓ live / N errors) dispatching lupmis:import-chip-click; src/styles/layerswitcher.css — chip variants. GIS export from Area / Circle Analysis popups: - MapView._showAnalysisPopup now accepts an exportContext (clipGeometry + parcelFeatures + zoneFeatures + otherByLayer) and renders an "Export GIS" button next to "Export PDF". Click dispatches lupmis:export-gis. - index.html — #exportGisModal markup. - src/export-gis-modal.js — Bootstrap modal: format toggle (GeoJSON default / Shapefile / KML), filename, field-rename table with SHP 10-char DBF warning. - src/gis-export.js — writers: GeoJSON via Blob, KML via OL KMLFormat, Shapefile via shp-write (with DBF-safe name sanitiser). - Adds shp-write@0.3.2 dependency. MapView style options: - addGeoJSONLayer now accepts strokeDash for line-dash patterns (used by the UPN-grid layer and available for any future contextual overlay). Service Worker v9 → v10 to evict the stale shell/module caches on the next deploy. Co-Authored-By: Claude Opus 4.7 (1M context) --- LUPMIS2_Import_Upload_Design.docx | Bin 0 -> 21039 bytes dist/assets/index-DR_U08k-.js | 904 ++++++++++++++++++ dist/assets/index-DR_U08k-.js.map | 1 + ...{index-BxlvFVPW.css => index-Dp-9_Fz_.css} | 2 +- dist/assets/index-YjHYbDyk.js | 803 ---------------- dist/assets/index-YjHYbDyk.js.map | 1 - ...ex.es-C7foCI8j.js => index.es-B1FLOTc-.js} | 4 +- ...foCI8j.js.map => index.es-B1FLOTc-.js.map} | 2 +- .../{jspdf-Dzj2Osmy.js => jspdf-BTK_8o8D.js} | 8 +- ...-Dzj2Osmy.js.map => jspdf-BTK_8o8D.js.map} | 2 +- ...{ol-ext-BR0zF6aa.js => ol-ext-P1ircg-B.js} | 4 +- ...BR0zF6aa.js.map => ol-ext-P1ircg-B.js.map} | 2 +- ...ers-CvK8xBSr.js => openlayers-D8ReJJOp.js} | 4 +- ...xBSr.js.map => openlayers-D8ReJJOp.js.map} | 2 +- ...ort-vzOHm8wb.js => pdf-export-BG6jqfsR.js} | 4 +- ...m8wb.js.map => pdf-export-BG6jqfsR.js.map} | 2 +- dist/assets/shpjs-CNrRgkgn.js | 5 - dist/assets/shpjs-CNrRgkgn.js.map | 1 - dist/assets/shpjs-iyObTF9J.js | 6 + dist/assets/shpjs-iyObTF9J.js.map | 1 + dist/index.html | 177 +++- dist/sw.js | 8 +- index.html | 168 ++++ main.js | 408 +++++++- package-lock.json | 41 + package.json | 1 + public/sw.js | 8 +- src/components/MapView.js | 151 ++- src/database.js | 350 ++++++- src/export-gis-modal.js | 206 ++++ src/gis-export.js | 233 +++++ src/import-detect.js | 250 +++++ src/import-modal.js | 221 +++++ src/remotedb.js | 23 + src/styles/layerswitcher.css | 26 + 35 files changed, 3186 insertions(+), 843 deletions(-) create mode 100644 LUPMIS2_Import_Upload_Design.docx create mode 100644 dist/assets/index-DR_U08k-.js create mode 100644 dist/assets/index-DR_U08k-.js.map rename dist/assets/{index-BxlvFVPW.css => index-Dp-9_Fz_.css} (94%) delete mode 100644 dist/assets/index-YjHYbDyk.js delete mode 100644 dist/assets/index-YjHYbDyk.js.map rename dist/assets/{index.es-C7foCI8j.js => index.es-B1FLOTc-.js} (99%) rename dist/assets/{index.es-C7foCI8j.js.map => index.es-B1FLOTc-.js.map} (99%) rename dist/assets/{jspdf-Dzj2Osmy.js => jspdf-BTK_8o8D.js} (99%) rename dist/assets/{jspdf-Dzj2Osmy.js.map => jspdf-BTK_8o8D.js.map} (99%) rename dist/assets/{ol-ext-BR0zF6aa.js => ol-ext-P1ircg-B.js} (99%) rename dist/assets/{ol-ext-BR0zF6aa.js.map => ol-ext-P1ircg-B.js.map} (99%) rename dist/assets/{openlayers-CvK8xBSr.js => openlayers-D8ReJJOp.js} (99%) rename dist/assets/{openlayers-CvK8xBSr.js.map => openlayers-D8ReJJOp.js.map} (99%) rename dist/assets/{pdf-export-vzOHm8wb.js => pdf-export-BG6jqfsR.js} (91%) rename dist/assets/{pdf-export-vzOHm8wb.js.map => pdf-export-BG6jqfsR.js.map} (99%) delete mode 100644 dist/assets/shpjs-CNrRgkgn.js delete mode 100644 dist/assets/shpjs-CNrRgkgn.js.map create mode 100644 dist/assets/shpjs-iyObTF9J.js create mode 100644 dist/assets/shpjs-iyObTF9J.js.map create mode 100644 src/export-gis-modal.js create mode 100644 src/gis-export.js create mode 100644 src/import-detect.js create mode 100644 src/import-modal.js diff --git a/LUPMIS2_Import_Upload_Design.docx b/LUPMIS2_Import_Upload_Design.docx new file mode 100644 index 0000000000000000000000000000000000000000..d0378fcfb645c793795aa650eae16f4fc0c68cff GIT binary patch literal 21039 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 OkYeZpviiM2mIDAC7~+Be literal 0 HcmV?d00001 diff --git a/dist/assets/index-DR_U08k-.js b/dist/assets/index-DR_U08k-.js new file mode 100644 index 0000000..7470df5 --- /dev/null +++ b/dist/assets/index-DR_U08k-.js @@ -0,0 +1,904 @@ +const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/pdf-export-BG6jqfsR.js","assets/jspdf-BTK_8o8D.js","assets/openlayers-D8ReJJOp.js","assets/openlayers-BtPuoxOl.css"])))=>i.map(i=>d[i]); +import{_ as Ct,h as M,F as A,j as I,k as ue,m as bo,b as G,V as O,L as pe,D as Pe,P as it,Q as st,n as de,U as Te,M as Io,W as Tr,X as Q,Y as Lr,S as kr,G as Ir,Z as Pr,o as ut,O as _e,$ as nt,a0 as Et,a1 as Me,A as Mr,T as ne,a2 as Le,a3 as Po,a4 as ie,a5 as Mo,e as Ar,u as Gt,s as Cr,a6 as kn,a7 as wo}from"./openlayers-D8ReJJOp.js";import{M as ct}from"./bootstrap-D1-uvFxm.js";import{o as Fr,a as Dr,b as Or,c as Rr,d as ao,e as Ao,f as et,g as Nr,h as ve,i as $r,j as Br}from"./ol-ext-P1ircg-B.js";import{r as Gr}from"./shpjs-iyObTF9J.js";(function(){const e=document.createElement("link").relList;if(e&&e.supports&&e.supports("modulepreload"))return;for(const o of document.querySelectorAll('link[rel="modulepreload"]'))r(o);new MutationObserver(o=>{for(const a of o)if(a.type==="childList")for(const i of a.addedNodes)i.tagName==="LINK"&&i.rel==="modulepreload"&&r(i)}).observe(document,{childList:!0,subtree:!0});function t(o){const a={};return o.integrity&&(a.integrity=o.integrity),o.referrerPolicy&&(a.referrerPolicy=o.referrerPolicy),o.crossOrigin==="use-credentials"?a.credentials="include":o.crossOrigin==="anonymous"?a.credentials="omit":a.credentials="same-origin",a}function r(o){if(o.ep)return;o.ep=!0;const a=t(o);fetch(o.href,a)}})();const Co="function",$e="64e10b34-2bf7-4616-9668-f99de5aa046e",jr="get",qr="has",zr="set",{isArray:wt}=Array;let{SharedArrayBuffer:xt,window:Ur}=globalThis,{notify:In,wait:Pn,waitAsync:St}=Atomics,Mn=null;St||(St=n=>({value:new Promise(e=>{let t=new Worker("data:application/javascript,onmessage%3D(%7Bdata%3Ab%7D)%3D%3E(Atomics.wait(b%2C0)%2CpostMessage(0))");t.onmessage=e,t.postMessage(n)})}));try{new xt(4)}catch{xt=ArrayBuffer;const e=new WeakMap;if(Ur){const t=new Map,{prototype:{postMessage:r}}=Worker,o=a=>{const i=a.data?.[$e];if(!wt(i)){a.stopImmediatePropagation();const{id:s,sb:l}=i;t.get(s)(l)}};Mn=function(a,...i){const s=a?.[$e];if(wt(s)){const[l,c]=s;e.set(c,l),this.addEventListener("message",o)}return r.call(this,a,...i)},St=a=>({value:new Promise(i=>{t.set(e.get(a),i)}).then(i=>{t.delete(e.get(a)),e.delete(a);for(let s=0;s({[$e]:{id:r,sb:o}});In=r=>{postMessage(t(e.get(r),r))},addEventListener("message",r=>{const o=r.data?.[$e];if(wt(o)){const[a,i]=o;e.set(i,a)}})}}/*! (c) Andrea Giammarchi - ISC */const{Int32Array:io,Map:Fo,Uint16Array:so}=globalThis,{BYTES_PER_ELEMENT:Do}=io,{BYTES_PER_ELEMENT:Hr}=so,Wr=(n,e,t)=>{for(;Pn(n,0,0,e)==="timed-out";)t()},lo=new WeakSet,jt=new WeakMap,Kr={value:{then:n=>n()}};let Xr=0;const _o=(n,{parse:e=JSON.parse,stringify:t=JSON.stringify,transform:r,interrupt:o}=JSON)=>{if(!jt.has(n)){const a=Mn||n.postMessage,i=(p,...h)=>a.call(n,{[$e]:h},{transfer:p}),s=typeof o===Co?o:o?.handler,l=o?.delay||42,c=new TextDecoder("utf-16"),d=(p,h)=>p?St(h,0):(s?Wr(h,l,s):Pn(h,0),Kr);let u=!1;jt.set(n,new Proxy(new Fo,{[qr]:(p,h)=>typeof h=="string"&&!h.startsWith("_"),[jr]:(p,h)=>h==="then"?null:((...f)=>{const y=Xr++;let g=new io(new xt(Do*2)),m=[];lo.has(f.at(-1)||m)&&lo.delete(m=f.pop()),i(m,y,g,h,r?f.map(r):f);const b=n!==globalThis;let _=0;return u&&b&&(_=setTimeout(console.warn,1e3,`💀🔒 - Possible deadlock if proxy.${h}(...args) is awaited`)),d(b,g).value.then(()=>{clearTimeout(_);const S=g[1];if(!S)return;const T=Hr*S;return g=new io(new xt(T+T%Do)),i([],y,g),d(b,g).value.then(()=>e(c.decode(new so(g.buffer).slice(0,S))))})}),[zr](p,h,f){const y=typeof f;if(y!==Co)throw new Error(`Unable to assign ${h} as ${y}`);if(!p.size){const g=new Fo;n.addEventListener("message",async m=>{const b=m.data?.[$e];if(wt(b)){m.stopImmediatePropagation();const[_,S,...T]=b;let v;if(T.length){const[x,P]=T;if(p.has(x)){u=!0;try{const k=await p.get(x)(...P);if(k!==void 0){const q=t(r?r(k):k);g.set(_,q),S[1]=q.length}}catch(k){v=k}finally{u=!1}}else v=new Error(`Unsupported action: ${x}`);S[0]=1}else{const x=g.get(_);g.delete(_);for(let P=new so(S.buffer),k=0;k(lo.add(n),n);function Oo(){let n,e;return{lock:async()=>{for(;n;)await n;n=new Promise(o=>{e=o})},unlock:async()=>{const o=e;n=void 0,e=void 0,o?.()}}}async function An(n,e){let t;if(n instanceof Blob?t=n.stream():t=n,t instanceof ReadableStream&&e){const o=t.getReader();switch(e){case"callback":return async()=>(await o.read()).value;case"buffer":const a=[];let i=!1;for(;!i;){const d=await o.read();d.value&&a.push(d.value),i=d.done}const s=a.reduce((d,u)=>d+u.length,0),l=new Uint8Array(s);let c=0;return a.forEach(d=>{l.set(d,c),c+=d.length}),l.buffer}}else return t}class Tt{constructor(e){Object.defineProperty(this,"sqlite3InitModule",{enumerable:!0,configurable:!0,writable:!0,value:e}),Object.defineProperty(this,"sqlite3",{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,"db",{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,"config",{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,"pointers",{enumerable:!0,configurable:!0,writable:!0,value:[]}),Object.defineProperty(this,"writeCallbacks",{enumerable:!0,configurable:!0,writable:!0,value:new Set}),Object.defineProperty(this,"storageType",{enumerable:!0,configurable:!0,writable:!0,value:"memory"})}async init(e){const{databasePath:t}=e,r=this.getFlags(e);if(!this.sqlite3InitModule){const{default:o}=await Ct(async()=>{const{default:a}=await import("./index-DTMgZTfd.js");return{default:a}},[]);this.sqlite3InitModule=o}this.sqlite3||(this.sqlite3=await this.sqlite3InitModule()),this.db&&await this.destroy(),this.db=new this.sqlite3.oo1.DB(t,r),this.config=e,this.initWriteHook()}onWrite(e){return this.writeCallbacks.add(e),()=>{this.writeCallbacks.delete(e)}}async exec(e){if(!this.db)throw new Error("Driver not initialized");return this.execOnDb(this.db,e)}async execBatch(e){if(!this.db)throw new Error("Driver not initialized");const t=[];return this.db.transaction(r=>{const o=new Map;try{for(let a of e){let i=o.get(a.sql);if(!i){const c=r.prepare(a.sql);o.set(a.sql,c),i=c}a.params?.length&&i.bind(a.params);let s=[],l=[];for(;i.step();)s=i.getColumnNames([]),l.push(i.get([]));t.push({columns:s,rows:l}),i.reset()}}finally{o.forEach(a=>{a.finalize()})}}),t}async isDatabasePersisted(){return!1}async getDatabaseSizeBytes(){const t=(await this.exec({sql:`SELECT page_count * page_size AS size + FROM pragma_page_count(), pragma_page_size()`,method:"get"}))?.rows?.[0];if(typeof t!="number")throw new Error("Failed to query database size");return t}async createFunction(e){if(!this.db)throw new Error("Driver not initialized");switch(e.type){case"callback":case"scalar":this.db.createFunction({name:e.name,xFunc:(t,...r)=>e.func(...r),arity:-1});break;case"aggregate":this.db.createFunction({name:e.name,xStep:(t,...r)=>e.func.step(...r),xFinal:(t,...r)=>e.func.final(...r),arity:-1});break}}async import(e){if(!this.sqlite3||!this.db||!this.config)throw new Error("Driver not initialized");const t=await An(e,"buffer"),r=this.sqlite3.wasm.allocFromTypedArray(t);this.pointers.push(r);const o=this.sqlite3.capi.sqlite3_deserialize(this.db,"main",r,t.byteLength,t.byteLength,this.config.readOnly?this.sqlite3.capi.SQLITE_DESERIALIZE_READONLY:this.sqlite3.capi.SQLITE_DESERIALIZE_RESIZEABLE);this.db.checkRc(o)}async export(){if(!this.sqlite3||!this.db)throw new Error("Driver not initialized");return{name:"database.sqlite3",data:this.sqlite3.capi.sqlite3_js_db_export(this.db)}}async clear(){}async destroy(){this.closeDb(),this.pointers.forEach(e=>this.sqlite3?.wasm.dealloc(e)),this.pointers=[],this.writeCallbacks.clear()}getFlags(e){const{readOnly:t,verbose:r}=e;return[t===!0?"r":"cw",r===!0?"t":""].join("")}execOnDb(e,t){const r={rows:[],columns:[]},o=e.exec({sql:t.sql,bind:t.params,returnValue:"resultRows",rowMode:"array",columnNames:r.columns});switch(t.method){case"run":break;case"get":r.rows=o[0]??[];break;case"all":default:r.rows=o;break}return r}initWriteHook(){if(!this.config?.reactive)return;if(!this.sqlite3||!this.db)throw new Error("Driver not initialized");const e={[this.sqlite3.capi.SQLITE_INSERT]:"insert",[this.sqlite3.capi.SQLITE_UPDATE]:"update",[this.sqlite3.capi.SQLITE_DELETE]:"delete"};this.sqlite3.capi.sqlite3_update_hook(this.db,(t,r,o,a,i)=>{this.writeCallbacks.forEach(s=>{s({table:a,rowid:i,operation:e[r]})})},0)}closeDb(){this.db&&(this.db.close(),this.db=void 0)}}function Vr(n,e,t){let r,o,a,i,s,l,c=0,d=!1,u=!1,p=!0;if(typeof n!="function")throw new TypeError("Expected a function");e=Number(e)||0,typeof t=="object"&&t!==null&&(d=!!t.leading,u="maxWait"in t,a=u?Math.max(Number(t.maxWait)||0,e):0,p="trailing"in t?!!t.trailing:p);function h(v){const x=r,P=o;return r=o=void 0,c=v,i=n.apply(P,x),i}function f(v){return c=v,s=setTimeout(m,e),d?h(v):i}function y(v){const x=v-(l??0),P=v-c,k=e-x;return u?Math.min(k,a-P):k}function g(v){const x=v-(l??0),P=v-c;return l===void 0||x>=e||x<0||u&&P>=a}function m(){const v=Date.now();if(g(v))return b(v);s=setTimeout(m,y(v))}function b(v){return s=void 0,p&&r?h(v):(r=o=void 0,i)}function _(){s!==void 0&&clearTimeout(s),c=0,r=l=o=s=void 0}function S(){return s===void 0?i:b(Date.now())}function T(){const v=Date.now(),x=g(v);if(r=arguments,o=this,l=v,x){if(s===void 0)return f(l);if(u)return s=setTimeout(m,e),h(l)}return s===void 0&&(s=setTimeout(m,e)),i}return T.cancel=_,T.flush=S,T}function _t(){return crypto.randomUUID()}function Cn(n,e){switch(n){case"session":case":sessionStorage:":let t=sessionStorage._sqlocal_session_key;return t||(t=_t(),sessionStorage._sqlocal_session_key=t),`session:${t}`;case"local":case":localStorage:":return"local";case":memory:":return`memory:${e}`;default:return`path:${n}`}}class pt{constructor(e){Object.defineProperty(this,"driver",{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,"config",{enumerable:!0,configurable:!0,writable:!0,value:{}}),Object.defineProperty(this,"userFunctions",{enumerable:!0,configurable:!0,writable:!0,value:new Map}),Object.defineProperty(this,"initMutex",{enumerable:!0,configurable:!0,writable:!0,value:Oo()}),Object.defineProperty(this,"transactionMutex",{enumerable:!0,configurable:!0,writable:!0,value:Oo()}),Object.defineProperty(this,"transactionKey",{enumerable:!0,configurable:!0,writable:!0,value:null}),Object.defineProperty(this,"proxy",{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,"dirtyTables",{enumerable:!0,configurable:!0,writable:!0,value:new Set}),Object.defineProperty(this,"effectsChannel",{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,"reinitChannel",{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,"onmessage",{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,"init",{enumerable:!0,configurable:!0,writable:!0,value:async o=>{if(!(!this.config.databasePath||!this.config.clientKey)){await this.initMutex.lock();try{try{await this.driver.init(this.config)}catch{console.warn(`Persistence failed, so ${this.config.databasePath} will not be saved. For origin private file system persistence, make sure your web server is configured to use the correct HTTP response headers (See https://sqlocal.dev/guide/setup#cross-origin-isolation).`),this.config.databasePath=":memory:",this.driver=new Tt,await this.driver.init(this.config)}const a=Cn(this.config.databasePath,this.config.clientKey);this.reinitChannel=new BroadcastChannel(`_sqlocal_reinit_(${a})`),this.reinitChannel.onmessage=i=>{const s=i.data;if(this.config.clientKey!==s.clientKey)switch(s.type){case"reinit":this.init(s.reason);break;case"close":this.driver.destroy();break}},this.config.reactive&&(this.effectsChannel=new BroadcastChannel(`_sqlocal_effects_(${a})`),this.driver.onWrite(async i=>{this.dirtyTables.add(i.table),await this.transactionMutex.lock(),this.emitEffectsDebounced(),await this.transactionMutex.unlock()})),await Promise.all(Array.from(this.userFunctions.values()).map(i=>this.initUserFunction(i))),await this.execInitStatements(),this.emitMessage({type:"event",event:"connect",reason:o})}catch(a){this.emitMessage({type:"error",error:a,queryKey:null}),await this.destroy()}finally{await this.initMutex.unlock()}}}}),Object.defineProperty(this,"postMessage",{enumerable:!0,configurable:!0,writable:!0,value:async(o,a)=>{const i=o instanceof MessageEvent?o.data:o;switch(await this.initMutex.lock(),i.type){case"config":this.editConfig(i);break;case"query":case"batch":case"transaction":this.exec(i);break;case"function":this.createUserFunction(i);break;case"getinfo":this.getDatabaseInfo(i);break;case"import":this.importDb(i);break;case"export":this.exportDb(i);break;case"delete":this.deleteDb(i);break;case"destroy":this.destroy(i);break}await this.initMutex.unlock()}}),Object.defineProperty(this,"emitMessage",{enumerable:!0,configurable:!0,writable:!0,value:(o,a=[])=>{this.onmessage&&this.onmessage(o,a)}}),Object.defineProperty(this,"emitEffects",{enumerable:!0,configurable:!0,writable:!0,value:()=>{!this.effectsChannel||this.dirtyTables.size===0||(this.effectsChannel.postMessage({type:"effects",tables:[...this.dirtyTables]}),this.dirtyTables.clear())}}),Object.defineProperty(this,"emitEffectsDebounced",{enumerable:!0,configurable:!0,writable:!0,value:Vr(()=>this.emitEffects(),32,{maxWait:180})}),Object.defineProperty(this,"editConfig",{enumerable:!0,configurable:!0,writable:!0,value:o=>{this.config=o.config,this.init("initial")}}),Object.defineProperty(this,"exec",{enumerable:!0,configurable:!0,writable:!0,value:async o=>{try{const a={type:"data",queryKey:o.queryKey,data:[]};switch(o.type){case"query":const i=this.transactionKey!==null&&this.transactionKey===o.transactionKey;try{i||await this.transactionMutex.lock();const s=await this.driver.exec(o);a.data.push(s)}finally{i||await this.transactionMutex.unlock()}break;case"batch":try{await this.transactionMutex.lock();const s=await this.driver.execBatch(o.statements);a.data.push(...s)}finally{await this.transactionMutex.unlock()}break;case"transaction":if(o.action==="begin"&&(await this.transactionMutex.lock(),this.transactionKey=o.transactionKey,await this.driver.exec({sql:"BEGIN"})),(o.action==="commit"||o.action==="rollback")&&this.transactionKey!==null&&this.transactionKey===o.transactionKey){const s=o.action==="commit"?"COMMIT":"ROLLBACK";await this.driver.exec({sql:s}),this.transactionKey=null,await this.transactionMutex.unlock()}break}this.emitMessage(a)}catch(a){this.emitMessage({type:"error",error:a,queryKey:o.queryKey})}}}),Object.defineProperty(this,"execInitStatements",{enumerable:!0,configurable:!0,writable:!0,value:async()=>{if(this.config.onInitStatements)for(let o of this.config.onInitStatements)await this.driver.exec(o)}}),Object.defineProperty(this,"getDatabaseInfo",{enumerable:!0,configurable:!0,writable:!0,value:async o=>{try{this.emitMessage({type:"info",queryKey:o.queryKey,info:{databasePath:this.config.databasePath,storageType:this.driver.storageType,databaseSizeBytes:await this.driver.getDatabaseSizeBytes(),persisted:await this.driver.isDatabasePersisted()}})}catch(a){this.emitMessage({type:"error",queryKey:o.queryKey,error:a})}}}),Object.defineProperty(this,"createUserFunction",{enumerable:!0,configurable:!0,writable:!0,value:async o=>{const{functionName:a,functionType:i,queryKey:s}=o;let l;if(this.userFunctions.has(a)){this.emitMessage({type:"error",error:new Error(`A user-defined function with the name "${a}" has already been created for this SQLocal instance.`),queryKey:s});return}switch(i){case"callback":l={type:i,name:a,func:(...c)=>{this.emitMessage({type:"callback",name:a,args:c})}};break;case"scalar":l={type:i,name:a,func:this.proxy[`_sqlocal_func_${a}`]};break;case"aggregate":l={type:i,name:a,func:{step:this.proxy[`_sqlocal_func_${a}_step`],final:this.proxy[`_sqlocal_func_${a}_final`]}};break}try{await this.initUserFunction(l),this.emitMessage({type:"success",queryKey:s})}catch(c){this.emitMessage({type:"error",error:c,queryKey:s})}}}),Object.defineProperty(this,"initUserFunction",{enumerable:!0,configurable:!0,writable:!0,value:async o=>{await this.driver.createFunction(o),this.userFunctions.set(o.name,o)}}),Object.defineProperty(this,"importDb",{enumerable:!0,configurable:!0,writable:!0,value:async o=>{const{queryKey:a,database:i}=o;let s=!1;try{await this.driver.import(i),this.driver.storageType==="memory"&&await this.execInitStatements()}catch(l){this.emitMessage({type:"error",error:l,queryKey:a}),s=!0}finally{this.driver.storageType!=="memory"&&await this.init("overwrite")}s||this.emitMessage({type:"success",queryKey:a})}}),Object.defineProperty(this,"exportDb",{enumerable:!0,configurable:!0,writable:!0,value:async o=>{const{queryKey:a}=o;try{const{name:i,data:s}=await this.driver.export();this.emitMessage({type:"buffer",queryKey:a,bufferName:i,buffer:s},[s])}catch(i){this.emitMessage({type:"error",error:i,queryKey:a})}}}),Object.defineProperty(this,"deleteDb",{enumerable:!0,configurable:!0,writable:!0,value:async o=>{const{queryKey:a}=o;let i=!1;try{await this.driver.clear()}catch(s){this.emitMessage({type:"error",error:s,queryKey:a}),i=!0}finally{await this.init("delete")}i||this.emitMessage({type:"success",queryKey:a})}}),Object.defineProperty(this,"destroy",{enumerable:!0,configurable:!0,writable:!0,value:async o=>{await this.driver.exec({sql:"PRAGMA optimize"}),await this.driver.destroy(),this.effectsChannel&&(this.emitEffectsDebounced.flush(),this.effectsChannel.close(),this.effectsChannel=void 0),this.reinitChannel&&(this.reinitChannel.close(),this.reinitChannel=void 0),o&&this.emitMessage({type:"success",queryKey:o.queryKey})}});const r=typeof WorkerGlobalScope<"u"&&globalThis instanceof WorkerGlobalScope?_o(globalThis):globalThis;this.proxy=r,this.driver=e}}function Lt(n,...e){return{sql:n.join("?"),params:e}}function Yr(n){return!n.some(e=>!Array.isArray(e))}function qt(n,e){let t;return Yr(n)?t=n:t=[n],t.map(r=>{const o={};return e.forEach((a,i)=>{o[a]=r[i]}),o})}function Jr(n){return typeof n=="object"&&n!==null&&"getSQL"in n&&typeof n.getSQL=="function"}function Zr(n){return typeof n=="object"&&n!==null&&"sql"in n&&typeof n.sql=="string"&&"params"in n}function Ro(n){if(typeof n=="function"&&(n=n(Lt)),Jr(n))try{if(!("toSQL"in n&&typeof n.toSQL=="function"))throw 1;const r=n.toSQL();if(!Zr(r))throw 2;const o="all"in n&&typeof n.all=="function"?n.all:void 0;return{...r,exec:o?()=>o():void 0}}catch{throw new Error("The passed statement could not be parsed.")}const e=n.sql;let t=[];return"params"in n?t=n.params:"parameters"in n&&(t=n.parameters),{sql:e,params:t}}function No(n,e){let t;return typeof n=="string"?t={sql:n,params:e}:t=Lt(n,...e),t}async function ft(n,e,t,r){return!e&&"locks"in navigator?navigator.locks.request(`_sqlocal_mutation_(${t.databasePath})`,{mode:n},r):r()}class $o extends Tt{constructor(e,t){super(t),Object.defineProperty(this,"storageType",{enumerable:!0,configurable:!0,writable:!0,value:e})}async init(e){const t=this.getFlags(e);if(e.readOnly)throw new Error(`SQLite storage type "${this.storageType}" does not support read-only mode.`);if(!this.sqlite3InitModule){const{default:r}=await Ct(async()=>{const{default:o}=await import("./index-DTMgZTfd.js");return{default:o}},[]);this.sqlite3InitModule=r}this.sqlite3||(this.sqlite3=await this.sqlite3InitModule()),this.db&&await this.destroy(),this.db=new this.sqlite3.oo1.JsStorageDb({filename:this.storageType,flags:t}),this.config=e,this.initWriteHook()}async isDatabasePersisted(){return navigator.storage?.persisted()}async getDatabaseSizeBytes(){if(!this.db)throw new Error("Driver not initialized");return this.db.storageSize()}async import(e){const t=new Tt;await t.init({}),await t.import(e),await this.clear(),await t.exec({sql:`VACUUM INTO 'file:${this.storageType}?vfs=kvvfs'`}),await t.destroy()}async clear(){if(!this.db)throw new Error("Driver not initialized");this.db.clearStorage()}async destroy(){this.closeDb(),this.writeCallbacks.clear()}}var Fn,Dn;class Qr{constructor(e){Object.defineProperty(this,"config",{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,"clientKey",{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,"processor",{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,"isDestroyed",{enumerable:!0,configurable:!0,writable:!0,value:!1}),Object.defineProperty(this,"bypassMutationLock",{enumerable:!0,configurable:!0,writable:!0,value:!1}),Object.defineProperty(this,"transactionQueryKeyQueue",{enumerable:!0,configurable:!0,writable:!0,value:[]}),Object.defineProperty(this,"userCallbacks",{enumerable:!0,configurable:!0,writable:!0,value:new Map}),Object.defineProperty(this,"queriesInProgress",{enumerable:!0,configurable:!0,writable:!0,value:new Map}),Object.defineProperty(this,"proxy",{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,"reinitChannel",{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,"effectsChannel",{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,"processMessageEvent",{enumerable:!0,configurable:!0,writable:!0,value:c=>{const d=c instanceof MessageEvent?c.data:c,u=this.queriesInProgress;switch(d.type){case"success":case"data":case"buffer":case"info":case"error":if(d.queryKey&&u.has(d.queryKey)){const[h,f]=u.get(d.queryKey);d.type==="error"?f(d.error):h(d),u.delete(d.queryKey)}else if(d.type==="error")throw d.error;break;case"callback":const p=this.userCallbacks.get(d.name);p&&p(...d.args??[]);break;case"event":this.config.onConnect?.(d.reason);break}}}),Object.defineProperty(this,"createQuery",{enumerable:!0,configurable:!0,writable:!0,value:async c=>ft("shared",this.bypassMutationLock||c.type==="import"||c.type==="delete",this.config,async()=>{if(this.isDestroyed===!0)throw new Error("This SQLocal client has been destroyed. You will need to initialize a new client in order to make further queries.");const d=_t();switch(c.type){case"import":this.processor.postMessage({...c,queryKey:d},[c.database]);break;default:this.processor.postMessage({...c,queryKey:d});break}return new Promise((u,p)=>{this.queriesInProgress.set(d,[u,p])})})}),Object.defineProperty(this,"broadcast",{enumerable:!0,configurable:!0,writable:!0,value:c=>{this.reinitChannel.postMessage(c)}}),Object.defineProperty(this,"exec",{enumerable:!0,configurable:!0,writable:!0,value:async(c,d,u="all",p)=>{const h=await this.createQuery({type:"query",transactionKey:p,sql:c,params:d,method:u}),f={rows:[],columns:[]};return h.type==="data"&&(f.rows=h.data[0]?.rows??[],f.columns=h.data[0]?.columns??[]),f}}),Object.defineProperty(this,"execBatch",{enumerable:!0,configurable:!0,writable:!0,value:async c=>{const d=await this.createQuery({type:"batch",statements:c}),u=new Array(c.length).fill({rows:[],columns:[]});return d.type==="data"&&d.data.forEach((p,h)=>{u[h]=p}),u}}),Object.defineProperty(this,"sql",{enumerable:!0,configurable:!0,writable:!0,value:async(c,...d)=>{const u=No(c,d),{rows:p,columns:h}=await this.exec(u.sql,u.params,"all");return qt(p,h)}}),Object.defineProperty(this,"batch",{enumerable:!0,configurable:!0,writable:!0,value:async c=>{const d=c(Lt);return(await this.execBatch(d)).map(({rows:p,columns:h})=>qt(p,h))}}),Object.defineProperty(this,"beginTransaction",{enumerable:!0,configurable:!0,writable:!0,value:async()=>{const c=_t();await this.createQuery({type:"transaction",transactionKey:c,action:"begin"});const d=async f=>{const y=Ro(f);if(y.exec)return this.transactionQueryKeyQueue.push(c),y.exec();const{rows:g,columns:m}=await this.exec(y.sql,y.params,"all",c);return qt(g,m)};return{query:d,sql:async(f,...y)=>{const g=No(f,y);return await d(g)},commit:async()=>{await this.createQuery({type:"transaction",transactionKey:c,action:"commit"})},rollback:async()=>{await this.createQuery({type:"transaction",transactionKey:c,action:"rollback"})}}}}),Object.defineProperty(this,"transaction",{enumerable:!0,configurable:!0,writable:!0,value:async c=>ft("exclusive",!1,this.config,async()=>{let d;this.bypassMutationLock=!0;try{d=await this.beginTransaction();const u=await c({sql:d.sql,query:d.query});return await d.commit(),u}catch(u){throw await d?.rollback(),u}finally{this.bypassMutationLock=!1}})}),Object.defineProperty(this,"reactiveQuery",{enumerable:!0,configurable:!0,writable:!0,value:c=>{let d=[],u=!1,p=!1,h=0;const f=Ro(c),y=new Set,g=new Set,m=new Set,b=async()=>{try{const S=++h;if(y.size===0){const v=await this.sql("SELECT name, wr FROM tables_used(?) WHERE type = 'table'",f.sql),x=new Set,P=new Set;if(v.forEach(k=>{typeof k.name=="string"&&(k.wr?P.add(k.name):x.add(k.name))}),x.size===0)throw new Error("The passed SQL does not read any tables.");if(Array.from(P).some(k=>x.has(k)))throw new Error("The passed SQL would mutate one or more of the tables that it reads. Doing this in a reactive query would create an infinite loop.");x.forEach(k=>y.add(k))}const T=f.exec?await f.exec():await this.sql(f.sql,...f.params);S===h&&(d=T,u=!0,g.forEach(v=>v(d)))}catch(S){m.forEach(T=>{T(S instanceof Error?S:new Error(String(S)))})}},_=S=>{S.data.tables.some(T=>y.has(T))&&b()};return{get value(){return d},subscribe:(S,T)=>{if(!this.effectsChannel)throw new Error('This SQLocal instance is not configured for reactive queries. Set the "reactive" option to enable them.');return T||(T=v=>{throw v}),g.add(S),m.add(T),p?u&&S(d):(this.effectsChannel.addEventListener("message",_),p=!0,b()),{unsubscribe:()=>{g.delete(S),m.delete(T),g.size===0&&(this.effectsChannel?.removeEventListener("message",_),p=!1)}}}}}}),Object.defineProperty(this,"createCallbackFunction",{enumerable:!0,configurable:!0,writable:!0,value:async(c,d)=>{await this.createQuery({type:"function",functionName:c,functionType:"callback"}),this.userCallbacks.set(c,d)}}),Object.defineProperty(this,"createScalarFunction",{enumerable:!0,configurable:!0,writable:!0,value:async(c,d)=>{const u=`_sqlocal_func_${c}`,p=()=>{this.proxy[u]=d};this.proxy===globalThis&&p(),await this.createQuery({type:"function",functionName:c,functionType:"scalar"}),this.proxy!==globalThis&&p()}}),Object.defineProperty(this,"createAggregateFunction",{enumerable:!0,configurable:!0,writable:!0,value:async(c,d)=>{const u=`_sqlocal_func_${c}`,p=()=>{this.proxy[`${u}_step`]=d.step,this.proxy[`${u}_final`]=d.final};this.proxy===globalThis&&p(),await this.createQuery({type:"function",functionName:c,functionType:"aggregate"}),this.proxy!==globalThis&&p()}}),Object.defineProperty(this,"getDatabaseInfo",{enumerable:!0,configurable:!0,writable:!0,value:async()=>{const c=await this.createQuery({type:"getinfo"});if(c.type==="info")return c.info;throw new Error("The database failed to return valid information.")}}),Object.defineProperty(this,"getDatabaseFile",{enumerable:!0,configurable:!0,writable:!0,value:async()=>{const c=await this.createQuery({type:"export"});if(c.type==="buffer")return new File([c.buffer],c.bufferName,{type:"application/x-sqlite3"});throw new Error("The database failed to export.")}}),Object.defineProperty(this,"overwriteDatabaseFile",{enumerable:!0,configurable:!0,writable:!0,value:async(c,d)=>{await ft("exclusive",!1,this.config,async()=>{try{this.broadcast({type:"close",clientKey:this.clientKey});const u=await An(c,"buffer");await this.createQuery({type:"import",database:u}),typeof d=="function"&&(this.bypassMutationLock=!0,await d()),this.broadcast({type:"reinit",clientKey:this.clientKey,reason:"overwrite"})}finally{this.bypassMutationLock=!1}})}}),Object.defineProperty(this,"deleteDatabaseFile",{enumerable:!0,configurable:!0,writable:!0,value:async c=>{await ft("exclusive",!1,this.config,async()=>{try{this.broadcast({type:"close",clientKey:this.clientKey}),await this.createQuery({type:"delete"}),typeof c=="function"&&(this.bypassMutationLock=!0,await c()),this.broadcast({type:"reinit",clientKey:this.clientKey,reason:"delete"})}finally{this.bypassMutationLock=!1}})}}),Object.defineProperty(this,"destroy",{enumerable:!0,configurable:!0,writable:!0,value:async()=>{await this.createQuery({type:"destroy"}),typeof globalThis.Worker<"u"&&this.processor instanceof Worker&&(this.processor.removeEventListener("message",this.processMessageEvent),this.processor.terminate()),this.queriesInProgress.clear(),this.userCallbacks.clear(),this.reinitChannel.close(),this.effectsChannel?.close(),this.isDestroyed=!0}}),Object.defineProperty(this,Fn,{enumerable:!0,configurable:!0,writable:!0,value:()=>{this.destroy()}}),Object.defineProperty(this,Dn,{enumerable:!0,configurable:!0,writable:!0,value:async()=>{await this.destroy()}});const t=typeof e=="string"?{databasePath:e}:e,{onInit:r,onConnect:o,processor:a,...i}=t,{databasePath:s}=i;this.config=t,this.clientKey=_t();const l=Cn(s,this.clientKey);if(this.reinitChannel=new BroadcastChannel(`_sqlocal_reinit_(${l})`),i.reactive&&(this.effectsChannel=new BroadcastChannel(`_sqlocal_effects_(${l})`)),typeof a<"u")this.processor=a;else if(s==="local"||s===":localStorage:"){const c=new $o("local");this.processor=new pt(c)}else if(s==="session"||s===":sessionStorage:"){const c=new $o("session");this.processor=new pt(c)}else if(typeof globalThis.Worker<"u"&&s!==":memory:")this.processor=new Worker(new URL("/assets/worker-CuIBOSaM.js",import.meta.url),{type:"module"});else{const c=new Tt;this.processor=new pt(c)}this.processor instanceof pt?(this.processor.onmessage=c=>this.processMessageEvent(c),this.proxy=globalThis):(this.processor.addEventListener("message",this.processMessageEvent),this.proxy=_o(this.processor)),this.processor.postMessage({type:"config",config:{...i,clientKey:this.clientKey,onInitStatements:r?.(Lt)??[]}})}}Fn=Symbol.dispose,Dn=Symbol.asyncDispose;const vo="lupmis2.db",ea="lupmis-db-sync",On=new Qr(vo),{sql:w}=On;console.log("[Database] SQLocal instance created for:",vo);const Rn=new BroadcastChannel(ea);let Nn=!1,$n,Bn;const Bo=new Promise((n,e)=>{$n=n,Bn=e}),kt=new Set;function ta(n){return kt.add(n),()=>kt.delete(n)}Rn.onmessage=n=>{const{type:e,payload:t}=n.data;if(e==="DB_CHANGE")for(const r of kt)try{r(t)}catch(o){console.error("[Database] Change listener error:",o)}};function se(n,e,t=null){Rn.postMessage({type:"DB_CHANGE",payload:{table:n,action:e,id:t,timestamp:Date.now()}});for(const r of kt)try{r({table:n,action:e,id:t,timestamp:Date.now(),local:!0})}catch(o){console.error("[Database] Change listener error:",o)}}async function oa(){try{console.log("[Database] Initializing schema...");const n=await w`SELECT sqlite_version() as version`;console.log("[Database] SQLite version:",n[0]?.version),console.log("[Database] Creating locations table..."),await w` + CREATE TABLE IF NOT EXISTS locations ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, + longitude REAL NOT NULL, + latitude REAL NOT NULL, + description TEXT, + category TEXT DEFAULT 'default', + created_at TEXT DEFAULT CURRENT_TIMESTAMP, + updated_at TEXT DEFAULT CURRENT_TIMESTAMP, + synced INTEGER DEFAULT 0 + ) + `;const e=await w`SELECT name FROM sqlite_master WHERE type='table' AND name='locations'`;console.log("[Database] Locations table exists:",e.length>0),console.log("[Database] Creating sync_log table..."),await w` + CREATE TABLE IF NOT EXISTS sync_log ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + table_name TEXT NOT NULL, + record_id INTEGER NOT NULL, + action TEXT NOT NULL, + timestamp TEXT DEFAULT CURRENT_TIMESTAMP, + synced INTEGER DEFAULT 0 + ) + `,console.log("[Database] Creating remote_data table..."),await w` + CREATE TABLE IF NOT EXISTS remote_data ( + key TEXT PRIMARY KEY, + data TEXT NOT NULL, + fetched_at TEXT DEFAULT CURRENT_TIMESTAMP + ) + `,console.log("[Database] Creating collector_zones table..."),await w` + CREATE TABLE IF NOT EXISTS collector_zones ( + id INTEGER PRIMARY KEY, + zone_name TEXT, + geometry_wkt TEXT, + properties TEXT, + fetched_at TEXT DEFAULT CURRENT_TIMESTAMP + ) + `,console.log("[Database] Creating parcels table...");try{const r=await w`PRAGMA table_info(parcels)`;r.length>0&&!r.some(o=>o.name==="upn")&&(console.log("[Database] Migrating parcels table to lu_parcels structure (dropping old cache)..."),await w`DROP TABLE parcels`)}catch(r){console.warn("[Database] parcels migration check failed:",r)}await w` + CREATE TABLE IF NOT EXISTS parcels ( + id INTEGER PRIMARY KEY, + upn TEXT, + style INTEGER, + landuse TEXT, + zone_code TEXT, + zone_name TEXT, + sector TEXT, + block TEXT, + parcel_no TEXT, + prop_no TEXT, + st_name TEXT, + prop_add TEXT, + fac_name TEXT, + min_height INTEGER, + max_height INTEGER, + eff_date TEXT, + lp_name TEXT, + locality TEXT, + mmda TEXT, + last_update TEXT, + remarks TEXT, + geometry_wkt TEXT, + created_at TEXT, + updated_at TEXT, + districtid INTEGER, + status TEXT DEFAULT 'verified', + fetched_at TEXT DEFAULT CURRENT_TIMESTAMP + ) + `,console.log("[Database] Creating building_footprints table..."),await w` + CREATE TABLE IF NOT EXISTS building_footprints ( + id INTEGER PRIMARY KEY, + geometry_wkt TEXT, + properties TEXT, + fetched_at TEXT DEFAULT CURRENT_TIMESTAMP + ) + `,console.log("[Database] Creating osm_roads table..."),await w` + CREATE TABLE IF NOT EXISTS osm_roads ( + osm_id INTEGER PRIMARY KEY, + geometry_wkt TEXT, + properties TEXT, + fetched_at TEXT DEFAULT CURRENT_TIMESTAMP + ) + `,console.log("[Database] Creating upn_grid table..."),await w` + CREATE TABLE IF NOT EXISTS upn_grid ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + districtid INTEGER, + upn_prefix TEXT, + geometry_wkt TEXT, + fetched_at TEXT DEFAULT CURRENT_TIMESTAMP + ) + `,console.log("[Database] Creating gps_trails table..."),await w` + CREATE TABLE IF NOT EXISTS gps_trails ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + client_uuid TEXT UNIQUE, + name TEXT, + district_id TEXT, + started_at TEXT NOT NULL, + ended_at TEXT, + status TEXT NOT NULL DEFAULT 'recording', + point_count INTEGER NOT NULL DEFAULT 0, + distance_m REAL NOT NULL DEFAULT 0, + synced INTEGER NOT NULL DEFAULT 0, + remote_id TEXT, + created_at TEXT DEFAULT CURRENT_TIMESTAMP + ) + `,console.log("[Database] Creating gps_trail_points table..."),await w` + CREATE TABLE IF NOT EXISTS gps_trail_points ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + trail_id INTEGER NOT NULL, + seq INTEGER NOT NULL, + longitude REAL NOT NULL, + latitude REAL NOT NULL, + altitude REAL, + accuracy REAL, + altitude_accuracy REAL, + heading REAL, + speed REAL, + satellites INTEGER, + recorded_at TEXT NOT NULL + ) + `,console.log("[Database] Creating external_imports table..."),await w` + CREATE TABLE IF NOT EXISTS external_imports ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + filename TEXT NOT NULL, + target_type TEXT NOT NULL DEFAULT 'other', + mapping_json TEXT, + status TEXT NOT NULL DEFAULT 'imported', + feature_count INTEGER NOT NULL DEFAULT 0, + error_count INTEGER NOT NULL DEFAULT 0, + client_import_id TEXT UNIQUE, + created_at TEXT DEFAULT CURRENT_TIMESTAMP, + last_uploaded_at TEXT + ) + `,console.log("[Database] Creating external_import_features table..."),await w` + CREATE TABLE IF NOT EXISTS external_import_features ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + import_id INTEGER NOT NULL REFERENCES external_imports(id) ON DELETE CASCADE, + client_uuid TEXT UNIQUE, + geometry_wkt TEXT NOT NULL, + properties_json TEXT, + upload_status TEXT NOT NULL DEFAULT 'pending', + server_id INTEGER, + error_message TEXT + ) + `,await w`CREATE INDEX IF NOT EXISTS idx_locations_category ON locations(category)`,await w`CREATE INDEX IF NOT EXISTS idx_locations_synced ON locations(synced)`,await w`CREATE INDEX IF NOT EXISTS idx_gps_trails_synced ON gps_trails(synced, status)`,await w`CREATE INDEX IF NOT EXISTS idx_gps_trail_points_trail ON gps_trail_points(trail_id, seq)`,await w`CREATE INDEX IF NOT EXISTS idx_external_imports_status ON external_imports(status)`,await w`CREATE INDEX IF NOT EXISTS idx_external_import_features_import ON external_import_features(import_id, upload_status)`;const t=await w`SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'`;console.log("[Database] All tables:",t.map(r=>r.name)),Nn=!0,$n(!0),console.log("[Database] ✓ Schema initialized")}catch(n){throw console.error("[Database] ✗ Schema init failed:",n),Bn(n),n}}async function na(n,e,t,r={}){const{description:o=null,category:a="default"}=r;console.log("[Database] Adding location:",n,e,t,a);try{const i=await w`SELECT name FROM sqlite_master WHERE type='table' AND name='locations'`;if(console.log("[Database] Table check before insert:",i),i.length===0)throw console.error("[Database] ✗ locations table does not exist!"),new Error("locations table does not exist");console.log("[Database] Executing INSERT..."),await w` + INSERT INTO locations (name, longitude, latitude, description, category) + VALUES (${n}, ${e}, ${t}, ${o}, ${a}) + `,console.log("[Database] INSERT completed");const l=(await w`SELECT last_insert_rowid() as id`)[0]?.id;console.log("[Database] New ID:",l);const c=await w`SELECT * FROM locations WHERE id = ${l}`;if(console.log("[Database] Verify insert:",c),c.length===0)throw console.error("[Database] ✗ Insert verification failed - row not found!"),new Error("Insert verification failed");return await w` + INSERT INTO sync_log (table_name, record_id, action) + VALUES ('locations', ${l}, 'INSERT') + `,se("locations","INSERT",l),console.log("[Database] ✓ Location added:",l),{id:l}}catch(i){throw console.error("[Database] ✗ Failed to add location:",i),i}}async function Gn(n={}){const{category:e=null,limit:t=1e3}=n;try{const r=await w`SELECT name FROM sqlite_master WHERE type='table' AND name='locations'`;if(console.log("[Database] getLocations - table exists:",r.length>0),r.length===0)return console.warn("[Database] locations table does not exist yet"),[];let o;return e?o=await w` + SELECT * FROM locations + WHERE category = ${e} + ORDER BY created_at DESC + LIMIT ${t} + `:o=await w` + SELECT * FROM locations + ORDER BY created_at DESC + LIMIT ${t} + `,console.log("[Database] getLocations returned",o.length,"rows"),o}catch(r){return console.error("[Database] getLocations error:",r),[]}}async function ra(){try{return(await w`SELECT COUNT(*) as count FROM locations`)[0]?.count??0}catch(n){return console.error("[Database] getLocationCount error:",n),0}}async function jn(n,e){try{const t=JSON.stringify(e);await w` + INSERT OR REPLACE INTO remote_data (key, data, fetched_at) + VALUES (${n}, ${t}, CURRENT_TIMESTAMP) + `,console.log("[Database] ✓ Remote data cached:",n)}catch(t){throw console.error("[Database] ✗ Failed to cache remote data:",n,t),t}}async function qn(n){try{const e=await w`SELECT data, fetched_at FROM remote_data WHERE key = ${n}`;if(e.length===0)return null;const t=JSON.parse(e[0].data);return console.log("[Database] ✓ Remote data loaded from cache:",n,"(fetched",e[0].fetched_at+")"),t}catch(e){return console.error("[Database] ✗ Failed to read cached remote data:",n,e),null}}async function aa(n){try{await w`DELETE FROM collector_zones`;for(const e of n){const t=JSON.stringify(e);await w` + INSERT INTO collector_zones (id, zone_name, geometry_wkt, properties, fetched_at) + VALUES (${e.colzonenr||e.id}, ${e.colzonename||e.zone_name||""}, ${e.polygon||e.boundary||""}, ${t}, CURRENT_TIMESTAMP) + `}console.log("[Database] ✓ Saved",n.length,"collector zones")}catch(e){throw console.error("[Database] ✗ Failed to save collector zones:",e),e}}async function ia(){try{const n=await w`SELECT properties FROM collector_zones ORDER BY id`;return n.length===0?null:n.map(e=>JSON.parse(e.properties))}catch(n){return console.error("[Database] ✗ Failed to read local collector zones:",n),null}}async function sa(n,e){try{await w`BEGIN`,await w`DELETE FROM upn_grid`;let t=0;for(const r of n){const o=r.polygon||r.geometry_wkt||r.geom||"";await w` + INSERT INTO upn_grid (districtid, upn_prefix, geometry_wkt, fetched_at) + VALUES (${X(e)}, ${r.upn_prefix??null}, ${o}, CURRENT_TIMESTAMP) + `,t++}await w`COMMIT`,console.log("[Database] ✓ Saved",t,"UPN-grid cells (district",e,")")}catch(t){try{await w`ROLLBACK`}catch{}throw console.error("[Database] ✗ Failed to save UPN grid:",t),t}}async function la(n){try{const e=await w` + SELECT id, districtid, upn_prefix, geometry_wkt + FROM upn_grid + WHERE districtid = ${X(n)} + ORDER BY id + `;return e.length===0?null:e}catch(e){return console.error("[Database] ✗ Failed to read UPN grid:",e),null}}function zn(){return typeof crypto<"u"&&typeof crypto.randomUUID=="function"?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,n=>{const e=Math.random()*16|0;return(n==="x"?e:e&3|8).toString(16)})}async function ca(n){const{filename:e,targetType:t="other",featureCount:r=0}=n,o=zn();try{await w` + INSERT INTO external_imports + (filename, target_type, status, feature_count, client_import_id) + VALUES + (${e}, ${t}, + ${t==="other"?"other":"imported"}, + ${r}, ${o}) + `;const i=(await w`SELECT last_insert_rowid() AS id`)[0]?.id;return se("external_imports","INSERT",i),{id:i,client_import_id:o}}catch(a){throw console.error("[Database] ✗ Failed to create external import:",a),a}}async function da(n,e){if(!Array.isArray(e)||e.length===0)return 0;try{await w`BEGIN`;let t=0;for(const r of e){const o=r.geometry_wkt||"";if(!o)continue;const a=JSON.stringify(r.properties??{}),i=r.client_uuid||zn();await w` + INSERT INTO external_import_features + (import_id, client_uuid, geometry_wkt, properties_json, upload_status) + VALUES + (${n}, ${i}, ${o}, ${a}, 'pending') + `,t++}return await w`COMMIT`,se("external_import_features","INSERT",n),t}catch(t){try{await w`ROLLBACK`}catch{}throw console.error("[Database] ✗ Failed to add import features:",t),t}}async function It(n,e={}){try{const t=await w`SELECT * FROM external_imports WHERE id = ${n}`;if(t.length===0)throw new Error(`Import ${n} not found`);const r=t[0],o=e.targetType??r.target_type,a=e.mapping!==void 0?e.mapping?JSON.stringify(e.mapping):null:r.mapping_json,i=e.status??r.status,s=e.errorCount??r.error_count,l=e.lastUploadedAt??r.last_uploaded_at;await w` + UPDATE external_imports SET + target_type = ${o}, + mapping_json = ${a}, + status = ${i}, + error_count = ${s}, + last_uploaded_at = ${l} + WHERE id = ${n} + `,se("external_imports","UPDATE",n)}catch(t){throw console.error("[Database] ✗ Failed to update external import:",t),t}}async function ua(n){try{const e=await w`SELECT * FROM external_imports WHERE id = ${n}`;if(e.length===0)return null;const t=e[0];return{...t,mapping:t.mapping_json?JSON.parse(t.mapping_json):null}}catch(e){return console.error("[Database] ✗ Failed to read external import:",e),null}}async function pa(n){try{return(await w` + SELECT id, client_uuid, geometry_wkt, properties_json, + upload_status, server_id, error_message + FROM external_import_features + WHERE import_id = ${n} + ORDER BY id + `).map(t=>({...t,properties:t.properties_json?JSON.parse(t.properties_json):{}}))}catch(e){return console.error("[Database] ✗ Failed to read import features:",e),[]}}async function fa(n,e){try{const t=await w` + SELECT id, properties_json + FROM external_import_features + WHERE import_id = ${n} + `;if(t.length===0)return 0;await w`BEGIN`;let r=0;for(const o of t){const a=o.properties_json?JSON.parse(o.properties_json):{},i=e(a)??{};await w` + UPDATE external_import_features + SET properties_json = ${JSON.stringify(i)} + WHERE id = ${o.id} + `,r++}return await w`COMMIT`,se("external_import_features","UPDATE",n),r}catch(t){try{await w`ROLLBACK`}catch{}throw console.error("[Database] ✗ Failed to remap import features:",t),t}}function X(n){if(n===""||n===null||n===void 0)return null;const e=Number(n);return Number.isNaN(e)?null:e}async function ha(n){try{await w`BEGIN`,await w`DELETE FROM parcels`;let e=0;for(const t of n){const r=t.id??t.parcelid??t.parcel_id??null;if(r==null)continue;const o=t.boundary||t.geometry_wkt||t.polygon||t.wkt||(typeof t.geom=="string"?t.geom:"");await w` + INSERT OR REPLACE INTO parcels ( + id, upn, style, landuse, zone_code, zone_name, sector, block, parcel_no, + prop_no, st_name, prop_add, fac_name, min_height, max_height, eff_date, + lp_name, locality, mmda, last_update, remarks, geometry_wkt, + created_at, updated_at, districtid, status, fetched_at + ) VALUES ( + ${r}, ${t.upn??null}, ${X(t.style)}, ${t.landuse??null}, + ${t.zone_code??null}, ${t.zone_name??null}, ${t.sector??null}, + ${t.block??null}, ${t.parcel_no??null}, ${t.prop_no??null}, + ${t.st_name??null}, ${t.prop_add??null}, ${t.fac_name??null}, + ${X(t.min_height)}, ${X(t.max_height)}, ${t.eff_date??null}, + ${t.lp_name??null}, ${t.locality??null}, ${t.mmda??null}, + ${t.last_update??null}, ${t.remarks??null}, ${o}, + ${t.created_at??null}, ${t.updated_at??null}, ${X(t.districtid)}, + 'verified', CURRENT_TIMESTAMP + ) + `,e++}await w`COMMIT`,console.log("[Database] ✓ Saved",e,"parcels (from",n.length,"rows,",n.length-e,"skipped/replaced)")}catch(e){try{await w`ROLLBACK`}catch{}throw console.error("[Database] ✗ Failed to save parcels:",e),e}}async function ga(){try{const n=await w`SELECT * FROM parcels ORDER BY id`;return n.length===0?null:n}catch(n){return console.error("[Database] ✗ Failed to read local parcels:",n),null}}async function ma(n,e){try{await w` + UPDATE parcels SET + upn = ${e.upn??null}, + style = ${X(e.style)}, + landuse = ${e.landuse??null}, + zone_code = ${e.zone_code??null}, + zone_name = ${e.zone_name??null}, + sector = ${e.sector??null}, + block = ${e.block??null}, + parcel_no = ${e.parcel_no??null}, + prop_no = ${e.prop_no??null}, + st_name = ${e.st_name??null}, + prop_add = ${e.prop_add??null}, + fac_name = ${e.fac_name??null}, + min_height = ${X(e.min_height)}, + max_height = ${X(e.max_height)}, + eff_date = ${e.eff_date??null}, + lp_name = ${e.lp_name??null}, + locality = ${e.locality??null}, + mmda = ${e.mmda??null}, + last_update = ${e.last_update??null}, + remarks = ${e.remarks??null}, + districtid = ${X(e.districtid)}, + updated_at = CURRENT_TIMESTAMP + WHERE id = ${n} + `,console.log("[Database] ✓ Parcel updated:",n),se("parcels","UPDATE",n)}catch(t){throw console.error("[Database] ✗ Failed to update parcel:",n,t),t}}async function ya(n,e={}){try{await w` + INSERT INTO parcels ( + id, upn, style, landuse, zone_code, zone_name, sector, block, parcel_no, + prop_no, st_name, prop_add, fac_name, min_height, max_height, eff_date, + lp_name, locality, mmda, last_update, remarks, geometry_wkt, + created_at, updated_at, districtid, status, fetched_at + ) VALUES ( + NULL, ${e.upn??null}, ${X(e.style)}, ${e.landuse??null}, + ${e.zone_code??null}, ${e.zone_name??null}, ${e.sector??null}, + ${e.block??null}, ${e.parcel_no??null}, ${e.prop_no??null}, + ${e.st_name??null}, ${e.prop_add??null}, ${e.fac_name??null}, + ${X(e.min_height)}, ${X(e.max_height)}, ${e.eff_date??null}, + ${e.lp_name??null}, ${e.locality??null}, ${e.mmda??null}, + ${e.last_update??null}, ${e.remarks??null}, ${n}, + CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, ${X(e.districtid)}, + 'new', CURRENT_TIMESTAMP + ) + `;const r=(await w`SELECT last_insert_rowid() as id`)[0]?.id;return console.log("[Database] ✓ New parcel inserted:",r,"(status: new)"),se("parcels","INSERT",r),{id:r}}catch(t){throw console.error("[Database] ✗ Failed to insert new parcel:",t),t}}async function ba(n){try{if(n.length>0){const e=n[0],t={};for(const[r,o]of Object.entries(e))t[r]=o===null?"null":typeof o;console.log("[Database] First footprint field types:",t)}await w`DELETE FROM building_footprints`;for(const e of n){const t=JSON.stringify(e);let r=e.polygon||e.boundary||e.geom||e.wkt||e.footprint||"";const o=typeof r=="object"?JSON.stringify(r):String(r);let a=e.id||e.footprint_id||e.building_id||null;await w` + INSERT INTO building_footprints (id, geometry_wkt, properties, fetched_at) + VALUES (${a!==null&&typeof a=="object"?null:a}, ${o}, ${t}, CURRENT_TIMESTAMP) + `}console.log("[Database] ✓ Saved",n.length,"building footprints")}catch(e){throw console.error("[Database] ✗ Failed to save building footprints:",e),e}}async function wa(){try{const n=await w`SELECT properties FROM building_footprints ORDER BY id`;return n.length===0?null:n.map(e=>JSON.parse(e.properties))}catch(n){return console.error("[Database] ✗ Failed to read local building footprints:",n),null}}async function _a(n){try{if(n.length>0){const e=n[0],t={};for(const[r,o]of Object.entries(e))t[r]=o===null?"null":typeof o;console.log("[Database] First road field types:",t)}await w`DELETE FROM osm_roads`;for(const e of n){const t=JSON.stringify(e);let r=e.geom||e.geometry||e.wkt||e.road||e.line||"";const o=typeof r=="object"?JSON.stringify(r):String(r);let a=e.osm_id??e.osmid??e.id??null;await w` + INSERT OR REPLACE INTO osm_roads (osm_id, geometry_wkt, properties, fetched_at) + VALUES (${a!==null&&typeof a=="object"?null:a}, ${o}, ${t}, CURRENT_TIMESTAMP) + `}console.log("[Database] ✓ Saved",n.length,"OSM roads")}catch(e){throw console.error("[Database] ✗ Failed to save OSM roads:",e),e}}async function va(){try{const n=await w`SELECT properties FROM osm_roads ORDER BY osm_id`;return n.length===0?null:n.map(e=>JSON.parse(e.properties))}catch(n){return console.error("[Database] ✗ Failed to read local OSM roads:",n),null}}async function Ea(){return On.getDatabaseFile()}async function xa(n="lupmis-backup.sqlite3"){const e=await Ea(),t=new Blob([e],{type:"application/x-sqlite3"}),r=URL.createObjectURL(t),o=document.createElement("a");o.href=r,o.download=n,o.click(),URL.revokeObjectURL(r)}async function Sa(){return{type:"FeatureCollection",features:(await Gn()).map(e=>({type:"Feature",properties:{id:e.id,name:e.name,category:e.category,notes:e.notes,created_at:e.created_at},geometry:{type:"Point",coordinates:[e.lon,e.lat]}}))}}async function Eo(){try{const n=await w` + SELECT name FROM sqlite_master + WHERE type='table' AND name NOT LIKE 'sqlite_%' + ORDER BY name + `,e=await ra();return{ready:Nn,databasePath:vo,tables:n.map(t=>t.name),locationCount:e}}catch(n){return{ready:!1,error:n.message}}}const Un=Object.freeze(["parcels","building_footprints","osm_roads","collector_zones","upn_grid","remote_data"]);function Hn(n){return Un.includes(n)}async function Wn(n){if(!Hn(n))throw new Error(`Refusing to clear "${n}" — not a known cached-layer table`);const t=(await w(`SELECT COUNT(*) AS n FROM "${n}"`))[0]?.n??0;return await w(`DELETE FROM "${n}"`),console.log(`[Database] ✓ Cleared "${n}" (${t} rows)`),se(n,"CLEAR",null),t}async function Ta(){const n=await w` + SELECT name FROM sqlite_master + WHERE type='table' AND name IN ( + 'parcels', 'building_footprints', 'osm_roads', 'collector_zones', 'upn_grid', 'remote_data' + ) + `,e=new Set(n.map(o=>o.name)),t=[];for(const o of Un)if(e.has(o))try{const a=await Wn(o);t.push({table:o,count:a})}catch(a){console.error(`[Database] Failed to clear ${o}:`,a),t.push({table:o,count:0,error:a.message})}const r=t.reduce((o,a)=>o+a.count,0);return console.log(`[Database] ✓ Cleared all cached layers: ${r} rows across ${t.length} tables`),t}async function La(){const n=await w` + SELECT name FROM sqlite_master + WHERE type='table' AND name NOT LIKE 'sqlite_%' + ORDER BY name + `;if(n.length===0)return[];const e=n.map(t=>`SELECT '${t.name}' AS name, COUNT(*) AS count FROM "${t.name}"`).join(" UNION ALL ");return w(e)}async function ka(n,e=200){if((await w` + SELECT name FROM sqlite_master + WHERE type='table' AND name = ${n} + `).length===0)throw new Error(`Table "${n}" does not exist`);const r=await w(`SELECT * FROM "${n}" LIMIT ${e}`);return{columns:r.length>0?Object.keys(r[0]):[],rows:r}}async function Ia(){console.log("=== DATABASE TEST ===");try{const n=await w`SELECT sqlite_version() as v`;console.log("1. SQLite version:",n[0].v);const e=await w`SELECT name FROM sqlite_master WHERE type='table'`;console.log("2. Tables:",e.map(o=>o.name)),console.log("3. Inserting test row..."),await w`INSERT INTO locations (name, longitude, latitude, category) VALUES ('TEST', -1.0, 7.0, 'test')`;const t=await w`SELECT * FROM locations WHERE name = 'TEST'`;console.log("4. Test row:",t);const r=await w`SELECT COUNT(*) as c FROM locations`;return console.log("5. Total rows:",r[0].c),await w`DELETE FROM locations WHERE name = 'TEST'`,console.log("6. Test row deleted"),console.log("=== TEST PASSED ==="),!0}catch(n){return console.error("=== TEST FAILED ===",n),!1}}typeof window<"u"&&(window.testDatabase=Ia,window.dbStatus=Eo);async function Pa(n){const{uuid:e,name:t=null,startedAt:r,districtId:o=null}=n;await w` + INSERT INTO gps_trails (client_uuid, name, district_id, started_at, status) + VALUES (${e}, ${t}, ${o}, ${r}, 'recording') + `;const i=(await w`SELECT last_insert_rowid() as id`)[0]?.id;return se("gps_trails","insert",i),i}async function Ma(n,e){const{seq:t,lon:r,lat:o,altitude:a=null,accuracy:i=null,altitudeAccuracy:s=null,heading:l=null,speed:c=null,satellites:d=null,timestamp:u}=e,p=typeof u=="number"?new Date(u).toISOString():u||new Date().toISOString();await w` + INSERT INTO gps_trail_points + (trail_id, seq, longitude, latitude, altitude, accuracy, altitude_accuracy, heading, speed, satellites, recorded_at) + VALUES + (${n}, ${t}, ${r}, ${o}, ${a}, ${i}, ${s}, ${l}, ${c}, ${d}, ${p}) + `}async function Aa(n,e){const{endedAt:t,pointCount:r=0,distanceM:o=0}=e;await w` + UPDATE gps_trails + SET ended_at = ${t}, point_count = ${r}, distance_m = ${o}, status = 'completed' + WHERE id = ${n} + `,se("gps_trails","update",n)}async function Ca(){return w`SELECT * FROM gps_trails WHERE synced = 0 AND status = 'completed' ORDER BY started_at ASC`}async function Fa(n){return w`SELECT * FROM gps_trail_points WHERE trail_id = ${n} ORDER BY seq ASC`}async function Da(n,e=null){await w`UPDATE gps_trails SET synced = 1, remote_id = ${e} WHERE id = ${n}`,se("gps_trails","update",n)}const Kn=3.28084,Xn=621371e-9,Vn=10.7639,Yn=247105e-9,Jn=3861e-10;function Ft(){return localStorage.getItem("measurement-system")||"metric"}function Pt(n){if(Ft()==="imperial"){const e=n*Kn;return e>=5280?Math.round(n*Xn*100)/100+" mi":Math.round(e)+" ft"}return n>1e3?Math.round(n/1e3*100)/100+" km":Math.round(n*100)/100+" m"}function Oa(n){if(Ft()==="imperial"){const e=n*Kn,t=n*Xn;return e>=5280?`${t.toFixed(2)} mi (${e.toLocaleString("en",{maximumFractionDigits:0})} ft)`:`${e.toLocaleString("en",{maximumFractionDigits:1})} ft`}return n>=1e3?`${(n/1e3).toFixed(2)} km (${n.toLocaleString("en",{maximumFractionDigits:0})} m)`:`${n.toLocaleString("en",{maximumFractionDigits:1})} m`}function rt(n){if(Ft()==="imperial"){const e=n*Yn;return e>=640?Math.round(n*Jn*100)/100+" mi²":e>=1?Math.round(e*100)/100+" acres":Math.round(n*Vn).toLocaleString("en")+" ft²"}return n>1e6?Math.round(n/1e6*100)/100+" km²":Math.round(n*100)/100+" m²"}function Ra(n){if(Ft()==="imperial"){const e=n*Vn,t=n*Yn,r=n*Jn;return t>=640?`${r.toFixed(2)} mi² (${t.toLocaleString("en",{maximumFractionDigits:0})} acres)`:t>=1?`${t.toLocaleString("en",{maximumFractionDigits:1})} acres (${e.toLocaleString("en",{maximumFractionDigits:0})} ft²)`:`${e.toLocaleString("en",{maximumFractionDigits:0})} ft²`}return n>1e6?`${(n/1e6).toFixed(2)} km² (${n.toLocaleString("en",{maximumFractionDigits:0})} m²)`:`${n.toLocaleString("en",{maximumFractionDigits:0})} m²`}function Na(n){return rt(Math.PI*n*n)}function $a(n,e,t,r,o=1e-10){const a=e[0]-n[0],i=e[1]-n[1],s=r[0]-t[0],l=r[1]-t[1],c=a*l-i*s;if(Math.abs(c)1+o||h<-o||h>1+o?null:{point:[n[0]+p*a,n[1]+p*i],t:Math.max(0,Math.min(1,p)),u:Math.max(0,Math.min(1,h))}}function Zn(n){let e=0;for(let t=0,r=n.length;tn[1]!=l>n[1]&&n[0]<(s-a)*(n[1]-i)/(l-i)+a&&(t=!t)}return t}function lt(n,e){return(n[0]-e[0])**2+(n[1]-e[1])**2}function Ba(n,e){const t=[];for(let o=0;oo.lineSegIdx!==a.lineSegIdx?o.lineSegIdx-a.lineSegIdx:o.lineT-a.lineT),t}function Ga(n,e){const t=e.map((a,i)=>({...a,origOrder:i}));t.sort((a,i)=>a.ringSegIdx!==i.ringSegIdx?a.ringSegIdx-i.ringSegIdx:a.ringT-i.ringT);const r=n.slice(),o=new Array(t.length);for(let a=t.length-1;a>=0;a--){const i=t[a],s=i.ringSegIdx+1,l=1e-6;if(lt(i.point,r[i.ringSegIdx])=s&&o[t[c].origOrder]++}return{ring:r,indices:o}}function Go(n,e,t){const r=n.length-1,o=(e%r+r)%r,a=(t%r+r)%r,i=[];let s=o;for(;i.push(n[s]),s!==a;)s=(s+1)%r;return i}function jo(n,e,t){const r=[e.point],o=e.lineSegIdx,a=t.lineSegIdx;for(let i=o+1;i<=a;i++)r.push(n[i]);return lt(r[r.length-1],t.point)>1e-10&&r.push(t.point),r}function qo(n,e){const t=Zn(n);return e&&t<0||!e&&t>0?n.slice().reverse():n}function zo(n){if(n.length<2)return n;const e=n[0],t=n[n.length-1];return lt(e,t)>1e-10?[...n,e.slice()]:n}function ja(n,e){let t=1/0,r=1/0,o=-1/0,a=-1/0;for(const c of e)c[0]o&&(o=c[0]),c[1]>a&&(a=c[1]);const i=Math.sqrt((o-t)**2+(a-r)**2)||1,s=n.slice();if(co(s[0],e)){const c=s[0],d=s[1],u=c[0]-d[0],p=c[1]-d[1],h=Math.sqrt(u*u+p*p)||1,f=i*2/h;s[0]=[c[0]+u*f,c[1]+p*f]}const l=s.length-1;if(co(s[l],e)){const c=s[l],d=s[l-1],u=c[0]-d[0],p=c[1]-d[1],h=Math.sqrt(u*u+p*p)||1,f=i*2/h;s[l]=[c[0]+u*f,c[1]+p*f]}return s}function vt(n,e){const t=n[0],r=n.slice(1),o=ja(e,t),a=Ba(t,o);if(a.length!==2)return console.warn(`[polygonSplit] Expected 2 intersections, got ${a.length}`),null;const[i,s]=a,{ring:l,indices:c}=Ga(t,a),d=c[0],u=c[1],[p,h]=d0,T=qo(m,S),v=qo(_,S),x=[T],P=[v];for(const k of r){const q=qa(k);co(q,T)?x.push(k):P.push(k)}return[x,P]}function qa(n){let e=0,t=0;const r=n.length-1;for(let o=0;o{a.style.opacity="1",a.style.transform="translateY(0)"});const i=()=>{a.style.opacity="0",a.style.transform="translateY(-8px)",setTimeout(()=>a.remove(),300)};a.addEventListener("click",i),setTimeout(i,t)}const ht=[{stroke:"#ef4444",fill:"rgba(239,68,68,0.25)"},{stroke:"#3b82f6",fill:"rgba(59,130,246,0.25)"}],Ua=new M({stroke:new I({color:"#0ea5e9",width:3}),fill:new A({color:"rgba(14,165,233,0.15)"})}),Ha=new M({stroke:new I({color:"#f43f5e",width:2,lineDash:[8,6]}),image:new ue({radius:5,fill:new A({color:"#f43f5e"}),stroke:new I({color:"#fff",width:1.5})})});class Wa extends bo{constructor(e={}){super({handleEvent:t=>this._handleEvent(t)}),this.snapDistance_=e.snapDistance||25,this._sources=e.sources?Array.isArray(e.sources)?e.sources:[e.sources]:null,this._phase="select",this._selectedFeature=null,this._selectedSource=null,this._drawInteraction=null,this._splitFeatures=null,this._overlaySource=new G({useSpatialIndex:!1}),this._overlayLayer=new O({source:this._overlaySource,displayInLayerSwitcher:!1,style:Ua})}setMap(e){this.getMap()&&(this.getMap().removeLayer(this._overlayLayer),this._removeDrawInteraction()),super.setMap(e),e&&this._overlayLayer.setMap(e)}setActive(e){super.setActive(e),e||this._reset()}_getSources(){if(this._sources)return this._sources;if(!this.getMap())return[];const e=[],t=r=>{r.forEach(o=>{o.getVisible()&&(o.getSource&&o.getSource()instanceof G?e.push(o.getSource()):o.getLayers&&t(o.getLayers()))})};return t(this.getMap().getLayers()),e}_handleEvent(e){if(!this.getActive())return!0;if(this._phase==="select"){if(e.type==="pointermove")return this._onSelectMove(e);if(e.type==="singleclick")return this._onSelectClick(e)}if(this._phase==="draw"&&e.type==="keydown"&&e.originalEvent?.key==="Escape")return this._cancelDraw(),!1;if(this._phase==="pick"){if(e.type==="pointermove")return this._onPickMove(e);if(e.type==="singleclick")return this._onPickClick(e);if(e.type==="keydown"&&e.originalEvent?.key==="Escape")return this._reset(),!1}return!0}_onSelectMove(e){const t=this.getMap();if(!t)return!0;this._overlaySource.clear();const r=this._closestPolygon(e);if(r){const o=r.feature.clone();this._overlaySource.addFeature(o),t.getTargetElement().style.cursor="pointer"}else t.getTargetElement().style.cursor="";return!0}_onSelectClick(e){const t=this._closestPolygon(e);if(!t)return!0;this._selectedFeature=t.feature,this._selectedSource=t.source,this._overlaySource.clear();const r=t.feature.clone();return this._overlaySource.addFeature(r),this._startDrawPhase(),!1}_closestPolygon(e){let t=null,r=this.snapDistance_+1;for(const o of this._getSources()){const a=o.getClosestFeatureToCoordinate(e.coordinate);if(!a)continue;const i=a.getGeometry();if(!i)continue;const s=i.getType();if(s!=="Polygon"&&s!=="MultiPolygon")continue;const l=i.getClosestPoint(e.coordinate),d=new pe([e.coordinate,l]).getLength()/e.frameState.viewState.resolution;d{const r=t.feature.getGeometry().getCoordinates();this._performSplit(r)}),e.addInteraction(this._drawInteraction))}_removeDrawInteraction(){this._drawInteraction&&this.getMap()&&this.getMap().removeInteraction(this._drawInteraction),this._drawInteraction=null}_cancelDraw(){this._removeDrawInteraction(),this._reset()}_performSplit(e){const t=this._selectedFeature,r=this._selectedSource,o=t.getGeometry();let a;o.getType()==="Polygon"?a=o.getCoordinates():o.getType()==="MultiPolygon"&&(a=o.getCoordinates()[0]);const i=vt(a,e);if(!i){console.warn("[PolygonSplit] Split failed — line must cross the polygon boundary at exactly 2 points."),this._removeDrawInteraction(),this._startDrawPhase();return}const[s,l]=i,c=t.clone();c.setGeometry(new it(s)),c.setStyle(new M({stroke:new I({color:ht[0].stroke,width:2.5}),fill:new A({color:ht[0].fill})}));const d=t.clone();d.setGeometry(new it(l)),d.setStyle(new M({stroke:new I({color:ht[1].stroke,width:2.5}),fill:new A({color:ht[1].fill})}));const u=[c,d];if(this.dispatchEvent({type:"beforesplit",original:t,features:u}),r.dispatchEvent({type:"beforesplit",original:t,features:u}),r.removeFeature(t),r.addFeature(c),r.addFeature(d),this.dispatchEvent({type:"aftersplit",original:t,features:u}),r.dispatchEvent({type:"aftersplit",original:t,features:u}),this._removeDrawInteraction(),t.get("_layerType")==="parcel"){this._splitFeatures=u,this._phase="pick",this._overlaySource.clear();const h=this.getMap();h&&(h.getTargetElement().style.cursor=""),B("Click the polygon that should keep the original identifier.","info",5e3),this.dispatchEvent({type:"splitparcel",features:u,originalProps:t.getProperties(),source:r})}else this._reset()}_onPickMove(e){const t=this.getMap();if(!t)return!0;this._overlaySource.clear();const r=this._closestSplitPiece(e);if(r){const o=r.clone();this._overlaySource.addFeature(o),t.getTargetElement().style.cursor="pointer"}else t.getTargetElement().style.cursor="";return!0}_onPickClick(e){const t=this._closestSplitPiece(e);return t?(this.dispatchEvent({type:"splitpick",picked:t,features:this._splitFeatures}),this._reset(),!1):!0}_closestSplitPiece(e){if(!this._splitFeatures)return null;let t=null,r=this.snapDistance_+1;for(const o of this._splitFeatures){const a=o.getGeometry();if(!a)continue;const i=a.getClosestPoint(e.coordinate),l=new pe([e.coordinate,i]).getLength()/e.frameState.viewState.resolution;ln[1]!=l>n[1]&&n[0]<(s-a)*(n[1]-i)/(l-i)+a&&(t=!t)}return t}function Xa(n,e){const t=tt(n);return e&&t<0||!e&&t>0?n.slice().reverse():n}function Va(n){return n.length<2?n:qe(n[0],n[n.length-1])>1e-10?[...n,n[0].slice()]:n}function Be(n,e,t){const r=t[0]-e[0],o=t[1]-e[1],a=r*r+o*o;if(a<1e-20)return qe(n,e);let i=((n[0]-e[0])*r+(n[1]-e[1])*o)/a;i=Math.max(0,Math.min(1,i));const s=e[0]+i*r,l=e[1]+i*o;return(n[0]-s)**2+(n[1]-l)**2}function Ho(n,e){let t=0,r=1/0;const o=n.length-1;for(let a=0;a0;){const v=(b+1)%a,x=g?(S-1+i)%i:(S+1)%i;if(v===m||x===_)break;if(De(n[v],e[x],s)){b=v,S=x;continue}if(Be(n[v],e[S],e[x])0;){const v=(m-1+a)%a,x=g?(_+1)%i:(_-1+i)%i;if(v===b||x===S)break;if(De(n[v],e[x],s)){m=v,_=x;continue}if(Be(n[v],e[_],e[x])r+1)););return o}function Ja(n,e,t,r,o=5){const a=n[0],i=e[0],s=n.slice(1),l=e.slice(1),c=Ho(a,t),d=Ho(i,r),u=Ya(a,i,c.segIdx,d.segIdx,o);if(!u)return console.warn("[polygonMerge] Could not find shared boundary between polygons — seed edges are not near the other ring"),{coords:null,error:"The selected edges are not on a shared boundary. Click edges that lie on the common border between the two polygons."};const{startA:p,endA:h,startB:f,endB:y,reversed:g}=u;a.length-1,i.length-1;const m=zt(a,h,p);let b;g?b=zt(i,f,y):b=zt(i,y,f);const _=[...m,...b.slice(1)],S=o*o;_.length>2&&qe(_[_.length-1],_[0])k*1.5)return console.warn(`[polygonMerge] Area mismatch: A=${v.toFixed(1)}, B=${x.toFixed(1)}, merged=${P.toFixed(1)}, expected≈${k.toFixed(1)}`),{coords:null,error:"Merge produced an invalid polygon (area mismatch). The polygons may not be truly adjacent — try clicking closer to the shared boundary."};const q=tt(a)>0,U=Xa(T,q),N=[...s,...l].filter(le=>{const Z=le.reduce((H,me)=>H+me[0],0)/(le.length-1),ge=le.reduce((H,me)=>H+me[1],0)/(le.length-1);return Ka([Z,ge],U)});return{coords:[U,...N]}}const Wo=new M({stroke:new I({color:"#0ea5e9",width:3}),fill:new A({color:"rgba(14,165,233,0.15)"})}),Za=new M({stroke:new I({color:"#f59e0b",width:3}),fill:new A({color:"rgba(245,158,11,0.15)"})}),Qa=new M({stroke:new I({color:"#0ea5e9",width:3}),fill:new A({color:"rgba(14,165,233,0.15)"}),text:new st({text:"A",font:"bold 22px Exo, sans-serif",fill:new A({color:"#0ea5e9"}),stroke:new I({color:"#fff",width:4}),overflow:!0})}),ei=new M({stroke:new I({color:"#f59e0b",width:3}),fill:new A({color:"rgba(245,158,11,0.15)"}),text:new st({text:"B",font:"bold 22px Exo, sans-serif",fill:new A({color:"#f59e0b"}),stroke:new I({color:"#fff",width:4}),overflow:!0})}),ti=new M({stroke:new I({color:"#ec4899",width:4,lineDash:[10,6]})}),oi=new M({stroke:new I({color:"#10b981",width:2.5}),fill:new A({color:"rgba(16,185,129,0.3)"})});class ni extends bo{constructor(e={}){super({handleEvent:t=>this._handleEvent(t)}),this.snapDistance_=e.snapDistance||25,this.tolerance_=e.tolerance||5,this._phase="select_a",this._featureA=null,this._sourceA=null,this._featureB=null,this._sourceB=null,this._edgeClickA=null,this._edgeClickB=null,this._highlightSource=new G({useSpatialIndex:!1}),this._highlightLayer=new O({source:this._highlightSource,displayInLayerSwitcher:!1,style:t=>t.get("_highlightStyle")||Wo}),this._edgeSource=new G({useSpatialIndex:!1}),this._edgeLayer=new O({source:this._edgeSource,displayInLayerSwitcher:!1,style:ti})}setMap(e){this.getMap()&&(this.getMap().removeLayer(this._highlightLayer),this.getMap().removeLayer(this._edgeLayer)),super.setMap(e),e&&(this._highlightLayer.setMap(e),this._edgeLayer.setMap(e))}setActive(e){super.setActive(e),e||this._reset()}_getSources(){if(!this.getMap())return[];const e=[],t=r=>{r.forEach(o=>{o.getVisible()&&(o.getSource&&o.getSource()instanceof G?e.push(o.getSource()):o.getLayers&&t(o.getLayers()))})};return t(this.getMap().getLayers()),e}_handleEvent(e){if(!this.getActive())return!0;if(e.type==="keydown"&&e.originalEvent?.key==="Escape")return this._reset(),!1;switch(this._phase){case"select_a":if(e.type==="pointermove")return this._onSelectMove(e,null);if(e.type==="singleclick")return this._onSelectAClick(e);break;case"select_b":if(e.type==="pointermove")return this._onSelectMove(e,this._featureA);if(e.type==="singleclick")return this._onSelectBClick(e);break;case"click_edge_a":if(e.type==="pointermove")return this._onEdgeMove(e,this._featureA);if(e.type==="singleclick")return this._onEdgeAClick(e);break;case"click_edge_b":if(e.type==="pointermove")return this._onEdgeMove(e,this._featureB);if(e.type==="singleclick")return this._onEdgeBClick(e);break}return!0}_onSelectMove(e,t){const r=this.getMap();if(!r)return!0;this._highlightSource.clear(),this._edgeSource.clear(),this._rebuildHighlights();const o=this._closestPolygon(e,t);if(o){const a=this._phase==="select_a"?Wo:Za,i=o.feature.clone();i.set("_highlightStyle",a),this._highlightSource.addFeature(i),r.getTargetElement().style.cursor="pointer"}else r.getTargetElement().style.cursor="";return!0}_onSelectAClick(e){const t=this._closestPolygon(e,null);return t?(this._featureA=t.feature,this._sourceA=t.source,this._phase="select_b",this._rebuildHighlights(),!1):!0}_onSelectBClick(e){const t=this._closestPolygon(e,this._featureA);return t?(this._featureB=t.feature,this._sourceB=t.source,this._phase="click_edge_a",this._rebuildHighlights(),this.getMap().getTargetElement().style.cursor="crosshair",!1):!0}_closestPolygon(e,t){let r=null,o=this.snapDistance_+1;for(const a of this._getSources()){const i=a.getClosestFeatureToCoordinate(e.coordinate);if(!i||t&&i===t)continue;const s=i.getGeometry();if(!s)continue;const l=s.getType();if(l!=="Polygon"&&l!=="MultiPolygon")continue;const c=s.getClosestPoint(e.coordinate),u=new pe([e.coordinate,c]).getLength()/e.frameState.viewState.resolution;u{t.get("_permanent")&&e.push(t)}),e.forEach(t=>this._highlightSource.removeFeature(t)),this._featureA){const t=this._featureA.clone();t.set("_highlightStyle",Qa),t.set("_permanent",!0),this._highlightSource.addFeature(t)}if(this._featureB){const t=this._featureB.clone();t.set("_highlightStyle",ei),t.set("_permanent",!0),this._highlightSource.addFeature(t)}}_reset(){this._phase="select_a",this._featureA=null,this._sourceA=null,this._featureB=null,this._sourceB=null,this._edgeClickA=null,this._edgeClickB=null,this._highlightSource.clear(),this._edgeSource.clear();const e=this.getMap();e&&(e.getTargetElement().style.cursor="")}}function ri(n,e){return(n[0]-e[0])**2+(n[1]-e[1])**2}function Ko(n){let e=0;for(let t=0,r=n.length;tt&&(t=d,r=c)}const o=n[r],a=n[r+1],i=Math.sqrt(t),s=[(a[0]-o[0])/i,(a[1]-o[1])/i],l=[-s[1],s[0]];return{p0:o,p1:a,along:s,perp:l}}function Ut(n,e,t,r,o){const a=n[0]+r*e[0],i=n[1]+r*e[1];return[[a-o*t[0],i-o*t[1]],[a+o*t[0],i+o*t[1]]]}function Oe(n,e,t){const r=n[0],o=r.length-1;let a=0,i=0;for(let c=0;cu&&(u=_)}const p=(u-d)*1.5,h=[];let f=n,y=e;for(let g=0;gv&&(v=Z)}let x=T,P=v,k=null,q=null,U=1/0;for(let J=0;J<40;J++){const N=(x+P)/2,le=Ut(l,i,s,N,p),Z=vt(f,le);if(!Z){const D=(P-x)*.01,W=Ut(l,i,s,N+D,p),oe=vt(f,W);if(oe){const[xe,Se]=oe,Ue=Oe(xe,l,i),He=Oe(Se,l,i),We=Uethis._handleEvent(t)}),this.snapDistance_=e.snapDistance||25,this._sources=e.sources?Array.isArray(e.sources)?e.sources:[e.sources]:null,this._phase="select",this._selectedFeature=null,this._selectedSource=null,this._selectedEdge=null,this._dividedFeatures=null,this._overlaySource=new G({useSpatialIndex:!1}),this._overlayLayer=new O({source:this._overlaySource,displayInLayerSwitcher:!1,style:si}),this._edgeSource=new G({useSpatialIndex:!1}),this._edgeLayer=new O({source:this._edgeSource,displayInLayerSwitcher:!1,style:li})}setMap(e){this.getMap()&&(this.getMap().removeLayer(this._overlayLayer),this.getMap().removeLayer(this._edgeLayer)),super.setMap(e),e&&(this._overlayLayer.setMap(e),this._edgeLayer.setMap(e))}setActive(e){super.setActive(e),e||this._reset()}_getSources(){if(this._sources)return this._sources;if(!this.getMap())return[];const e=[],t=r=>{r.forEach(o=>{o.getVisible()&&(o.getSource&&o.getSource()instanceof G?e.push(o.getSource()):o.getLayers&&t(o.getLayers()))})};return t(this.getMap().getLayers()),e}_handleEvent(e){if(!this.getActive())return!0;if(e.type==="keydown"&&e.originalEvent?.key==="Escape")return this._phase==="form"?this.cancelDivide():this._reset(),!1;if(this._phase==="select"){if(e.type==="pointermove")return this._onSelectMove(e);if(e.type==="singleclick")return this._onSelectClick(e)}if(this._phase==="edge"){if(e.type==="pointermove")return this._onEdgeMove(e);if(e.type==="singleclick")return this._onEdgeClick(e)}if(this._phase==="pick"){if(e.type==="pointermove")return this._onPickMove(e);if(e.type==="singleclick")return this._onPickClick(e)}return!0}_onSelectMove(e){const t=this.getMap();if(!t)return!0;this._overlaySource.clear();const r=this._closestPolygon(e);if(r){const o=r.feature.clone();this._overlaySource.addFeature(o),t.getTargetElement().style.cursor="pointer"}else t.getTargetElement().style.cursor="";return!0}_onSelectClick(e){const t=this._closestPolygon(e);if(!t)return!0;this._selectedFeature=t.feature,this._selectedSource=t.source,this._overlaySource.clear();const r=t.feature.clone();return r.set("_permanent",!0),this._overlaySource.addFeature(r),this._phase="edge",B("Click the edge to divide along.","info",3e3),!1}_closestPolygon(e){let t=null,r=this.snapDistance_+1;for(const o of this._getSources()){const a=o.getClosestFeatureToCoordinate(e.coordinate);if(!a)continue;const i=a.getGeometry();if(!i)continue;const s=i.getType();if(s!=="Polygon"&&s!=="MultiPolygon")continue;const l=i.getClosestPoint(e.coordinate),d=new pe([e.coordinate,l]).getLength()/e.frameState.viewState.resolution;d{const f=t.clone();return f.setGeometry(new it(p)),f.setStyle(new M({stroke:new I({color:s[h].stroke,width:2.5}),fill:new A({color:s[h].fill})})),f}),c={type:"beforedivide",original:t,features:l};this.dispatchEvent(c),r.dispatchEvent({...c}),r.removeFeature(t);for(const p of l)r.addFeature(p);const d={type:"afterdivide",original:t,features:l};this.dispatchEvent(d),r.dispatchEvent({...d}),t.get("_layerType")==="parcel"?(this._dividedFeatures=l,this._phase="pick",B("Click the polygon that should keep the original identifier.","info",5e3),this.dispatchEvent({type:"dividedparcel",features:l,originalProps:t.getProperties(),source:r})):(B(`Polygon divided into ${e} equal pieces.`,"success"),this._reset())}_onPickMove(e){const t=this.getMap();if(!t)return!0;this._overlaySource.clear();const r=this._closestDividedPiece(e);if(r){const o=r.clone();this._overlaySource.addFeature(o),t.getTargetElement().style.cursor="pointer"}else t.getTargetElement().style.cursor="";return!0}_onPickClick(e){const t=this._closestDividedPiece(e);return t?(this.dispatchEvent({type:"dividepick",picked:t,features:this._dividedFeatures}),this._reset(),!1):!0}_closestDividedPiece(e){if(!this._dividedFeatures)return null;let t=null,r=this.snapDistance_+1;for(const o of this._dividedFeatures){const a=o.getGeometry();if(!a)continue;const i=a.getClosestPoint(e.coordinate),l=new pe([e.coordinate,i]).getLength()/e.frameState.viewState.resolution;l{const l=this.categoryEmojis[s];return l?l.emoji:"📍"},this.getCategoryOptionsHtml=()=>Object.entries(this.categoryEmojis).map(([s,{emoji:l,label:c}])=>``).join(` + `),this.createEmojiStyle=(s,l=24)=>new M({text:new st({text:s,font:`${l}px sans-serif`,textBaseline:"bottom",textAlign:"center",offsetY:-5})}),this.defaultStyle=this.createEmojiStyle("📍",32),this.selectedStyle=this.createEmojiStyle("📍",42),this.categoryStyles={};for(const[s,{emoji:l}]of Object.entries(this.categoryEmojis))this.categoryStyles[s]=this.createEmojiStyle(l,32);const r=this.createBaseLayers(t.basemap||"topo");this.markersLayer=new O({title:"Markers",source:this.markerSource,style:s=>this.getFeatureStyle(s),visible:!1}),this.overlayGroup=new Te({title:"Overlays"}),this.map=new Io({target:e,layers:[r,this.markersLayer,this.overlayGroup],view:new Tr({center:Q(t.center||[0,0]),zoom:t.zoom||2,minZoom:t.minZoom||2,maxZoom:t.maxZoom||19})});const o=new Fr({collapsed:!0,mouseover:!0,extent:!0,trash:!1,oninfo:null});this.map.addControl(o),queueMicrotask(()=>{const s=o.element?.querySelector(":scope > button");if(s){const l="/".replace(/\/?$/,"/");s.style.backgroundImage=`url('${l}app-icons/luspa-72x72.png')`}});let a=!1;o.on("drawlist",s=>{this._decorateLayerListItem(s.layer,s.li),a||(a=!0,queueMicrotask(()=>{a=!1,this._refreshLayerSwitcherChrome(o)}))}),this.map.getLayers().on("change",()=>{this._refreshLayerSwitcherChrome(o)}),this._wireLayerSwitcherVisibilityHooks(o),this._createAddLayerDialog(),this._createLegendPanel(),this.scaleBar=new Lr({bar:!0,steps:4,text:!0,minWidth:140}),this.map.addControl(this.scaleBar),this._initGpsRendering(),this._createLocationControl(),this._createBaseMapPicker();const i=new Dr({placeholder:"Search location...",typing:300,minLength:3,maxItems:10,collapsed:!0});this.map.addControl(i),i.on("select",s=>{const l=s.search;if(l){const c=parseFloat(l.lon),d=parseFloat(l.lat),u=[c,d],p=Q(u);this.navigateTo(c,d,14);const h={coordinate:p,lonLat:u,name:l.display_name||l.name||"Unknown",searchResult:l};this.searchSelectCallbacks.forEach(f=>f(h))}}),this.searchNominatim=i,this.searchSelectCallbacks=[],this.selectedFeature=null,this.createPopup(),this.createInfoPopup(),this.createAddLocationPopup(),this.createParcelEditPopup(),this.createDrawnPolygonPopup(),this.createMergePopup(),this.createDividePopup(),this.dblClickCallbacks=[],this.editBar=null,this.drawingsSource=null,this.drawingsLayer=null,this.touchCursor=null,this._editBarActive=!1}initEditBar(){this.drawingsSource=new G,this.drawingsLayer=new O({title:"sketches",source:this.drawingsSource,style:new M({stroke:new I({color:"#f59e0b",width:2.5}),fill:new A({color:"rgba(245,158,11,0.15)"}),image:new ue({radius:6,fill:new A({color:"#f59e0b"}),stroke:new I({color:"#fff",width:1.5})})})}),this._drawingsGroup=new Te({title:"Drawings",layers:[this.drawingsLayer]});const e=this.map.getLayers(),t=e.getArray().indexOf(this.overlayGroup);e.insertAt(t>=0?t:e.getLength(),this._drawingsGroup),this._selectInteraction=new kr({condition:Ir,filter:(f,y)=>!!y,layers:f=>f instanceof O}),this._selectInteraction.setActive(!1),this.map.addInteraction(this._selectInteraction),this._modifyInteraction=new Or({features:this._selectInteraction.getFeatures()}),this._modifyInteraction.setActive(!1),this._undoRedo=new Rr,this.map.addInteraction(this._undoRedo),this.editBar=new ao({source:this.drawingsSource,interactions:{Select:this._selectInteraction,ModifySelect:this._modifyInteraction,DrawPoint:!0,DrawLine:!0,DrawPolygon:!0,DrawRegular:!0,DrawHole:!0,Delete:!0,Info:!0,Transform:!0,Split:!1}}),this.map.addControl(this.editBar),this._setupVertexOverlay();const r=new Ao({group:!0,className:"ol-editbar-actions",controls:[new et({html:'',className:"ol-undo",title:"Undo",handleClick:()=>{this._undoRedo.hasUndo()&&this._undoRedo.undo()}}),new et({html:'',className:"ol-redo",title:"Redo",handleClick:()=>{this._undoRedo.hasRedo()&&this._undoRedo.redo()}}),new et({html:'',className:"ol-save",title:"Save drawings",handleClick:()=>{this.dispatchEditEvent("save")}})]});this.editBar.addControl(r),this._lineSplitInteraction=new Nr,this._polygonSplitInteraction=new Wa,this.map.addInteraction(this._lineSplitInteraction),this.map.addInteraction(this._polygonSplitInteraction),this._lineSplitInteraction.setActive(!1),this._polygonSplitInteraction.setActive(!1),this._polygonSplitInteraction.on("splitpick",f=>{const y=["UPN","upn","id","parcelid","parcel_id","PARCELID","PARCEL_ID","ID"];for(const g of f.features)if(g!==f.picked)for(const m of y)g.get(m)!==void 0&&g.set(m,"")}),this._polygonDivideInteraction=new di,this.map.addInteraction(this._polygonDivideInteraction),this._polygonDivideInteraction.setActive(!1);const o=new ve({html:'',className:"ol-split-line",title:"Split Lines",name:"SplitLine",interaction:this._lineSplitInteraction,autoActivate:!0}),a=new ve({html:'',className:"ol-split-polygon",title:"Split Polygons",name:"SplitPolygon",interaction:this._polygonSplitInteraction}),i=new ve({html:'',className:"ol-split-divide",title:"Divide Polygon",name:"DividePolygon",interaction:this._polygonDivideInteraction}),s=new Ao({toggleOne:!0,autoDeactivate:!0,controls:[o,a,i]}),l=new ve({className:"ol-split",title:"Split",name:"Split",bar:s,onToggle:f=>{f||(this._lineSplitInteraction.setActive(!1),this._polygonSplitInteraction.setActive(!1),this._polygonDivideInteraction.setActive(!1))}});this.editBar.addControl(l),this._polygonDivideInteraction.on("divideform",f=>{this.showDividePopup(f.feature,f.source,f.coordinate)}),this._polygonDivideInteraction.on("dividecancel",()=>{this.hideDividePopup()}),this._polygonDivideInteraction.on("dividepick",f=>{const y=["UPN","upn","id","parcelid","parcel_id","PARCELID","PARCEL_ID","ID"];for(const g of f.features)if(g!==f.picked)for(const m of y)g.get(m)!==void 0&&g.set(m,"")}),this._polygonMergeInteraction=new ni,this.map.addInteraction(this._polygonMergeInteraction),this._polygonMergeInteraction.setActive(!1);const c=new ve({html:'',className:"ol-merge",title:"Merge Polygons",name:"Merge",interaction:this._polygonMergeInteraction});this.editBar.addControl(c),this._polygonMergeInteraction.on("mergedparcel",f=>{this.showMergeIdentifierPopup(f.merged,f.propsA,f.propsB,f.coordinate)});const d=this.editBar.element;if(d&&r.element&&r.element.parentNode===d){const f=document.createElement("div");f.className="ol-editbar-break",d.insertBefore(f,r.element)}this._snapGuidesEnabled=localStorage.getItem("snap-guides-enabled")==="1",this._snapGuides=new $r({pixelTolerance:10,vectorClass:Pr}),this.map.addInteraction(this._snapGuides);const u=["DrawPoint","DrawLine","DrawPolygon","DrawHole","DrawRegular"];for(const f of u){const y=this.editBar.getInteraction(f);y&&y.on("change:active",()=>{y.getActive()&&this._snapGuides.setDrawInteraction(y)})}this._modifyInteraction&&this._snapGuides.setModifyInteraction(this._modifyInteraction);const p=new et({html:'',className:"ol-snap-toggle"+(this._snapGuidesEnabled?" ol-active":""),title:"Toggle Snap Guides",handleClick:()=>{this._snapGuidesEnabled=!this._snapGuidesEnabled,localStorage.setItem("snap-guides-enabled",this._snapGuidesEnabled?"1":"0"),p.element.classList.toggle("ol-active",this._snapGuidesEnabled),this._snapGuides&&this._snapGuides.setActive(this._snapGuidesEnabled&&this._editBarActive),console.log("[MapView] Snap guides:",this._snapGuidesEnabled?"ON":"OFF")}});this._snapToggleBtn=p,r.addControl(p),this.setEditMode(!1),this._drawingsGroup.on("change:visible",()=>{const f=this._drawingsGroup.getVisible();this.setEditMode(f)}),("ontouchstart"in window||navigator.maxTouchPoints>0||navigator.msMaxTouchPoints>0)&&(this.touchCursor=new Br({className:"ol-editbar-cursor"}),this.map.addInteraction(this.touchCursor),this.touchCursor.setActive(!1),console.log("[MapView] Touch device detected — TouchCursor added")),this.drawingsSource.on("addfeature",f=>{const y=f.feature,g=y.getGeometry();if(!g||g.getType()!=="Polygon")return;const m=g.getInteriorPoint().getCoordinates();this.showDrawnPolygonPopup(y,m)}),console.log("[MapView] EditBar initialised with Drawings group, UndoRedo and SnapGuides (default:",this._snapGuidesEnabled?"ON":"OFF",")")}dispatchEditEvent(e){if(!this._editEventListeners)return;const t=this._editEventListeners[e];t&&t.forEach(r=>r())}onEditEvent(e,t){this._editEventListeners||(this._editEventListeners={}),this._editEventListeners[e]||(this._editEventListeners[e]=[]),this._editEventListeners[e].push(t)}setEditMode(e){this._editBarActive=!!e,this.editBar&&(this.editBar.setVisible(this._editBarActive),this._editBarActive||this.editBar.deactivateControls()),this._selectInteraction&&(this._editBarActive||this._selectInteraction.getFeatures().clear(),this._selectInteraction.setActive(this._editBarActive)),this._modifyInteraction&&this._modifyInteraction.setActive(this._editBarActive),this._snapGuides&&this._snapGuides.setActive(this._snapGuidesEnabled&&this._editBarActive),this.touchCursor&&this.touchCursor.setActive(this._editBarActive),!this._editBarActive&&this._vertexOverlaySource&&this._vertexOverlaySource.clear(),console.log("[MapView] Edit mode:",this._editBarActive?"ON":"OFF")}isEditMode(){return this._editBarActive}_setupVertexOverlay(){this._vertexOverlaySource=new G,this._vertexOverlayLayer=new O({title:"__vertex_highlight__",source:this._vertexOverlaySource,zIndex:990,style:new M({image:new ue({radius:4,fill:new A({color:"rgba(14,165,233,0.85)"}),stroke:new I({color:"#fff",width:1.2})})})}),this._vertexOverlayLayer.set("displayInLayerSwitcher",!1),this.map.addLayer(this._vertexOverlayLayer),this._onSelectedFeatureGeomChange=()=>this._refreshVertexOverlay(),this._vertexTrackedFeatures=new Set,this._selectInteraction.on("select",()=>this._refreshVertexOverlay())}_refreshVertexOverlay(){if(!this._vertexOverlaySource)return;if(this._vertexOverlaySource.clear(),this._vertexTrackedFeatures){for(const t of this._vertexTrackedFeatures)t.un("change",this._onSelectedFeatureGeomChange);this._vertexTrackedFeatures.clear()}if(!this._editBarActive||!this._selectInteraction)return;const e=this._selectInteraction.getFeatures().getArray();for(const t of e){const r=t.getGeometry();if(!r)continue;const o=r.getType();if(!["Polygon","MultiPolygon","LineString","MultiLineString"].includes(o))continue;const a=this._collectAllVertices(r);for(const i of a)this._vertexOverlaySource.addFeature(new de(new ut(i)));t.on("change",this._onSelectedFeatureGeomChange),this._vertexTrackedFeatures.add(t)}}_collectAllVertices(e){const t=[],r=s=>Array.isArray(s)&&typeof s[0]=="number",o=(s,l)=>{const c=l&&s.length>1?s.length-1:s.length;for(let d=0;d{if(r(l))t.push(l);else if(Array.isArray(l))for(const c of l)s(c)};s(i)}return t}getDrawingsLayer(){return this.drawingsLayer}getDrawingsSource(){return this.drawingsSource}getEditBar(){return this.editBar}setScaleBarUnits(e){this.scaleBar&&this.scaleBar.setUnits(e==="imperial"?"imperial":"metric")}createPopup(){this.popupElement=document.createElement("div"),this.popupElement.className="map-popup",this.popupElement.style.cssText=` + position: absolute; + background: var(--card, #fff); + color: var(--card-foreground, #1e1a4b); + border-radius: 8px; + padding: 10px 14px; + box-shadow: 0 2px 8px rgba(0,0,0,0.25); + font-family: var(--font-body, 'Exo', sans-serif); + font-size: 13px; + min-width: 150px; + max-width: 280px; + pointer-events: none; + z-index: 1000; + border: 1px solid var(--border, #1e1a4b1f); + `,this.popup=new _e({element:this.popupElement,positioning:"bottom-center",offset:[0,-15],stopEvent:!1}),this.map.addOverlay(this.popup),this.setupHoverPopup()}setupHoverPopup(){let e=null;this.map.on("pointermove",t=>{if(t.dragging){this.hidePopup();return}const r=this.map.forEachFeatureAtPixel(t.pixel,o=>o.get("name")?o:null);r&&r!==e?(e=r,this.showPopup(r,t.coordinate)):!r&&e&&(e=null,this.hidePopup()),this.map.getTargetElement().style.cursor=r?"pointer":""}),this.map.getTargetElement().addEventListener("mouseleave",()=>{this.hidePopup(),e=null})}showPopup(e,t){const r=e.get("name")||"Unnamed",o=e.get("category")||"default",a=e.get("description"),i=e.get("lon"),s=e.get("lat");let c=` +
+ ${this.getEmoji(o)} ${this.escapeHtml(r)} +
+ `;const u={water:"#3b82f6",school:"#f59e0b",health:"#ef4444",market:"#8b5cf6",default:"#2d5016",other:"#6b7280"}[o]||"#6b7280";c+=` +
+ ${o} +
+ `,a&&(c+=` +
+ ${this.escapeHtml(a)} +
+ `),i!==void 0&&s!==void 0&&(c+=` +
+ ${Number(i).toFixed(5)}, ${Number(s).toFixed(5)} +
+ `),this.popupElement.innerHTML=c,this.popup.setPosition(t)}hidePopup(){this.popup.setPosition(void 0)}createInfoPopup(){this.infoPopupElement=document.createElement("div"),this.infoPopupElement.className="map-info-popup",this.infoPopupElement.style.cssText=` + position: absolute; + background: var(--card, #fff); + color: var(--card-foreground, #1e1a4b); + border-radius: 10px; + padding: 0; + box-shadow: 0 4px 16px rgba(0,0,0,0.3); + font-family: var(--font-body, 'Exo', sans-serif); + font-size: 13px; + min-width: 220px; + max-width: 320px; + max-height: 70vh; + display: flex; + flex-direction: column; + z-index: 1001; + border: 1px solid var(--border, #1e1a4b1f); + overflow: hidden; + `,this.infoPopup=new _e({element:this.infoPopupElement,positioning:"bottom-center",offset:[0,-10],stopEvent:!0,autoPan:!0,autoPanAnimation:{duration:250}}),this.map.addOverlay(this.infoPopup)}showInfoPopup(e,t,r={}){const{title:o="Feature Info",color:a="#e11d48"}=r,i=e.getProperties(),s=e.getGeometry(),l=s.getType(),c=["geometry","_layerType"];let d="";for(const[p,h]of Object.entries(i))c.includes(p)||h===void 0||h===null||(d+=` + + ${this.escapeHtml(p)} + ${this.escapeHtml(String(h))} + + `);if(l==="Polygon"||l==="MultiPolygon"){const p=nt(s,{projection:"EPSG:3857"}),h=Ra(p);d+=` + + area + ${h} + + `}else if(l==="LineString"||l==="MultiLineString"){const p=Et(s,{projection:"EPSG:3857"}),h=Oa(p);d+=` + + length + ${h} + + `}else if(l==="Point"){const p=Me(s.getCoordinates()),h=p[0].toFixed(6),f=p[1].toFixed(6);d+=` + + longitude + ${h} + + + latitude + ${f} + + `}const u=` +
+ ${this.escapeHtml(o)} + +
+
+ + ${d} +
+
+ `;this.infoPopupElement.innerHTML=u,this.infoPopup.setPosition(t),this.infoPopupElement.querySelector("#info-popup-close").addEventListener("click",()=>{this.hideInfoPopup()})}hideInfoPopup(){this.infoPopup.setPosition(void 0)}_collectIntersectionRows(e,t,r){const o=[];if(e.length>0&&o.push({label:"Parcels",value:String(e.length),color:"#0ea5e9"}),t.length>0){const a=t.map(i=>i.get("colzonename")||i.get("zone_name")||i.get("name")||"unnamed");o.push({label:"Zones",value:String(t.length),color:"#7c3aed"}),o.push({label:"Zone Names",value:a.map(i=>this.escapeHtml(i)).join(", "),color:"#7c3aed"})}for(const[a,i]of Object.entries(r))o.push({label:this.escapeHtml(a),value:`${i.length} feature(s)`});return o.length===0&&o.push({label:"",value:"No intersecting features found",empty:!0}),o}_buildAnalysisPopupHtml(e,t,r){let o="";for(const a of r){if(a.empty){o+=` + + ${a.value} + `;continue}const i=a.color||"var(--muted-foreground, #7a7a7a)",s=a._first?"":"border-top:1px solid var(--border, #1e1a4b1f);";o+=` + + ${a.label} + ${a.value} + `}return` +
+ ${e} ${t} + +
+
+ + ${o} +
+
+
+ + +
`}_showAnalysisPopup(e,t,r,o,a=null){this.infoPopupElement.innerHTML=this._buildAnalysisPopupHtml(e,t,r),this.infoPopup.setPosition(o),this.infoPopupElement.querySelector("#info-popup-close").addEventListener("click",()=>{this.hideInfoPopup()}),this.infoPopupElement.querySelector("#info-popup-export-pdf")?.addEventListener("click",()=>{const s=r.filter(l=>!l.empty).map(l=>({label:l.label,value:l.value.replace(/<[^>]*>/g,"")}));Ct(async()=>{const{exportAnalysisPDF:l}=await import("./pdf-export-BG6jqfsR.js");return{exportAnalysisPDF:l}},__vite__mapDeps([0,1,2,3])).then(({exportAnalysisPDF:l})=>{l({title:t,rows:s})}).catch(l=>{console.error("[MapView] PDF export failed:",l)})});const i=this.infoPopupElement.querySelector("#info-popup-export-gis");if(i){const s=a?a.parcelFeatures.length+a.zoneFeatures.length+Object.values(a.otherByLayer).reduce((l,c)=>l+c.length,0):0;!a||s===0?(i.disabled=!0,i.style.opacity="0.5",i.style.cursor="not-allowed",i.title="No intersecting features to export"):i.addEventListener("click",()=>{window.dispatchEvent(new CustomEvent("lupmis:export-gis",{detail:{title:t,...a}}))})}}showCircleIntersectionPopup(e,t){const r=e.getGeometry();if(!r||typeof r.getCenter!="function")return;const o=Mr(r,64),a=o.getExtent(),i=e.get("_radius")||r.getRadius(),s=[],l=[],c={},d=g=>{const m=g.getGeometry();if(!m)return!1;const b=m.getExtent();return b[2]a[2]||b[3]a[3]?!1:o.intersectsExtent(b)&&this._geometriesIntersect(o,m)},u=(g,m)=>{g.getLayers().forEach(b=>{if(b instanceof Te)u(b,b.get("title")||m);else if(b instanceof O&&b.getVisible()){const _=b.get("title")||m||"Unknown",S=b.getSource();if(!S)return;const T=S.getFeaturesInExtent(a);for(const v of T){const x=v.get("_layerType");x==="measure_circle"||x==="measure_circle_radius"||d(v)&&(x==="parcel"?s.push(v):x==="collector_zone"?l.push(v):(c[_]||(c[_]=[]),c[_].push(v)))}}})};u(this.overlayGroup,"Overlays");const p=Pt(i),h=Math.PI*i*i,f=rt(h),y=[{label:"Radius",value:p,_first:!0},{label:"Area",value:f},...this._collectIntersectionRows(s,l,c)];this._showAnalysisPopup("⭕","Circle Analysis",y,t,{kind:"circle",clipGeometry:o,parcelFeatures:s,zoneFeatures:l,otherByLayer:c})}showAreaIntersectionPopup(e,t){const r=e.getGeometry();if(!r)return;const o=r.getExtent(),a=nt(r,{projection:"EPSG:3857"}),i=rt(a),s=Et(r,{projection:"EPSG:3857"}),l=Pt(s),c=[],d=[],u={},p=y=>{const g=y.getGeometry();if(!g)return!1;const m=g.getExtent();return m[2]o[2]||m[3]o[3]?!1:r.intersectsExtent(m)&&this._geometriesIntersect(r,g)},h=(y,g)=>{y.getLayers().forEach(m=>{if(m instanceof Te)h(m,m.get("title")||g);else if(m instanceof O&&m.getVisible()){const b=m.get("title")||g||"Unknown",_=m.getSource();if(!_)return;const S=_.getFeaturesInExtent(o);for(const T of S){const v=T.get("_layerType");v==="measure_area"||v==="measure_circle"||v==="measure_circle_radius"||p(T)&&(v==="parcel"?c.push(T):v==="collector_zone"?d.push(T):(u[b]||(u[b]=[]),u[b].push(T)))}}})};h(this.overlayGroup,"Overlays");const f=[{label:"Area",value:i,_first:!0},{label:"Perimeter",value:l},...this._collectIntersectionRows(c,d,u)];this._showAnalysisPopup("📐","Area Analysis",f,t,{kind:"area",clipGeometry:r,parcelFeatures:c,zoneFeatures:d,otherByLayer:u})}_geometriesIntersect(e,t){const r=t.getType();if(r==="Polygon"||r==="MultiPolygon"){const o=t.getFlatCoordinates(),a=t.getStride();for(let l=0;l + + + + `}const i=` +
+ ✏️ Edit Parcel + +
+
+ ${a} +
+ + +
+
+ `;this.parcelEditElement.innerHTML=i,this.parcelEditPopup.setPosition(t),this.parcelEditElement.querySelector(".parcel-edit-close").addEventListener("click",()=>{this.hideParcelEditPopup()}),this.parcelEditElement.querySelector(".parcel-edit-cancel").addEventListener("click",()=>{this.hideParcelEditPopup()});const s=this.parcelEditElement.querySelector(".parcel-edit-form");s.addEventListener("submit",l=>{l.preventDefault();const c=new FormData(s),d={};for(const[u,p]of c.entries())d[u]=p;d._layerType="parcel";for(const[u,p]of Object.entries(d))this._parcelEditFeature.set(u,p);for(const u of this._parcelEditCallbacks)u(this._parcelEditFeature,d);this.hideParcelEditPopup()})}hideParcelEditPopup(){this.parcelEditPopup.setPosition(void 0),this._parcelEditFeature=null}onParcelEdit(e){this._parcelEditCallbacks.push(e)}createMergePopup(){this.mergePopupElement=document.createElement("div"),this.mergePopupElement.className="map-merge-popup",this.mergePopupElement.style.cssText=` + position: absolute; + background: var(--card, #fff); + color: var(--card-foreground, #1e1a4b); + border-radius: 10px; + box-shadow: 0 4px 20px rgba(0,0,0,0.3); + font-family: var(--font-body, 'Exo', sans-serif); + font-size: 13px; + min-width: 280px; + max-width: 360px; + z-index: 1002; + border: 2px solid #10b981; + overflow: hidden; + display: flex; + flex-direction: column; + `,this.mergePopup=new _e({element:this.mergePopupElement,positioning:"bottom-center",offset:[0,-10],stopEvent:!0,autoPan:!0,autoPanAnimation:{duration:250}}),this.map.addOverlay(this.mergePopup)}showMergeIdentifierPopup(e,t,r,o){const a=["UPN","upn","id","parcelid","parcel_id","PARCELID","PARCEL_ID","ID"],i=f=>{for(const y of a)if(f[y]!==void 0&&f[y]!==null&&String(f[y]).trim())return{field:y,value:String(f[y])};return{field:"id",value:"Unknown"}},s=i(t),l=i(r),c=` +
+ 🔗 Merged Parcel — Choose Identifier + +
+
+

+ Select which parcel's attributes the merged polygon should keep: +

+ + +
+ + +
+
+ `;this.mergePopupElement.innerHTML=c,this.mergePopup.setPosition(o);const d=()=>{this.mergePopup.setPosition(void 0)};this.mergePopupElement.querySelector(".merge-popup-close").addEventListener("click",d),this.mergePopupElement.querySelector(".merge-popup-cancel").addEventListener("click",d),this.mergePopupElement.querySelector(".merge-popup-confirm").addEventListener("click",()=>{const y=this.mergePopupElement.querySelector('input[name="merge-choice"]:checked').value==="A"?t:r,g=["geometry"];for(const[m,b]of Object.entries(y))g.includes(m)||e.set(m,b);e.set("_layerType","parcel");for(const m of this._parcelEditCallbacks)m(e,y);d()});const u=this.mergePopupElement.querySelectorAll("label"),p=this.mergePopupElement.querySelectorAll('input[name="merge-choice"]'),h=()=>{u.forEach(f=>{const y=f.querySelector("input");f.style.borderColor=y.checked?y.value==="A"?"#0ea5e9":"#f59e0b":"var(--border, #1e1a4b1f)"})};p.forEach(f=>f.addEventListener("change",h)),h()}createDividePopup(){this.dividePopupElement=document.createElement("div"),this.dividePopupElement.className="map-divide-popup",this.dividePopupElement.style.cssText=` + position: absolute; + background: var(--card, #fff); + color: var(--card-foreground, #1e1a4b); + border-radius: 10px; + box-shadow: 0 4px 20px rgba(0,0,0,0.3); + font-family: var(--font-body, 'Exo', sans-serif); + font-size: 13px; + min-width: 260px; + max-width: 320px; + z-index: 1002; + border: 2px solid #8b5cf6; + overflow: hidden; + display: flex; + flex-direction: column; + `,this.dividePopup=new _e({element:this.dividePopupElement,positioning:"bottom-center",offset:[0,-10],stopEvent:!0,autoPan:!0,autoPanAnimation:{duration:250}}),this.map.addOverlay(this.dividePopup)}showDividePopup(e,t,r){const o=` +
+ Divide Polygon + +
+
+

+ Enter the number of equal pieces: +

+ +
+ + +
+
+ `;this.dividePopupElement.innerHTML=o,this.dividePopup.setPosition(r);const a=this.dividePopupElement.querySelector(".divide-input");a.focus(),a.select();const i=()=>{this.hideDividePopup(),this._polygonDivideInteraction.cancelDivide()};this.dividePopupElement.querySelector(".divide-popup-close").addEventListener("click",i),this.dividePopupElement.querySelector(".divide-popup-cancel").addEventListener("click",i),this.dividePopupElement.querySelector(".divide-popup-confirm").addEventListener("click",()=>{const s=parseInt(a.value,10);if(!s||s<2){a.style.borderColor="#ef4444";return}this.hideDividePopup(),this._polygonDivideInteraction.performDivide(s)}),a.addEventListener("keydown",s=>{s.key==="Enter"&&(s.preventDefault(),this.dividePopupElement.querySelector(".divide-popup-confirm").click())})}hideDividePopup(){this.dividePopup.setPosition(void 0)}createDrawnPolygonPopup(){this.drawnPolygonElement=document.createElement("div"),this.drawnPolygonElement.className="map-drawn-polygon-popup",this.drawnPolygonElement.style.cssText=` + position: absolute; + background: var(--card, #fff); + border-radius: var(--radius-xl, 0.75rem); + box-shadow: 0 4px 20px rgba(0,0,0,0.2); + font-family: var(--font-body, 'Exo', sans-serif); + font-size: 13px; + min-width: 280px; + max-width: 360px; + max-height: 420px; + z-index: 1002; + border: 2px solid var(--success, #006b3f); + overflow: hidden; + display: flex; + flex-direction: column; + `,this.drawnPolygonPopup=new _e({element:this.drawnPolygonElement,positioning:"bottom-center",offset:[0,-10],stopEvent:!0,autoPan:!0,autoPanAnimation:{duration:250}}),this.map.addOverlay(this.drawnPolygonPopup),this._drawnPolygonCallbacks=[],this._drawnPolygonFeature=null}getParcelAttributeKeys(){const e=["geometry","_layerType"],t=[],r=o=>{t.length>0||o.getLayers().forEach(a=>{if(!(t.length>0)){if(a instanceof Te)r(a);else if(a instanceof O){const i=a.getSource();if(!i)return;for(const s of i.getFeatures()){if(s.get("_layerType")!=="parcel")continue;const l=s.getProperties();for(const c of Object.keys(l))e.includes(c)||t.push(c);return}}}})};return r(this.overlayGroup),t}showDrawnPolygonPopup(e,t){this._drawnPolygonFeature=e;const r=this.getParcelAttributeKeys();if(r.length===0){console.warn("[MapView] No parcel attributes found — cannot build form");return}let o="";for(const d of r){const u=this.escapeHtml(d);o+=` +
+ + +
+ `}const a=e.getGeometry(),i=nt(a,{projection:"EPSG:3857"}),l=` +
+ 📐 Polygon Attributes + +
+
+ Area: ${rt(i)} +
+
+ ${o} +
+ + +
+
+ `;this.drawnPolygonElement.innerHTML=l,this.drawnPolygonPopup.setPosition(t),this.drawnPolygonElement.querySelector(".drawn-polygon-close").addEventListener("click",()=>{this.hideDrawnPolygonPopup()}),this.drawnPolygonElement.querySelector(".drawn-polygon-cancel").addEventListener("click",()=>{this.hideDrawnPolygonPopup()});const c=this.drawnPolygonElement.querySelector(".drawn-polygon-form");c.addEventListener("submit",d=>{d.preventDefault();const u=new FormData(c),p={};for(const[h,f]of u.entries())p[h]=f;for(const[h,f]of Object.entries(p))this._drawnPolygonFeature.set(h,f);this._drawnPolygonFeature.set("_layerType","parcel");for(const h of this._drawnPolygonCallbacks)h(this._drawnPolygonFeature,p);this.hideDrawnPolygonPopup()})}hideDrawnPolygonPopup(){this.drawnPolygonPopup.setPosition(void 0),this._drawnPolygonFeature=null}onDrawnPolygonSave(e){this._drawnPolygonCallbacks.push(e)}onDblClick(e){return this.dblClickCallbacks.push(e),this.dblClickCallbacks.length===1&&this.map.on("dblclick",t=>{const[r,o]=Me(t.coordinate);let a=null;this.map.forEachFeatureAtPixel(t.pixel,i=>(a=i,!0)),a&&(t.preventDefault(),t.stopPropagation());for(const i of this.dblClickCallbacks)i(r,o,a,t);if(a)return!1}),()=>{const t=this.dblClickCallbacks.indexOf(e);t>-1&&this.dblClickCallbacks.splice(t,1)}}escapeHtml(e){if(!e)return"";const t=document.createElement("div");return t.textContent=e,t.innerHTML}createAddLocationPopup(){this.addLocationPopupElement=document.createElement("div"),this.addLocationPopupElement.className="map-add-location-popup",this.addLocationPopupElement.innerHTML=` +
+ ➕ Add Location + +
+
+
+ + +
+
+ + +
+
+ + +
+
+ 📍 +
+ +
+ `,this.addLocationPopup=new _e({element:this.addLocationPopupElement,positioning:"bottom-center",offset:[0,-10],stopEvent:!0,autoPan:!0,autoPanAnimation:{duration:250}}),this.map.addOverlay(this.addLocationPopup),this.addLocationCoords=null,this.addLocationPopupElement.querySelector(".add-location-popup-close").addEventListener("click",()=>{this.hideAddLocationPopup()}),this.addLocationCallbacks=[]}showAddLocationPopup(e){const[t,r]=Me(e);this.addLocationCoords={lon:t,lat:r};const o=this.addLocationPopupElement.querySelector("#map-location-coords");o.textContent=`${t.toFixed(6)}, ${r.toFixed(6)}`,this.addLocationPopupElement.querySelector("#map-add-location-form").reset(),this.addLocationPopup.setPosition(e)}hideAddLocationPopup(){this.addLocationPopup.setPosition(void 0),this.addLocationCoords=null}onAddLocation(e){if(this.addLocationCallbacks.push(e),this.addLocationCallbacks.length===1){const t=this.addLocationPopupElement.querySelector("#map-add-location-form");t.addEventListener("submit",r=>{if(r.preventDefault(),!this.addLocationCoords)return;const o=new FormData(t),a={name:o.get("name"),category:o.get("category"),description:o.get("description"),lon:this.addLocationCoords.lon,lat:this.addLocationCoords.lat};this.addLocationCallbacks.forEach(i=>i(a)),this.hideAddLocationPopup()})}}createBaseLayers(e){const t=new ne({title:"Topographic",type:"base",zIndex:-100,visible:e==="topo",source:new Le({url:"https://{a-c}.tile.opentopomap.org/{z}/{x}/{y}.png",attributions:"Map data: © OpenTopoMap",maxZoom:17,crossOrigin:"anonymous"})});t.set("basemapKey","topo");const r=new ne({title:"Carto Light",type:"base",zIndex:-100,visible:e==="carto-light",source:new Le({url:"https://{a-c}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png",attributions:"© CARTO",maxZoom:19,crossOrigin:"anonymous"})});r.set("basemapKey","carto-light");const o=new ne({title:"Carto Dark",type:"base",zIndex:-100,visible:e==="carto-dark",source:new Le({url:"https://{a-c}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}.png",attributions:"© CARTO",maxZoom:19,crossOrigin:"anonymous"})});o.set("basemapKey","carto-dark");const a=new ne({title:"OSM Cycle map",type:"base",zIndex:-100,visible:!1,source:new Po({url:"https://tile.thunderforest.com/cycle/{z}/{x}/{y}.png?apikey=ae1339c46dd3446b9c491e7336d38760"})});a.set("basemapKey","cycle");const i=new ne({title:"Satellite",type:"base",zIndex:-100,visible:e==="satellite",source:new Le({url:"https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}",attributions:"Tiles © Esri",maxZoom:19,crossOrigin:"anonymous"})});i.set("basemapKey","satellite");const s=new ne({title:"Google Sat",type:"base",zIndex:-100,visible:e==="googlesat",source:new Le({url:"http://mt0.google.com/vt/lyrs=y&hl=en&x={x}&y={y}&z={z}&s=Ga",attributions:"Tiles © Google",maxZoom:19,crossOrigin:"anonymous"})});s.set("basemapKey","googlesat");const l=new ne({title:"OpenStreetMap",type:"base",zIndex:-100,visible:e==="osm",source:new Po});l.set("basemapKey","osm"),this._baseMapLayers=[r,o,a,i,s,l,t];const c=new Te({title:"Base Maps",layers:[r,o,i,a,s,l,t]});return c.set("displayInLayerSwitcher",!1),c}setBaseMap(e){if(!this._baseMapLayers)return!1;if(e==="none"){for(const r of this._baseMapLayers)r.setVisible(!1);return console.log("[MapView] Base map switched off (none)"),this.map.dispatchEvent({type:"basemapchange",key:"none"}),!0}let t=!1;for(const r of this._baseMapLayers){const o=r.get("basemapKey")===e;r.setVisible(o),o&&(t=!0)}return t&&(console.log("[MapView] Base map switched to:",e),this.map.dispatchEvent({type:"basemapchange",key:e})),t}_createBaseMapPicker(){const e=[{key:"topo",label:"Topographic",grad:"linear-gradient(135deg,#e8d5b7,#a67c52)"},{key:"osm",label:"OpenStreetMap",grad:"linear-gradient(135deg,#d4e6f1,#85c1e9)"},{key:"satellite",label:"Satellite",grad:"linear-gradient(135deg,#1b4332,#40916c)"},{key:"googlesat",label:"Google Sat",grad:"linear-gradient(135deg,#2a5d3d,#4a8c5a)"},{key:"carto-light",label:"Carto Light",grad:"linear-gradient(135deg,#f5f5f5,#d4d4d4)"},{key:"carto-dark",label:"Carto Dark",grad:"linear-gradient(135deg,#1a1a2e,#0f3460)"},{key:"none",label:"None",grad:"repeating-conic-gradient(#e5e7eb 0 25%, #fff 0 50%) 50% / 12px 12px"}],t=this.map.getTargetElement();if(!t)return;const r=document.createElement("button");r.type="button",r.className="ls-basemap-toggle",r.title="Switch base map",r.setAttribute("aria-label","Switch base map"),r.innerHTML='',t.appendChild(r);const o=document.createElement("div");o.className="ls-basemap-panel",o.innerHTML='
Base Map
'+e.map(i=>` + + `).join("")+"
",t.appendChild(o),this._basemapPanel=o,this._basemapToggle=r;const a=i=>{const s=i||this._baseMapLayers?.find(l=>l.getVisible())?.get("basemapKey");o.querySelectorAll('input[name="lupmis-basemap"]').forEach(l=>{l.checked=l.value===s})};a(),r.addEventListener("click",i=>{i.stopPropagation();const s=!o.classList.contains("open");o.classList.toggle("open",s),r.classList.toggle("active",s),s&&a()}),document.addEventListener("click",i=>{o.classList.contains("open")&&(o.contains(i.target)||r.contains(i.target)||(o.classList.remove("open"),r.classList.remove("active")))}),o.addEventListener("change",i=>{const s=i.target.closest('input[type=radio][name="lupmis-basemap"]');if(!s)return;const l=s.value;this.setBaseMap(l);try{localStorage.setItem("default-basemap",l)}catch{}o.classList.remove("open"),r.classList.remove("active")}),this.map.on("basemapchange",i=>a(i.key))}_initGpsRendering(){this._gpsPositionSource=new G,this._gpsTrailSource=new G,this._gpsTrailCoords=[],this._gpsTrailLayer=new O({source:this._gpsTrailSource,zIndex:940,style:new M({stroke:new I({color:"#ff6d00",width:4,lineCap:"round",lineJoin:"round"})}),properties:{title:"GPS Trail",displayInLayerSwitcher:!1}}),this._gpsPositionLayer=new O({source:this._gpsPositionSource,zIndex:950,style:e=>e.get("_kind")==="accuracy"?new M({fill:new A({color:"rgba(0,94,184,0.12)"}),stroke:new I({color:"rgba(0,94,184,0.35)",width:1})}):new M({image:new ue({radius:7,fill:new A({color:"#005eb8"}),stroke:new I({color:"#ffffff",width:2.5})})}),properties:{title:"GPS Position",displayInLayerSwitcher:!1}}),this.map.addLayer(this._gpsTrailLayer),this.map.addLayer(this._gpsPositionLayer),this._gpsCallbacks={locate:[],record:[]},this._gpsRecording=!1}onLocateMe(e){this._gpsCallbacks.locate.push(e)}onToggleRecording(e){this._gpsCallbacks.record.push(e)}showCurrentPosition(e,t,r=null){if(e==null||t==null)return;const o=Q([e,t]);if(this._gpsPositionSource.clear(),r&&r>0){const i=r/Math.cos(t*Math.PI/180),s=new de({geometry:new it([this._circleRing(o,i)])});s.set("_kind","accuracy"),this._gpsPositionSource.addFeature(s)}const a=new de({geometry:new ut(o)});a.set("_kind","dot"),this._gpsPositionSource.addFeature(a)}_circleRing(e,t,r=48){const o=[],i=t/1;for(let s=0;s<=r;s++){const l=s/r*2*Math.PI;o.push([e[0]+i*Math.cos(l),e[1]+i*Math.sin(l)])}return o}centerOn(e,t,r=16){this.map.getView().animate({center:Q([e,t]),zoom:r,duration:500})}startTrailRender(){this._gpsTrailCoords=[],this._gpsTrailSource.clear()}appendTrailPoint(e,t){e==null||t==null||(this._gpsTrailCoords.push(Q([e,t])),this._gpsTrailSource.clear(),this._gpsTrailCoords.length>=2&&this._gpsTrailSource.addFeature(new de({geometry:new pe(this._gpsTrailCoords)})))}clearTrailRender(){this._gpsTrailCoords=[],this._gpsTrailSource.clear()}setRecordingState(e){this._gpsRecording=!!e,this._recordBtn&&(this._recordBtn.classList.toggle("recording",this._gpsRecording),this._recordBtn.title=this._gpsRecording?"Stop trail recording":"Record GPS trail",this._recordBtn.innerHTML=this._gpsRecording?'':''),this._locateToggle&&this._locateToggle.classList.toggle("recording",this._gpsRecording)}_createLocationControl(){const e=this.map.getTargetElement();if(!e)return;const t=document.createElement("button");t.type="button",t.className="ls-locate-toggle",t.title="My Location",t.setAttribute("aria-label","My Location"),t.innerHTML='',e.appendChild(t);const r=document.createElement("div");r.className="ls-locate-actions",r.innerHTML='',e.appendChild(r),this._locateToggle=t,this._locateActions=r,this._locateMeBtn=r.querySelector(".ls-locate-me"),this._recordBtn=r.querySelector(".ls-locate-record");const o=()=>{r.classList.remove("open"),t.classList.remove("active")},a=()=>{r.classList.add("open"),t.classList.add("active")};t.addEventListener("click",i=>{i.stopPropagation(),r.classList.contains("open")?o():a()}),document.addEventListener("click",i=>{r.classList.contains("open")&&(r.contains(i.target)||t.contains(i.target)||this._gpsRecording||o())}),this._locateMeBtn.addEventListener("click",i=>{i.stopPropagation();for(const s of this._gpsCallbacks.locate)try{s()}catch(l){console.error(l)}this._gpsRecording||o()}),this._recordBtn.addEventListener("click",i=>{i.stopPropagation();const s=!this._gpsRecording;for(const l of this._gpsCallbacks.record)try{l(s)}catch(c){console.error(c)}})}getFeatureStyle(e){const t=e.get("category")||"default",r=this.getEmoji(t);if(e===this.selectedFeature)return[new M({image:new ue({radius:22,fill:new A({color:"rgba(220, 38, 38, 0.25)"}),stroke:new I({color:"#dc2626",width:3})})}),new M({text:new st({text:r,font:"40px sans-serif",textBaseline:"bottom",textAlign:"center",offsetY:-5})})];const o=e.get("style");return o||(this.categoryStyles[t]?this.categoryStyles[t]:this.defaultStyle)}setCategoryStyles(e){for(const[t,r]of Object.entries(e)){r.emoji&&(this.categoryEmojis[t]?(this.categoryEmojis[t].emoji=r.emoji,r.label&&(this.categoryEmojis[t].label=r.label)):this.categoryEmojis[t]={emoji:r.emoji,label:r.label||t});const o=this.getEmoji(t),a=r.fontSize||28;this.categoryStyles[t]=this.createEmojiStyle(o,a)}this.markerSource.changed()}addMarker(e,t,r={}){console.log("[MapView] Adding marker at",e,t,"with properties:",r);const o=new de({geometry:new ut(Q([e,t])),...r});return o.set("lon",e),o.set("lat",t),this.markerSource.addFeature(o),console.log("[MapView] Marker added, total features:",this.markerSource.getFeatures().length),o}addMarkers(e){console.log("[MapView] Adding",e.length,"markers");const t=e.map(r=>new de({geometry:new ut(Q([r.longitude,r.latitude])),id:r.id,name:r.name,description:r.description,category:r.category,lon:r.longitude,lat:r.latitude}));return this.markerSource.addFeatures(t),console.log("[MapView] Markers added, total features:",this.markerSource.getFeatures().length),t}clearMarkers(){this.markerSource.clear(),this.selectedFeature=null}removeMarker(e){if(typeof e=="object")this.markerSource.removeFeature(e);else{const t=this.markerSource.getFeatures().find(r=>r.get("id")===e);t&&this.markerSource.removeFeature(t)}}getMarkers(){return this.markerSource.getFeatures()}findMarker(e){return this.markerSource.getFeatures().find(t=>t.get("id")===e)}selectMarker(e){return typeof e=="object"?this.selectedFeature=e:this.selectedFeature=this.findMarker(e),this.markerSource.changed(),this.selectedFeature}clearSelection(){this.selectedFeature=null,this.markerSource.changed()}zoomTo(e,t,r=15){this.map.getView().animate({center:Q([e,t]),zoom:r,duration:500})}fitToMarkers(e=50){const t=this.markerSource.getExtent();t&&t[0]!==1/0&&this.map.getView().fit(t,{padding:[e,e,e,e],duration:500,maxZoom:16})}getCenter(){const e=this.map.getView().getCenter();return Me(e)}getZoom(){return this.map.getView().getZoom()}setCenter(e,t){this.map.getView().setCenter(Q([e,t]))}setZoom(e){this.map.getView().setZoom(e)}onClick(e){return this.clickCallbacks.push(e),this.clickCallbacks.length===1&&(this._clickTimer=null,this.map.on("dblclick",()=>{this._clickTimer&&(clearTimeout(this._clickTimer),this._clickTimer=null)}),this.map.on("click",t=>{this._clickTimer&&(clearTimeout(this._clickTimer),this._clickTimer=null),!this._editBarActive&&this._selectInteraction&&this._selectInteraction.getFeatures().clear();let r=!1,o=!1,a=null;if(this.map.forEachFeatureAtPixel(t.pixel,l=>{l.get("_layerType")==="parcel"&&(o=!0),l.get("name")&&(a=l),r=!0}),r&&!o&&!a)return;const[i,s]=Me(t.coordinate);this._clickTimer=setTimeout(()=>{this._clickTimer=null;let l=null;this.map.forEachFeatureAtPixel(t.pixel,c=>{if(c.get("name"))return l=c,!0});for(const c of this.clickCallbacks)c(i,s,l,t)},300)})),()=>{const t=this.clickCallbacks.indexOf(e);t>-1&&this.clickCallbacks.splice(t,1)}}onPointerMove(e){this.map.on("pointermove",t=>{if(t.dragging)return;const[r,o]=Me(t.coordinate);let a=null;this.map.forEachFeatureAtPixel(t.pixel,i=>{if(i.get("name"))return a=i,!0}),this.map.getTargetElement().style.cursor=a?"pointer":"",e(r,o,a,t)})}enableHoverCursor(){}addGeoJSONLayer(e,t,r={},o=null){const{strokeColor:a="#3b82f6",strokeWidth:i=2,strokeDash:s=null,fillColor:l="rgba(59,130,246,0.1)",lineCasingColor:c=null,lineCasingWidth:d=null,pointRadius:u=5,pointFillColor:p=null,pointStrokeColor:h="#ffffff",pointStrokeWidth:f=1.5}=r,y=new G({features:new ie().readFeatures(e,{featureProjection:"EPSG:3857"})}),g=new A({color:l}),m=new ue({radius:u,fill:new A({color:p||a}),stroke:new I({color:h,width:f})}),b=new I({color:a,width:i,...s?{lineDash:s}:{}});let _;if(c){const x=d??i+2;_=[new M({stroke:new I({color:c,width:x})}),new M({stroke:b,fill:g,image:m})]}else _=new M({stroke:b,fill:g,image:m});const S=new O({title:t,source:y,style:_});S.set("typeTag",r.typeTag||"VEC");const T=x=>x?x.includes("Polygon")?"Vector / Polygon":x.includes("LineString")?"Vector / Line":x.includes("Point")?"Vector / Point":"Vector":null;if(r.typeDescription)S.set("typeDescription",r.typeDescription);else{const x=y.getFeatures(),P=T(x[0]?.getGeometry?.()?.getType?.());if(P)S.set("typeDescription",P);else{const k=q=>{const U=T(q.feature.getGeometry?.()?.getType?.());U&&S.set("typeDescription",U),y.un("addfeature",k)};y.on("addfeature",k)}}return(o||this.overlayGroup).getLayers().push(S),console.log("[MapView] GeoJSON layer added:",t,"→",y.getFeatures().length,"features",o?`(in group "${o.get("title")}")`:""),S}addLayerGroup(e,t,r=""){const o=new Te({title:t.trim()});return o.set("layerId",e),o.set("description",r),this.overlayGroup.getLayers().push(o),console.log("[MapView] Layer group added:",t.trim(),"(id:",e+")"),o}addWMSLayer(e,t,r,o,a={}){const i=this.getLayerGroupByTitle(e);if(!i)return console.warn(`[MapView] Layer group "${e}" not found — cannot add WMS layer "${t}"`),null;const s={LAYERS:o,TILED:!0,WIDTH:256,HEIGHT:256};a.style!==void 0&&(s.STYLES=a.style);const l=new Mo({url:r,params:s,serverType:a.serverType!==void 0?a.serverType:"geoserver",crossOrigin:"anonymous",hidpi:!1,attributions:a.attributions}),c=new ne({title:t,visible:a.visible!==void 0?a.visible:!0,source:l,opacity:a.opacity!==void 0?a.opacity:1,zIndex:a.zIndex});if(c.set("typeTag","WMS"),c.set("typeDescription","WMS / Raster"),l.on("tileloaderror",()=>{B(`WMS layer "${t}" — tile load error. Check the URL and layer name.`,"warning",5e3)}),i.getLayers().push(c),a.legendUrl)try{this._registerLegend(c,t,a.legendUrl)}catch(d){console.warn(`[MapView] Could not register legend for "${t}":`,d)}return a.onlineOnly&&this._attachOnlineOnlyHandler(c,t),console.log(`[MapView] WMS layer added: "${t}" → group "${e}"`),c}addXYZLayer(e,t,r,o={}){const a=this.getLayerGroupByTitle(e);if(!a)return console.warn(`[MapView] Layer group "${e}" not found — cannot add XYZ layer "${t}"`),null;const i=new Le({url:r,crossOrigin:"anonymous",maxZoom:o.maxZoom!==void 0?o.maxZoom:19,attributions:o.attributions}),s=new ne({title:t,visible:o.visible!==void 0?o.visible:!0,source:i,opacity:o.opacity!==void 0?o.opacity:1,zIndex:o.zIndex});if(s.set("typeTag","XYZ"),s.set("typeDescription","XYZ / Tile"),i.on("tileloaderror",()=>{B(`XYZ layer "${t}" — tile load error. Check the URL.`,"warning",5e3)}),a.getLayers().push(s),o.legendUrl)try{this._registerLegend(s,t,o.legendUrl)}catch(l){console.warn(`[MapView] Could not register legend for "${t}":`,l)}return o.onlineOnly&&this._attachOnlineOnlyHandler(s,t),console.log(`[MapView] XYZ layer added: "${t}" → group "${e}"`),s}_createAddLayerDialog(){this._addLayerDialog=document.createElement("div"),this._addLayerDialog.className="map-add-layer-dialog",this._addLayerDialog.style.cssText=` + display:none;position:absolute;top:0;left:0;right:0;bottom:0; + z-index:1100;background:rgba(0,0,0,0.4); + align-items:center;justify-content:center; + `;const e=document.createElement("div");e.style.cssText=` + background:var(--card, #fff);color:var(--card-foreground, #1e1a4b); + border-radius:12px;box-shadow:0 8px 30px rgba(0,0,0,0.35); + font-family:var(--font-body, 'Exo', sans-serif);font-size:13px; + width:340px;max-width:90vw;border:2px solid #10b981;overflow:hidden; + `,e.innerHTML=` +
+ Add External Layer + +
+
+
+ +
+ + + +
+
+
+ + +
+
+ + +
+ WMS LAYERS parameter (e.g. workspace:layer) +
+
+
+ + +
+
+ + +
+
+ `,this._addLayerDialog.appendChild(e),this.map.getTargetElement().appendChild(this._addLayerDialog);const t=e.querySelector(".add-layer-name-row"),r=e.querySelector(".add-layer-name-hint"),o=e.querySelector(".add-layer-url");e.querySelectorAll('input[name="add-layer-type"]').forEach(i=>{i.addEventListener("change",()=>{const s=i.value;s==="xyz"?(t.style.display="none",o.placeholder="https://example.com/tiles/{z}/{x}/{y}.png"):(t.style.display="",o.placeholder=s==="wms"?"https://example.com/wms":"https://example.com/wfs",r.textContent=s==="wms"?"WMS LAYERS parameter (e.g. workspace:layer)":"WFS typename (e.g. workspace:layer)")})});const a=()=>this._hideAddLayerDialog();e.querySelector(".add-layer-close").addEventListener("click",a),e.querySelector(".add-layer-cancel").addEventListener("click",a),this._addLayerDialog.addEventListener("click",i=>{i.target===this._addLayerDialog&&a()}),e.querySelector(".add-layer-confirm").addEventListener("click",()=>{const i=e.querySelector('input[name="add-layer-type"]:checked').value,s=e.querySelector(".add-layer-url").value.trim(),l=e.querySelector(".add-layer-name").value.trim(),c=e.querySelector(".add-layer-title").value.trim();if(!s){e.querySelector(".add-layer-url").style.borderColor="#ef4444";return}if((i==="wms"||i==="wfs")&&!l){e.querySelector(".add-layer-name").style.borderColor="#ef4444";return}if(!c){e.querySelector(".add-layer-title").style.borderColor="#ef4444";return}this._addExternalLayer(i,s,l,c),this._hideAddLayerDialog()}),e.addEventListener("keydown",i=>{i.key==="Enter"&&(i.preventDefault(),e.querySelector(".add-layer-confirm").click()),i.key==="Escape"&&(i.preventDefault(),a())})}showAddLayerDialog(){const e=this._addLayerDialog;e.querySelector(".add-layer-url").value="",e.querySelector(".add-layer-name").value="",e.querySelector(".add-layer-title").value="",e.querySelectorAll('input[name="add-layer-type"]')[0].checked=!0,e.querySelector(".add-layer-name-row").style.display="",e.querySelector(".add-layer-url").placeholder="https://example.com/wms",e.querySelector(".add-layer-name-hint").textContent="WMS LAYERS parameter (e.g. workspace:layer)",e.querySelectorAll('input[type="text"]').forEach(t=>{t.style.borderColor="var(--border, #1e1a4b1f)"}),e.style.display="flex",e.querySelector(".add-layer-url").focus()}_hideAddLayerDialog(){this._addLayerDialog.style.display="none"}_addExternalLayer(e,t,r,o){const a=this._externalSourceGroup;if(!a){B('Layer group "External Source" not found.',"error",4e3);return}let i;switch(e){case"wms":{const s=new Mo({url:t,params:{LAYERS:r,TILED:!0,WIDTH:256,HEIGHT:256},serverType:"geoserver",crossOrigin:"anonymous",hidpi:!1});i=new ne({title:o,visible:!0,source:s}),s.on("tileloaderror",()=>{B(`WMS "${o}" — tile load error. Check URL and layer name.`,"warning",5e3)});break}case"wfs":{const s=`${t}${t.includes("?")?"&":"?"}service=WFS&version=1.1.0&request=GetFeature&typename=${encodeURIComponent(r)}&outputFormat=application/json&srsname=EPSG:3857`,l=new G({url:s,format:new ie});l.on("featuresloaderror",()=>{B(`WFS "${o}" — load error. Check URL and layer name.`,"warning",5e3)}),i=new O({title:o,visible:!0,source:l,style:new M({stroke:new I({color:"#e11d48",width:2}),fill:new A({color:"rgba(225,29,72,0.15)"})})});break}case"xyz":i=new ne({title:o,visible:!0,source:new Le({url:t,crossOrigin:"anonymous"})}),i.getSource().on("tileloaderror",()=>{B(`XYZ "${o}" — tile load error. Check the URL template.`,"warning",5e3)});break;default:B(`Unknown layer type: ${e}`,"error",4e3);return}i.set("typeTag",e.toUpperCase()),i.set("typeDescription",{wms:"WMS / Raster",wfs:"WFS / Vector",xyz:"XYZ / Tile"}[e]||e.toUpperCase()),i.set("removable",!0),a.getLayers().push(i),B(`Layer "${o}" added to External Source.`,"success",3e3),console.log(`[MapView] External ${e.toUpperCase()} layer added: "${o}"`)}_decorateLayerListItem(e,t){const r=e.get("typeTag");if(r){const c=t.querySelector(":scope > .li-content > label > span");if(c&&!c.querySelector(":scope > .ls-type-tag")){const d=document.createElement("span");d.className=`ls-type-tag ls-type-tag-${String(r).toLowerCase()}`,d.textContent=String(r),d.title=`${r} layer`,c.appendChild(d)}}const o=t.querySelector(":scope > .ol-layerswitcher-buttons");if(o){const c=o.querySelector(":scope > .expend-layers, :scope > .collapse-layers");c&&!c.querySelector(":scope > svg.ls-chevron-svg")&&(c.innerHTML='')}const a=t.querySelector(":scope > .li-content"),i=()=>{if(!a)return;const c=e.get("typeDescription");let d=a.querySelector(":scope > .ls-layer-subtitle");if(!c){d&&d.remove();return}if(!d){d=document.createElement("div"),d.className="ls-layer-subtitle";const u=a.querySelector(":scope > label");u&&u.nextSibling?a.insertBefore(d,u.nextSibling):a.appendChild(d)}d.textContent=c};if(i(),e._lsSubtitleHooked||(e._lsSubtitleHooked=!0,e.on("change:typeDescription",()=>{i()})),e.get("removable")===!0&&o&&!o.querySelector(":scope > .ls-remove-btn")){const c=document.createElement("button");c.type="button",c.className="ls-remove-btn",c.title="Remove this layer",c.setAttribute("aria-label","Remove layer"),c.innerHTML='',c.addEventListener("click",d=>{d.stopPropagation(),this._removeLayer(e)}),o.appendChild(c)}const s=e.get("_externalImportId");if(s!=null){const c=t.querySelector(":scope > .li-content > label > span");let d=c?c.querySelector(":scope > .ls-import-chip"):null;const u=e.get("_externalImportStatus")||"mapped",p=e.getSource()?.getFeatures().length??0,h=e.get("_externalImportErrorCount")??0,f=(()=>{switch(u){case"mapped":return{text:`Upload ${p}`,cls:"ls-import-chip-mapped",title:"Upload this dataset to the database",clickable:!0};case"uploading":return{text:"…",cls:"ls-import-chip-uploading",title:"Uploading…",clickable:!1};case"submitted":return{text:"✓ submitted",cls:"ls-import-chip-submitted",title:"Uploaded — awaiting supervisor review",clickable:!1};case"migrated":return{text:"✓ live",cls:"ls-import-chip-migrated",title:"Approved by supervisor and live on the server",clickable:!1};case"failed":return{text:`${h} errors — fix?`,cls:"ls-import-chip-failed",title:"Some rows failed; click to review",clickable:!0};case"other":case null:case void 0:default:return null}})();if(!f)d&&d.remove();else if(c){d||(d=document.createElement("span"),d.className="ls-import-chip",c.appendChild(d)),d.className=`ls-import-chip ${f.cls}`,d.textContent=f.text,d.title=f.title,d.style.cursor=f.clickable?"pointer":"default",d.style.opacity=f.clickable?"1":"0.85";const y=d.cloneNode(!0);d.replaceWith(y),d=y,f.clickable&&d.addEventListener("click",g=>{g.preventDefault(),g.stopPropagation(),window.dispatchEvent(new CustomEvent("lupmis:import-chip-click",{detail:{importId:s,status:u,layer:e}}))})}}if((e.get("title")||"").toLowerCase().includes("external")&&(this._externalSourceGroup=e,o&&!o.querySelector(".ol-add-layer"))){const c=document.createElement("span");c.className="ol-add-layer",c.title="Add external layer",c.textContent="+",c.style.cssText=` + display:inline-flex !important;align-items:center;justify-content:center; + width:22px !important;height:22px !important;border-radius:50%; + background:#41b6a6 !important;color:#fff !important; + font-size:15px !important;font-weight:700; + cursor:pointer;line-height:1 !important; + margin:0 4px 0 0;vertical-align:middle; + transition:background 0.2s;box-sizing:border-box;border:none; + `,c.addEventListener("mouseenter",()=>{c.style.background="#329686"}),c.addEventListener("mouseleave",()=>{c.style.background="#41b6a6"}),c.addEventListener("click",d=>{d.stopPropagation(),this.showAddLayerDialog()}),o.prepend(c)}}_removeLayer(e){const t=e.get("title")||"this layer";if(!confirm(`Remove "${t}" from the map? + +This only affects the current session — built-in layers cannot be removed.`))return;const r=a=>{const i=a.getLayers();if(i.getArray().includes(e))return i.remove(e),!0;let s=!1;return i.forEach(l=>{!s&&l.getLayers&&(s=r(l))}),s};r(this.overlayGroup)?(console.log(`[MapView] Removed layer "${t}"`),B(`Removed "${t}" from the map.`,"info",3e3)):console.warn(`[MapView] Could not find layer "${t}" in any group`)}_refreshLayerSwitcherChrome(e){const t=e.element?.querySelector(".panel-container"),r=e.element?.querySelector("ul.panel");if(!t||!r)return;let o=t.querySelector(":scope > .ls-active-badge");o||(o=document.createElement("div"),o.className="ls-active-badge",o.innerHTML=` + Layers + 0 active + `,t.insertBefore(o,r));let a=t.querySelector(":scope > .ls-footer-row");a||(a=document.createElement("div"),a.className="ls-footer-row",a.innerHTML=` + — layers total + + `,t.appendChild(a),a.querySelector(".ls-footer-btn").addEventListener("click",s=>{s.stopPropagation(),this._resetAllOverlays()}));const i=this._countLayers();o.querySelector(".ls-active-badge-count").textContent=`${i.activeOverlays} active`,a.querySelector(".ls-footer-note").textContent=`${i.totalOverlays} overlay${i.totalOverlays===1?"":"s"}`}_countLayers(){let e=0,t=0;const r=new Set(["__vertex_highlight__"]),o=a=>{a.getLayers().forEach(i=>{i.get("displayInLayerSwitcher")!==!1&&(r.has(i.get("title"))||(i.getLayers?o(i):(e++,i.getVisible()&&t++)))})};return this.overlayGroup&&o(this.overlayGroup),{totalOverlays:e,activeOverlays:t}}_resetAllOverlays(){const e=new Set(["__vertex_highlight__"]),t=r=>{r.getLayers().forEach(o=>{o.get("displayInLayerSwitcher")!==!1&&(e.has(o.get("title"))||(o.getLayers?t(o):o.setVisible(!1)))})};this.overlayGroup&&t(this.overlayGroup),console.log("[MapView] Reset overlays — all hidden")}_wireLayerSwitcherVisibilityHooks(e){const t=()=>this._refreshLayerSwitcherChrome(e),r=a=>{a._lsVisHooked||(a._lsVisHooked=!0,a.on("change:visible",t))},o=a=>{a.getLayers().forEach(i=>{i.getLayers?(o(i),a._lsAddHooked||(a._lsAddHooked=!0,a.getLayers().on("add",s=>{const l=s.element;l.getLayers?o(l):r(l),t()}))):r(i)})};this.overlayGroup&&o(this.overlayGroup)}_attachOnlineOnlyHandler(e,t){e.set("onlineOnly",!0),e.on("change:visible",()=>{e.getVisible()&&!navigator.onLine&&B(`"${t}" requires an internet connection. Connect to view this layer.`,"info",5e3)})}_createLegendPanel(){this._legendPanel=document.createElement("div"),this._legendPanel.className="map-legend-panel",this._legendPanel.style.cssText=` + position:absolute;right:10px;bottom:40px;z-index:900; + display:none;flex-direction:column;gap:6px; + background:var(--card, #fff);color:var(--card-foreground, #1e1a4b); + border:1px solid var(--border, #1e1a4b1f);border-radius:8px; + box-shadow:0 4px 12px rgba(0,0,0,0.15); + font-family:var(--font-body, 'Exo', sans-serif);font-size:11px; + max-width:220px;max-height:60%;overflow-y:auto; + padding:8px 10px; + `,this.map.getTargetElement().appendChild(this._legendPanel),this._legendEntries=new Io}_registerLegend(e,t,r){if(!this._legendPanel)return;const o=document.createElement("div");o.className="map-legend-entry",o.style.cssText="border-bottom:1px solid var(--border, #1e1a4b1f);padding-bottom:6px;",o.innerHTML=` +
+ ${this._escapeHtml(t)} +
+ ${this._escapeHtml(t)} legend + `,this._legendEntries.set(e,o);const a=()=>{try{this._updateLegendPanel()}catch(i){console.warn("[MapView] legend panel update failed:",i)}};e.on("change:visible",a),a()}_updateLegendPanel(){if(!this._legendPanel)return;const e=[];for(const[t,r]of this._legendEntries)t.getVisible()&&e.push(r);this._legendEntries.forEach(t=>{t.style.borderBottom="1px solid var(--border, #1e1a4b1f)",t.style.paddingBottom="6px"}),e.length>0&&(e[e.length-1].style.borderBottom="none",e[e.length-1].style.paddingBottom="0"),this._legendPanel.replaceChildren(...e),this._legendPanel.style.display=e.length>0?"flex":"none"}_escapeHtml(e){return String(e).replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'")}getLayerGroup(e){let t=null;return this.overlayGroup.getLayers().forEach(r=>{r.get("layerId")===e&&(t=r)}),t}getLayerGroupByTitle(e){let t=null;return this.overlayGroup.getLayers().forEach(r=>{r.get("title")===e&&(t=r)}),t}getOverlayGroup(){return this.overlayGroup}getMap(){return this.map}getCurrentViewExtent(){const e=this.map.getView(),t=this.map.getSize();return t?e.calculateExtent(t):null}getDistrictBoundaryExtent(){let e=null;const t=r=>{r.getLayers().forEach(o=>{if(o.getLayers)t(o);else if(o.get("title")==="District Boundary"){const a=o.getSource&&o.getSource();if(a&&typeof a.getExtent=="function"){const i=a.getExtent();i&&Number.isFinite(i[0])&&(e={extent:i,title:o.get("title")})}}})};return t(this.overlayGroup),e}getMarkerSource(){return this.markerSource}getMarkersLayer(){return this.markersLayer}updateSize(){this.map.updateSize()}onSearchSelect(e){this.searchSelectCallbacks.push(e)}navigateTo(e,t,r=14,o=500){const a=Q([e,t]);this.map.getView().animate({center:a,zoom:r,duration:o})}}class pi{constructor(e,t={}){this.map=e,this.options=t,this.measureSource=new G,this.measureLayer=new O({source:this.measureSource,style:this.getMeasureStyle(),title:"Measurements",zIndex:100}),this.drawSource=new G,this.drawLayer=new O({source:this.drawSource,style:this.getDrawStyle(),title:"Draw sketches",displayInLayerSwitcher:!1,zIndex:99});const r=this.map.getLayers();let o=r.getArray().findIndex(a=>a.get("title")==="Overlays");o<0&&(o=r.getLength()),r.insertAt(o,this.drawLayer),r.insertAt(o,this.measureLayer),this.activeInteraction=null,this.measureTooltip=null,this.measureTooltipElement=null,this.onMeasureCompleteCallbacks=[],this.onDrawCompleteCallbacks=[]}getMeasureStyle(){return new M({fill:new A({color:"rgba(255, 233, 106, 0.2)"}),stroke:new I({color:"#8B008B",lineDash:[10,10],width:2}),image:new ue({radius:5,stroke:new I({color:"#8B008B"}),fill:new A({color:"rgba(255, 233, 106, 0.5)"})})})}getDrawStyle(){return new M({fill:new A({color:"rgba(255, 233, 106, 0.3)"}),stroke:new I({color:"#8B008B",width:2}),image:new ue({radius:6,stroke:new I({color:"#8B008B",width:2}),fill:new A({color:"#FFE96A"})})})}createMeasureTooltip(){this.measureTooltipElement&&this.measureTooltipElement.parentNode.removeChild(this.measureTooltipElement),this.measureTooltipElement=document.createElement("div"),this.measureTooltipElement.className="measure-tooltip",this.measureTooltip=new _e({element:this.measureTooltipElement,offset:[15,0],positioning:"center-left",stopEvent:!1}),this.map.addOverlay(this.measureTooltip)}deactivate(){this.activeInteraction&&(this.map.removeInteraction(this.activeInteraction),this.activeInteraction=null),this.measureTooltip&&(this.map.removeOverlay(this.measureTooltip),this.measureTooltip=null),this.measureTooltipElement&&this.measureTooltipElement.parentNode&&(this.measureTooltipElement.parentNode.removeChild(this.measureTooltipElement),this.measureTooltipElement=null)}startCircleMeasure(){this.deactivate(),this.createMeasureTooltip();const e=new Pe({source:this.measureSource,type:"Circle",style:new M({fill:new A({color:"rgba(255, 233, 106, 0.2)"}),stroke:new I({color:"rgba(139, 0, 139, 0.7)",lineDash:[10,10],width:2}),image:new ue({radius:5,stroke:new I({color:"rgba(139, 0, 139, 0.7)"}),fill:new A({color:"rgba(255, 233, 106, 0.5)"})})})});this.activeInteraction=e,this.map.addInteraction(e);let t;return e.on("drawstart",r=>{t=r.feature.getGeometry().on("change",a=>{const i=a.target;if(i instanceof Ar){const s=i.getRadius(),l=Na(s),d=`${Pt(s)}
${l}`;this.measureTooltipElement.innerHTML=d,this.measureTooltip.setPosition(i.getLastCoordinate())}})}),e.on("drawend",r=>{const o=r.feature,a=o.getGeometry(),i=a.getCenter(),s=a.getRadius();o.set("_layerType","measure_circle"),o.set("_radius",s),o.set("_center",i);const l=new de({geometry:new pe([i,[i[0]+s,i[1]]])});l.set("_layerType","measure_circle_radius"),this.measureSource.addFeature(l),this.measureTooltipElement.className="measure-tooltip measure-tooltip-static",this.measureTooltip.setOffset([0,-7]),this.measureTooltipElement=null,this.createMeasureTooltip(),Gt(t);const c={type:"circle",center:i,radius:s,area:Math.PI*s*s,feature:o};this.onMeasureCompleteCallbacks.forEach(d=>d(c))}),e}startLineMeasure(){this.deactivate(),this.createMeasureTooltip();const e=new Pe({source:this.measureSource,type:"LineString",style:this.getMeasureStyle()});this.activeInteraction=e,this.map.addInteraction(e);let t;return e.on("drawstart",r=>{t=r.feature.getGeometry().on("change",a=>{const i=a.target,s=Et(i),l=Pt(s);this.measureTooltipElement.innerHTML=l,this.measureTooltip.setPosition(i.getLastCoordinate())})}),e.on("drawend",r=>{const o=r.feature,a=o.getGeometry(),i=Et(a);this.measureTooltipElement.className="measure-tooltip measure-tooltip-static",this.measureTooltipElement=null,this.createMeasureTooltip(),Gt(t);const s={type:"line",length:i,feature:o};this.onMeasureCompleteCallbacks.forEach(l=>l(s))}),e}startAreaMeasure(){this.deactivate(),this.createMeasureTooltip();const e=new Pe({source:this.measureSource,type:"Polygon",style:this.getMeasureStyle()});this.activeInteraction=e,this.map.addInteraction(e);let t;return e.on("drawstart",r=>{t=r.feature.getGeometry().on("change",a=>{const i=a.target,s=nt(i),l=rt(s);this.measureTooltipElement.innerHTML=l,this.measureTooltip.setPosition(i.getInteriorPoint().getCoordinates())})}),e.on("drawend",r=>{const o=r.feature,a=o.getGeometry(),i=nt(a);o.set("_layerType","measure_area"),o.set("_area",i),this.measureTooltipElement.className="measure-tooltip measure-tooltip-static",this.measureTooltipElement=null,this.createMeasureTooltip(),Gt(t);const s={type:"polygon",area:i,feature:o,coordinate:a.getInteriorPoint().getCoordinates()};this.onMeasureCompleteCallbacks.forEach(l=>l(s))}),e}startDrawPoint(){this.deactivate();const e=new Pe({source:this.drawSource,type:"Point",style:this.getDrawStyle()});return this.activeInteraction=e,this.map.addInteraction(e),e.on("drawend",t=>{const r={type:"point",feature:t.feature};this.onDrawCompleteCallbacks.forEach(o=>o(r))}),e}startDrawLine(){this.deactivate();const e=new Pe({source:this.drawSource,type:"LineString",style:this.getDrawStyle()});return this.activeInteraction=e,this.map.addInteraction(e),e.on("drawend",t=>{const r={type:"line",feature:t.feature};this.onDrawCompleteCallbacks.forEach(o=>o(r))}),e}startDrawPolygon(){this.deactivate();const e=new Pe({source:this.drawSource,type:"Polygon",style:this.getDrawStyle()});return this.activeInteraction=e,this.map.addInteraction(e),e.on("drawend",t=>{const r={type:"polygon",feature:t.feature};this.onDrawCompleteCallbacks.forEach(o=>o(r))}),e}clearMeasurements(){this.measureSource.clear(),document.querySelectorAll(".measure-tooltip-static").forEach(t=>t.parentNode.removeChild(t))}clearDrawings(){this.drawSource.clear()}clearAll(){this.clearMeasurements(),this.clearDrawings()}onMeasureComplete(e){this.onMeasureCompleteCallbacks.push(e)}onDrawComplete(e){this.onDrawCompleteCallbacks.push(e)}createControlBar(e={}){e.position;const t=new ao({group:!0,className:"map-tools-bar"}),r=new ao({toggleOne:!0,group:!0}),o=new ve({html:'',title:"Measure Circle (radius & area)",className:"measure-circle-btn",onToggle:l=>{l?this.startCircleMeasure():this.deactivate()}});r.addControl(o);const a=new ve({html:'📏',title:"Measure Distance",className:"measure-line-btn",onToggle:l=>{l?this.startLineMeasure():this.deactivate()}});r.addControl(a);const i=new ve({html:'',title:"Measure Area",className:"measure-area-btn",onToggle:l=>{l?this.startAreaMeasure():this.deactivate()}});r.addControl(i);const s=new et({html:'🗑️',title:"Clear Measurements",className:"clear-measure-btn",handleClick:()=>{this.clearMeasurements(),o.setActive(!1),a.setActive(!1),i.setActive(!1)}});return r.addControl(s),t.addControl(r),t}getMeasureLayer(){return this.measureLayer}getDrawLayer(){return this.drawLayer}getMeasureSource(){return this.measureSource}getDrawSource(){return this.drawSource}isActive(){return this.activeInteraction!==null}}let Ce=null;async function fi(){if(!("serviceWorker"in navigator))return console.warn("[PWA] Service Workers not supported"),null;try{return Ce=await navigator.serviceWorker.register("/sw.js",{scope:"/"}),console.log("[PWA] Service Worker registered:",Ce.scope),Ce.addEventListener("updatefound",()=>{const n=Ce.installing;n.addEventListener("statechange",()=>{n.state==="installed"&&navigator.serviceWorker.controller&&(console.log("[PWA] New version available"),yi())})}),Ce}catch(n){return console.error("[PWA] Service Worker registration failed:",n),null}}let Re=null,be=null;function hi(n="#install-btn"){if(be=typeof n=="string"?document.querySelector(n):n,!be){console.warn("[PWA] Install button not found:",n);return}be.style.display="none",window.addEventListener("beforeinstallprompt",e=>{e.preventDefault(),Re=e,be.style.display="block",console.log("[PWA] Install prompt ready")}),be.addEventListener("click",async()=>{if(!Re){gi();return}Re.prompt();const{outcome:e}=await Re.userChoice;console.log("[PWA] Install prompt outcome:",e),Re=null,be.style.display="none"}),window.addEventListener("appinstalled",()=>{console.log("[PWA] App installed"),Re=null,be.style.display="none"}),window.matchMedia("(display-mode: standalone)").matches&&(be.style.display="none")}function gi(){const n=/iPad|iPhone|iPod/.test(navigator.userAgent),e=/^((?!chrome|android).)*safari/i.test(navigator.userAgent);let t=`To install this app: + +`;n?(t+=`1. Tap the Share button (square with arrow) +`,t+='2. Scroll down and tap "Add to Home Screen"'):e?(t+=`1. Click File menu +`,t+='2. Click "Add to Dock"'):(t+=`1. Click the menu button (three dots) +`,t+='2. Click "Install" or "Add to Home Screen"'),alert(t)}let uo=null;const po=new Set;function mi(n="#offline-indicator"){uo=typeof n=="string"?document.querySelector(n):n,Ht(!navigator.onLine),window.addEventListener("online",()=>{console.log("[PWA] Back online"),Ht(!1),Xo(!1)}),window.addEventListener("offline",()=>{console.log("[PWA] Gone offline"),Ht(!0),Xo(!0)})}function Ht(n){uo&&(uo.style.display=n?"block":"none"),document.body.classList.toggle("is-offline",n)}function Qn(n){return po.add(n),n(!navigator.onLine),()=>po.delete(n)}function Xo(n){for(const e of po)try{e(n)}catch(t){console.error("[PWA] Offline listener error:",t)}}function V(){return navigator.onLine}function yi(){confirm("A new version is available. Reload now?")&&bi()}function bi(){Ce?.waiting&&Ce.waiting.postMessage({type:"SKIP_WAITING"}),window.location.reload()}async function wi({timeoutMs:n=1e4}={}){if(!("serviceWorker"in navigator))throw new Error("Service Workers not supported in this browser");if(navigator.serviceWorker.controller)return navigator.serviceWorker.controller;const e=navigator.serviceWorker.ready,t=new Promise((a,i)=>setTimeout(()=>i(new Error("Service-worker readiness timeout")),n)),r=await Promise.race([e,t]),o=navigator.serviceWorker.controller||r.active;if(!o)throw new Error("No active service worker available");return o}function _i(n){if(!("serviceWorker"in navigator))return()=>{};const e=()=>{try{n()}catch(t){console.error("[PWA] controllerchange handler error:",t)}};return navigator.serviceWorker.addEventListener("controllerchange",e),()=>navigator.serviceWorker.removeEventListener("controllerchange",e)}async function xo(n,e,t={},r=5e3,o=1e4){const a=await wi({timeoutMs:o});return new Promise((i,s)=>{const l=new MessageChannel,c=setTimeout(()=>{l.port1.close(),s(new Error(`Service-worker reply "${e}" timed out`))},r);l.port1.onmessage=d=>{if(d.data?.type===e){clearTimeout(c),l.port1.close();const{type:u,...p}=d.data;i(p)}},a.postMessage({type:n,...t},[l.port2])})}async function vi(){try{return(await xo("GET_TILE_STATS","TILE_STATS")).stats}catch(n){return console.warn("[PWA] getTileCacheStats failed:",n),null}}async function Ei(){try{return await xo("CLEAR_TILE_CACHES","TILE_CACHES_CLEARED"),!0}catch(n){return console.warn("[PWA] clearTileCaches failed:",n),!1}}async function xi(n){if(!n)return!1;try{return!!(await xo("CLEAR_TILE_CACHE","TILE_CACHE_CLEARED",{cacheName:n})).deleted}catch(e){return console.warn(`[PWA] clearTileCacheForProvider(${n}) failed:`,e),!1}}async function Si(){if(!navigator.storage?.estimate)return null;try{const{usage:n,quota:e}=await navigator.storage.estimate();return{usage:n||0,quota:e||0}}catch(n){return console.warn("[PWA] getStorageEstimate failed:",n),null}}async function Ti(n={}){const{installButton:e="#install-btn",offlineIndicator:t="#offline-indicator",autoRegisterSW:r=!0}=n;r&&await fi(),hi(e),mi(t),console.log("[PWA] Initialized")}const er={topo:{url:"https://a.tile.opentopomap.org/{z}/{x}/{y}.png",label:"Topographic",maxZoom:17,cacheKey:"tiles-topo"},osm:{url:"https://a.tile.openstreetmap.org/{z}/{x}/{y}.png",label:"OpenStreetMap",maxZoom:19,cacheKey:"tiles-osm"}},Li=30*1024,Mt=2*Math.PI*6378137/2;function Vo(n,e){const t=n/Mt*180;let r=e/Mt*180;return r=180/Math.PI*(2*Math.atan(Math.exp(r*Math.PI/180))-Math.PI/2),[t,r]}function Yo(n,e,t){const r=Math.pow(2,t),o=Math.floor((n+180)/360*r),a=e*Math.PI/180,i=Math.floor((1-Math.log(Math.tan(a)+1/Math.cos(a))/Math.PI)/2*r);return{x:o,y:i}}function tr(n,e){const[t,r,o,a]=n,[i,s]=Vo(t,r),[l,c]=Vo(o,a),d=Yo(i,c,e),u=Yo(l,s,e),p=Math.pow(2,e),h=Math.max(0,Math.min(d.x,u.x)),f=Math.min(p-1,Math.max(d.x,u.x)),y=Math.max(0,Math.min(d.y,u.y)),g=Math.min(p-1,Math.max(d.y,u.y));return{z:e,minX:h,maxX:f,minY:y,maxY:g,count:(f-h+1)*(g-y+1)}}function ki(n,e,t){let r=0;for(let o=e;o<=t;o++)r+=tr(n,o).count;return r}function Ii(n,e,t){const r=[];for(let o=e;o<=t;o++){const a=tr(n,o);for(let i=a.minX;i<=a.maxX;i++)for(let s=a.minY;s<=a.maxY;s++)r.push({z:o,x:i,y:s})}return r}function Pi(n,{z:e,x:t,y:r}){return n.replace("{z}",e).replace("{x}",t).replace("{y}",r)}class Mi{constructor({baseMap:e,extent3857:t,minZoom:r,maxZoom:o,concurrency:a=2,interBatchDelayMs:i=50,onProgress:s=()=>{}}){const l=er[e];if(!l)throw new Error(`Unknown base map: ${e}`);o>l.maxZoom&&(console.warn(`[OfflineTiles] ${e}: maxZoom ${o} > supported ${l.maxZoom}; clamping`),o=l.maxZoom),this.baseMap=e,this.template=l.url,this.extent=t,this.minZoom=r,this.maxZoom=o,this.concurrency=Math.max(1,Math.min(a,6)),this.interBatchDelayMs=i,this.onProgress=s,this._abortCtrl=null,this._cancelled=!1}async start(){if(this._abortCtrl)throw new Error("Downloader already started");this._abortCtrl=new AbortController,this._cancelled=!1;const e=Ii(this.extent,this.minZoom,this.maxZoom),t=e.length,r=Date.now();let o=0,a=0,i=0,s=0;const l=c=>{const d=Date.now()-r,u=o>0?Math.round(d/o*(t-o)):null;this.onProgress({phase:c,done:o,total:t,ok:a,failed:i,cached:s,elapsedMs:d,etaMs:u})};l("running");for(let c=0;c{if(this._cancelled)return;const p=Pi(this.template,u);try{const h=await fetch(p,{signal:this._abortCtrl.signal,cache:"default"});h.ok?(a++,h.body&&h.body.cancel().catch(()=>{})):(h.status,i++)}catch(h){h.name==="AbortError"||i++}o++})),l("running"),this.interBatchDelayMs>0&&c+this.concurrencysetTimeout(u,this.interBatchDelayMs))}return l(this._cancelled?"cancelled":"done"),{phase:this._cancelled?"cancelled":"done",done:o,total:t,ok:a,failed:i,cached:s,elapsedMs:Date.now()-r}}cancel(){this._cancelled=!0,this._abortCtrl&&this._abortCtrl.abort()}}const Ai=(()=>{const n=(r,o)=>{const a=r*Mt/180,i=Math.log(Math.tan((90+o)*Math.PI/360))/(Math.PI/180);return[a,i*Mt/180]},e=n(-3.3,4.5),t=n(1.2,11.2);return[e[0],e[1],t[0],t[1]]})();function Ci(n){return n*Li}const or="https://api.lupmis4luspa.org/api/spatial_planning",Wt="1",Fi="1c46538c712e9b5b";function Di(){try{if(typeof window>"u")return Wt;const n=window.LUPMIS_SESSION;if(!n||typeof n!="object")return Wt;const e=n.district_id;return e==null||String(e).length===0?null:String(e)}catch{}return Wt}const nr={get district_id(){return Di()},api_token:Fi};function Dt(){if(typeof window<"u"&&window.LUPMIS_SESSION&&window.LUPMIS_SESSION.user_id)return window.LUPMIS_SESSION;try{const n=localStorage.getItem("dev-session");if(n){const e=JSON.parse(n);if(e&&e.user_id)return e}}catch{}return null}typeof window<"u"&&(window.lupmisDevSession=n=>{n==null?(localStorage.removeItem("dev-session"),console.log("[Dev] Session override cleared. Reload to apply.")):(localStorage.setItem("dev-session",JSON.stringify(n)),console.log("[Dev] Session override saved. Reload to apply:",n))});const Oi=3e4,Ri=5e3;let Ae=null;async function Ni(n=!1){if(Ae!==null&&!n)return Ae;const e=new AbortController,t=setTimeout(()=>e.abort(),Ri);try{Ae=(await fetch(`${or}/get_layers.php`,{method:"POST",headers:{"Content-Type":"application/json",Accept:"application/json"},body:JSON.stringify(nr),signal:e.signal})).ok}catch{Ae=!1}finally{clearTimeout(t)}return console.log("[RemoteDB] Server reachable:",Ae),Ae}function Ee(){return Ae}function $i(n,e=Oi){const t=new AbortController,r=setTimeout(()=>t.abort(),e);return n.signal&&n.signal.addEventListener("abort",()=>t.abort()),{signal:t.signal,clear:()=>clearTimeout(r)}}async function he(n,e={},t={}){const r=`${or}/${n}`,o={...nr,...e};console.log("[RemoteDB] POST",r);const a=$i(t);try{const i=await fetch(r,{method:"POST",headers:{"Content-Type":"application/json",Accept:"application/json"},body:JSON.stringify(o),...t,signal:a.signal});if(!i.ok)throw new Error(`HTTP ${i.status}: ${i.statusText}`);const s=await i.json();return console.log("[RemoteDB] POST response:",n,"→",typeof s=="object"?`${Array.isArray(s)?s.length+" items":"object"}`:s),s}catch(i){throw i.name==="AbortError"?(console.error("[RemoteDB] POST timed out:",n),new Error(`Request timed out: ${n}`)):(console.error("[RemoteDB] POST failed:",n,i),i)}finally{a.clear()}}async function Bi(){return he("get_district_boundary.php")}async function Gi(){return he("get_layers.php")}async function ji(){return he("get_all_collector_zone_per_district.php")}async function qi(){return he("get_parcels_per_district.php")}async function zi(){return he("get_all_footprint_per_district.php")}async function Ui(){return he("get_contours_hillshade.php")}async function Hi(){return he("get_osm_roads.php")}async function Wi(){return he("get_upn_grid_per_district.php")}async function Ki(n,e){const t={client_uuid:n.client_uuid,name:n.name??null,started_at:n.started_at,ended_at:n.ended_at,point_count:n.point_count??e.length,distance_m:n.distance_m??0,points:(e||[]).map(o=>({seq:o.seq,longitude:o.longitude,latitude:o.latitude,altitude:o.altitude??null,accuracy:o.accuracy??null,altitude_accuracy:o.altitude_accuracy??null,heading:o.heading??null,speed:o.speed??null,satellites:o.satellites??null,recorded_at:o.recorded_at}))},r=await he("save_gps_trail.php",t);return{remoteId:r?.id??r?.remote_id??null}}const Xi=63710088e-1,mt=Math.PI/180;function Vi(n,e,t,r){const o=(r-e)*mt,a=(t-n)*mt,i=Math.sin(o/2)**2+Math.cos(e*mt)*Math.cos(r*mt)*Math.sin(a/2)**2;return 2*Xi*Math.asin(Math.min(1,Math.sqrt(i)))}function Jo(n,e=5){return n==null||Number.isNaN(n)?"—":n.toFixed(e)}function Yi(n){return n==null||Number.isNaN(n)?"—":n<1e3?`${Math.round(n)} m`:`${(n/1e3).toFixed(2)} km`}function Ji(n){return n==null||Number.isNaN(n)?"—":`±${Math.round(n)} m`}function Zi(n){return n==null||Number.isNaN(n)?"none":n<=10?"good":n<=30?"fair":"poor"}const Qi={minDistanceM:5,minIntervalMs:1e3,heartbeatMs:2e4,maxAccuracyM:50,enableHighAccuracy:!0,timeoutMs:15e3,maximumAgeMs:0};class at{constructor(e={}){this.opts={...Qi,...e},this.storage=e.storage||null,this.sync=e.sync||null,this._geo=e.geolocation||(typeof navigator<"u"?navigator.geolocation:null),this._state="idle",this._watchId=null,this._live=!1,this._recording=!1,this._activeTrailId=null,this._activeTrailUuid=null,this._lastRecorded=null,this._lastRecordedAt=0,this._distanceM=0,this._pointCount=0,this._lastFix=null,this._listeners=Object.create(null)}on(e,t){return(this._listeners[e]||(this._listeners[e]=new Set)).add(t),()=>this._listeners[e]?.delete(t)}_emit(e,t){const r=this._listeners[e];if(r)for(const o of r)try{o(t)}catch(a){console.error(`[GeoTracker] listener for "${e}" threw`,a)}}get state(){return this._state}get isRecording(){return this._recording}get lastFix(){return this._lastFix}get isSupported(){return!!this._geo}_setState(e){this._state!==e&&(this._state=e,this._emit("statechange",e))}startLive(){if(!this._geo){this._emit("error",new Error("Geolocation not supported"));return}this._live=!0,this._ensureWatch()}stopLive(){this._live=!1,this._recording||this._teardownWatch()}getCurrentPosition(){return new Promise((e,t)=>{if(!this._geo){t(new Error("Geolocation not supported"));return}this._geo.getCurrentPosition(r=>{const o=at.normalize(r);this._lastFix=o,this._emit("position",o),e(o)},r=>{this._emit("error",r),t(r)},{enableHighAccuracy:this.opts.enableHighAccuracy,timeout:this.opts.timeoutMs,maximumAge:this.opts.maximumAgeMs})})}async startRecording(e={}){if(!this._geo)throw new Error("Geolocation not supported");if(!this.storage)throw new Error("GeoTracker: no storage adapter configured");if(this._recording)return{trailId:this._activeTrailId,uuid:this._activeTrailUuid};const t=at.uuid(),r=new Date().toISOString(),o={uuid:t,name:e.name||null,startedAt:r,...e},a=await this.storage.createTrail(o);return this._activeTrailId=a,this._activeTrailUuid=t,this._lastRecorded=null,this._lastRecordedAt=0,this._distanceM=0,this._pointCount=0,this._recording=!0,this._ensureWatch(),this._setState("recording"),this._emit("trailstart",{trailId:a,uuid:t,startedAt:r}),{trailId:a,uuid:t}}async stopRecording(){if(!this._recording)return null;const e=this._activeTrailId,r={endedAt:new Date().toISOString(),pointCount:this._pointCount,distanceM:this._distanceM};this._recording=!1,this._live||this._teardownWatch(),this._setState(this._live?"watching":"idle");try{await this.storage.finishTrail(e,r)}catch(a){this._emit("error",a)}this._emit("trailstop",{trailId:e,...r});let o=!1;if(this.sync)try{o=await this._syncTrail(e)}catch(a){this._emit("error",a)}return this._activeTrailId=null,this._activeTrailUuid=null,{trailId:e,pointCount:r.pointCount,distanceM:r.distanceM,synced:o}}async syncPending(){if(!this.sync||!this.storage)return{pushed:0,failed:0};if(this.sync.isOnline&&!this.sync.isOnline())return{pushed:0,failed:0};let e=0,t=0;const r=await this.storage.getUnsyncedTrails();for(const o of r)try{await this._syncTrail(o.id??o.trailId,o)?e++:t++}catch(a){t++,this._emit("error",a)}return this._emit("syncstatus",{pushed:e,failed:t}),{pushed:e,failed:t}}async _syncTrail(e,t){const r=await this.storage.getTrailPoints(e),o=t||{id:e},a=await this.sync.pushTrail(o,r),i=a&&(a.remoteId??a.id??null);return await this.storage.markTrailSynced(e,i),!0}_ensureWatch(){if(this._watchId!=null||!this._geo){this._state==="idle"&&this._live&&this._setState("watching");return}this._watchId=this._geo.watchPosition(e=>this._onFix(e),e=>this._emit("error",e),{enableHighAccuracy:this.opts.enableHighAccuracy,timeout:this.opts.timeoutMs,maximumAge:this.opts.maximumAgeMs}),this._recording||this._setState("watching")}_teardownWatch(){this._watchId!=null&&this._geo&&this._geo.clearWatch(this._watchId),this._watchId=null}async _onFix(e){const t=at.normalize(e);if(this._lastFix=t,this._emit("position",t),!this._recording)return;const{minIntervalMs:r,minDistanceM:o,heartbeatMs:a,maxAccuracyM:i}=this.opts,s=t.timestamp;if(this._lastRecordedAt&&s-this._lastRecordedAt0&&t.accuracy!=null&&t.accuracy>i&&this._lastRecorded)return;let l=!1,c=0;if(!this._lastRecorded)l=!0;else{c=Vi(this._lastRecorded.lon,this._lastRecorded.lat,t.lon,t.lat);const d=s-this._lastRecordedAt;(c>=o||d>=a)&&(l=!0)}if(l){this._lastRecorded&&(this._distanceM+=c),this._pointCount+=1,this._lastRecorded={lon:t.lon,lat:t.lat,timestamp:s},this._lastRecordedAt=s;try{await this.storage.addPoint(this._activeTrailId,{...t,seq:this._pointCount}),this._emit("point",{trailId:this._activeTrailId,seq:this._pointCount,point:t,distanceM:this._distanceM,pointCount:this._pointCount})}catch(d){this._emit("error",d)}}}static normalize(e){const t=e.coords||{},r=o=>o!=null&&!Number.isNaN(o)?o:null;return{lon:t.longitude,lat:t.latitude,accuracy:r(t.accuracy),altitude:r(t.altitude),altitudeAccuracy:r(t.altitudeAccuracy),heading:r(t.heading),speed:r(t.speed),satellites:null,timestamp:e.timestamp||Date.now()}}static uuid(){return typeof crypto<"u"&&crypto.randomUUID?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,e=>{const t=Math.random()*16|0;return(e==="x"?t:t&3|8).toString(16)})}}const es={async createTrail(n){const e=n.districtId??Dt()?.district_id??null;return Pa({...n,districtId:e!=null?String(e):null})},addPoint:(n,e)=>Ma(n,e),finishTrail:(n,e)=>Aa(n,e),getUnsyncedTrails:()=>Ca(),getTrailPoints:n=>Fa(n),markTrailSynced:(n,e)=>Da(n,e)},ts={pushTrail:(n,e)=>Ki(n,e),isOnline:()=>V()},we=new at({storage:es,sync:ts,minDistanceM:5,minIntervalMs:1e3,heartbeatMs:2e4,maxAccuracyM:50,enableHighAccuracy:!0}),os=new Set(["set:view","set:selected","clear:selected","set:basemap"]);function ns({mapView:n,embedConfig:e}){const t=n.getMap(),r=window.parent&&window.parent!==window?window.parent:null,o=new G,a=new O({source:o,zIndex:9999,style:new M({stroke:new I({color:"#f97316",width:3}),fill:new A({color:"rgba(249,115,22,0.18)"})}),properties:{title:"Permit selection",displayInLayerSwitcher:!1}});t.addLayer(a);let i=null,s=e?.upn?String(e.upn):null,l=!1;function c(g){if(!r){console.warn("[embed-bridge] No parent window — would have sent:",g);return}try{r.postMessage(g,"*")}catch(m){console.warn("[embed-bridge] postMessage failed:",m)}}function d(g,m){c({type:"error",code:g,message:m})}function u(){l||(l=!0,c({type:"ready"}))}function p(g,m,b){const _=g.getProperties();let S=m,T=b;if(S==null||T==null){const v=g.getGeometry()?.getExtent();if(v){const[x,P]=Me(Cr(v));S=x,T=P}}return{type:"parcel:select",upn:_.upn??null,parcel_id:_.id??null,lon:S??null,lat:T??null,zone_code:_.zone_code??null,zone_name:_.zone_name??null,landuse:_.landuse??null,min_height:_.min_height??null,max_height:_.max_height??null}}function h(g){if(o.clear(),g){const m=g.clone();o.addFeature(m)}}n.onClick((g,m,b,_)=>{let S=null;t.forEachFeatureAtPixel(_.pixel,T=>{if(T.get("_layerType")==="parcel")return S=T,!0}),S?(h(S),c(p(S,g,m))):(h(null),c({type:"parcel:cleared"}))}),window.addEventListener("message",g=>{const m=g.data;if(!(!m||typeof m!="object"||!os.has(m.type)))try{switch(m.type){case"set:view":{if(typeof m.lon=="number"&&typeof m.lat=="number"){const b=t.getView();b.setCenter(Q([m.lon,m.lat])),typeof m.zoom=="number"&&b.setZoom(m.zoom)}break}case"set:selected":m.upn&&f(String(m.upn));break;case"clear:selected":h(null),s=null;break;case"set:basemap":m.key&&typeof n.setBaseMap=="function"&&n.setBaseMap(m.key);break}}catch(b){d("COMMAND_FAILED",`Failed to handle ${m.type}: ${b.message}`)}});function f(g){if(!i){s=g;return}const b=i.getSource().getFeatures().find(S=>String(S.get("upn")??"")===g);if(!b){s=g;return}s=null,h(b);const _=b.getGeometry()?.getExtent();_&&t.getView().fit(_,{padding:[50,50,50,50],duration:400,maxZoom:17}),c(p(b,null,null))}function y(g){i=g;const m=g.getSource(),b=()=>{queueMicrotask(()=>{s&&f(s),u()})};if(m.getFeatures().length>0)b();else{let _=!1;m.on("addfeature",()=>{_||(_=!0,queueMicrotask(()=>{_=!1,s&&f(s),u()}))})}}if(e?.basemap&&typeof n.setBaseMap=="function"&&n.setBaseMap(e.basemap),typeof e?.lon=="number"&&typeof e?.lat=="number"){const g=t.getView();g.setCenter(Q([e.lon,e.lat])),g.setZoom(typeof e?.zoom=="number"?e.zoom:15)}return{attachParcelsLayer:y,emitError:d}}const rs=[{key:"parcels",label:"Parcels",geometryFamily:"polygon"},{key:"collector_zones",label:"Collector Zones",geometryFamily:"polygon"},{key:"building_footprints",label:"Building Footprints",geometryFamily:"polygon"},{key:"osm_roads",label:"OSM Roads",geometryFamily:"line"},{key:"other",label:"Other (view only)",geometryFamily:"any"}],rr={parcels:["upn","landuse","zone_code","zone_name","sector","block","parcel_no","prop_no","st_name","prop_add","fac_name","min_height","max_height","eff_date","lp_name","locality","mmda","last_update","remarks"],collector_zones:["zone_name"],building_footprints:[],osm_roads:["osm_id","name","highway"],other:[]};function as(n){if(!n?.features?.length)return"none";let e=0,t=0,r=0;for(const i of n.features){const s=i?.geometry?.type;s&&(s==="Polygon"||s==="MultiPolygon"?e++:s==="LineString"||s==="MultiLineString"?t++:(s==="Point"||s==="MultiPoint")&&r++)}const o=e+t+r;if(o===0)return"none";const a=Math.max(e,t,r);return a``).join(""),L.target.dataset.populated="1"),L.target&&!L.target.dataset.wired&&(L.target.dataset.wired="1",L.target.addEventListener("change",sr)),L.btnSave&&!L.btnSave.dataset.wired&&(L.btnSave.dataset.wired="1",L.btnSave.addEventListener("click",()=>Qo("save"))),L.btnSaveUpload&&!L.btnSaveUpload.dataset.wired&&(L.btnSaveUpload.dataset.wired="1",L.btnSaveUpload.addEventListener("click",()=>Qo("upload"))),L.root&&!L.root.dataset.wired&&(L.root.dataset.wired="1",L.root.addEventListener("hidden.bs.modal",()=>{R?.onResult&&!R._resolved&&(R._resolved=!0,R.onResult({action:"cancel"})),R=null})))}function us(){const n=R.targetType,e=rr[n]||[];if(n==="other"||e.length===0){L.fieldsWrap.style.display="none";return}L.fieldsWrap.style.display="";const t=[''].concat(R.sourceFields.map(r=>``)).join("");L.tbody.innerHTML=e.map(r=>{const o=R.mapping[r]||"",a=t.replace(`