| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321 |
- <template>
- <div id="controlPanelBox">
- <div>
- <div class="sceneNameBox">
- <div class="">
- 场景名称:
- <el-cascader
- :disabled="$route.query.sceneId != undefined"
- v-model="SceneValue"
- placeholder="试试搜索:距离"
- :options="SceneList"
- :props="{ expandTrigger: 'hover' }"
- @change="handleChange"
- filterable
- ></el-cascader>
- <el-tooltip
- v-if="SceneValue && dmsServerItem.functionalDefinition"
- :content="dmsServerItem.functionalDefinition"
- placement="bottom"
- :popper-style="{ maxWidth: '300px' }"
- >
- <el-icon
- style="margin-left: 1rem; width: 1rem; height: 1rem"
- v-show="SceneValue && dmsServerItem.functionalDefinition"
- ><QuestionFilled
- /></el-icon>
- </el-tooltip>
- </div>
- <div>
- <el-button type="primary" @click="sendGeometriesToBackend"
- >发送<i class="el-icon-s-promotion el-icon--right"></i
- ></el-button>
- </div>
- </div>
- <el-divider></el-divider>
- <div>
- 元素个数:{{ SceneValue && dmsServerItem ? dmsServerItem.numberOf : "0" }}
- </div>
- <div>
- 参数类型:{{ SceneValue && dmsServerItem ? dmsServerItem.elementTypes : "[]" }}
- </div>
- <div>接口地址:{{ SceneValue && dmsServerItem ? dmsServerItem.apiUrl : "/" }}</div>
- </div>
- <el-divider></el-divider>
- <div>
- <!-- 元素文本渲染和操作区域 -->
- <el-tabs tab-position="left" style="height: calc(100vh - 250px)" class="demo-tabs">
- <el-tab-pane label="入参">
- <div v-if="SceneValue && dmsServerItem && ifHasType('file')">
- <el-upload
- v-model:file-list="fileList"
- class="upload-demo"
- action=""
- :limit="1"
- :on-change="handleFileChange"
- :on-exceed="handleExceed"
- :on-remove="uploadRemove"
- :auto-upload="false"
- >
- <el-button type="primary">上传文件</el-button>
- </el-upload>
- </div>
- <div v-if="SceneValue && dmsServerItem">
- <div v-for="item in dmsServerItem.elementTypes" :key="item">
- <div
- v-if="SceneValue && dmsServerItem && selectOptions[item]"
- style="margin: 0.5rem 0"
- >
- {{ item }}:<el-select
- @change="handleSelectChange(item, $event)"
- v-if="selectOptions[item]"
- v-model="params[item]"
- :placeholder="'请选择' + item"
- style="width: 240px"
- >
- <el-option
- v-for="item2 in selectOptions[item]"
- :key="item2.value"
- :label="item2.label"
- :value="item2.value"
- />
- </el-select>
- </div>
- <div
- v-if="
- [
- 'lon',
- 'lat',
- 'lonKey',
- 'latKey',
- 'filePath',
- 'EPSILON',
- 'distance',
- ].includes(item)
- "
- >
- {{ item }}:
- <el-input
- v-model="params[item]"
- @input="handleSelectChange(item, $event)"
- style="width: 240px"
- :placeholder="'请输入' + item"
- />
- </div>
- </div>
- </div>
- <div
- class="vueJsonEditor_box"
- v-show="
- SceneValue &&
- dmsServerItem &&
- (ifHasType('point') || ifHasType('polyline') || ifHasType('polygon'))
- "
- >
- <div class="vueJsonEditor_tools">
- <span
- v-if="jsonData && (jsonData.features || jsonData.geometry)"
- @click="showToMap(jsonData, 'input')"
- >渲染到地图中</span
- >
- <el-tooltip content="定位到当前入参渲染要素" placement="top">
- <span v-if="renderStatus.input" @click="locateRenderedGeojson('input')">
- 定位
- </span>
- </el-tooltip>
- <span @click="copyJsonData(jsonData)">copy</span>
- </div>
- <vue-json-editor
- :key="'json-input-editor-' + inputEditorKey"
- v-model="jsonData"
- :value="jsonData"
- :show-btns="false"
- :mode="'code'"
- :lang="'zh'"
- @json-change="handleJsonChange"
- >
- </vue-json-editor>
- </div>
- </el-tab-pane>
- <el-tab-pane label="返回">
- <div
- v-if="backData.message || backData.error"
- :style="{
- backgroundColor: backData.code === 200 ? '#67C23A' : '#F56C6C',
- color: '#fff',
- padding: '5px 10px',
- fontSize: '14px',
- }"
- >
- {{ backData.message || backData.error }}
- </div>
- <div class="vueJsonEditor_box" v-show="SceneValue && dmsServerItem">
- <div class="vueJsonEditor_tools">
- <span
- v-if="
- backData.content &&
- (backData.content.features || backData.content.geometry)
- "
- @click="showToMap(backData.content, 'output')"
- >渲染到地图中</span
- >
- <el-tooltip content="定位到当前返回渲染要素" placement="top">
- <span v-if="renderStatus.output" @click="locateRenderedGeojson('output')">
- 定位
- </span>
- </el-tooltip>
- <span @click="copyJsonData(backData.content)">copy</span>
- </div>
- <vue-json-editor
- v-if="backData.content"
- :key="'json-output-editor-' + outputEditorKey"
- v-model="backData.content"
- :value="backData.content"
- @json-change="handleJsonChange2"
- :show-btns="false"
- :mode="'code'"
- :lang="'zh'"
- >
- </vue-json-editor></div
- ></el-tab-pane>
- </el-tabs>
- </div>
- <el-dialog
- v-model="propertyDialog.visible"
- title="要素属性"
- width="420px"
- draggable
- :modal="false"
- append-to-body
- class="feature-property-dialog"
- >
- <div class="feature-property-content">
- <div class="feature-property-tip">支持直接编辑属性值,失焦后自动同步 JSON。</div>
- <div
- class="feature-property-item"
- v-for="item in propertyDialog.list"
- :key="'feature-property-' + item.id"
- >
- <div class="feature-property-row">
- <el-input
- v-model="item.editKey"
- class="feature-property-input feature-property-key-input"
- placeholder="属性名"
- @change="handlePropertyKeyChange(item)"
- />
- <el-button
- type="danger"
- plain
- size="small"
- class="feature-property-delete-btn"
- @click="deleteProperty(item)"
- >
- 删除
- </el-button>
- </div>
- <el-input
- v-model="item.editValue"
- class="feature-property-input"
- placeholder="属性值"
- @change="handlePropertyValueChange(item)"
- />
- </div>
- <el-empty
- v-if="!propertyDialog.list.length"
- description="当前要素无属性信息"
- :image-size="80"
- />
- </div>
- <template #footer>
- <el-button type="primary" plain @click="addProperty">+ 新增属性</el-button>
- </template>
- </el-dialog>
- <!-- 绘制工具栏 -->
- <div
- class="toolbar"
- v-if="
- SceneValue &&
- dmsServerItem &&
- (ifHasType('point') || ifHasType('polyline') || ifHasType('polygon'))
- "
- >
- <div
- class="tool-item"
- @click="activateDraw('point')"
- :class="{ active: currentTool === 'point' }"
- >
- 绘制点
- <el-tooltip
- content="绘制开关,蓝色状态代表开启,再次点击结束绘制,地图左键点击选点,"
- placement="right"
- >
- <el-icon style="width: 1rem; height: 1rem"><QuestionFilled /></el-icon>
- </el-tooltip>
- </div>
- <div
- class="tool-item"
- @click="activateDraw('polyline')"
- :class="{ active: currentTool === 'polyline' }"
- >
- 绘制线<el-tooltip
- content="绘制线最少需要两个点,鼠标左键点击选点,点之间自动连线,鼠标右键结束绘制"
- placement="right"
- >
- <el-icon style="width: 1rem; height: 1rem"><QuestionFilled /></el-icon>
- </el-tooltip>
- </div>
- <div
- class="tool-item"
- @click="activateDraw('polygon')"
- :class="{ active: currentTool === 'polygon' }"
- >
- 绘制面<el-tooltip
- content="绘制面最少需要三个点,鼠标左键点击选点,面自动必合,鼠标右键结束绘制"
- placement="right"
- >
- <el-icon style="width: 1rem; height: 1rem"><QuestionFilled /></el-icon>
- </el-tooltip>
- </div>
- <!-- <div class="tool-item" @click="startHoleDrawing" :class="{ active: isDrawingHole }">
- 绘制镂空
- </div> -->
- <div class="tool-item" @click="clearAll">清除所有</div>
- </div>
- </div>
- </template>
- <script>
- import wgn from "../../api/wgn";
- // 控制面板
- export default {
- name: "ControlPanel",
- // 2. 接收父组件传递的 props(单向数据流,子组件不能直接修改)
- props: {
- btnText: {
- type: String, // 类型限制
- default: "默认按钮", // 默认值
- required: false, // 是否必传
- },
- showCount: {
- type: Boolean,
- default: false,
- },
- },
- data() {
- return {
- currentFile: null, // 当前选中的文件
- fileList: [],
- // 其他参数
- params: {
- unit: "",
- outFileType: "",
- outPrj: "",
- inPrj: "",
- compressionRatio: "",
- lon: "",
- lat: "",
- },
- // 请求参数
- jsonData: {},
- // 返回参数
- backData: {},
- // 当前场景值
- SceneValue: "",
- // 场景列表
- SceneList: [
- {
- value: "1.5.1",
- label: "1.5.1拓扑计算",
- children: [
- {
- value: "1.5.1.1",
- label: "点线面拓扑关系",
- },
- ],
- },
- {
- value: "1.5.2",
- label: "1.5.2空间量算",
- children: [
- {
- value: "1.5.2.1",
- label: "两点欧氏距离",
- },
- {
- value: "1.5.2.2",
- label: "点到线的最短距离",
- },
- {
- value: "1.5.2.3",
- label: "点到面的最短距离",
- },
- {
- value: "1.5.2.4",
- label: "面到面的最短距离",
- },
- {
- value: "1.5.2.5",
- label: "折线距离(累加线段长度)",
- },
- {
- value: "1.5.2.6",
- label: "平面面积、不规则面面积",
- },
- {
- value: "1.5.2.7",
- label: "曲面面积,考虑地形起伏",
- },
- {
- value: "1.5.2.8",
- label: "单点高程查询",
- },
- {
- value: "1.5.2.9",
- label: "两点高程差",
- },
- {
- value: "1.5.2.10",
- label: "区域高程统计",
- },
- ],
- },
- {
- value: "1.5.3",
- label: "1.5.3几何运算",
- children: [
- {
- value: "1.5.3.1",
- label: "并集运算",
- },
- {
- value: "1.5.3.2",
- label: "交集运算",
- },
- {
- value: "1.5.3.3",
- label: "差集运算",
- },
- {
- value: "1.5.3.4",
- label: "几何参数计算",
- },
- ],
- },
- {
- value: "1.5.4",
- label: "1.5.4关系分析",
- children: [
- {
- value: "1.5.4.1",
- label: "邻接关系分析",
- },
- {
- value: "1.5.4.2",
- label: "包含关系分析",
- },
- {
- value: "1.5.4.3",
- label: "相交关系分析",
- },
- {
- value: "1.5.4.4",
- label: "相离关系分析",
- },
- ],
- },
- {
- value: "1.5.5",
- label: "1.5.5非空间数据转换",
- children: [
- {
- value: "1.5.5.1",
- label: "非空间数据转空间数据",
- },
- ],
- },
- {
- value: "1.5.6",
- label: "1.5.6坐标转换",
- children: [
- {
- value: "1.5.6.1",
- label: "单点的坐标转换接口",
- },
- {
- value: "1.5.6.2",
- label: "geojson坐标转换接口",
- },
- ],
- },
- {
- value: "1.5.7",
- label: "1.5.7时空数据格式转换",
- children: [
- {
- value: "1.5.7.1",
- label: "文件格式转换",
- },
- {
- value: "1.5.7.2",
- label: "转换文件下载",
- },
- ],
- },
- {
- value: "1.5.8",
- label: "1.5.8自定义工具",
- children: [
- {
- value: "1.5.8.1",
- label: "obj三维模型轻量化",
- },
- ],
- },
- // 时空算子库
- {
- value: "1.2.5",
- label: "1.2.5时空算子库",
- children: [
- {
- value: "1.2.5.1",
- label: "墨卡托计算距离面积",
- },
- {
- value: "1.2.5.2",
- label: "设置障碍的路径计算",
- },
- {
- value: "1.2.5.3",
- label: "点线面体的相互状态",
- },
- {
- value: "1.2.5.4",
- label: "缓冲区计算",
- },
- {
- value: "1.2.5.5",
- label: "线面体分割",
- },
- {
- value: "1.2.5.6",
- label: "时空差异分析",
- },
- ],
- },
- ],
- dmsServerItem: {},
- selectOptions: {
- // 单位
- unit: [
- {
- value: "METER",
- label: "米",
- },
- {
- value: "KILOMETER",
- label: "千米",
- },
- ],
- inPrj: [
- {
- value: "WGS84",
- label: "WGS84",
- },
- {
- value: "SH2000",
- label: "SH2000",
- },
- {
- value: "UTM",
- label: "UTM",
- },
- {
- value: "WEB_Mercator",
- label: "WEB_Mercator",
- },
- ],
- compressionRatio: [
- {
- value: "100%",
- label: "100%",
- },
- {
- value: "80%",
- label: "80%",
- },
- {
- value: "50%",
- label: "50%",
- },
- {
- value: "25%",
- label: "25%",
- },
- ],
- outPrj: [
- {
- value: "WGS84",
- label: "WGS84",
- },
- {
- value: "SH2000",
- label: "SH2000",
- },
- {
- value: "UTM",
- label: "UTM",
- },
- {
- value: "WEB_Mercator",
- label: "WEB_Mercator",
- },
- ],
- outFileType: [
- {
- value: "geoJson",
- label: "geoJson",
- },
- {
- value: "shp",
- label: "shp",
- },
- {
- value: "csv",
- label: "csv",
- },
- {
- value: "xlsx",
- label: "xlsx",
- },
- ],
- },
- currentTool: null,
- drawingMode: null,
- handler: null,
- drawnEntities: [],
- geometries: [], // 存储绘制的几何对象
- // 绘制状态相关
- currentEntity: null, // 当前正在绘制的实体
- currentPositions: [], // 当前正在绘制的位置数组
- isDrawingHole: false, // 是否正在绘制镂空
- currentPolygonEntity: null, // 当前要添加镂空的多边形实体
- currentPolygonGeometry: null, // 当前要添加镂空的多边形几何对象
- tempEntity: null, // 临时预览实体
- // 地图渲染状态(入参/返回)
- renderStatus: {
- input: false,
- output: false,
- },
- // 缓存两侧最近一次成功渲染的geojson,用于定位时恢复
- renderedGeojsonCache: {
- input: null,
- output: null,
- },
- currentRenderedSource: "",
- // 地图点击属性弹窗
- featurePickHandler: null,
- inputEditorKey: 0,
- outputEditorKey: 0,
- propertyIdSeed: 1,
- propertyDialog: {
- visible: false,
- list: [],
- source: "",
- propertiesRef: null,
- featureIndex: -1,
- },
- };
- },
- mounted() {
- this.SceneValue = this.$route.query.sceneId ? this.$route.query.sceneId : "";
- },
- beforeUnmount() {
- // 组件卸载前清理资源
- this.deactivateDraw();
- if (this.handler) {
- this.handler.destroy();
- }
- if (this.featurePickHandler) {
- this.featurePickHandler.destroy();
- this.featurePickHandler = null;
- }
- },
- watch: {
- SceneValue(newVal, oldVal) {
- if (newVal && newVal != oldVal) {
- // 请求DMS
- this.searchServerList(this.SceneValue);
- }
- },
- },
- methods: {
- ifHasType(type) {
- if (this.dmsServerItem.elementTypes && this.dmsServerItem.elementTypes.length > 0) {
- return (
- this.dmsServerItem.elementTypes.includes(type) ||
- this.dmsServerItem.elementTypes.includes(type + "Z")
- );
- } else {
- return false;
- }
- },
- handleJsonChange(jsonStr) {
- this.jsonData = jsonStr;
- },
- handleJsonChange2(jsonStr) {
- this.backData.content = jsonStr;
- },
- // 搜索微功能服务
- searchServerList() {
- let requestParams = {
- columnId: systemConfig.columnIds[5],
- states: 0,
- pageSize: 999,
- page: 0,
- };
- if (this.SceneValue) {
- requestParams.search = JSON.stringify([
- {
- field: "c_scene_name",
- searchType: 1,
- content: { value: this.SceneValue },
- },
- ]);
- // 获取微功能服务列表
- wgn
- .getDmsData(requestParams)
- .then((res) => {
- if (res.code === 200 && res.content.data[0]) {
- let dmsServiceData = res.content.data[0];
- this.dmsServerItem = {
- // 功能描述
- functionalDefinition: dmsServiceData.content,
- // 元素类型
- elementTypes: JSON.parse(dmsServiceData.c_input_parameter_rules),
- // 元素个数
- numberOf: JSON.parse(dmsServiceData.c_number_of_elements),
- // 后台接口路径
- apiUrl: dmsServiceData.c_url,
- };
- } else {
- this.$message({
- message: "搜索微功能服务失败,请检查场景名称是否正确",
- type: "warning",
- });
- }
- })
- .catch((e) => {
- this.$message({
- message: "搜索微功能服务失败" + e,
- type: "error",
- });
- });
- }
- },
- getSourceGeojsonData(source) {
- return source === "output" ? this.backData.content : this.jsonData;
- },
- // 将用户输入或后台返回的geojson渲染到地图中
- showToMap(geojson, source = "input") {
- if (!geojson || (!geojson.features && !geojson.geometry)) {
- this.$message({
- message: "当前JSON缺少有效几何信息,无法渲染",
- type: "warning",
- });
- return;
- }
- this.currentRenderedSource = source;
- // 1. 清除所有地图中的元素
- this.clearAllMap();
- // 2. 将geojson添加到地图中
- this.addToMap(geojson);
- this.currentRenderedSource = source;
- this.renderStatus[source] = true;
- // 仅缓存纯数据,避免响应式对象带来的引用副作用
- this.renderedGeojsonCache[source] = JSON.parse(JSON.stringify(geojson));
- this.flyToGeojson(geojson);
- },
- // 定位到已渲染要素
- locateRenderedGeojson(source) {
- if (!this.renderStatus[source]) {
- return;
- }
- // 当前地图不是该来源数据时,优先恢复对应渲染结果,再飞行定位
- if (this.currentRenderedSource !== source && this.renderedGeojsonCache[source]) {
- this.showToMap(this.renderedGeojsonCache[source], source);
- return;
- }
- const sourceGeojson =
- this.getSourceGeojsonData(source) || this.renderedGeojsonCache[source];
- this.flyToGeojson(sourceGeojson);
- },
- collectGeometryCoordinates(geometry) {
- if (!geometry || !geometry.coordinates) {
- return [];
- }
- const { type, coordinates } = geometry;
- const points = [];
- if (type === "Point") {
- points.push([coordinates[0], coordinates[1]]);
- } else if (type === "LineString") {
- coordinates.forEach((coord) => points.push([coord[0], coord[1]]));
- } else if (type === "Polygon") {
- coordinates.forEach((ring) => {
- ring.forEach((coord) => points.push([coord[0], coord[1]]));
- });
- } else if (type === "MultiPolygon") {
- coordinates.forEach((polygon) => {
- polygon.forEach((ring) => {
- ring.forEach((coord) => points.push([coord[0], coord[1]]));
- });
- });
- }
- return points;
- },
- collectGeojsonCoordinates(geojson) {
- if (!geojson) {
- return [];
- }
- if (geojson.features && Array.isArray(geojson.features)) {
- return geojson.features.flatMap((feature) =>
- this.collectGeometryCoordinates(feature.geometry)
- );
- }
- if (geojson.geometry) {
- return this.collectGeometryCoordinates(geojson.geometry);
- }
- return [];
- },
- // 飞行定位到geojson范围
- flyToGeojson(geojson) {
- if (!viewer || !geojson) {
- return;
- }
- const points = this.collectGeojsonCoordinates(geojson);
- if (!points.length) {
- return;
- }
- if (points.length === 1) {
- viewer.camera.flyTo({
- destination: SkyScenery.Cartesian3.fromDegrees(
- points[0][0],
- points[0][1],
- 1200
- ),
- duration: 1.1,
- orientation: {
- heading: viewer.camera.heading,
- pitch: SkyScenery.Math.toRadians(-65),
- roll: 0,
- },
- });
- viewer.scene.requestRender();
- return;
- }
- let minLon = Infinity;
- let maxLon = -Infinity;
- let minLat = Infinity;
- let maxLat = -Infinity;
- points.forEach((point) => {
- minLon = Math.min(minLon, point[0]);
- maxLon = Math.max(maxLon, point[0]);
- minLat = Math.min(minLat, point[1]);
- maxLat = Math.max(maxLat, point[1]);
- });
- const lonPad = Math.max((maxLon - minLon) * 0.25, 0.0008);
- const latPad = Math.max((maxLat - minLat) * 0.25, 0.0008);
- viewer.camera.flyTo({
- destination: SkyScenery.Rectangle.fromDegrees(
- minLon - lonPad,
- minLat - latPad,
- maxLon + lonPad,
- maxLat + latPad
- ),
- duration: 1.1,
- });
- viewer.scene.requestRender();
- },
- // 把属性对象格式化为可编辑列表
- formatFeatureProperties(properties) {
- if (!properties || typeof properties !== "object") {
- return [];
- }
- return Object.keys(properties).map((key) => {
- const rawValue = properties[key];
- const editValue =
- rawValue === null || rawValue === undefined
- ? ""
- : typeof rawValue === "object"
- ? JSON.stringify(rawValue)
- : String(rawValue);
- return {
- id: this.propertyIdSeed++,
- originalKey: key,
- editKey: key,
- editValue,
- };
- });
- },
- parsePropertyValue(inputValue) {
- if (typeof inputValue !== "string") {
- return inputValue;
- }
- const value = inputValue.trim();
- if (value === "") {
- return "";
- }
- if (value === "true") {
- return true;
- }
- if (value === "false") {
- return false;
- }
- if (value === "null") {
- return null;
- }
- if (!isNaN(Number(value))) {
- return Number(value);
- }
- if (
- (value.startsWith("{") && value.endsWith("}")) ||
- (value.startsWith("[") && value.endsWith("]"))
- ) {
- try {
- return JSON.parse(value);
- } catch (e) {
- return inputValue;
- }
- }
- return inputValue;
- },
- syncJsonEditorData(source) {
- if (source === "output") {
- if (!this.backData.content) {
- return;
- }
- this.backData = {
- ...this.backData,
- content: JSON.parse(JSON.stringify(this.backData.content)),
- };
- } else {
- this.jsonData = JSON.parse(JSON.stringify(this.jsonData));
- }
- if (source === "output") {
- this.outputEditorKey += 1;
- } else {
- this.inputEditorKey += 1;
- }
- this.renderedGeojsonCache[source] = JSON.parse(
- JSON.stringify(this.getSourceGeojsonData(source))
- );
- const latestGeojson = this.getSourceGeojsonData(source);
- if (
- latestGeojson &&
- this.propertyDialog.visible &&
- this.propertyDialog.source === source
- ) {
- if (
- this.propertyDialog.featureIndex >= 0 &&
- latestGeojson.features &&
- latestGeojson.features[this.propertyDialog.featureIndex]
- ) {
- if (!latestGeojson.features[this.propertyDialog.featureIndex].properties) {
- latestGeojson.features[this.propertyDialog.featureIndex].properties = {};
- }
- this.propertyDialog.propertiesRef =
- latestGeojson.features[this.propertyDialog.featureIndex].properties;
- } else {
- if (!latestGeojson.properties) {
- latestGeojson.properties = {};
- }
- this.propertyDialog.propertiesRef = latestGeojson.properties;
- }
- }
- },
- handlePropertyValueChange(item) {
- if (!this.propertyDialog.propertiesRef || !item) {
- return;
- }
- const currentKey = (item.editKey || "").trim();
- if (!currentKey) {
- this.$message({
- message: "属性名不能为空",
- type: "warning",
- });
- item.editKey = item.originalKey || "";
- return;
- }
- this.propertyDialog.propertiesRef[currentKey] = this.parsePropertyValue(
- item.editValue
- );
- item.originalKey = currentKey;
- item.editKey = currentKey;
- this.syncPropertyToEntity(this.propertyDialog.propertiesRef);
- this.syncJsonEditorData(
- this.propertyDialog.source || this.currentRenderedSource || "input"
- );
- },
- handlePropertyKeyChange(item) {
- if (!this.propertyDialog.propertiesRef || !item) {
- return;
- }
- const oldKey = item.originalKey;
- const newKey = (item.editKey || "").trim();
- if (!newKey) {
- this.$message({
- message: "属性名不能为空",
- type: "warning",
- });
- item.editKey = oldKey;
- return;
- }
- if (
- newKey !== oldKey &&
- Object.prototype.hasOwnProperty.call(this.propertyDialog.propertiesRef, newKey)
- ) {
- this.$message({
- message: "属性名已存在,请更换",
- type: "warning",
- });
- item.editKey = oldKey;
- return;
- }
- const oldValue = this.propertyDialog.propertiesRef[oldKey];
- if (newKey !== oldKey) {
- delete this.propertyDialog.propertiesRef[oldKey];
- this.propertyDialog.propertiesRef[newKey] = oldValue;
- }
- item.originalKey = newKey;
- item.editKey = newKey;
- this.syncPropertyToEntity(this.propertyDialog.propertiesRef);
- this.syncJsonEditorData(
- this.propertyDialog.source || this.currentRenderedSource || "input"
- );
- },
- deleteProperty(item) {
- if (!this.propertyDialog.propertiesRef || !item) {
- return;
- }
- const key = (item.originalKey || "").trim();
- if (
- key &&
- Object.prototype.hasOwnProperty.call(this.propertyDialog.propertiesRef, key)
- ) {
- delete this.propertyDialog.propertiesRef[key];
- }
- this.propertyDialog.list = this.propertyDialog.list.filter(
- (property) => property.id !== item.id
- );
- this.syncPropertyToEntity(this.propertyDialog.propertiesRef);
- this.syncJsonEditorData(
- this.propertyDialog.source || this.currentRenderedSource || "input"
- );
- },
- addProperty() {
- if (!this.propertyDialog.propertiesRef) {
- this.$message({
- message: "请先点击地图中的已渲染要素",
- type: "warning",
- });
- return;
- }
- let index = 1;
- let newKey = "newKey";
- while (
- Object.prototype.hasOwnProperty.call(this.propertyDialog.propertiesRef, newKey)
- ) {
- newKey = `newKey${index}`;
- index += 1;
- }
- this.propertyDialog.propertiesRef[newKey] = "";
- this.propertyDialog.list.push({
- id: this.propertyIdSeed++,
- originalKey: newKey,
- editKey: newKey,
- editValue: "",
- });
- this.syncPropertyToEntity(this.propertyDialog.propertiesRef);
- this.syncJsonEditorData(
- this.propertyDialog.source || this.currentRenderedSource || "input"
- );
- },
- syncPropertyToEntity(propertiesRef) {
- if (!propertiesRef || !viewer || !this.currentRenderedSource) {
- return;
- }
- const targetSource = this.propertyDialog.source || this.currentRenderedSource;
- const targetIndex = this.propertyDialog.featureIndex;
- this.drawnEntities.forEach((entity) => {
- if (!entity || !entity.__featureRef) {
- return;
- }
- if (
- entity.__featureRef.source === targetSource &&
- entity.__featureRef.featureIndex === targetIndex
- ) {
- entity.__featureProperties = propertiesRef;
- }
- });
- },
- // 将geojson添加到地图中
- addToMap(geojson) {
- const source = this.currentRenderedSource || "input";
- if (!geojson.features && geojson.geometry) {
- const { type, coordinates } = geojson.geometry;
- const featureProperties = geojson.properties || {};
- const featureRefInfo = {
- source,
- featureIndex: -1,
- };
- switch (type) {
- case "Point":
- // 点
- this.addPoint(coordinates, featureProperties, featureRefInfo);
- break;
- case "LineString":
- // 线
- this.addLine(coordinates, featureProperties, featureRefInfo);
- break;
- case "Polygon":
- case "MultiPolygon":
- // 面
- this.addPolygon(coordinates, featureProperties, featureRefInfo);
- break;
- default:
- break;
- }
- } else if (geojson.features) {
- const features = geojson.features;
- // 2. 遍历features,根据type添加到地图中
- console.log("features", features);
- features.forEach((feature, featureIndex) => {
- const { type, coordinates } = feature.geometry;
- const featureProperties = feature.properties || {};
- const featureRefInfo = {
- source,
- featureIndex,
- };
- switch (type) {
- case "Point":
- // 点
- this.addPoint(coordinates, featureProperties, featureRefInfo);
- break;
- case "LineString":
- // 线
- this.addLine(coordinates, featureProperties, featureRefInfo);
- break;
- case "Polygon":
- case "MultiPolygon":
- // 面
- this.addPolygon(coordinates, featureProperties, featureRefInfo);
- break;
- default:
- break;
- }
- });
- }
- setTimeout(() => {
- viewer.scene.requestRender();
- });
- },
- // 添加点到地图中
- addPoint(coordinates, featureProperties = null, featureRefInfo = null) {
- // 1. 解析点的坐标
- console.log("addPoint coordinates", coordinates);
- // 2. 创建点实体
- const pointEntity = viewer.entities.add({
- name: "point",
- position:
- coordinates.length > 2
- ? SkyScenery.Cartesian3.fromDegrees(
- coordinates[0],
- coordinates[1],
- coordinates[2]
- )
- : SkyScenery.Cartesian3.fromDegrees(coordinates[0], coordinates[1]),
- point: {
- show: true,
- color: SkyScenery.Color.RED,
- pixelSize: 10,
- outlineColor: SkyScenery.Color.WHITE,
- outlineWidth: 2,
- },
- });
- pointEntity.__featureProperties = featureProperties;
- pointEntity.__featureRef = featureRefInfo;
- // 3. 将点实体添加到drawnEntities中
- this.drawnEntities.push(pointEntity);
- },
- // 添加线到地图中
- addLine(coordinates, featureProperties = null, featureRefInfo = null) {
- // 1. 解析线的坐标
- console.log("addLine coordinates", coordinates);
- // 2. 处理坐标格式:如果是二维数组则取第一个元素,否则直接使用
- const lineCoordinates =
- Array.isArray(coordinates[0]) && Array.isArray(coordinates[0][0])
- ? coordinates[0]
- : coordinates;
- // 3. 检测坐标是否包含Z值
- const hasZValues = lineCoordinates.some(
- (coord) => coord.length > 2 && typeof coord[2] === "number"
- );
- // 4. 根据是否有Z值选择合适的坐标转换方法
- let positions;
- if (hasZValues) {
- // 包含Z值,使用带高度的坐标转换方法
- const flatCoordinatesWithHeights = [];
- lineCoordinates.forEach((coord) => {
- flatCoordinatesWithHeights.push(coord[0], coord[1], coord[2] || 0);
- });
- positions = SkyScenery.Cartesian3.fromDegreesArrayHeights(
- flatCoordinatesWithHeights
- );
- } else {
- // 不包含Z值,使用普通坐标转换方法
- const flatCoordinates = [];
- lineCoordinates.forEach((coord) => {
- flatCoordinates.push(coord[0], coord[1]);
- });
- positions = SkyScenery.Cartesian3.fromDegreesArray(flatCoordinates);
- }
- // 5. 创建线实体
- const lineEntity = viewer.entities.add({
- name: "line",
- polyline: {
- show: true,
- positions: positions,
- material: SkyScenery.Color.BLUE.withAlpha(0.8),
- width: 3,
- },
- });
- lineEntity.__featureProperties = featureProperties;
- lineEntity.__featureRef = featureRefInfo;
- // 6. 将线实体添加到drawnEntities中
- this.drawnEntities.push(lineEntity);
- },
- // 添加面到地图中
- addPolygon(coordinates, featureProperties = null, featureRefInfo = null) {
- // 检测是否为MultiPolygon类型(MultiPolygon的坐标是三维数组)
- if (
- Array.isArray(coordinates[0]) &&
- Array.isArray(coordinates[0][0]) &&
- Array.isArray(coordinates[0][0][0])
- ) {
- console.log("MultiPolygon coordinates", coordinates);
- // 是MultiPolygon类型,遍历每个Polygon
- coordinates.forEach((polygonCoordinates) => {
- this.renderSinglePolygon(polygonCoordinates, featureProperties, featureRefInfo);
- });
- } else {
- // 是单个Polygon类型
- this.renderSinglePolygon(coordinates, featureProperties, featureRefInfo);
- }
- },
- // 渲染单个Polygon
- renderSinglePolygon(coordinates, featureProperties = null, featureRefInfo = null) {
- // 1. 处理坐标格式:确保获取外部环坐标
- const outerRingCoordinates =
- Array.isArray(coordinates[0]) && Array.isArray(coordinates[0][0])
- ? coordinates[0]
- : coordinates;
- // 2. 检测坐标是否包含Z值
- const hasZValues = outerRingCoordinates.some(
- (coord) => coord.length > 2 && typeof coord[2] === "number"
- );
- // 3. 根据是否有Z值选择合适的坐标转换方法
- let positions;
- let baseHeight = 0;
- let extrudedHeight = 0; // 默认挤压高度
- if (hasZValues) {
- // 包含Z值,使用带高度的坐标转换方法
- const flatCoordinatesWithHeights = [];
- let minZ = Infinity;
- let maxZ = -Infinity;
- // 计算Z值的范围
- outerRingCoordinates.forEach((coord) => {
- const z = coord[2] || 0;
- minZ = Math.min(minZ, z);
- maxZ = Math.max(maxZ, z);
- flatCoordinatesWithHeights.push(coord[0], coord[1], minZ); // 使用最小Z值作为基础高度
- });
- positions = SkyScenery.Cartesian3.fromDegreesArrayHeights(
- flatCoordinatesWithHeights
- );
- baseHeight = minZ;
- extrudedHeight = maxZ;
- } else {
- // 不包含Z值,使用普通坐标转换方法
- const flatCoordinates = [];
- outerRingCoordinates.forEach((coord) => {
- flatCoordinates.push(coord[0], coord[1]);
- });
- positions = SkyScenery.Cartesian3.fromDegreesArray(flatCoordinates);
- }
- // 4. 创建面实体
- const polygonEntity = viewer.entities.add({
- name: "polygon",
- polygon: {
- hierarchy: {
- positions: positions,
- },
- height: baseHeight, // 多边形底部高度
- extrudedHeight: extrudedHeight, // 多边形顶部高度,实现3D挤压效果
- heightReference: hasZValues
- ? SkyScenery.HeightReference.NONE
- : SkyScenery.HeightReference.CLAMP_TO_GROUND,
- material: SkyScenery.Color.GREEN.withAlpha(0.5),
- outline: true,
- outlineColor: SkyScenery.Color.WHITE,
- outlineWidth: 2,
- // 设置侧面材质
- extrudedMaterial: SkyScenery.Color.GREEN.withAlpha(0.8),
- },
- });
- polygonEntity.__featureProperties = featureProperties;
- polygonEntity.__featureRef = featureRefInfo;
- // 5. 将面实体添加到drawnEntities中
- this.drawnEntities.push(polygonEntity);
- },
- handleSelectChange(item, value) {
- this.jsonData[item] = value;
- },
- // 复制json数据
- copyJsonData(data) {
- let textarea = document.createElement("textarea");
- try {
- textarea.value = JSON.stringify(data, null, 2);
- document.body.appendChild(textarea);
- // 3. 选中文本(兼容移动端)
- textarea.select();
- // 兼容移动端:设置选择范围覆盖全部文本
- textarea.setSelectionRange(0, textarea.value.length);
- // 4. 执行复制命令
- const isSuccess = document.execCommand("copy");
- if (isSuccess) {
- this.$message({
- message: "复制成功",
- type: "success",
- });
- }
- } catch (err) {
- this.$message({
- message: err,
- type: "error",
- });
- } finally {
- document.body.removeChild(textarea);
- }
- },
- handleExceed(file) {
- this.$message({
- message: "最多只能上传一个文件",
- type: "warning",
- });
- },
- handleFileChange(file, fileList) {
- this.currentFile = file; // 注意:Element UI Plus 无需 .raw
- },
- uploadRemove() {
- this.currentFile = null;
- },
- handleChange(emit) {
- this.params.unit = "";
- this.currentFile = null;
- // 清除所有地图中的元素
- this.clearAll();
- this.jsonData = {};
- this.backData = {};
- this.renderStatus = {
- input: false,
- output: false,
- };
- this.renderedGeojsonCache = {
- input: null,
- output: null,
- };
- this.currentRenderedSource = "";
- this.propertyDialog.visible = false;
- this.propertyDialog.list = [];
- this.propertyDialog.source = "";
- this.propertyDialog.propertiesRef = null;
- this.propertyDialog.featureIndex = -1;
- this.SceneValue = emit[emit.length - 1];
- },
- // 初始化绘制处理器
- initDrawHandler() {
- // 创建绘制处理器
- if (!this.handler) {
- this.handler = new SkyScenery.ScreenSpaceEventHandler(viewer.canvas);
- }
- this.initFeaturePickHandler();
- },
- // 初始化地图要素拾取处理器(用于属性弹框)
- initFeaturePickHandler() {
- if (this.featurePickHandler || !viewer || !viewer.canvas) {
- return;
- }
- this.featurePickHandler = new SkyScenery.ScreenSpaceEventHandler(viewer.canvas);
- this.featurePickHandler.setInputAction((movement) => {
- // 绘制模式中不触发属性弹框,避免用户操作冲突
- if (this.currentTool || this.isDrawingHole) {
- return;
- }
- const pickedObject = viewer.scene.pick(movement.position);
- if (!SkyScenery.defined(pickedObject) || !SkyScenery.defined(pickedObject.id)) {
- this.propertyDialog.visible = false;
- this.propertyDialog.list = [];
- this.propertyDialog.source = "";
- this.propertyDialog.propertiesRef = null;
- this.propertyDialog.featureIndex = -1;
- return;
- }
- const entity = pickedObject.id;
- const featureIndex =
- entity.__featureRef && typeof entity.__featureRef.featureIndex === "number"
- ? entity.__featureRef.featureIndex
- : -1;
- const propertySource =
- (entity.__featureRef && entity.__featureRef.source) ||
- this.currentRenderedSource ||
- "input";
- const geojsonData =
- this.getSourceGeojsonData(propertySource) ||
- this.renderedGeojsonCache[propertySource];
- let propertiesRef = null;
- if (geojsonData) {
- if (
- entity.__featureRef &&
- typeof entity.__featureRef.featureIndex === "number" &&
- entity.__featureRef.featureIndex >= 0 &&
- geojsonData.features &&
- geojsonData.features[entity.__featureRef.featureIndex]
- ) {
- if (!geojsonData.features[entity.__featureRef.featureIndex].properties) {
- geojsonData.features[entity.__featureRef.featureIndex].properties = {};
- }
- propertiesRef =
- geojsonData.features[entity.__featureRef.featureIndex].properties;
- } else {
- if (!geojsonData.properties) {
- geojsonData.properties = {};
- }
- propertiesRef = geojsonData.properties;
- }
- }
- const propertyList = this.formatFeatureProperties(
- propertiesRef || entity.__featureProperties || {}
- );
- if (
- propertiesRef ||
- (entity.__featureProperties && typeof entity.__featureProperties === "object")
- ) {
- this.propertyDialog.source = propertySource;
- this.propertyDialog.propertiesRef =
- propertiesRef || entity.__featureProperties || {};
- this.propertyDialog.featureIndex = featureIndex;
- this.propertyDialog.list = propertyList;
- this.propertyDialog.visible = true;
- } else {
- this.propertyDialog.visible = false;
- this.propertyDialog.list = [];
- this.propertyDialog.source = "";
- this.propertyDialog.propertiesRef = null;
- this.propertyDialog.featureIndex = -1;
- }
- }, SkyScenery.ScreenSpaceEventType.LEFT_CLICK);
- },
- // 激活绘制工具
- activateDraw(type) {
- // 取消镂空绘制模式
- this.isDrawingHole = false;
- // 如果已经是当前激活的工具,则取消激活
- if (this.currentTool === type) {
- this.deactivateDraw();
- return;
- }
- // 先取消之前的绘制模式
- this.deactivateDraw();
- // 设置当前工具
- this.currentTool = type;
- this.drawingMode = type;
- // 重置当前绘制状态
- this.resetDrawingState();
- switch (type) {
- case "point":
- this.drawPoint();
- break;
- case "polyline":
- this.drawPolyline();
- break;
- case "polygon":
- this.drawPolygon();
- break;
- }
- },
- // 重置绘制状态
- resetDrawingState() {
- this.currentPositions = [];
- this.currentEntity = null;
- // 移除临时预览实体
- if (this.tempEntity) {
- viewer.entities.remove(this.tempEntity);
- viewer.scene.requestRender();
- this.tempEntity = null;
- }
- },
- // 取消绘制模式
- deactivateDraw() {
- if (this.handler) {
- // 移除所有事件监听器
- this.handler.removeInputAction(SkyScenery.ScreenSpaceEventType.LEFT_CLICK);
- this.handler.removeInputAction(SkyScenery.ScreenSpaceEventType.MOUSE_MOVE);
- this.handler.removeInputAction(SkyScenery.ScreenSpaceEventType.RIGHT_CLICK);
- }
- // 重置状态
- this.resetDrawingState();
- this.currentTool = null;
- this.drawingMode = null;
- this.isDrawingHole = false;
- },
- // 绘制点
- drawPoint() {
- const that = this;
- this.handler.setInputAction(function (event) {
- const ray = viewer.camera.getPickRay(event.position);
- const position = viewer.scene.globe.pick(ray, viewer.scene);
- if (position) {
- const cartographic = SkyScenery.Cartographic.fromCartesian(position);
- const longitude = SkyScenery.Math.toDegrees(cartographic.longitude);
- const latitude = SkyScenery.Math.toDegrees(cartographic.latitude);
- // 创建点实体(实时渲染)
- const entity = viewer.entities.add({
- position: position,
- point: {
- show: true,
- color: SkyScenery.Color.RED,
- pixelSize: 10,
- outlineColor: SkyScenery.Color.WHITE,
- outlineWidth: 2,
- },
- });
- viewer.scene.requestRender();
- that.drawnEntities.push(entity);
- // 转换为geometry格式并保存
- const geometry = {
- type: "Point",
- coordinates: [longitude, latitude],
- };
- that.geometries.push(geometry);
- that.changeGeometries();
- console.log("绘制了点:", geometry);
- }
- }, SkyScenery.ScreenSpaceEventType.LEFT_CLICK);
- },
- // 绘制线
- drawPolyline() {
- const that = this;
- // 鼠标移动时更新临时线(实时渲染预览)
- this.handler.setInputAction(function (event) {
- if (that.currentPositions.length > 0) {
- const ray = viewer.camera.getPickRay(event.endPosition);
- const endPosition = viewer.scene.globe.pick(ray, viewer.scene);
- if (endPosition) {
- const tempPositions = [...that.currentPositions, endPosition];
- // 更新临时预览实体
- if (!that.tempEntity) {
- that.tempEntity = viewer.entities.add({
- polyline: {
- show: true,
- positions: tempPositions,
- material: SkyScenery.Color.WHITE.withAlpha(0.5),
- width: 3,
- },
- });
- viewer.scene.requestRender();
- } else {
- that.tempEntity.polyline.positions = tempPositions;
- }
- }
- }
- }, SkyScenery.ScreenSpaceEventType.MOUSE_MOVE);
- // 左键点击添加点
- this.handler.setInputAction(function (event) {
- const ray = viewer.camera.getPickRay(event.position);
- const position = viewer.scene.globe.pick(ray, viewer.scene);
- if (position) {
- that.currentPositions.push(position.clone());
- // 如果是第一个点,不需要更新实体
- if (that.currentPositions.length > 1) {
- // 如果已有实体,更新它
- if (that.currentEntity) {
- viewer.entities.remove(that.currentEntity);
- }
- // 创建当前实体(实时渲染)
- that.currentEntity = viewer.entities.add({
- polyline: {
- show: true,
- positions: [...that.currentPositions],
- material: SkyScenery.Color.RED,
- width: 3,
- },
- });
- viewer.scene.requestRender();
- }
- }
- }, SkyScenery.ScreenSpaceEventType.LEFT_CLICK);
- // 右键点击完成绘制
- this.handler.setInputAction(function () {
- if (that.currentPositions.length > 1) {
- // 移除临时预览实体
- if (that.tempEntity) {
- viewer.entities.remove(that.tempEntity);
- that.tempEntity = null;
- }
- // 否则创建线
- // 确保当前实体存在
- if (!that.currentEntity) {
- that.currentEntity = viewer.entities.add({
- polyline: {
- show: true,
- positions: [...that.currentPositions],
- material: SkyScenery.Color.RED,
- width: 3,
- },
- });
- }
- that.drawnEntities.push(that.currentEntity);
- viewer.scene.requestRender();
- // 转换为geometry格式并保存
- const coordinates = [];
- that.currentPositions.forEach((pos) => {
- const cartographic = SkyScenery.Cartographic.fromCartesian(pos);
- coordinates.push([
- SkyScenery.Math.toDegrees(cartographic.longitude),
- SkyScenery.Math.toDegrees(cartographic.latitude),
- ]);
- });
- const geometry = {
- type: "LineString",
- coordinates: coordinates,
- };
- that.geometries.push(geometry);
- that.changeGeometries();
- console.log("绘制了线:", geometry);
- // 取消当前绘制模式
- that.deactivateDraw();
- }
- }, SkyScenery.ScreenSpaceEventType.RIGHT_CLICK);
- },
- // 绘制面
- drawPolygon() {
- const that = this;
- // 鼠标移动时更新临时面(实时渲染预览)
- this.handler.setInputAction(function (event) {
- if (that.currentPositions.length > 0) {
- const ray = viewer.camera.getPickRay(event.endPosition);
- const endPosition = viewer.scene.globe.pick(ray, viewer.scene);
- if (endPosition) {
- // 为了预览效果,临时闭合多边形
- const tempPositions = [
- ...that.currentPositions,
- endPosition,
- that.currentPositions[0],
- ];
- // 更新临时预览实体
- if (!that.tempEntity) {
- that.tempEntity = viewer.entities.add({
- polygon: {
- show: true,
- hierarchy: new SkyScenery.PolygonHierarchy(tempPositions),
- material: SkyScenery.Color.RED.withAlpha(0.3),
- outline: true,
- outlineColor: SkyScenery.Color.RED.withAlpha(0.8),
- outlineWidth: 2,
- },
- });
- viewer.scene.requestRender();
- } else {
- that.tempEntity.polygon.hierarchy = new SkyScenery.PolygonHierarchy(
- tempPositions
- );
- }
- }
- }
- }, SkyScenery.ScreenSpaceEventType.MOUSE_MOVE);
- // 左键点击添加点
- this.handler.setInputAction(function (event) {
- const ray = viewer.camera.getPickRay(event.position);
- const position = viewer.scene.globe.pick(ray, viewer.scene);
- if (position) {
- // 检查是否点击了第一个点附近(自动闭合)
- if (
- that.currentPositions.length > 2 &&
- that.isPositionNearFirst(position, that.currentPositions[0])
- ) {
- // 完成多边形绘制
- that.finalizePolygonDrawing();
- that.deactivateDraw();
- return;
- }
- that.currentPositions.push(position.clone());
- // 如果有足够的点,更新多边形实体(实时渲染)
- if (that.currentPositions.length > 2) {
- // 闭合多边形
- const closedPositions = [...that.currentPositions, that.currentPositions[0]];
- // 如果已有实体,更新它
- if (that.currentEntity) {
- viewer.entities.remove(that.currentEntity);
- }
- // 创建当前实体
- that.currentEntity = viewer.entities.add({
- polygon: {
- show: true,
- hierarchy: new SkyScenery.PolygonHierarchy(closedPositions),
- material: SkyScenery.Color.RED.withAlpha(0.5),
- outline: true,
- outlineColor: SkyScenery.Color.BLUE,
- outlineWidth: 2,
- },
- });
- viewer.scene.requestRender();
- }
- }
- }, SkyScenery.ScreenSpaceEventType.LEFT_CLICK);
- // 右键点击完成绘制
- this.handler.setInputAction(function () {
- if (that.currentPositions.length > 2) {
- that.finalizePolygonDrawing();
- // 取消当前绘制模式
- that.deactivateDraw();
- }
- }, SkyScenery.ScreenSpaceEventType.RIGHT_CLICK);
- },
- // 完成多边形绘制
- finalizePolygonDrawing() {
- // 移除临时预览实体
- if (this.tempEntity) {
- viewer.entities.remove(this.tempEntity);
- this.tempEntity = null;
- }
- // 确保多边形是闭合的
- let closedPositions = [...this.currentPositions];
- // 创建最终的多边形实体
- if (this.currentEntity) {
- viewer.entities.remove(this.currentEntity);
- }
- this.currentEntity = viewer.entities.add({
- polygon: {
- show: true,
- hierarchy: new SkyScenery.PolygonHierarchy(closedPositions),
- material: SkyScenery.Color.RED.withAlpha(0.5),
- outline: true,
- outlineColor: SkyScenery.Color.BLUE,
- outlineWidth: 2,
- },
- });
- viewer.scene.requestRender();
- this.drawnEntities.push(this.currentEntity);
- // 转换为geometry格式并保存(不包含重复的最后一个点)
- const coordinates = [];
- const mainRing = [];
- for (let i = 0; i < closedPositions.length; i++) {
- const cartographic = SkyScenery.Cartographic.fromCartesian(closedPositions[i]);
- mainRing.push([
- SkyScenery.Math.toDegrees(cartographic.longitude),
- SkyScenery.Math.toDegrees(cartographic.latitude),
- ]);
- }
- const cartographic = SkyScenery.Cartographic.fromCartesian(closedPositions[0]);
- mainRing.push([
- SkyScenery.Math.toDegrees(cartographic.longitude),
- SkyScenery.Math.toDegrees(cartographic.latitude),
- ]);
- coordinates.push(mainRing);
- const geometry = {
- type: "Polygon",
- coordinates: coordinates, // GeoJSON格式
- holes: [], // 用于存储镂空区域
- };
- // 保存实体和几何对象的关联
- this.currentEntity.geometryRef = geometry;
- this.geometries.push(geometry);
- this.changeGeometries();
- console.log("绘制了面:", geometry);
- },
- // 开始绘制镂空
- startHoleDrawing() {
- // 如果已经在绘制镂空,则取消
- if (this.isDrawingHole) {
- this.isDrawingHole = false;
- this.deactivateDraw();
- return;
- }
- // 取消其他绘制模式
- this.deactivateDraw();
- // 设置为镂空绘制模式
- this.isDrawingHole = true;
- console.log("请点击一个多边形以添加镂空");
- // 设置点击事件选择多边形
- const that = this;
- this.handler.setInputAction(function (event) {
- // 拾取实体
- const pickedObject = viewer.scene.pick(event.position);
- if (pickedObject && pickedObject.id && pickedObject.id.polygon) {
- const polygonEntity = pickedObject.id;
- // 检查是否有对应的几何对象
- if (polygonEntity.geometryRef && polygonEntity.geometryRef.type === "Polygon") {
- that.currentPolygonEntity = polygonEntity;
- that.currentPolygonGeometry = polygonEntity.geometryRef;
- console.log("已选择多边形,现在绘制镂空区域");
- // 开始绘制镂空区域(使用多边形绘制逻辑,但最后添加为镂空)
- that.resetDrawingState();
- that.drawHole();
- } else {
- console.log("选择的不是有效的多边形");
- }
- } else {
- console.log("未选中任何多边形");
- }
- }, SkyScenery.ScreenSpaceEventType.LEFT_CLICK);
- },
- // 绘制镂空区域
- drawHole() {
- const that = this;
- // 清除之前的事件
- this.handler.removeInputAction(SkyScenery.ScreenSpaceEventType.LEFT_CLICK);
- // 鼠标移动时更新临时镂空区域
- this.handler.setInputAction(function (event) {
- if (that.currentPositions.length > 0) {
- const ray = viewer.camera.getPickRay(event.endPosition);
- const endPosition = viewer.scene.globe.pick(ray, viewer.scene);
- if (endPosition) {
- // 为了预览效果,临时闭合多边形
- const tempPositions = [
- ...that.currentPositions,
- endPosition,
- that.currentPositions[0],
- ];
- // 更新临时预览实体
- if (!that.tempEntity) {
- that.tempEntity = viewer.entities.add({
- polygon: {
- show: true,
- hierarchy: new SkyScenery.PolygonHierarchy(tempPositions),
- material: new SkyScenery.ColorMaterialProperty(
- new SkyScenery.Color(1, 0, 0, 0.3)
- ),
- outline: true,
- outlineColor: SkyScenery.Color.RED,
- outlineWidth: 2,
- },
- });
- viewer.scene.requestRender();
- } else {
- that.tempEntity.polygon.hierarchy = new SkyScenery.PolygonHierarchy(
- tempPositions
- );
- }
- }
- }
- }, SkyScenery.ScreenSpaceEventType.MOUSE_MOVE);
- // 左键点击添加点
- this.handler.setInputAction(function (event) {
- const ray = viewer.camera.getPickRay(event.position);
- const position = viewer.scene.globe.pick(ray, viewer.scene);
- if (position) {
- // 检查是否点击了第一个点附近(自动闭合)
- if (
- that.currentPositions.length > 2 &&
- that.isPositionNearFirst(position, that.currentPositions[0])
- ) {
- // 完成镂空绘制
- that.finalizeHoleDrawing();
- return;
- }
- that.currentPositions.push(position.clone());
- }
- }, SkyScenery.ScreenSpaceEventType.LEFT_CLICK);
- // 右键点击完成绘制
- this.handler.setInputAction(function () {
- if (that.currentPositions.length > 2) {
- that.finalizeHoleDrawing();
- }
- }, SkyScenery.ScreenSpaceEventType.RIGHT_CLICK);
- },
- // 完成镂空绘制
- finalizeHoleDrawing() {
- // 移除临时预览实体
- if (this.tempEntity) {
- viewer.entities.remove(this.tempEntity);
- viewer.scene.requestRender();
- this.tempEntity = null;
- }
- // 确保镂空区域是闭合的
- let closedPositions = [...this.currentPositions];
- // 转换为几何坐标
- const holeCoordinates = [];
- for (let i = 0; i < closedPositions.length; i++) {
- const cartographic = SkyScenery.Cartographic.fromCartesian(closedPositions[i]);
- holeCoordinates.push([
- SkyScenery.Math.toDegrees(cartographic.longitude),
- SkyScenery.Math.toDegrees(cartographic.latitude),
- ]);
- }
- const cartographic = SkyScenery.Cartographic.fromCartesian(closedPositions[0]);
- holeCoordinates.push([
- SkyScenery.Math.toDegrees(cartographic.longitude),
- SkyScenery.Math.toDegrees(cartographic.latitude),
- ]);
- // 添加到几何对象的镂空数组
- if (!this.currentPolygonGeometry.holes) {
- this.currentPolygonGeometry.holes = [];
- }
- this.currentPolygonGeometry.holes.push(holeCoordinates);
- // 更新多边形的层级结构以包含镂空
- const polygonHierarchy = this.buildPolygonHierarchyWithHoles(
- this.currentPolygonGeometry.coordinates[0],
- this.currentPolygonGeometry.holes
- );
- // 更新实体显示
- this.currentPolygonEntity.polygon.hierarchy = polygonHierarchy;
- console.log("添加了镂空区域:", holeCoordinates);
- console.log("更新后的多边形几何:", this.currentPolygonGeometry);
- // 重置状态
- this.resetDrawingState();
- this.isDrawingHole = false;
- this.currentPolygonEntity = null;
- this.currentPolygonGeometry = null;
- // 取消绘制模式
- this.deactivateDraw();
- },
- // 构建包含镂空的多边形层级结构
- buildPolygonHierarchyWithHoles(outerRing, holes) {
- // 将外部环转换为Cartesian3数组
- const outerRingPositions = [];
- outerRing.forEach((coord) => {
- const cartesian = SkyScenery.Cartesian3.fromDegrees(coord[0], coord[1]);
- outerRingPositions.push(cartesian);
- });
- // 闭合外部环
- outerRingPositions.push(outerRingPositions[0]);
- // 创建层级结构
- const hierarchy = new SkyScenery.PolygonHierarchy(outerRingPositions);
- // 添加镂空
- if (holes && holes.length > 0) {
- hierarchy.holes = holes.map((hole) => {
- const holePositions = [];
- hole.forEach((coord) => {
- const cartesian = SkyScenery.Cartesian3.fromDegrees(coord[0], coord[1]);
- holePositions.push(cartesian);
- });
- // 闭合镂空环
- holePositions.push(holePositions[0]);
- return new SkyScenery.PolygonHierarchy(holePositions);
- });
- }
- return hierarchy;
- },
- // 检查点是否靠近第一个点
- isPositionNearFirst(position, firstPosition, tolerance = 10) {
- // 单位:米
- const distance = SkyScenery.Cartesian3.distance(position, firstPosition);
- return distance < tolerance;
- },
- changeGeometries() {
- let requestData = {};
- const geometriesToSend = this.geometries;
- let FeatureCollectionFeatures = [];
- geometriesToSend.forEach((item) => {
- FeatureCollectionFeatures.push({
- type: "Feature",
- properties: {},
- geometry: {
- type: item.type,
- coordinates: item.coordinates,
- },
- });
- });
- // 构造请求数据
- requestData = {
- type: "FeatureCollection",
- features: FeatureCollectionFeatures,
- timestamp: new Date().getTime(),
- };
- if (
- this.SceneValue &&
- this.dmsServerItem &&
- this.dmsServerItem.elementTypes &&
- this.ifHasType("unit") &&
- this.params.unit
- ) {
- requestData.unit = this.params.unit;
- }
- if (
- this.SceneValue &&
- this.dmsServerItem &&
- this.dmsServerItem.elementTypes &&
- this.ifHasType("EPSILON") &&
- this.params.EPSILON
- ) {
- requestData.EPSILON = this.params.EPSILON;
- }
- if (
- this.SceneValue &&
- this.dmsServerItem &&
- this.dmsServerItem.elementTypes &&
- this.ifHasType("inPrj") &&
- this.params.inPrj
- ) {
- requestData.inPrj = this.params.inPrj;
- }
- if (
- this.SceneValue &&
- this.dmsServerItem &&
- this.dmsServerItem.elementTypes &&
- this.ifHasType("compressionRatio") &&
- this.params.compressionRatio
- ) {
- requestData.compressionRatio = this.params.compressionRatio;
- }
- if (
- this.SceneValue &&
- this.dmsServerItem &&
- this.dmsServerItem.elementTypes &&
- this.ifHasType("outPrj") &&
- this.params.outPrj
- ) {
- requestData.outPrj = this.params.outPrj;
- }
- if (
- this.SceneValue &&
- this.dmsServerItem &&
- this.dmsServerItem.elementTypes &&
- this.ifHasType("distance") &&
- this.params.distance
- ) {
- requestData.distance = this.params.distance;
- }
- this.jsonData = requestData;
- },
- // 发送几何数据到后台接口
- sendGeometriesToBackend() {
- // 这里使用axios发送请求,需要确保项目中已安装并导入axios
- let requestData;
- this.backData = {};
- let requestUrl = this.dmsServerItem.apiUrl;
- if (
- this.ifHasType("point") ||
- this.ifHasType("polyline") ||
- this.ifHasType("polygon")
- ) {
- requestData = this.jsonData;
- } else {
- requestData = new FormData();
- this.dmsServerItem.elementTypes.forEach((key) => {
- if (key == "file") {
- if (!this.currentFile) {
- return this.$message({
- message: "请选择文件",
- type: "error",
- });
- }
- requestData.append("file", this.currentFile.raw);
- } else {
- requestData.append(key, this.params[key]);
- }
- });
- }
- let that = this;
- // 实际项目中使用以下代码发送请求
- wgn
- .topology(requestUrl, requestData)
- .then((res) => {
- if (requestUrl.indexOf("downloadFile") == -1) {
- that.backData = res;
- if (res.code && res.code == 200) {
- that.$message({
- message: res.message,
- type: "success",
- });
- } else {
- that.$message({
- message: res.content,
- type: "error",
- });
- }
- } else {
- const blob = res; // 响应体是 Blob 类型
- if (!blob) {
- that.$message.error("下载失败:文件流为空");
- reject("文件流为空");
- return;
- }
- let _downloadFile = that.params.filePath + "";
- let fileName = _downloadFile.substring(
- _downloadFile.indexOf("down_files") + 11
- );
- // D:\work\backCode\one_map_portal_server\down_files\0378b1c2e92a4c36ab447f552c0c7888.xlsx
- // 替换原代码中创建 url 的逻辑,先加校验
- console.log("blob 类型:", Object.prototype.toString.call(blob)); // 正常应输出 [object Blob]
- console.log("blob 大小:", blob.size); // 正常应大于 0
- if (!(blob instanceof Blob) || blob.size === 0) {
- this.$message.error("文件流解析失败,无法生成下载链接");
- return;
- }
- const url = window.URL.createObjectURL(blob); // 将 Blob 转为临时 URL
- const link = document.createElement("a");
- link.href = url;
- link.download = fileName; // 设置下载文件名
- link.style.display = "none";
- document.body.appendChild(link);
- link.click(); // 触发点击下载
- document.body.removeChild(link);
- window.URL.revokeObjectURL(url); // 销毁临时 URL,避免内存泄漏
- }
- })
- .catch((error) => {
- console.error("后台接口调用失败:", error);
- });
- },
- // 清除所有绘制的元素,并更新geometries
- clearAll() {
- this.clearAllMap();
- this.changeGeometries();
- },
- // 清除所有绘制的元素
- clearAllMap() {
- // 移除所有实体
- this.drawnEntities.forEach((entity) => {
- viewer.entities.remove(entity);
- });
- viewer.scene.requestRender();
- // 清空数组
- this.drawnEntities = [];
- this.geometries = [];
- this.currentRenderedSource = "";
- this.propertyDialog.visible = false;
- this.propertyDialog.list = [];
- this.propertyDialog.source = "";
- this.propertyDialog.propertiesRef = null;
- this.propertyDialog.featureIndex = -1;
- // 取消当前绘制模式
- this.deactivateDraw();
- console.log("已清除所有绘制的元素");
- },
- },
- };
- </script>
- <style lang="less" scoped>
- // 操作栏样式
- #controlPanelBox {
- width: calc(30vw - 2rem);
- min-width: 500px;
- height: calc(100vh - 2rem);
- display: flex;
- flex-direction: column;
- padding: 1rem;
- position: fixed;
- top: 0px;
- right: 0;
- background: #08224a;
- }
- // 绘制按钮区域
- .toolbar {
- position: absolute;
- top: 10px;
- left: -120px;
- z-index: 1000;
- background: #08224a;
- border-radius: 8px;
- padding: 3px 5px;
- box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
- display: flex;
- flex-direction: column;
- gap: 8px;
- }
- .tool-item {
- padding: 10px 20px;
- cursor: pointer;
- border-radius: 4px;
- transition: all 0.3s ease;
- user-select: none;
- font-size: 14px;
- }
- .tool-item:hover {
- background: #ffffff32;
- }
- .tool-item.active {
- background: #409eff;
- color: white;
- }
- .sceneNameBox {
- display: flex;
- align-items: center;
- justify-content: space-between;
- }
- .vueJsonEditor_box {
- width: 100%;
- height: 100%;
- position: relative;
- }
- .vueJsonEditor_tools {
- position: absolute;
- top: 8px;
- right: 10px;
- font-size: 14px;
- z-index: 1;
- color: #fff;
- }
- .vueJsonEditor_tools span {
- padding: 2px 5px;
- opacity: 0.8;
- cursor: pointer;
- border: 1px solid #ffffff00;
- border-radius: 2px;
- margin-right: 5px;
- &:hover {
- background-color: rgba(255, 255, 255, 0.2);
- border-color: rgba(255, 255, 255, 0.4);
- }
- }
- .feature-property-content {
- max-height: 360px;
- overflow: auto;
- padding-right: 4px;
- }
- .feature-property-tip {
- color: #9ed2ff;
- margin-bottom: 12px;
- font-size: 13px;
- }
- .feature-property-item {
- padding: 10px 12px;
- margin-bottom: 8px;
- border-radius: 8px;
- background: rgba(61, 132, 205, 0.14);
- border: 1px solid rgba(111, 186, 255, 0.35);
- }
- .feature-property-row {
- display: flex;
- align-items: center;
- gap: 8px;
- margin-bottom: 8px;
- }
- .feature-property-delete-btn {
- flex-shrink: 0;
- }
- .feature-property-key-input {
- width: 180px;
- }
- :deep(.feature-property-input .el-input__wrapper) {
- background: rgba(8, 34, 74, 0.68);
- box-shadow: inset 0 0 0 1px rgba(130, 198, 255, 0.4);
- }
- :deep(.feature-property-input .el-input__inner) {
- color: #ffffff;
- }
- :deep(.feature-property-dialog .el-dialog) {
- background: rgba(7, 24, 48, 0.95);
- border: 1px solid rgba(111, 186, 255, 0.5);
- }
- :deep(.feature-property-dialog .el-dialog__title) {
- color: #e8f4ff;
- font-weight: 700;
- }
- :deep(.feature-property-dialog .el-dialog__headerbtn .el-dialog__close) {
- color: #d8ecff;
- }
- :deep(.feature-property-dialog .el-dialog__body) {
- padding-top: 12px;
- }
- :deep(.ace_editor) {
- height: 600px !important;
- max-height: calc(100vh - 300px) !important;
- }
- :deep(.jsoneditor-modes),
- :deep(.jsoneditor-poweredBy) {
- display: none;
- }
- </style>
|