const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/geotiff-BaoeLn6q.js","assets/openlayers-J9qS6Th1.js","assets/openlayers-BtPuoxOl.css","assets/pako-Xa-UToif.js","assets/pdf-export-BONdNTHD.js","assets/jspdf-FJgf4ijI.js","assets/shpjs-Doisr2ug.js","assets/analysis-modal-D_xpkByf.js","assets/bootstrap-D1-uvFxm.js","assets/bootstrap-BtmJYOxZ.css","assets/turf-B-0GbNlN.js","assets/ol-ext-C5frl4dY.js","assets/ol-ext-BgKrOIxx.css"])))=>i.map(i=>d[i]); import{_ as Le}from"./geotiff-BaoeLn6q.js";import{M as ht}from"./bootstrap-D1-uvFxm.js";import{h as M,F as A,j as I,k as fe,m as xo,b as j,V as D,L as he,D as Fe,P as pt,ak as We,n as pe,al as Pe,M as No,am as Br,an as ee,ao as Gr,S as jr,J as Ur,G as qr,ap as zr,aq as Hr,ar as Wr,o as mt,O as Ee,as as ct,at as kt,au as Oe,A as Kr,T as ae,av as Me,aw as $o,ax as le,ay as Bo,e as Vr,u as qt,s as Xr,az as Gn,aA as So}from"./openlayers-J9qS6Th1.js";import{o as Yr,a as Jr,b as Zr,c as Qr,d as co,e as Go,f as it,g as ea,h as xe,i as ta,j as jo}from"./ol-ext-C5frl4dY.js";import{r as oa}from"./shpjs-Doisr2ug.js";import"./pako-Xa-UToif.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 Uo="function",qe="64e10b34-2bf7-4616-9668-f99de5aa046e",na="get",ra="has",aa="set",{isArray:xt}=Array;let{SharedArrayBuffer:It,window:ia}=globalThis,{notify:jn,wait:Un,waitAsync:Pt}=Atomics,qn=null;Pt||(Pt=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 It(4)}catch{It=ArrayBuffer;const e=new WeakMap;if(ia){const t=new Map,{prototype:{postMessage:r}}=Worker,o=a=>{const i=a.data?.[qe];if(!xt(i)){a.stopImmediatePropagation();const{id:s,sb:l}=i;t.get(s)(l)}};qn=function(a,...i){const s=a?.[qe];if(xt(s)){const[l,c]=s;e.set(c,l),this.addEventListener("message",o)}return r.call(this,a,...i)},Pt=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({[qe]:{id:r,sb:o}});jn=r=>{postMessage(t(e.get(r),r))},addEventListener("message",r=>{const o=r.data?.[qe];if(xt(o)){const[a,i]=o;e.set(i,a)}})}}/*! (c) Andrea Giammarchi - ISC */const{Int32Array:uo,Map:qo,Uint16Array:po}=globalThis,{BYTES_PER_ELEMENT:zo}=uo,{BYTES_PER_ELEMENT:sa}=po,la=(n,e,t)=>{for(;Un(n,0,0,e)==="timed-out";)t()},fo=new WeakSet,zt=new WeakMap,ca={value:{then:n=>n()}};let da=0;const Lo=(n,{parse:e=JSON.parse,stringify:t=JSON.stringify,transform:r,interrupt:o}=JSON)=>{if(!zt.has(n)){const a=qn||n.postMessage,i=(u,...f)=>a.call(n,{[qe]:f},{transfer:u}),s=typeof o===Uo?o:o?.handler,l=o?.delay||42,c=new TextDecoder("utf-16"),d=(u,f)=>u?Pt(f,0):(s?la(f,l,s):Un(f,0),ca);let p=!1;zt.set(n,new Proxy(new qo,{[ra]:(u,f)=>typeof f=="string"&&!f.startsWith("_"),[na]:(u,f)=>f==="then"?null:((...h)=>{const g=da++;let m=new uo(new It(zo*2)),y=[];fo.has(h.at(-1)||y)&&fo.delete(y=h.pop()),i(y,g,m,f,r?h.map(r):h);const w=n!==globalThis;let _=0;return p&&w&&(_=setTimeout(console.warn,1e3,`💀🔒 - Possible deadlock if proxy.${f}(...args) is awaited`)),d(w,m).value.then(()=>{clearTimeout(_);const x=m[1];if(!x)return;const L=sa*x;return m=new uo(new It(L+L%zo)),i([],g,m),d(w,m).value.then(()=>e(c.decode(new po(m.buffer).slice(0,x))))})}),[aa](u,f,h){const g=typeof h;if(g!==Uo)throw new Error(`Unable to assign ${f} as ${g}`);if(!u.size){const m=new qo;n.addEventListener("message",async y=>{const w=y.data?.[qe];if(xt(w)){y.stopImmediatePropagation();const[_,x,...L]=w;let v;if(L.length){const[S,P]=L;if(u.has(S)){p=!0;try{const k=await u.get(S)(...P);if(k!==void 0){const q=t(r?r(k):k);m.set(_,q),x[1]=q.length}}catch(k){v=k}finally{p=!1}}else v=new Error(`Unsupported action: ${S}`);x[0]=1}else{const S=m.get(_);m.delete(_);for(let P=new po(x.buffer),k=0;k(fo.add(n),n);function Ho(){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 zn(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,p)=>d+p.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 Mt{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 Le(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 zn(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 ua(n,e,t){let r,o,a,i,s,l,c=0,d=!1,p=!1,u=!0;if(typeof n!="function")throw new TypeError("Expected a function");e=Number(e)||0,typeof t=="object"&&t!==null&&(d=!!t.leading,p="maxWait"in t,a=p?Math.max(Number(t.maxWait)||0,e):0,u="trailing"in t?!!t.trailing:u);function f(v){const S=r,P=o;return r=o=void 0,c=v,i=n.apply(P,S),i}function h(v){return c=v,s=setTimeout(y,e),d?f(v):i}function g(v){const S=v-(l??0),P=v-c,k=e-S;return p?Math.min(k,a-P):k}function m(v){const S=v-(l??0),P=v-c;return l===void 0||S>=e||S<0||p&&P>=a}function y(){const v=Date.now();if(m(v))return w(v);s=setTimeout(y,g(v))}function w(v){return s=void 0,u&&r?f(v):(r=o=void 0,i)}function _(){s!==void 0&&clearTimeout(s),c=0,r=l=o=s=void 0}function x(){return s===void 0?i:w(Date.now())}function L(){const v=Date.now(),S=m(v);if(r=arguments,o=this,l=v,S){if(s===void 0)return h(l);if(p)return s=setTimeout(y,e),f(l)}return s===void 0&&(s=setTimeout(y,e)),i}return L.cancel=_,L.flush=x,L}function St(){return crypto.randomUUID()}function Hn(n,e){switch(n){case"session":case":sessionStorage:":let t=sessionStorage._sqlocal_session_key;return t||(t=St(),sessionStorage._sqlocal_session_key=t),`session:${t}`;case"local":case":localStorage:":return"local";case":memory:":return`memory:${e}`;default:return`path:${n}`}}class yt{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:Ho()}),Object.defineProperty(this,"transactionMutex",{enumerable:!0,configurable:!0,writable:!0,value:Ho()}),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 Mt,await this.driver.init(this.config)}const a=Hn(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:ua(()=>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?Lo(globalThis):globalThis;this.proxy=r,this.driver=e}}function At(n,...e){return{sql:n.join("?"),params:e}}function pa(n){return!n.some(e=>!Array.isArray(e))}function Ht(n,e){let t;return pa(n)?t=n:t=[n],t.map(r=>{const o={};return e.forEach((a,i)=>{o[a]=r[i]}),o})}function fa(n){return typeof n=="object"&&n!==null&&"getSQL"in n&&typeof n.getSQL=="function"}function ha(n){return typeof n=="object"&&n!==null&&"sql"in n&&typeof n.sql=="string"&&"params"in n}function Wo(n){if(typeof n=="function"&&(n=n(At)),fa(n))try{if(!("toSQL"in n&&typeof n.toSQL=="function"))throw 1;const r=n.toSQL();if(!ha(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 Ko(n,e){let t;return typeof n=="string"?t={sql:n,params:e}:t=At(n,...e),t}async function bt(n,e,t,r){return!e&&"locks"in navigator?navigator.locks.request(`_sqlocal_mutation_(${t.databasePath})`,{mode:n},r):r()}class Vo extends Mt{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 Le(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 Mt;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 Wn,Kn;class ga{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,p=this.queriesInProgress;switch(d.type){case"success":case"data":case"buffer":case"info":case"error":if(d.queryKey&&p.has(d.queryKey)){const[f,h]=p.get(d.queryKey);d.type==="error"?h(d.error):f(d),p.delete(d.queryKey)}else if(d.type==="error")throw d.error;break;case"callback":const u=this.userCallbacks.get(d.name);u&&u(...d.args??[]);break;case"event":this.config.onConnect?.(d.reason);break}}}),Object.defineProperty(this,"createQuery",{enumerable:!0,configurable:!0,writable:!0,value:async c=>bt("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=St();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((p,u)=>{this.queriesInProgress.set(d,[p,u])})})}),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,p="all",u)=>{const f=await this.createQuery({type:"query",transactionKey:u,sql:c,params:d,method:p}),h={rows:[],columns:[]};return f.type==="data"&&(h.rows=f.data[0]?.rows??[],h.columns=f.data[0]?.columns??[]),h}}),Object.defineProperty(this,"execBatch",{enumerable:!0,configurable:!0,writable:!0,value:async c=>{const d=await this.createQuery({type:"batch",statements:c}),p=new Array(c.length).fill({rows:[],columns:[]});return d.type==="data"&&d.data.forEach((u,f)=>{p[f]=u}),p}}),Object.defineProperty(this,"sql",{enumerable:!0,configurable:!0,writable:!0,value:async(c,...d)=>{const p=Ko(c,d),{rows:u,columns:f}=await this.exec(p.sql,p.params,"all");return Ht(u,f)}}),Object.defineProperty(this,"batch",{enumerable:!0,configurable:!0,writable:!0,value:async c=>{const d=c(At);return(await this.execBatch(d)).map(({rows:u,columns:f})=>Ht(u,f))}}),Object.defineProperty(this,"beginTransaction",{enumerable:!0,configurable:!0,writable:!0,value:async()=>{const c=St();await this.createQuery({type:"transaction",transactionKey:c,action:"begin"});const d=async h=>{const g=Wo(h);if(g.exec)return this.transactionQueryKeyQueue.push(c),g.exec();const{rows:m,columns:y}=await this.exec(g.sql,g.params,"all",c);return Ht(m,y)};return{query:d,sql:async(h,...g)=>{const m=Ko(h,g);return await d(m)},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=>bt("exclusive",!1,this.config,async()=>{let d;this.bypassMutationLock=!0;try{d=await this.beginTransaction();const p=await c({sql:d.sql,query:d.query});return await d.commit(),p}catch(p){throw await d?.rollback(),p}finally{this.bypassMutationLock=!1}})}),Object.defineProperty(this,"reactiveQuery",{enumerable:!0,configurable:!0,writable:!0,value:c=>{let d=[],p=!1,u=!1,f=0;const h=Wo(c),g=new Set,m=new Set,y=new Set,w=async()=>{try{const x=++f;if(g.size===0){const v=await this.sql("SELECT name, wr FROM tables_used(?) WHERE type = 'table'",h.sql),S=new Set,P=new Set;if(v.forEach(k=>{typeof k.name=="string"&&(k.wr?P.add(k.name):S.add(k.name))}),S.size===0)throw new Error("The passed SQL does not read any tables.");if(Array.from(P).some(k=>S.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.");S.forEach(k=>g.add(k))}const L=h.exec?await h.exec():await this.sql(h.sql,...h.params);x===f&&(d=L,p=!0,m.forEach(v=>v(d)))}catch(x){y.forEach(L=>{L(x instanceof Error?x:new Error(String(x)))})}},_=x=>{x.data.tables.some(L=>g.has(L))&&w()};return{get value(){return d},subscribe:(x,L)=>{if(!this.effectsChannel)throw new Error('This SQLocal instance is not configured for reactive queries. Set the "reactive" option to enable them.');return L||(L=v=>{throw v}),m.add(x),y.add(L),u?p&&x(d):(this.effectsChannel.addEventListener("message",_),u=!0,w()),{unsubscribe:()=>{m.delete(x),y.delete(L),m.size===0&&(this.effectsChannel?.removeEventListener("message",_),u=!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 p=`_sqlocal_func_${c}`,u=()=>{this.proxy[p]=d};this.proxy===globalThis&&u(),await this.createQuery({type:"function",functionName:c,functionType:"scalar"}),this.proxy!==globalThis&&u()}}),Object.defineProperty(this,"createAggregateFunction",{enumerable:!0,configurable:!0,writable:!0,value:async(c,d)=>{const p=`_sqlocal_func_${c}`,u=()=>{this.proxy[`${p}_step`]=d.step,this.proxy[`${p}_final`]=d.final};this.proxy===globalThis&&u(),await this.createQuery({type:"function",functionName:c,functionType:"aggregate"}),this.proxy!==globalThis&&u()}}),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 bt("exclusive",!1,this.config,async()=>{try{this.broadcast({type:"close",clientKey:this.clientKey});const p=await zn(c,"buffer");await this.createQuery({type:"import",database:p}),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 bt("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,Wn,{enumerable:!0,configurable:!0,writable:!0,value:()=>{this.destroy()}}),Object.defineProperty(this,Kn,{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=St();const l=Hn(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 Vo("local");this.processor=new yt(c)}else if(s==="session"||s===":sessionStorage:"){const c=new Vo("session");this.processor=new yt(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 Mt;this.processor=new yt(c)}this.processor instanceof yt?(this.processor.onmessage=c=>this.processMessageEvent(c),this.proxy=globalThis):(this.processor.addEventListener("message",this.processMessageEvent),this.proxy=Lo(this.processor)),this.processor.postMessage({type:"config",config:{...i,clientKey:this.clientKey,onInitStatements:r?.(At)??[]}})}}Wn=Symbol.dispose,Kn=Symbol.asyncDispose;const To="lupmis2.db",ma="lupmis-db-sync",Vn=new ga(To),{sql:b}=Vn;console.log("[Database] SQLocal instance created for:",To);const Xn=new BroadcastChannel(ma);let Yn=!1,Jn,Zn;const Xo=new Promise((n,e)=>{Jn=n,Zn=e}),Ct=new Set;function ya(n){return Ct.add(n),()=>Ct.delete(n)}Xn.onmessage=n=>{const{type:e,payload:t}=n.data;if(e==="DB_CHANGE")for(const r of Ct)try{r(t)}catch(o){console.error("[Database] Change listener error:",o)}};function Y(n,e,t=null){Xn.postMessage({type:"DB_CHANGE",payload:{table:n,action:e,id:t,timestamp:Date.now()}});for(const r of Ct)try{r({table:n,action:e,id:t,timestamp:Date.now(),local:!0})}catch(o){console.error("[Database] Change listener error:",o)}}async function ba(){try{console.log("[Database] Initializing schema...");const n=await b`SELECT sqlite_version() as version`;console.log("[Database] SQLite version:",n[0]?.version),console.log("[Database] Creating locations table..."),await b` 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 b`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 b` 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 b` 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 b` 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 b`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 b`DROP TABLE parcels`)}catch(r){console.warn("[Database] parcels migration check failed:",r)}await b` 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 b` 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 b` 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 b` 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 b` 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 b` 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 b` 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 b` 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 b`CREATE INDEX IF NOT EXISTS idx_locations_category ON locations(category)`,await b`CREATE INDEX IF NOT EXISTS idx_locations_synced ON locations(synced)`,await b`CREATE INDEX IF NOT EXISTS idx_gps_trails_synced ON gps_trails(synced, status)`,await b`CREATE INDEX IF NOT EXISTS idx_gps_trail_points_trail ON gps_trail_points(trail_id, seq)`,await b`CREATE INDEX IF NOT EXISTS idx_external_imports_status ON external_imports(status)`,await b`CREATE INDEX IF NOT EXISTS idx_external_import_features_import ON external_import_features(import_id, upload_status)`;const t=await b`SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'`;console.log("[Database] All tables:",t.map(r=>r.name)),Yn=!0,Jn(!0),console.log("[Database] ✓ Schema initialized")}catch(n){throw console.error("[Database] ✗ Schema init failed:",n),Zn(n),n}}async function wa(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 b`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 b` INSERT INTO locations (name, longitude, latitude, description, category) VALUES (${n}, ${e}, ${t}, ${o}, ${a}) `,console.log("[Database] INSERT completed");const l=(await b`SELECT last_insert_rowid() as id`)[0]?.id;console.log("[Database] New ID:",l);const c=await b`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 b` INSERT INTO sync_log (table_name, record_id, action) VALUES ('locations', ${l}, 'INSERT') `,Y("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 Qn(n={}){const{category:e=null,limit:t=1e3}=n;try{const r=await b`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 b` SELECT * FROM locations WHERE category = ${e} ORDER BY created_at DESC LIMIT ${t} `:o=await b` 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 _a(){try{return(await b`SELECT COUNT(*) as count FROM locations`)[0]?.count??0}catch(n){return console.error("[Database] getLocationCount error:",n),0}}async function er(n,e){try{const t=JSON.stringify(e);await b` 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 tr(n){try{const e=await b`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 va(n){try{await b`DELETE FROM collector_zones`;for(const e of n){const t=JSON.stringify(e);await b` 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 Ea(){try{const n=await b`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 xa(n,e){try{await b`BEGIN`,await b`DELETE FROM upn_grid`;let t=0;for(const r of n){const o=r.polygon||r.geometry_wkt||r.geom||"";await b` INSERT INTO upn_grid (districtid, upn_prefix, geometry_wkt, fetched_at) VALUES (${X(e)}, ${r.upn_prefix??null}, ${o}, CURRENT_TIMESTAMP) `,t++}await b`COMMIT`,console.log("[Database] ✓ Saved",t,"UPN-grid cells (district",e,")")}catch(t){try{await b`ROLLBACK`}catch{}throw console.error("[Database] ✗ Failed to save UPN grid:",t),t}}async function Sa(n){try{const e=await b` 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 or(){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 La(n){const{filename:e,targetType:t="other",featureCount:r=0}=n,o=or();try{await b` 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 b`SELECT last_insert_rowid() AS id`)[0]?.id;return Y("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 Ta(n,e){if(!Array.isArray(e)||e.length===0)return 0;try{await b`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||or();await b` INSERT INTO external_import_features (import_id, client_uuid, geometry_wkt, properties_json, upload_status) VALUES (${n}, ${i}, ${o}, ${a}, 'pending') `,t++}return await b`COMMIT`,Y("external_import_features","INSERT",n),t}catch(t){try{await b`ROLLBACK`}catch{}throw console.error("[Database] ✗ Failed to add import features:",t),t}}async function Ft(n,e={}){try{const t=await b`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 b` UPDATE external_imports SET target_type = ${o}, mapping_json = ${a}, status = ${i}, error_count = ${s}, last_uploaded_at = ${l} WHERE id = ${n} `,Y("external_imports","UPDATE",n)}catch(t){throw console.error("[Database] ✗ Failed to update external import:",t),t}}async function ka(n){try{const e=await b`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 Ia(n){try{return(await b` 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 Pa(n,e){try{const t=await b` SELECT id, properties_json FROM external_import_features WHERE import_id = ${n} `;if(t.length===0)return 0;await b`BEGIN`;let r=0;for(const o of t){const a=o.properties_json?JSON.parse(o.properties_json):{},i=e(a)??{};await b` UPDATE external_import_features SET properties_json = ${JSON.stringify(i)} WHERE id = ${o.id} `,r++}return await b`COMMIT`,Y("external_import_features","UPDATE",n),r}catch(t){try{await b`ROLLBACK`}catch{}throw console.error("[Database] ✗ Failed to remap import features:",t),t}}async function Ma(n,e,t){try{if(t!==void 0){const r=JSON.stringify(t);await b` UPDATE external_import_features SET geometry_wkt = ${e}, properties_json = ${r} WHERE client_uuid = ${n} `}else await b` UPDATE external_import_features SET geometry_wkt = ${e} WHERE client_uuid = ${n} `;Y("external_import_features","UPDATE",n)}catch(r){throw console.error("[Database] ✗ Failed to update import feature geometry:",r),r}}async function Aa(n){try{const e=await b` SELECT import_id FROM external_import_features WHERE client_uuid = ${n} `;if(e.length===0)return;const t=e[0].import_id;await b`BEGIN`;try{await b`DELETE FROM external_import_features WHERE client_uuid = ${n}`,await b` UPDATE external_imports SET feature_count = MAX(0, feature_count - 1) WHERE id = ${t} `,await b`COMMIT`}catch(r){try{await b`ROLLBACK`}catch{}throw r}Y("external_import_features","DELETE",n),Y("external_imports","UPDATE",t)}catch(e){throw console.error("[Database] ✗ Failed to delete import feature:",e),e}}function X(n){if(n===""||n===null||n===void 0)return null;const e=Number(n);return Number.isNaN(e)?null:e}async function Ca(n){try{await b`BEGIN`,await b`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 b` 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 b`COMMIT`,console.log("[Database] ✓ Saved",e,"parcels (from",n.length,"rows,",n.length-e,"skipped/replaced)")}catch(e){try{await b`ROLLBACK`}catch{}throw console.error("[Database] ✗ Failed to save parcels:",e),e}}async function Fa(){try{const n=await b`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 Oa(n,e){try{await b` 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),Y("parcels","UPDATE",n)}catch(t){throw console.error("[Database] ✗ Failed to update parcel:",n,t),t}}async function Da(n,e={}){try{await b` 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 b`SELECT last_insert_rowid() as id`)[0]?.id;return console.log("[Database] ✓ New parcel inserted:",r,"(status: new)"),Y("parcels","INSERT",r),{id:r}}catch(t){throw console.error("[Database] ✗ Failed to insert new parcel:",t),t}}async function Ra(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 b`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 b` 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 Na(){try{const n=await b`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 b`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 b` 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 Ba(){try{const n=await b`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 Ga(){return Vn.getDatabaseFile()}async function ja(n="lupmis-backup.sqlite3"){const e=await Ga(),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 Ua(){return{type:"FeatureCollection",features:(await Qn()).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 ko(){try{const n=await b` SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' ORDER BY name `,e=await _a();return{ready:Yn,databasePath:To,tables:n.map(t=>t.name),locationCount:e}}catch(n){return{ready:!1,error:n.message}}}const nr=Object.freeze(["parcels","building_footprints","osm_roads","collector_zones","upn_grid","remote_data"]);function rr(n){return nr.includes(n)}async function ar(n){if(!rr(n))throw new Error(`Refusing to clear "${n}" — not a known cached-layer table`);const t=(await b(`SELECT COUNT(*) AS n FROM "${n}"`))[0]?.n??0;return await b(`DELETE FROM "${n}"`),console.log(`[Database] ✓ Cleared "${n}" (${t} rows)`),Y(n,"CLEAR",null),t}async function Io(){const n=await b` 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 nr)if(e.has(o))try{const a=await ar(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 qa(){const n=await b` 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 b(e)}async function za(n,e=200){if((await b` SELECT name FROM sqlite_master WHERE type='table' AND name = ${n} `).length===0)throw new Error(`Table "${n}" does not exist`);const r=await b(`SELECT * FROM "${n}" LIMIT ${e}`);return{columns:r.length>0?Object.keys(r[0]):[],rows:r}}async function Ha(){console.log("=== DATABASE TEST ===");try{const n=await b`SELECT sqlite_version() as v`;console.log("1. SQLite version:",n[0].v);const e=await b`SELECT name FROM sqlite_master WHERE type='table'`;console.log("2. Tables:",e.map(o=>o.name)),console.log("3. Inserting test row..."),await b`INSERT INTO locations (name, longitude, latitude, category) VALUES ('TEST', -1.0, 7.0, 'test')`;const t=await b`SELECT * FROM locations WHERE name = 'TEST'`;console.log("4. Test row:",t);const r=await b`SELECT COUNT(*) as c FROM locations`;return console.log("5. Total rows:",r[0].c),await b`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=Ha,window.dbStatus=ko);async function Wa(n){const{uuid:e,name:t=null,startedAt:r,districtId:o=null}=n;await b` INSERT INTO gps_trails (client_uuid, name, district_id, started_at, status) VALUES (${e}, ${t}, ${o}, ${r}, 'recording') `;const i=(await b`SELECT last_insert_rowid() as id`)[0]?.id;return Y("gps_trails","insert",i),i}async function Ka(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:p}=e,u=typeof p=="number"?new Date(p).toISOString():p||new Date().toISOString();await b` 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}, ${u}) `}async function Va(n,e){const{endedAt:t,pointCount:r=0,distanceM:o=0}=e;await b` UPDATE gps_trails SET ended_at = ${t}, point_count = ${r}, distance_m = ${o}, status = 'completed' WHERE id = ${n} `,Y("gps_trails","update",n)}async function Xa(){return b`SELECT * FROM gps_trails WHERE synced = 0 AND status = 'completed' ORDER BY started_at ASC`}async function Ya(n){return b`SELECT * FROM gps_trail_points WHERE trail_id = ${n} ORDER BY seq ASC`}async function Ja(n,e=null){await b`UPDATE gps_trails SET synced = 1, remote_id = ${e} WHERE id = ${n}`,Y("gps_trails","update",n)}const ir=3.28084,sr=621371e-9,lr=10.7639,cr=247105e-9,dr=3861e-10;function Nt(){return localStorage.getItem("measurement-system")||"metric"}function Ot(n){if(Nt()==="imperial"){const e=n*ir;return e>=5280?Math.round(n*sr*100)/100+" mi":Math.round(e)+" ft"}return n>1e3?Math.round(n/1e3*100)/100+" km":Math.round(n*100)/100+" m"}function Za(n){if(Nt()==="imperial"){const e=n*ir,t=n*sr;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 dt(n){if(Nt()==="imperial"){const e=n*cr;return e>=640?Math.round(n*dr*100)/100+" mi²":e>=1?Math.round(e*100)/100+" acres":Math.round(n*lr).toLocaleString("en")+" ft²"}return n>1e6?Math.round(n/1e6*100)/100+" km²":Math.round(n*100)/100+" m²"}function Qa(n){if(Nt()==="imperial"){const e=n*lr,t=n*cr,r=n*dr;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 ei(n){return dt(Math.PI*n*n)}function ti(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||f<-o||f>1+o?null:{point:[n[0]+u*a,n[1]+u*i],t:Math.max(0,Math.min(1,u)),u:Math.max(0,Math.min(1,f))}}function ur(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 ft(n,e){return(n[0]-e[0])**2+(n[1]-e[1])**2}function oi(n,e){const t=[];for(let o=0;oo.lineSegIdx!==a.lineSegIdx?o.lineSegIdx-a.lineSegIdx:o.lineT-a.lineT),t}function ni(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(ft(i.point,r[i.ringSegIdx])=s&&o[t[c].origOrder]++}return{ring:r,indices:o}}function Yo(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 ft(r[r.length-1],t.point)>1e-10&&r.push(t.point),r}function Zo(n,e){const t=ur(n);return e&&t<0||!e&&t>0?n.slice().reverse():n}function Qo(n){if(n.length<2)return n;const e=n[0],t=n[n.length-1];return ft(e,t)>1e-10?[...n,e.slice()]:n}function ri(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(ho(s[0],e)){const c=s[0],d=s[1],p=c[0]-d[0],u=c[1]-d[1],f=Math.sqrt(p*p+u*u)||1,h=i*2/f;s[0]=[c[0]+p*h,c[1]+u*h]}const l=s.length-1;if(ho(s[l],e)){const c=s[l],d=s[l-1],p=c[0]-d[0],u=c[1]-d[1],f=Math.sqrt(p*p+u*u)||1,h=i*2/f;s[l]=[c[0]+p*h,c[1]+u*h]}return s}function Lt(n,e){const t=n[0],r=n.slice(1),o=ri(e,t),a=oi(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}=ni(t,a),d=c[0],p=c[1],[u,f]=d0,L=Zo(y,x),v=Zo(_,x),S=[L],P=[v];for(const k of r){const q=ai(k);ho(q,L)?S.push(k):P.push(k)}return[S,P]}function ai(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 wt=[{stroke:"#ef4444",fill:"rgba(239,68,68,0.25)"},{stroke:"#3b82f6",fill:"rgba(59,130,246,0.25)"}],si=new M({stroke:new I({color:"#0ea5e9",width:3}),fill:new A({color:"rgba(14,165,233,0.15)"})}),li=new M({stroke:new I({color:"#f43f5e",width:2,lineDash:[8,6]}),image:new fe({radius:5,fill:new A({color:"#f43f5e"}),stroke:new I({color:"#fff",width:1.5})})});class ci extends xo{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 j({useSpatialIndex:!1}),this._overlayLayer=new D({source:this._overlaySource,displayInLayerSwitcher:!1,style:si})}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 j?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 he([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=Lt(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 pt(s)),c.setStyle(new M({stroke:new I({color:wt[0].stroke,width:2.5}),fill:new A({color:wt[0].fill})}));const d=t.clone();d.setGeometry(new pt(l)),d.setStyle(new M({stroke:new I({color:wt[1].stroke,width:2.5}),fill:new A({color:wt[1].fill})}));const p=[c,d];this.dispatchEvent({type:"beforesplit",original:t,features:p}),r.dispatchEvent({type:"beforesplit",original:t,features:p});const u=this.getMap();if(u?.dispatchEvent("undoblockstart"),r.removeFeature(t),r.addFeature(c),r.addFeature(d),u?.dispatchEvent("undoblockend"),this.dispatchEvent({type:"aftersplit",original:t,features:p}),r.dispatchEvent({type:"aftersplit",original:t,features:p}),this._removeDrawInteraction(),t.get("_layerType")==="parcel"){this._splitFeatures=p,this._phase="pick",this._overlaySource.clear();const h=this.getMap();h&&(h.getTargetElement().style.cursor=""),F("Click the polygon that should keep the original identifier.","info",5e3),this.dispatchEvent({type:"splitparcel",features:p,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 he([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 ui(n,e){const t=st(n);return e&&t<0||!e&&t>0?n.slice().reverse():n}function pi(n){return n.length<2?n:Ke(n[0],n[n.length-1])>1e-10?[...n,n[0].slice()]:n}function ze(n,e,t){const r=t[0]-e[0],o=t[1]-e[1],a=r*r+o*o;if(a<1e-20)return Ke(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 tn(n,e){let t=0,r=1/0;const o=n.length-1;for(let a=0;a0;){const v=(w+1)%a,S=m?(x-1+i)%i:(x+1)%i;if(v===y||S===_)break;if(Be(n[v],e[S],s)){w=v,x=S;continue}if(ze(n[v],e[x],e[S])0;){const v=(y-1+a)%a,S=m?(_+1)%i:(_-1+i)%i;if(v===w||S===x)break;if(Be(n[v],e[S],s)){y=v,_=S;continue}if(ze(n[v],e[_],e[S])r+1)););return o}function hi(n,e,t,r,o=5){const a=n[0],i=e[0],s=n.slice(1),l=e.slice(1),c=tn(a,t),d=tn(i,r),p=fi(a,i,c.segIdx,d.segIdx,o);if(!p)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:u,endA:f,startB:h,endB:g,reversed:m}=p;a.length-1,i.length-1;const y=Wt(a,f,u);let w;m?w=Wt(i,h,g):w=Wt(i,g,h);const _=[...y,...w.slice(1)],x=o*o;_.length>2&&Ke(_[_.length-1],_[0])k*1.5)return console.warn(`[polygonMerge] Area mismatch: A=${v.toFixed(1)}, B=${S.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=st(a)>0,z=ui(L,q),B=[...s,...l].filter(ce=>{const Q=ce.reduce((H,be)=>H+be[0],0)/(ce.length-1),ye=ce.reduce((H,be)=>H+be[1],0)/(ce.length-1);return di([Q,ye],z)});return{coords:[z,...B]}}const on=new M({stroke:new I({color:"#0ea5e9",width:3}),fill:new A({color:"rgba(14,165,233,0.15)"})}),gi=new M({stroke:new I({color:"#f59e0b",width:3}),fill:new A({color:"rgba(245,158,11,0.15)"})}),mi=new M({stroke:new I({color:"#0ea5e9",width:3}),fill:new A({color:"rgba(14,165,233,0.15)"}),text:new We({text:"A",font:"bold 22px Exo, sans-serif",fill:new A({color:"#0ea5e9"}),stroke:new I({color:"#fff",width:4}),overflow:!0})}),yi=new M({stroke:new I({color:"#f59e0b",width:3}),fill:new A({color:"rgba(245,158,11,0.15)"}),text:new We({text:"B",font:"bold 22px Exo, sans-serif",fill:new A({color:"#f59e0b"}),stroke:new I({color:"#fff",width:4}),overflow:!0})}),bi=new M({stroke:new I({color:"#ec4899",width:4,lineDash:[10,6]})}),wi=new M({stroke:new I({color:"#10b981",width:2.5}),fill:new A({color:"rgba(16,185,129,0.3)"})});class _i extends xo{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 j({useSpatialIndex:!1}),this._highlightLayer=new D({source:this._highlightSource,displayInLayerSwitcher:!1,style:t=>t.get("_highlightStyle")||on}),this._edgeSource=new j({useSpatialIndex:!1}),this._edgeLayer=new D({source:this._edgeSource,displayInLayerSwitcher:!1,style:bi})}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 j?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"?on:gi,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),p=new he([e.coordinate,c]).getLength()/e.frameState.viewState.resolution;p{t.get("_permanent")&&e.push(t)}),e.forEach(t=>this._highlightSource.removeFeature(t)),this._featureA){const t=this._featureA.clone();t.set("_highlightStyle",mi),t.set("_permanent",!0),this._highlightSource.addFeature(t)}if(this._featureB){const t=this._featureB.clone();t.set("_highlightStyle",yi),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 vi(n,e){return(n[0]-e[0])**2+(n[1]-e[1])**2}function nn(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 Kt(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 Ge(n,e,t){const r=n[0],o=r.length-1;let a=0,i=0;for(let c=0;cp&&(p=_)}const u=(p-d)*1.5,f=[];let h=n,g=e;for(let m=0;mv&&(v=Q)}let S=L,P=v,k=null,q=null,z=1/0;for(let Z=0;Z<40;Z++){const B=(S+P)/2,ce=Kt(l,i,s,B,u),Q=Lt(h,ce);if(!Q){const R=(P-S)*.01,W=Kt(l,i,s,B+R,u),re=Lt(h,W);if(re){const[ke,Ie]=re,Ye=Ge(ke,l,i),Je=Ge(Ie,l,i),Ze=Yethis._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 j({useSpatialIndex:!1}),this._overlayLayer=new D({source:this._overlaySource,displayInLayerSwitcher:!1,style:Si}),this._edgeSource=new j({useSpatialIndex:!1}),this._edgeLayer=new D({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 j?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",F("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 he([e.coordinate,l]).getLength()/e.frameState.viewState.resolution;d{const g=t.clone();return g.setGeometry(new pt(f)),g.setStyle(new M({stroke:new I({color:s[h].stroke,width:2.5}),fill:new A({color:s[h].fill})})),g}),c={type:"beforedivide",original:t,features:l};this.dispatchEvent(c),r.dispatchEvent({...c});const d=this.getMap();d?.dispatchEvent("undoblockstart"),r.removeFeature(t);for(const f of l)r.addFeature(f);d?.dispatchEvent("undoblockend");const p={type:"afterdivide",original:t,features:l};this.dispatchEvent(p),r.dispatchEvent({...p}),t.get("_layerType")==="parcel"?(this._dividedFeatures=l,this._phase="pick",F("Click the polygon that should keep the original identifier.","info",5e3),this.dispatchEvent({type:"dividedparcel",features:l,originalProps:t.getProperties(),source:r})):(F(`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 he([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 We({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 D({title:"Markers",source:this.markerSource,style:s=>this.getFeatureStyle(s),visible:!1}),this.overlayGroup=new Pe({title:"Overlays"}),this.map=new No({target:e,layers:[r,this.markersLayer,this.overlayGroup],view:new Br({center:ee(t.center||[0,0]),zoom:t.zoom||2,minZoom:t.minZoom||2,maxZoom:t.maxZoom||19})});const o=new Yr({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 Gr({bar:!0,steps:4,text:!0,minWidth:140}),this.map.addControl(this.scaleBar),this._initGpsRendering(),this._createLocationControl(),this._createBaseMapPicker();const i=new Jr({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),p=[c,d],u=ee(p);this.navigateTo(c,d,14);const f={coordinate:u,lonLat:p,name:l.display_name||l.name||"Unknown",searchResult:l};this.searchSelectCallbacks.forEach(h=>h(f))}}),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 j,this.drawingsLayer=new D({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 fe({radius:6,fill:new A({color:"#f59e0b"}),stroke:new I({color:"#fff",width:1.5})})})}),this._drawingsGroup=new Pe({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 jr({condition:qr,toggleCondition:Ur,filter:(u,f)=>!!f,layers:u=>u instanceof D&&u.get("selectable")!==!1}),this._selectInteraction.setActive(!1),this.map.addInteraction(this._selectInteraction),this._dragBoxSelect=new zr({condition:Hr}),this._dragBoxSelect.setActive(!1),this.map.addInteraction(this._dragBoxSelect),this._dragBoxSelect.on("boxend",()=>{const u=this._dragBoxSelect.getGeometry().getExtent(),f=this._selectInteraction.getFeatures();this.map.getLayers().forEach(h=>this._boxAddFromLayer(h,u,f)),this._refreshVertexOverlay?.()}),this._selectInteraction.on("change:active",()=>{this._dragBoxSelect.setActive(this._selectInteraction.getActive())}),this._modifyInteraction=new Zr({features:this._selectInteraction.getFeatures()}),this._modifyInteraction.setActive(!1),this._modifyInteraction.on("modifyend",u=>{if(!this._featureModifiedCallbacks?.length)return;const f=u.features?.getArray?.()||[];for(const h of f)for(const g of this._featureModifiedCallbacks)try{g(h)}catch(m){console.warn("[MapView] onFeatureModified callback failed:",m)}}),this._undoRedo=new Qr,this.map.addInteraction(this._undoRedo),this.editBar=new co({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 Go({group:!0,className:"ol-editbar-actions",controls:[new it({html:'',className:"ol-undo",title:"Undo",handleClick:()=>{this._undoRedo.hasUndo()&&this._undoRedo.undo()}}),new it({html:'',className:"ol-redo",title:"Redo",handleClick:()=>{this._undoRedo.hasRedo()&&this._undoRedo.redo()}}),new it({html:'',className:"ol-save",title:"Save drawings",handleClick:()=>{this.dispatchEditEvent("save")}})]});this.editBar.addControl(r),this._lineSplitInteraction=new ea,this._polygonSplitInteraction=new ci,this.map.addInteraction(this._lineSplitInteraction),this.map.addInteraction(this._polygonSplitInteraction),this._lineSplitInteraction.setActive(!1),this._polygonSplitInteraction.setActive(!1),this._polygonSplitInteraction.on("splitpick",u=>{const f=["UPN","upn","id","parcelid","parcel_id","PARCELID","PARCEL_ID","ID"];for(const h of u.features)if(h!==u.picked)for(const g of f)h.get(g)!==void 0&&h.set(g,"")}),this._polygonDivideInteraction=new ki,this.map.addInteraction(this._polygonDivideInteraction),this._polygonDivideInteraction.setActive(!1);const o=new xe({html:'',className:"ol-split-line",title:"Split Lines",name:"SplitLine",interaction:this._lineSplitInteraction,autoActivate:!0}),a=new xe({html:'',className:"ol-split-polygon",title:"Split Polygons",name:"SplitPolygon",interaction:this._polygonSplitInteraction}),i=new xe({html:'',className:"ol-split-divide",title:"Divide Polygon",name:"DividePolygon",interaction:this._polygonDivideInteraction}),s=new Go({toggleOne:!0,autoDeactivate:!0,controls:[o,a,i]}),l=new xe({className:"ol-split",title:"Split",name:"Split",bar:s,onToggle:u=>{u||(this._lineSplitInteraction.setActive(!1),this._polygonSplitInteraction.setActive(!1),this._polygonDivideInteraction.setActive(!1))}});this.editBar.addControl(l),this._polygonDivideInteraction.on("divideform",u=>{this.showDividePopup(u.feature,u.source,u.coordinate)}),this._polygonDivideInteraction.on("dividecancel",()=>{this.hideDividePopup()}),this._polygonDivideInteraction.on("dividepick",u=>{const f=["UPN","upn","id","parcelid","parcel_id","PARCELID","PARCEL_ID","ID"];for(const h of u.features)if(h!==u.picked)for(const g of f)h.get(g)!==void 0&&h.set(g,"")}),this._polygonMergeInteraction=new _i,this.map.addInteraction(this._polygonMergeInteraction),this._polygonMergeInteraction.setActive(!1);const c=new xe({html:'',className:"ol-merge",title:"Merge Polygons",name:"Merge",interaction:this._polygonMergeInteraction});this.editBar.addControl(c),this._polygonMergeInteraction.on("mergedparcel",u=>{this.showMergeIdentifierPopup(u.merged,u.propsA,u.propsB,u.coordinate)}),this._snapGuidesEnabled=localStorage.getItem("snap-guides-enabled")==="1",this._snapGuides=new ta({pixelTolerance:10,vectorClass:Wr}),this.map.addInteraction(this._snapGuides);const d=["DrawPoint","DrawLine","DrawPolygon","DrawHole","DrawRegular"];for(const u of d){const f=this.editBar.getInteraction(u);f&&f.on("change:active",()=>{f.getActive()&&this._snapGuides.setDrawInteraction(f)})}this._modifyInteraction&&this._snapGuides.setModifyInteraction(this._modifyInteraction);const p=new it({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._buildToolbarRail(),this.setEditMode(!1),this._drawingsGroup.on("change:visible",()=>{const u=this._drawingsGroup.getVisible();this.setEditMode(u)}),this._isTouchOnlyDevice()?(this.touchCursor=new jo({className:"ol-editbar-cursor"}),this.map.addInteraction(this.touchCursor),this.touchCursor.setActive(!1),console.log("[MapView] Touch-only device detected — TouchCursor added")):console.log("[MapView] Fine pointer available — TouchCursor skipped"),this._installPointerCapabilityWatcher(),this.drawingsSource.on("addfeature",u=>{const f=u.feature,h=f.getGeometry();if(!h||h.getType()!=="Polygon")return;const g=h.getInteriorPoint().getCoordinates();this.showDrawnPolygonPopup(f,g)}),this._editKeyHandler=u=>{if(u.key!=="Delete"&&u.key!=="Backspace"||!this._editBarActive)return;const f=u.target,h=f&&f.tagName;f?.isContentEditable||h==="INPUT"||h==="TEXTAREA"||h==="SELECT"||this._deleteSelectedFeatures()&&u.preventDefault()},document.addEventListener("keydown",this._editKeyHandler),console.log("[MapView] EditBar initialised with Drawings group, UndoRedo and SnapGuides (default:",this._snapGuidesEnabled?"ON":"OFF",")")}_deleteSelectedFeatures(){if(!this._editBarActive)return!1;const e=this.editBar?.getInteraction("Delete"),t=this._selectInteraction?.getFeatures();return!e||!t||t.getLength()===0?!1:(e.delete(t),t.clear(),this._refreshVertexOverlay?.(),!0)}_boxAddFromLayer(e,t,r){if(!e)return;if(typeof e.getLayers=="function"){e.getLayers().forEach(a=>this._boxAddFromLayer(a,t,r));return}if(!(e instanceof D)||e.getVisible&&!e.getVisible()||e===this._vertexOverlayLayer||e===this._gpsTrailLayer||e===this._gpsPositionLayer||e.get("selectable")===!1||e.get("displayInLayerSwitcher")===!1)return;const o=e.getSource&&e.getSource();!o||typeof o.forEachFeatureIntersectingExtent!="function"||o.forEachFeatureIntersectingExtent(t,a=>{r.getArray().includes(a)||r.push(a)})}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){const t=this._editBarActive;if(this._editBarActive=!!e,this._editBarActive&&!t&&this._undoRedo)try{this._undoRedo._watchSources(),this._undoRedo.clear()}catch(r){console.warn("[MapView] UndoRedo re-watch/clear failed:",r)}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._withoutUndoRecording(()=>this._silentClearSource(this._vertexOverlaySource)),console.log("[MapView] Edit mode:",this._editBarActive?"ON":"OFF")}isEditMode(){return this._editBarActive}_isTouchOnlyDevice(){if(!("ontouchstart"in window||navigator.maxTouchPoints>0||navigator.msMaxTouchPoints>0))return!1;if(typeof window.matchMedia!="function")return!0;const t=window.matchMedia("(any-pointer: fine)").matches,r=window.matchMedia("(any-hover: hover)").matches;return!t&&!r}_refreshTouchCursor(){const e=this._isTouchOnlyDevice();e&&!this.touchCursor?(this.touchCursor=new jo({className:"ol-editbar-cursor"}),this.map.addInteraction(this.touchCursor),this.touchCursor.setActive(this._editBarActive),console.log("[MapView] Pointer change → TouchCursor added")):!e&&this.touchCursor&&(this.map.removeInteraction(this.touchCursor),this.touchCursor=null,console.log("[MapView] Pointer change → TouchCursor removed (fine pointer present)"))}_installPointerCapabilityWatcher(){if(typeof window.matchMedia!="function"||this._pointerMediaQueries)return;const e=["(any-pointer: fine)","(any-hover: hover)"].map(r=>window.matchMedia(r)),t=()=>this._refreshTouchCursor();for(const r of e)typeof r.addEventListener=="function"?r.addEventListener("change",t):typeof r.addListener=="function"&&r.addListener(t);this._pointerMediaQueries=e,this._pointerMediaListener=t}_buildToolbarRail(){const e=this.editBar?.element;if(!e)return;e.classList.add("ol-left","ol-editbar-rail");for(const[o,a]of Object.entries(Ii))for(const i of e.querySelectorAll(`.${o}`)){const s=i.matches("button")?i:i.querySelector(":scope > button");s&&(s.innerHTML=``)}const t=document.createDocumentFragment();for(const o of Pi){const a=o.controls.map(s=>e.querySelector(`:scope > .${s}`)).filter(Boolean);if(!a.length)continue;const i=document.createElement("div");i.className="ol-rail-label",i.textContent=o.label,i.setAttribute("aria-hidden","true"),t.appendChild(i);for(const s of a)t.appendChild(s)}e.appendChild(t);const r=document.createElement("div");r.className="ol-rail-grip",r.title="Drag to move the toolbar — double-click to reset",r.setAttribute("role","button"),r.setAttribute("aria-label","Move the drawing toolbar"),e.insertBefore(r,e.firstChild),this._makeToolbarDraggable(e,r)}_makeToolbarDraggable(e,t){const r="editbar-position",o=window.matchMedia("(max-width: 576px)"),a=(g,m,y)=>Math.min(Math.max(g,m),y),i=(g,m)=>{const y=e.offsetParent||this.map.getTargetElement();if(!y)return;const w=Math.max(0,y.clientWidth-e.offsetWidth-4),_=Math.max(0,y.clientHeight-e.offsetHeight-4);e.style.left=`${a(g,4,w)}px`,e.style.top=`${a(m,4,_)}px`,e.style.transform="none",e.style.bottom="auto"},s=()=>{e.style.left="",e.style.top="",e.style.transform="",e.style.bottom=""},l=()=>{if(o.matches){s();return}try{const g=JSON.parse(localStorage.getItem(r)||"null");g&&Number.isFinite(g.left)&&Number.isFinite(g.top)&&i(g.left,g.top)}catch{}};let c=0,d=0,p=0,u=0;const f=g=>{i(p+(g.clientX-c),u+(g.clientY-d))},h=g=>{t.releasePointerCapture?.(g.pointerId),window.removeEventListener("pointermove",f),window.removeEventListener("pointerup",h),e.classList.remove("is-dragging");try{localStorage.setItem(r,JSON.stringify({left:parseFloat(e.style.left)||0,top:parseFloat(e.style.top)||0}))}catch{}};t.addEventListener("pointerdown",g=>{if(o.matches||g.button!==0)return;g.preventDefault();const m=e.getBoundingClientRect(),y=(e.offsetParent||this.map.getTargetElement()).getBoundingClientRect();c=g.clientX,d=g.clientY,p=m.left-y.left,u=m.top-y.top,e.classList.add("is-dragging"),t.setPointerCapture?.(g.pointerId),window.addEventListener("pointermove",f),window.addEventListener("pointerup",h)}),t.addEventListener("dblclick",()=>{try{localStorage.removeItem(r)}catch{}s()}),o.addEventListener("change",l),l()}_setupVertexOverlay(){this._vertexOverlaySource=new j,this._vertexOverlayLayer=new D({title:"__vertex_highlight__",source:this._vertexOverlaySource,zIndex:990,style:new M({image:new fe({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())}_withoutUndoRecording(e){const t=this._undoRedo;if(!t)return e();const r=t._record;t._record=!1;try{return e()}finally{t._record=r}}_silentClearSource(e){if(!e)return;const t=e.getFeatures();for(let r=t.length-1;r>=0;r--)e.removeFeature(t[r])}_refreshVertexOverlay(){this._vertexOverlaySource&&this._withoutUndoRecording(()=>{if(this._silentClearSource(this._vertexOverlaySource),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 pe(new mt(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 Ee({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 p={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 Ee({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[u,f]of Object.entries(i))c.includes(u)||f===void 0||f===null||(d+=` ${this.escapeHtml(u)} ${this.escapeHtml(String(f))} `);if(l==="Polygon"||l==="MultiPolygon"){const u=ct(s,{projection:"EPSG:3857"}),f=Qa(u);d+=` area ${f} `}else if(l==="LineString"||l==="MultiLineString"){const u=kt(s,{projection:"EPSG:3857"}),f=Za(u);d+=` length ${f} `}else if(l==="Point"){const u=Oe(s.getCoordinates()),f=u[0].toFixed(6),h=u[1].toFixed(6);d+=` longitude ${f} latitude ${h} `}const p=`
${this.escapeHtml(o)}
${d}
`;this.infoPopupElement.innerHTML=p,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()});const i=r.filter(c=>!c.empty).map(c=>({label:c.label,value:String(c.value).replace(/<[^>]*>/g,"")})),s=this.infoPopupElement.querySelector("#info-popup-analyse");if(s){const c=a?a.parcelFeatures.length+a.zoneFeatures.length+Object.values(a.otherByLayer).reduce((d,p)=>d+p.length,0):0;!a||c===0?(s.disabled=!0,s.style.opacity="0.5",s.style.cursor="not-allowed",s.title="No intersecting features to analyse"):s.addEventListener("click",()=>{window.dispatchEvent(new CustomEvent("lupmis:analyse-features",{detail:{title:t,...a}}))})}const l=this.infoPopupElement.querySelector("#info-popup-export-gis");if(l){const c=a?a.parcelFeatures.length+a.zoneFeatures.length+Object.values(a.otherByLayer).reduce((d,p)=>d+p.length,0):0;!a||c===0?(l.disabled=!0,l.style.opacity="0.5",l.style.cursor="not-allowed",l.title="No intersecting features to export"):l.addEventListener("click",()=>{window.dispatchEvent(new CustomEvent("lupmis:export-gis",{detail:{title:t,pdfRows:i,...a}}))})}}showCircleIntersectionPopup(e,t){const r=e.getGeometry();if(!r||typeof r.getCenter!="function")return;const o=Kr(r,64),a=o.getExtent(),i=r.getRadius(),s=[],l=[],c={},d=m=>{const y=m.getGeometry();if(!y)return!1;const w=y.getExtent();return w[2]a[2]||w[3]a[3]?!1:o.intersectsExtent(w)&&this._geometriesIntersect(o,y)},p=(m,y)=>{m.getLayers().forEach(w=>{if(w instanceof Pe)p(w,w.get("title")||y);else if(w instanceof D&&w.getVisible()){const _=w.get("title")||y||"Unknown",x=w.getSource();if(!x)return;const L=x.getFeaturesInExtent(a);for(const v of L){const S=v.get("_layerType");S==="measure_circle"||S==="measure_circle_radius"||d(v)&&(S==="parcel"?s.push(v):S==="collector_zone"?l.push(v):(c[_]||(c[_]=[]),c[_].push(v)))}}})};p(this.overlayGroup,"Overlays");const u=Ot(i),f=Math.PI*i*i,h=dt(f),g=[{label:"Radius",value:u,_first:!0},{label:"Area",value:h},...this._collectIntersectionRows(s,l,c)];this._showAnalysisPopup("⭕","Circle Analysis",g,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=ct(r,{projection:"EPSG:3857"}),i=dt(a),s=kt(r,{projection:"EPSG:3857"}),l=Ot(s),c=[],d=[],p={},u=g=>{const m=g.getGeometry();if(!m)return!1;const y=m.getExtent();return y[2]o[2]||y[3]o[3]?!1:r.intersectsExtent(y)&&this._geometriesIntersect(r,m)},f=(g,m)=>{g.getLayers().forEach(y=>{if(y instanceof Pe)f(y,y.get("title")||m);else if(y instanceof D&&y.getVisible()){const w=y.get("title")||m||"Unknown",_=y.getSource();if(!_)return;const x=_.getFeaturesInExtent(o);for(const L of x){const v=L.get("_layerType");v==="measure_area"||v==="measure_circle"||v==="measure_circle_radius"||u(L)&&(v==="parcel"?c.push(L):v==="collector_zone"?d.push(L):(p[w]||(p[w]=[]),p[w].push(L)))}}})};f(this.overlayGroup,"Overlays");const h=[{label:"Area",value:i,_first:!0},{label:"Perimeter",value:l},...this._collectIntersectionRows(c,d,p)];this._showAnalysisPopup("📐","Area Analysis",h,t,{kind:"area",clipGeometry:r,parcelFeatures:c,zoneFeatures:d,otherByLayer:p})}_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[p,u]of c.entries())d[p]=u;d._layerType="parcel";for(const[p,u]of Object.entries(d))this._parcelEditFeature.set(p,u);for(const p of this._parcelEditCallbacks)p(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 Ee({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=h=>{for(const g of a)if(h[g]!==void 0&&h[g]!==null&&String(h[g]).trim())return{field:g,value:String(h[g])};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 g=this.mergePopupElement.querySelector('input[name="merge-choice"]:checked').value==="A"?t:r,m=["geometry"];for(const[y,w]of Object.entries(g))m.includes(y)||e.set(y,w);e.set("_layerType","parcel");for(const y of this._parcelEditCallbacks)y(e,g);d()});const p=this.mergePopupElement.querySelectorAll("label"),u=this.mergePopupElement.querySelectorAll('input[name="merge-choice"]'),f=()=>{p.forEach(h=>{const g=h.querySelector("input");h.style.borderColor=g.checked?g.value==="A"?"#0ea5e9":"#f59e0b":"var(--border, #1e1a4b1f)"})};u.forEach(h=>h.addEventListener("change",f)),f()}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 Ee({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 Ee({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 Pe)r(a);else if(a instanceof D){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 p=this.escapeHtml(d);o+=`
`}const a=e.getGeometry(),i=ct(a,{projection:"EPSG:3857"}),l=`
📐 Polygon Attributes
Area: ${dt(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 p=new FormData(c),u={};for(const[f,h]of p.entries())u[f]=h;for(const[f,h]of Object.entries(u))this._drawnPolygonFeature.set(f,h);this._drawnPolygonFeature.set("_layerType","parcel");for(const f of this._drawnPolygonCallbacks)f(this._drawnPolygonFeature,u);this.hideDrawnPolygonPopup()})}hideDrawnPolygonPopup(){this.drawnPolygonPopup.setPosition(void 0),this._drawnPolygonFeature=null}onDrawnPolygonSave(e){this._drawnPolygonCallbacks.push(e)}onFeatureModified(e){this._featureModifiedCallbacks||(this._featureModifiedCallbacks=[]),this._featureModifiedCallbacks.push(e)}onDblClick(e){return this.dblClickCallbacks.push(e),this.dblClickCallbacks.length===1&&this.map.on("dblclick",t=>{const[r,o]=Oe(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 Ee({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]=Oe(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 ae({title:"Topographic",type:"base",zIndex:-100,visible:e==="topo",source:new Me({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 ae({title:"Carto Light",type:"base",zIndex:-100,visible:e==="carto-light",source:new Me({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 ae({title:"Carto Dark",type:"base",zIndex:-100,visible:e==="carto-dark",source:new Me({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 ae({title:"OSM Cycle map",type:"base",zIndex:-100,visible:!1,source:new $o({url:"https://tile.thunderforest.com/cycle/{z}/{x}/{y}.png?apikey=ae1339c46dd3446b9c491e7336d38760"})});a.set("basemapKey","cycle");const i=new ae({title:"Satellite",type:"base",zIndex:-100,visible:e==="satellite",source:new Me({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 ae({title:"Google Sat",type:"base",zIndex:-100,visible:e==="googlesat",source:new Me({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 ae({title:"OpenStreetMap",type:"base",zIndex:-100,visible:e==="osm",source:new $o});l.set("basemapKey","osm"),this._baseMapLayers=[r,o,a,i,s,l,t];const c=new Pe({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 j,this._gpsTrailSource=new j,this._gpsTrailCoords=[],this._gpsTrailLayer=new D({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 D({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 fe({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=ee([e,t]);if(this._gpsPositionSource.clear(),r&&r>0){const i=r/Math.cos(t*Math.PI/180),s=new pe({geometry:new pt([this._circleRing(o,i)])});s.set("_kind","accuracy"),this._gpsPositionSource.addFeature(s)}const a=new pe({geometry:new mt(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:ee([e,t]),zoom:r,duration:500})}startTrailRender(){this._gpsTrailCoords=[],this._gpsTrailSource.clear()}appendTrailPoint(e,t){e==null||t==null||(this._gpsTrailCoords.push(ee([e,t])),this._gpsTrailSource.clear(),this._gpsTrailCoords.length>=2&&this._gpsTrailSource.addFeature(new pe({geometry:new he(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 fe({radius:22,fill:new A({color:"rgba(220, 38, 38, 0.25)"}),stroke:new I({color:"#dc2626",width:3})})}),new M({text:new We({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 pe({geometry:new mt(ee([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 pe({geometry:new mt(ee([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:ee([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 Oe(e)}getZoom(){return this.map.getView().getZoom()}setCenter(e,t){this.map.getView().setCenter(ee([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]=Oe(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]=Oe(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:p=5,pointFillColor:u=null,pointStrokeColor:f="#ffffff",pointStrokeWidth:h=1.5}=r,g=new j({features:new le().readFeatures(e,{featureProjection:"EPSG:3857"})}),m=new A({color:l}),y=new fe({radius:p,fill:new A({color:u||a}),stroke:new I({color:f,width:h})}),w=new I({color:a,width:i,...s?{lineDash:s}:{}});let _;if(c){const S=d??i+2;_=[new M({stroke:new I({color:c,width:S})}),new M({stroke:w,fill:m,image:y})]}else _=new M({stroke:w,fill:m,image:y});const x=new D({title:t,source:g,style:_,...r.declutter?{declutter:r.declutter}:{}});x.set("typeTag",r.typeTag||"VEC");const L=S=>S?S.includes("Polygon")?"Vector / Polygon":S.includes("LineString")?"Vector / Line":S.includes("Point")?"Vector / Point":"Vector":null;if(r.typeDescription)x.set("typeDescription",r.typeDescription);else{const S=g.getFeatures(),P=L(S[0]?.getGeometry?.()?.getType?.());if(P)x.set("typeDescription",P);else{const k=q=>{const z=L(q.feature.getGeometry?.()?.getType?.());z&&x.set("typeDescription",z),g.un("addfeature",k)};g.on("addfeature",k)}}return(o||this.overlayGroup).getLayers().push(x),console.log("[MapView] GeoJSON layer added:",t,"→",g.getFeatures().length,"features",o?`(in group "${o.get("title")}")`:""),x}addLayerGroup(e,t,r=""){const o=new Pe({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 Bo({url:r,params:s,serverType:a.serverType!==void 0?a.serverType:"geoserver",crossOrigin:"anonymous",hidpi:!1,attributions:a.attributions}),c=new ae({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",()=>{F(`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 Me({url:r,crossOrigin:"anonymous",maxZoom:o.maxZoom!==void 0?o.maxZoom:19,attributions:o.attributions}),s=new ae({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",()=>{F(`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}async addCOGLayer(e,t,r,o={}){const a=this.getLayerGroupByTitle(e);if(!a)return console.warn(`[MapView] Layer group "${e}" not found — cannot add COG layer "${t}"`),null;const i=await this._probeCOGUrl(r);if(i)return console.warn(`[MapView] COG pre-flight failed for "${t}":`,i),F(i,"error",8e3),null;let s,l;try{[{default:s},{default:l}]=await Promise.all([Le(()=>import("./geotiff-BaoeLn6q.js").then(d=>d.W),__vite__mapDeps([0,1,2,3])),Le(()=>import("./geotiff-BaoeLn6q.js").then(d=>d.G),__vite__mapDeps([0,1,2,3]))])}catch(d){return console.error("[MapView] Could not load COG support:",d),F("Raster (COG) support could not be loaded — check your connection.","error",6e3),null}let c=null;try{const d={sources:[{url:r,...o.min!==void 0?{min:o.min}:{},...o.max!==void 0?{max:o.max}:{}}],normalize:o.normalize!==void 0?o.normalize:!0,attributions:o.attributions},p=new l(d);c=new s({title:t,source:p,visible:o.visible!==void 0?o.visible:!0,opacity:o.opacity!==void 0?o.opacity:1,zIndex:o.zIndex,...o.style?{style:o.style}:{}}),c.set("typeTag","COG"),c.set("typeDescription","Cloud-Optimized GeoTIFF"),p.on("error",u=>{console.warn(`[MapView] COG "${t}" source error:`,u),F(`Raster "${t}" could not be loaded. Check that the file is a valid COG and is CORS-enabled.`,"warning",6e3)}),a.getLayers().push(c)}catch(d){return console.error(`[MapView] Failed to add COG layer "${t}":`,d),F(`Could not add raster "${t}": ${d.message}`,"error",6e3),null}return o.onlineOnly&&this._attachOnlineOnlyHandler(c,t),console.log(`[MapView] COG layer added: "${t}" → group "${e}"`),c}async _probeCOGUrl(e){let t;try{t=await fetch(e,{headers:{Range:"bytes=0-0"}})}catch{return"Could not reach that file. It may be offline, or the server may not allow cross-origin (CORS) requests from this app."}if(t.status===404)return"No file at that URL (404). Check the address.";if(!t.ok&&t.status!==206)return`The server refused the request (HTTP ${t.status}).`;const r=(t.headers.get("content-type")||"").toLowerCase();return r.includes("text/html")?"That URL is a web page, not a GeoTIFF. Paste the direct link to the .tif file itself — a documentation or viewer page will not work.":r.startsWith("text/")||r.includes("json")?`That URL returns ${r.split(";")[0]}rather than image data. Paste the direct link to the .tif file.`:(t.status===200&&F("This server does not support byte-range requests, so the whole raster will be downloaded. It may be slow for a large file.","warning",7e3),null)}getSelectedFeatures(){return this._selectInteraction?this._selectInteraction.getFeatures().getArray().slice():[]}listVectorLayers(e={}){const{visibleOnly:t=!1,nonEmptyOnly:r=!0}=e,o=[],a=i=>{if(!i)return;if(typeof i.getLayers=="function"){i.getLayers().forEach(a);return}if(!(i instanceof D)||i===this._vertexOverlayLayer||i===this._gpsTrailLayer||i===this._gpsPositionLayer||i.get("displayInLayerSwitcher")===!1||t&&i.getVisible&&!i.getVisible())return;const s=i.getSource&&i.getSource();if(!s||typeof s.getFeatures!="function")return;const l=s.getFeatures().length;r&&l===0||o.push({layer:i,title:i.get("title")||"(untitled layer)",count:l})};return this.map.getLayers().forEach(a),o}_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"),a=e.querySelector(".add-layer-type-hint");e.querySelectorAll('input[name="add-layer-type"]').forEach(s=>{s.addEventListener("change",()=>{const l=s.value;a.textContent=l==="cog"?"Cloud-Optimized GeoTIFF — the server must allow CORS and byte-range requests.":"",l==="cog"?(t.style.display="none",o.placeholder="https://example.com/data/elevation_cog.tif"):l==="xyz"?(t.style.display="none",o.placeholder="https://example.com/tiles/{z}/{x}/{y}.png"):(t.style.display="",o.placeholder=l==="wms"?"https://example.com/wms":"https://example.com/wfs",r.textContent=l==="wms"?"WMS LAYERS parameter (e.g. workspace:layer)":"WFS typename (e.g. workspace:layer)")})});const i=()=>this._hideAddLayerDialog();e.querySelector(".add-layer-close").addEventListener("click",i),e.querySelector(".add-layer-cancel").addEventListener("click",i),this._addLayerDialog.addEventListener("click",s=>{s.target===this._addLayerDialog&&i()}),e.querySelector(".add-layer-confirm").addEventListener("click",()=>{const s=e.querySelector('input[name="add-layer-type"]:checked').value,l=e.querySelector(".add-layer-url").value.trim(),c=e.querySelector(".add-layer-name").value.trim(),d=e.querySelector(".add-layer-title").value.trim();if(!l){e.querySelector(".add-layer-url").style.borderColor="#ef4444";return}if((s==="wms"||s==="wfs")&&!c){e.querySelector(".add-layer-name").style.borderColor="#ef4444";return}if(!d){e.querySelector(".add-layer-title").style.borderColor="#ef4444";return}this._addExternalLayer(s,l,c,d),this._hideAddLayerDialog()}),e.addEventListener("keydown",s=>{s.key==="Enter"&&(s.preventDefault(),e.querySelector(".add-layer-confirm").click()),s.key==="Escape"&&(s.preventDefault(),i())})}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)";const t=e.querySelector(".add-layer-type-hint");t&&(t.textContent=""),e.querySelectorAll('input[type="text"]').forEach(r=>{r.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){F('Layer group "External Source" not found.',"error",4e3);return}let i;switch(e){case"wms":{const s=new Bo({url:t,params:{LAYERS:r,TILED:!0,WIDTH:256,HEIGHT:256},serverType:"geoserver",crossOrigin:"anonymous",hidpi:!1});i=new ae({title:o,visible:!0,source:s}),s.on("tileloaderror",()=>{F(`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 j({url:s,format:new le});l.on("featuresloaderror",()=>{F(`WFS "${o}" — load error. Check URL and layer name.`,"warning",5e3)}),i=new D({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 ae({title:o,visible:!0,source:new Me({url:t,crossOrigin:"anonymous"})}),i.getSource().on("tileloaderror",()=>{F(`XYZ "${o}" — tile load error. Check the URL template.`,"warning",5e3)});break;case"cog":{const s=a.get("title")||"External Source";this.addCOGLayer(s,o,t).then(l=>{l&&(l.set("removable",!0),F(`Layer "${o}" added to External Source.`,"success",3e3),console.log(`[MapView] External COG layer added: "${o}"`))});return}default:F(`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),F(`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 p=a.querySelector(":scope > label");p&&p.nextSibling?a.insertBefore(d,p.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 p=e.get("_externalImportStatus")||"mapped",u=e.getSource()?.getFeatures().length??0,f=e.get("_externalImportErrorCount")??0,h=(()=>{switch(p){case"mapped":return{text:`Upload ${u}`,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:`${f} 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(!h)d&&d.remove();else if(c){d||(d=document.createElement("span"),d.className="ls-import-chip",c.appendChild(d)),d.className=`ls-import-chip ${h.cls}`,d.textContent=h.text,d.title=h.title,d.style.cursor=h.clickable?"pointer":"default",d.style.opacity=h.clickable?"1":"0.85";const g=d.cloneNode(!0);d.replaceWith(g),d=g,h.clickable&&d.addEventListener("click",m=>{m.preventDefault(),m.stopPropagation(),window.dispatchEvent(new CustomEvent("lupmis:import-chip-click",{detail:{importId:s,status:p,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}"`),F(`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&&F(`"${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 No}_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=ee([e,t]);this.map.getView().animate({center:a,zoom:r,duration:o})}}class Ai{constructor(e,t={}){this.map=e,this.options=t,this.measureSource=new j,this.measureLayer=new D({source:this.measureSource,style:this.getMeasureStyle(),title:"Measurements",zIndex:100,selectable:!1}),this.drawSource=new j,this.drawLayer=new D({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 fe({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 fe({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 Ee({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 Fe({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 fe({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 Vr){const s=i.getRadius(),l=ei(s),d=`${Ot(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 pe({geometry:new he([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(),qt(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 Fe({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=kt(i),l=Ot(s);this.measureTooltipElement.innerHTML=l,this.measureTooltip.setPosition(i.getLastCoordinate())})}),e.on("drawend",r=>{const o=r.feature,a=o.getGeometry(),i=kt(a);this.measureTooltipElement.className="measure-tooltip measure-tooltip-static",this.measureTooltipElement=null,this.createMeasureTooltip(),qt(t);const s={type:"line",length:i,feature:o};this.onMeasureCompleteCallbacks.forEach(l=>l(s))}),e}startAreaMeasure(){this.deactivate(),this.createMeasureTooltip();const e=new Fe({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=ct(i),l=dt(s);this.measureTooltipElement.innerHTML=l,this.measureTooltip.setPosition(i.getInteriorPoint().getCoordinates())})}),e.on("drawend",r=>{const o=r.feature,a=o.getGeometry(),i=ct(a);o.set("_layerType","measure_area"),o.set("_area",i),this.measureTooltipElement.className="measure-tooltip measure-tooltip-static",this.measureTooltipElement=null,this.createMeasureTooltip(),qt(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 Fe({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 Fe({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 Fe({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 co({group:!0,className:"map-tools-bar"}),r=new co({toggleOne:!0,group:!0}),o=new xe({html:'',title:"Measure Circle (radius & area)",className:"measure-circle-btn",onToggle:l=>{l?this.startCircleMeasure():this.deactivate()}});r.addControl(o);const a=new xe({html:'📏',title:"Measure Distance",className:"measure-line-btn",onToggle:l=>{l?this.startLineMeasure():this.deactivate()}});r.addControl(a);const i=new xe({html:'',title:"Measure Area",className:"measure-area-btn",onToggle:l=>{l?this.startAreaMeasure():this.deactivate()}});r.addControl(i);const s=new it({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 Re=null;async function Ci(){if(!("serviceWorker"in navigator))return console.warn("[PWA] Service Workers not supported"),null;try{return Re=await navigator.serviceWorker.register("/sw.js",{scope:"/"}),console.log("[PWA] Service Worker registered:",Re.scope),Re.addEventListener("updatefound",()=>{const n=Re.installing;n.addEventListener("statechange",()=>{n.state==="installed"&&navigator.serviceWorker.controller&&(console.log("[PWA] New version available"),Ri())})}),Re}catch(n){return console.error("[PWA] Service Worker registration failed:",n),null}}let je=null,_e=null;function Fi(n="#install-btn"){if(_e=typeof n=="string"?document.querySelector(n):n,!_e){console.warn("[PWA] Install button not found:",n);return}_e.style.display="none",window.addEventListener("beforeinstallprompt",e=>{e.preventDefault(),je=e,_e.style.display="block",console.log("[PWA] Install prompt ready")}),_e.addEventListener("click",async()=>{if(!je){Oi();return}je.prompt();const{outcome:e}=await je.userChoice;console.log("[PWA] Install prompt outcome:",e),je=null,_e.style.display="none"}),window.addEventListener("appinstalled",()=>{console.log("[PWA] App installed"),je=null,_e.style.display="none"}),window.matchMedia("(display-mode: standalone)").matches&&(_e.style.display="none")}function Oi(){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 go=null;const mo=new Set;function Di(n="#offline-indicator"){go=typeof n=="string"?document.querySelector(n):n,Vt(!navigator.onLine),window.addEventListener("online",()=>{console.log("[PWA] Back online"),Vt(!1),rn(!1)}),window.addEventListener("offline",()=>{console.log("[PWA] Gone offline"),Vt(!0),rn(!0)})}function Vt(n){go&&(go.style.display=n?"block":"none"),document.body.classList.toggle("is-offline",n)}function pr(n){return mo.add(n),n(!navigator.onLine),()=>mo.delete(n)}function rn(n){for(const e of mo)try{e(n)}catch(t){console.error("[PWA] Offline listener error:",t)}}function V(){return navigator.onLine}function Ri(){confirm("A new version is available. Reload now?")&&Ni()}function Ni(){Re?.waiting&&Re.waiting.postMessage({type:"SKIP_WAITING"}),window.location.reload()}async function $i({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 Bi(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 Po(n,e,t={},r=5e3,o=1e4){const a=await $i({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:p,...u}=d.data;i(u)}},a.postMessage({type:n,...t},[l.port2])})}async function Gi(){try{return(await Po("GET_TILE_STATS","TILE_STATS")).stats}catch(n){return console.warn("[PWA] getTileCacheStats failed:",n),null}}async function ji(){try{return await Po("CLEAR_TILE_CACHES","TILE_CACHES_CLEARED"),!0}catch(n){return console.warn("[PWA] clearTileCaches failed:",n),!1}}async function Ui(n){if(!n)return!1;try{return!!(await Po("CLEAR_TILE_CACHE","TILE_CACHE_CLEARED",{cacheName:n})).deleted}catch(e){return console.warn(`[PWA] clearTileCacheForProvider(${n}) failed:`,e),!1}}async function qi(){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 zi(n={}){const{installButton:e="#install-btn",offlineIndicator:t="#offline-indicator",autoRegisterSW:r=!0}=n;r&&await Ci(),Fi(e),Di(t),console.log("[PWA] Initialized")}const fr={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"}},Hi=30*1024,Dt=2*Math.PI*6378137/2;function an(n,e){const t=n/Dt*180;let r=e/Dt*180;return r=180/Math.PI*(2*Math.atan(Math.exp(r*Math.PI/180))-Math.PI/2),[t,r]}function sn(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 hr(n,e){const[t,r,o,a]=n,[i,s]=an(t,r),[l,c]=an(o,a),d=sn(i,c,e),p=sn(l,s,e),u=Math.pow(2,e),f=Math.max(0,Math.min(d.x,p.x)),h=Math.min(u-1,Math.max(d.x,p.x)),g=Math.max(0,Math.min(d.y,p.y)),m=Math.min(u-1,Math.max(d.y,p.y));return{z:e,minX:f,maxX:h,minY:g,maxY:m,count:(h-f+1)*(m-g+1)}}function Wi(n,e,t){let r=0;for(let o=e;o<=t;o++)r+=hr(n,o).count;return r}function Ki(n,e,t){const r=[];for(let o=e;o<=t;o++){const a=hr(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 Vi(n,{z:e,x:t,y:r}){return n.replace("{z}",e).replace("{x}",t).replace("{y}",r)}class Xi{constructor({baseMap:e,extent3857:t,minZoom:r,maxZoom:o,concurrency:a=2,interBatchDelayMs:i=50,onProgress:s=()=>{}}){const l=fr[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=Ki(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,p=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:p})};l("running");for(let c=0;c{if(this._cancelled)return;const u=Vi(this.template,p);try{const f=await fetch(u,{signal:this._abortCtrl.signal,cache:"default"});f.ok?(a++,f.body&&f.body.cancel().catch(()=>{})):(f.status,i++)}catch(f){f.name==="AbortError"||i++}o++})),l("running"),this.interBatchDelayMs>0&&c+this.concurrencysetTimeout(p,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 Yi=(()=>{const n=(r,o)=>{const a=r*Dt/180,i=Math.log(Math.tan((90+o)*Math.PI/360))/(Math.PI/180);return[a,i*Dt/180]},e=n(-3.3,4.5),t=n(1.2,11.2);return[e[0],e[1],t[0],t[1]]})();function Ji(n){return n*Hi}const gr="https://api.lupmis4luspa.org/api",mr=`${gr}/spatial_planning`;function Zi(n){return n.includes("/")?`${gr}/${n}`:`${mr}/${n}`}const Xt="1",Qi="1c46538c712e9b5b";function es(){try{if(typeof window>"u")return Xt;const n=window.LUPMIS_SESSION;if(!n||typeof n!="object")return Xt;const e=n.district_id;return e==null||String(e).length===0?null:String(e)}catch{}return Xt}const Mo={get district_id(){return es()},api_token:Qi};function Ve(){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 ts=3e4,os=5e3;let De=null;async function ns(n=!1){if(De!==null&&!n)return De;const e=new AbortController,t=setTimeout(()=>e.abort(),os);try{De=(await fetch(`${mr}/get_layers.php`,{method:"POST",headers:{"Content-Type":"application/json",Accept:"application/json"},body:JSON.stringify(Mo),signal:e.signal})).ok}catch{De=!1}finally{clearTimeout(t)}return console.log("[RemoteDB] Server reachable:",De),De}function Te(){return De}function rs(n,e=ts){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 me(n,e={},t={}){const r=Zi(n),o={...Mo,...e};console.log("[RemoteDB] POST",r);const a=rs(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 as(){return me("get_district_boundary.php")}async function is(){return me("get_layers.php")}async function ss(){return me("get_all_collector_zone_per_district.php")}async function ls(){return me("get_parcels_per_district.php")}async function cs(){return me("get_all_footprint_per_district.php")}async function ds(){const n=Mo.district_id;return me("contours/get_by_district.php",{districtid:n==null?null:Number(n)})}async function us(){return me("get_osm_roads.php")}async function ps(){return me("get_upn_grid_per_district.php")}async function fs(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 me("save_gps_trail.php",t);return{remoteId:r?.id??r?.remote_id??null}}const hs=63710088e-1,Ne=Math.PI/180;function gs(n,e,t,r){const o=(r-e)*Ne,a=(t-n)*Ne,i=Math.sin(o/2)**2+Math.cos(e*Ne)*Math.cos(r*Ne)*Math.sin(a/2)**2;return 2*hs*Math.asin(Math.min(1,Math.sqrt(i)))}function ln(n,e=5){return n==null||Number.isNaN(n)?"—":n.toFixed(e)}const cn=6378137,dn=1/298.257223563,un=.9996,te=dn*(2-dn),Yt=te/(1-te);function ms(n,e){if(n==null||e==null||Number.isNaN(n)||Number.isNaN(e))return null;let t=((e+180)%360+360)%360-180;const r=Math.floor((t+180)/6)+1,o=(r-1)*6-180+3,a=n*Ne,i=t*Ne,s=o*Ne,l=Math.sin(a),c=Math.cos(a),d=Math.tan(a),p=cn/Math.sqrt(1-te*l*l),u=d*d,f=Yt*c*c,h=c*(i-s),g=cn*((1-te/4-3*te**2/64-5*te**3/256)*a-(3*te/8+3*te**2/32+45*te**3/1024)*Math.sin(2*a)+(15*te**2/256+45*te**3/1024)*Math.sin(4*a)-35*te**3/3072*Math.sin(6*a));let m=un*p*(h+(1-u+f)*h**3/6+(5-18*u+u*u+72*f-58*Yt)*h**5/120)+5e5,y=un*(g+p*d*(h*h/2+(5-u+9*f+4*f*f)*h**4/24+(61-58*u+u*u+600*f-330*Yt)*h**6/720));const w=n>=0?"N":"S";return w==="S"&&(y+=1e7),{zone:r,hemisphere:w,easting:Math.round(m),northing:Math.round(y)}}function ys(n,e){const t=ms(n,e);return t?`${t.zone}${t.hemisphere} ${t.easting}E ${t.northing}N`:"—"}function bs(n){return n==null||Number.isNaN(n)?"—":n<1e3?`${Math.round(n)} m`:`${(n/1e3).toFixed(2)} km`}function ws(n){return n==null||Number.isNaN(n)?"—":`±${Math.round(n)} m`}function _s(n){return n==null||Number.isNaN(n)?"none":n<=10?"good":n<=30?"fair":"poor"}const vs={minDistanceM:5,minIntervalMs:1e3,heartbeatMs:2e4,maxAccuracyM:50,enableHighAccuracy:!0,timeoutMs:15e3,maximumAgeMs:0};class ut{constructor(e={}){this.opts={...vs,...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=ut.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=ut.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=ut.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=gs(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??Ve()?.district_id??null;return Wa({...n,districtId:e!=null?String(e):null})},addPoint:(n,e)=>Ka(n,e),finishTrail:(n,e)=>Va(n,e),getUnsyncedTrails:()=>Xa(),getTrailPoints:n=>Ya(n),markTrailSynced:(n,e)=>Ja(n,e)},xs={pushTrail:(n,e)=>fs(n,e),isOnline:()=>V()},ve=new ut({storage:Es,sync:xs,minDistanceM:5,minIntervalMs:1e3,heartbeatMs:2e4,maxAccuracyM:50,enableHighAccuracy:!0}),Ss=new Set(["set:view","set:selected","clear:selected","set:basemap"]);function Ls({mapView:n,embedConfig:e}){const t=n.getMap(),r=window.parent&&window.parent!==window?window.parent:null,o=new j,a=new D({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(m){if(!r){console.warn("[embed-bridge] No parent window — would have sent:",m);return}try{r.postMessage(m,"*")}catch(y){console.warn("[embed-bridge] postMessage failed:",y)}}function d(m,y){c({type:"error",code:m,message:y})}function p(){l||(l=!0,c({type:"ready"}))}function u(m,y,w){const _=m.getProperties();let x=y,L=w;if(x==null||L==null){const v=m.getGeometry()?.getExtent();if(v){const[S,P]=Oe(Xr(v));x=S,L=P}}return{type:"parcel:select",upn:_.upn??null,parcel_id:_.id??null,lon:x??null,lat:L??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 f(m){if(o.clear(),m){const y=m.clone();o.addFeature(y)}}n.onClick((m,y,w,_)=>{let x=null;t.forEachFeatureAtPixel(_.pixel,L=>{if(L.get("_layerType")==="parcel")return x=L,!0}),x?(f(x),c(u(x,m,y))):(f(null),c({type:"parcel:cleared"}))}),window.addEventListener("message",m=>{const y=m.data;if(!(!y||typeof y!="object"||!Ss.has(y.type)))try{switch(y.type){case"set:view":{if(typeof y.lon=="number"&&typeof y.lat=="number"){const w=t.getView();w.setCenter(ee([y.lon,y.lat])),typeof y.zoom=="number"&&w.setZoom(y.zoom)}break}case"set:selected":y.upn&&h(String(y.upn));break;case"clear:selected":f(null),s=null;break;case"set:basemap":y.key&&typeof n.setBaseMap=="function"&&n.setBaseMap(y.key);break}}catch(w){d("COMMAND_FAILED",`Failed to handle ${y.type}: ${w.message}`)}});function h(m){if(!i){s=m;return}const w=i.getSource().getFeatures().find(x=>String(x.get("upn")??"")===m);if(!w){s=m;return}s=null,f(w);const _=w.getGeometry()?.getExtent();_&&t.getView().fit(_,{padding:[50,50,50,50],duration:400,maxZoom:17}),c(u(w,null,null))}function g(m){i=m;const y=m.getSource(),w=()=>{queueMicrotask(()=>{s&&h(s),p()})};if(y.getFeatures().length>0)w();else{let _=!1;y.on("addfeature",()=>{_||(_=!0,queueMicrotask(()=>{_=!1,s&&h(s),p()}))})}}if(e?.basemap&&typeof n.setBaseMap=="function"&&n.setBaseMap(e.basemap),typeof e?.lon=="number"&&typeof e?.lat=="number"){const m=t.getView();m.setCenter(ee([e.lon,e.lat])),m.setZoom(typeof e?.zoom=="number"?e.zoom:15)}return{attachParcelsLayer:g,emitError:d}}const Ts=[{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"}],yr={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 ks(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 ae&&(s=s.slice(0,e-1)+"…"),t[a]=s)}}return t}const T={};let yo=null,N=null;function Fs(){T.root||(T.root=document.getElementById("importMappingModal"),T.filename=document.getElementById("import-modal-filename"),T.summary=document.getElementById("import-modal-summary"),T.target=document.getElementById("import-modal-target"),T.targetHint=document.getElementById("import-modal-target-hint"),T.fieldsWrap=document.getElementById("import-modal-fields-wrap"),T.tbody=document.getElementById("import-modal-fields-tbody"),T.btnSave=document.getElementById("import-modal-save"),T.btnSaveUpload=document.getElementById("import-modal-save-upload"),T.btnCancel=document.getElementById("import-modal-cancel"),T.target&&!T.target.dataset.populated&&(T.target.innerHTML=Ts.map(n=>``).join(""),T.target.dataset.populated="1"),T.target&&!T.target.dataset.wired&&(T.target.dataset.wired="1",T.target.addEventListener("change",_r)),T.btnSave&&!T.btnSave.dataset.wired&&(T.btnSave.dataset.wired="1",T.btnSave.addEventListener("click",()=>fn("save"))),T.btnSaveUpload&&!T.btnSaveUpload.dataset.wired&&(T.btnSaveUpload.dataset.wired="1",T.btnSaveUpload.addEventListener("click",()=>fn("upload"))),T.root&&!T.root.dataset.wired&&(T.root.dataset.wired="1",T.root.addEventListener("hidden.bs.modal",()=>{N?.onResult&&!N._resolved&&(N._resolved=!0,N.onResult({action:"cancel"})),N=null})))}function Os(){const n=N.targetType,e=yr[n]||[];if(n==="other"||e.length===0){T.fieldsWrap.style.display="none";return}T.fieldsWrap.style.display="";const t=o=>{const a=N.sourceSamples[o];return a?` — [${Tt(a)}]`:" — [(empty)]"},r=[''].concat(N.sourceFields.map(o=>``)).join("");T.tbody.innerHTML=e.map(o=>{const a=N.mapping[o]||"",i=r.replace(`