覽編輯器開源:一個(gè)純前端實(shí)現(xiàn)的拆解)
背景日常處理行政區(qū)劃、POI、軌跡等 GeoJSON 數(shù)據(jù)時(shí)經(jīng)常需要先看一眼邊界畫得對不對、坐標(biāo)系是不是 WGS84、字段結(jié)構(gòu)是否符合預(yù)期。常見的做法是把文件傳到在線工具或者打開一個(gè)完整的桌面 GIS。前者有數(shù)據(jù)外泄的顧慮后者對于一個(gè)看一眼的需求又太重。我們把 GeoJSONcn 站點(diǎn)里的預(yù)覽編輯器抽出來做成了一個(gè)不依賴任何后端的純前端工具并開源出來。它解決什么問題本地打開 GeoJSON 即可渲染數(shù)據(jù)不上傳服務(wù)器支持要素級的查看與編輯能把同一份數(shù)據(jù)導(dǎo)出成 GeoJSON / KML / SVG / CSV / PNG對接不同的下游系統(tǒng)不需要npm install不需要構(gòu)建雙擊index.html就能用。技術(shù)實(shí)現(xiàn)渲染Leaflet 與坐標(biāo)系核心就是用地圖庫把 GeoJSON 畫出來。下面是一段最小可用的加載代碼// 初始化地圖并加載一份 GeoJSON// 要素量大時(shí)把渲染器從默認(rèn) SVG 換成 Canvas避免 DOM 節(jié)點(diǎn)過多卡頓constmapL.map(map,{renderer:L.canvas()}).setView([39.9,116.4],4);L.tileLayer(https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png,{attribution:? OpenStreetMap}).addTo(map);fetch(example.geojson).then(rr.json()).then(geojson{L.geoJSON(geojson,{style:{color:#2563eb,weight:1.5},onEachFeature:(feature,layer){constnamefeature.propertiesfeature.properties.name;if(name)layer.bindPopup(name);}}).addTo(map);});這里有個(gè)容易混淆的點(diǎn)Leaflet 的地圖視圖默認(rèn)使用 Web MercatorEPSG:3857投影但Feature 的坐標(biāo)本身仍是 EPSG:4326WGS84Leaflet 在加載時(shí)會(huì)自動(dòng)完成經(jīng)緯度到墨卡托的轉(zhuǎn)換開發(fā)者無需手動(dòng)處理。需要提醒的是本項(xiàng)目只處理 WGS84 數(shù)據(jù)。國內(nèi)的地圖數(shù)據(jù)常?;煊?GCJ-02火星坐標(biāo)系或 BD-09本項(xiàng)目目前沒有內(nèi)置糾偏邏輯如果拿到的數(shù)據(jù)偏移明顯需要先自行糾偏再導(dǎo)入。計(jì)算Turf.js要素的幾何操作計(jì)算包圍盒、合并、切分、面積量算等交給 Turf.js。本倉庫把 Turf 以本地文件形式打包在turf/目錄里頁面里直接通過全局turf對象調(diào)用無需打包工具// 用 Turf 計(jì)算要素包圍盒與面積turf 為倉庫內(nèi)置的全局對象constbboxturf.bbox(geojson);// [minX, minY, maxX, maxY]constareaturf.area(geojson);// 單位平方米console.log(包圍盒:,bbox,面積(㎡):,area);預(yù)覽頁里切分選中要素這類能力就是從主站編輯器移植過來的底層依賴 Turf 的幾何運(yùn)算。導(dǎo)出管線幾種導(dǎo)出格式各自有幾個(gè)容易踩坑的技術(shù)點(diǎn)// KML 坐標(biāo)順序是 經(jīng)度,緯度,海拔注意和 GeoJSON 的 [lon, lat] 對應(yīng)多一個(gè)高程// 多環(huán) / 多維坐標(biāo)需要遞歸處理functioncoordsToKml(coords){if(typeofcoords[0][0]number){returncoords.map(c${c[0]},${c[1]},0).join( );}returncoords.map(coordsToKml).join( );}// 導(dǎo)出 CSV 時(shí)在開頭寫入 BOM避免 Excel 打開中文亂碼constcsvname,lon,lat\nrows.join(\n);constblobnewBlob([\uFEFFcsv],{type:text/csv;charsetutf-8;});GeoJSON最直接把內(nèi)存里的 FeatureCollection 序列化即可注意保留properties與坐標(biāo)系聲明。KML需要按DocumentPlacemarkPolygonouterBoundaryIsLinearRingcoordinates的結(jié)構(gòu)組織坐標(biāo)順序經(jīng)度,緯度,海拔轉(zhuǎn)換時(shí)別寫反見上。SVG把[經(jīng)度, 緯度]用等距圓柱投影線性映射到viewBox的像素坐標(biāo)輸出path dM x y L x y Z。投影本身很簡單難點(diǎn)在視口自適應(yīng)和描邊寬度的處理。CSV每個(gè)要素一行屬性平鋪成列字段含中文且要進(jìn) Excel 時(shí)文件開頭需帶 BOMEF BB BF否則中文亂碼見上。PNG把當(dāng)前渲染結(jié)果繪制到canvas后用toBlob/toDataURL導(dǎo)出圖片。目錄結(jié)構(gòu)geojson-preview/ ├── index.html # 入口頁說明 主站入口 ├── preview.html # 預(yù)覽編輯器本體 ├── leaflet/ # 地圖渲染引擎 ├── turf/ # 幾何計(jì)算 ├── vendor/ # 其他第三方依賴 ├── LICENSE # MIT ├── NOTICE └── README.md整個(gè)項(xiàng)目是靜態(tài)文件沒有打包步驟依賴都以本地文件形式存在leaflet/、turf/、vendor/目錄里。快速開始# GitHubgitclone https://github.com/laukuo/geojson-preview.git# Giteegitclone https://gitee.com/SmartMailer/geojson-preview.git克隆后用瀏覽器直接打開index.html即可無需安裝依賴、無需啟動(dòng)服務(wù)。與 GeoJSONcn 主站的關(guān)系這個(gè)倉庫是 GeoJSONcn全國行政區(qū)劃邊界矢量數(shù)據(jù)下載平臺預(yù)覽功能的獨(dú)立開源版。主站還提供省、市、區(qū)縣、鄉(xiāng)鎮(zhèn)街道四級行政區(qū)劃邊界數(shù)據(jù)的在線預(yù)覽與下載你也可以直接在 在線預(yù)覽頁 體驗(yàn)完整功能開源版把輕量預(yù)覽 編輯 導(dǎo)出這一段單獨(dú)拆了出來方便在自己的項(xiàng)目里直接集成或做二次開發(fā)。二次開發(fā)與集成幾種常見的接入方式在自己的頁面里直接引用本倉庫的leaflet/與turf/復(fù)用渲染與計(jì)算邏輯把preview.html以iframe的形式嵌到內(nèi)部系統(tǒng)里本地加載 GeoJSON 時(shí)優(yōu)先用FileReader讀取用戶選定的文件而不是fetch本地路徑——后者在file://協(xié)議下會(huì)受到瀏覽器同源策略限制// file:// 下用 FileReader 讀本地文件避開 fetch 的同源限制constinputdocument.getElementById(file);input.addEventListener(change,(){constreadernewFileReader();reader.onloaderenderGeoJSON(JSON.parse(e.target.result));reader.readAsText(input.files[0]);});開源地址代碼基于 MIT 協(xié)議發(fā)布同時(shí)托管在 GitHub 與 GiteeGitHubhttps://github.com/laukuo/geojson-previewGiteehttps://gitee.com/SmartMailer/geojson-preview歡迎在倉庫里提 Issue 和 PR。相關(guān)鏈接開源倉庫GitHubhttps://github.com/laukuo/geojson-preview開源倉庫Giteehttps://gitee.com/SmartMailer/geojson-preview項(xiàng)目主站https://geojsoncn.com/在線預(yù)覽https://geojsoncn.com/preview.html