| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804 |
- <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
- ref="sceneFileUpload"
- v-model:file-list="fileList"
- class="upload-demo"
- action=""
- accept=".geojson,.csv,.xlsx,.zip"
- :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-input
- v-if="item === 'geocodeColumnKeyword' && isGeocodeImportZip"
- v-model="params[item]"
- placeholder="zip 内含多个表格时请手动输入列名"
- clearable
- style="width: 240px"
- @input="handleSelectChange(item, $event)"
- />
- <el-select
- v-else-if="selectOptions[item]"
- @change="handleSelectChange(item, $event)"
- v-model="params[item]"
- :placeholder="
- item === 'geocodeColumnKeyword' && (!selectOptions[item] || !selectOptions[item].length)
- ? '请先上传 csv / xlsx 解析列名'
- : '请选择' + item
- "
- style="width: 240px"
- :disabled="
- item === 'geocodeColumnKeyword' &&
- !isGeocodeImportZip &&
- (!selectOptions[item] || !selectOptions[item].length)
- "
- filterable
- clearable
- >
- <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。选中地图上的点/线/面后,可按住顶点拖动以实时修改坐标(与右侧入参 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>
- <div class="feature-property-footer">
- <el-button type="primary" plain @click="addProperty">+ 添加字段</el-button>
- <div class="feature-property-actions">
- <el-button link type="primary" @click="startEditGeometry">更改绘制</el-button>
- <el-button link type="danger" @click="deleteSelectedFeature">删除</el-button>
- </div>
- </div>
- </template>
- </el-dialog>
- <div
- v-if="hoverHint.visible"
- class="map-edit-hover-hint"
- :style="{ left: hoverHint.left + 'px', top: hoverHint.top + 'px' }"
- >
- {{ hoverHint.text }}
- </div>
- <!-- 绘制工具栏 -->
- <div
- class="toolbar"
- v-if="
- SceneValue &&
- dmsServerItem &&
- (ifHasType('point') || ifHasType('polyline') || ifHasType('polygon'))
- "
- >
- <div
- v-if="ifHasType('point')"
- 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
- v-if="ifHasType('polyline')"
- 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
- v-if="ifHasType('polygon')"
- 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 * as XLSX from "xlsx";
- import wgn from "../../api/wgn";
- import { getFile } from "../../utils/request";
- import { WGN_SCENE_LIST } from "@/data/wgnSceneList";
- // 控制面板
- 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: "",
- // 非空转空时,类型字段下拉选择[地址\村居\街镇\区县\要素ID]
- keyType: "",
- // 非空转空时,上传文件解析csv或xlsx文件后,渲染列名到下拉选择框中,指定某列作为类型字段
- geocodeColumnKeyword: "",
- },
- // 请求参数
- jsonData: {},
- // 返回参数
- backData: {},
- // 当前场景值
- SceneValue: "",
- // 场景列表(与微功能中心侧栏同源)
- SceneList: WGN_SCENE_LIST,
- 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",
- },
- ],
- keyType: [
- { value: "地址", label: "地址" },
- { value: "村居", label: "村居" },
- { value: "街镇", label: "街镇" },
- { value: "区县", label: "区县" },
- // { value: "要素ID", label: "要素ID" },
- ],
- geocodeColumnKeyword: [],
- },
- 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,
- },
- /** 地图编辑选中:高亮要素 + 顶点柄 */
- mapEditSelection: null,
- vertexHandleEntities: [],
- hoveredVertexIndex: -1,
- hoveredHandleEntity: null,
- hoveredFeatureEntity: null,
- geometryEditMode: false,
- pickedFeatureRef: null,
- hoverHint: {
- visible: false,
- text: "",
- left: 0,
- top: 0,
- },
- /** 拖动/绘制时临时关闭 requestRenderMode,避免面片异步剖分导致与顶点不同步 */
- _interactiveRenderDepth: 0,
- _savedSceneRenderMode: null,
- };
- },
- computed: {
- /** zip 多表场景下列名无法预解析,改用手动输入 */
- isGeocodeImportZip() {
- const f = this.currentFile;
- const name = f ? f.name || (f.raw && f.raw.name) || "" : "";
- return String(name).toLowerCase().endsWith(".zip");
- },
- },
- 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;
- }
- this.resetPickDragState();
- this.setScreenSpaceCameraForDrag(true);
- while (this._interactiveRenderDepth > 0) {
- this.exitInteractiveSceneRender();
- }
- },
- watch: {
- SceneValue(newVal, oldVal) {
- if (newVal && newVal != oldVal) {
- // 请求DMS
- this.searchServerList(this.SceneValue);
- }
- },
- dmsServerItem: {
- handler() {
- this.$nextTick(() => this.syncDrawToolWithElementTypes());
- },
- deep: true,
- },
- },
- methods: {
- /** 与 DMS 入参规则 c_input_parameter_rules 解析后的 elementTypes 对齐,支持少量别名 */
- expandParameterTypeAliases(type) {
- const aliases = [type, type + "Z"];
- if (type === "polyline") {
- aliases.push("line", "lineZ", "LineString", "LineStringZ");
- }
- if (type === "polygon") {
- aliases.push("multipolygon", "MultiPolygon", "MultiPolygonZ");
- }
- return aliases;
- },
- ifHasType(type) {
- if (
- !this.dmsServerItem ||
- !this.dmsServerItem.elementTypes ||
- this.dmsServerItem.elementTypes.length === 0
- ) {
- return false;
- }
- const declared = this.dmsServerItem.elementTypes;
- const candidates = this.expandParameterTypeAliases(type);
- return declared.some((t) => candidates.includes(t));
- },
- /** DMS 声明的单个参数是否属于点/线/面几何槽位,返回规范名 point | polyline | polygon */
- geometrySlotCanonical(declaredType) {
- if (declaredType == null || declaredType === "") {
- return null;
- }
- const d = String(declaredType);
- for (const canon of ["point", "polyline", "polygon"]) {
- const aliases = this.expandParameterTypeAliases(canon);
- const hit = aliases.some(
- (a) => String(a) === d || String(a).toLowerCase() === d.toLowerCase()
- );
- if (hit) {
- return canon;
- }
- }
- return null;
- },
- /** GeoJSON geometry.type -> 与 geometrySlotCanonical 同一套规范名 */
- featureGeometryCanonical(geoJsonType) {
- if (!geoJsonType) {
- return null;
- }
- const t = String(geoJsonType);
- if (t === "Point") {
- return "point";
- }
- if (t === "LineString" || t === "MultiLineString") {
- return "polyline";
- }
- if (t === "Polygon" || t === "MultiPolygon") {
- return "polygon";
- }
- return null;
- },
- /**
- * 发送前校验:当「元素个数」numberOf > 0 时,与入参 FeatureCollection.features 数量对齐;
- * numberOf 为 0 时表示几何要素个数不固定(支持多个),不拦截数量。
- * 「参数类型」elementTypes 中的 point/polyline/polygon 仅表示允许的几何类型(可多选),
- * 不要求每种类型各来一个,也不要求与声明顺序一一对应(忽略 unit/file 等非几何项)。
- */
- validateSendGeometryParams() {
- if (!this.dmsServerItem || !Array.isArray(this.dmsServerItem.elementTypes)) {
- return { ok: true };
- }
- const types = this.dmsServerItem.elementTypes;
- const geoSlots = types
- .map((declared) => ({
- declared,
- canon: this.geometrySlotCanonical(declared),
- }))
- .filter((s) => s.canon != null);
- if (geoSlots.length === 0) {
- return { ok: true };
- }
- const allowedCanonSet = new Set(geoSlots.map((s) => s.canon));
- const rawN = this.dmsServerItem.numberOf;
- let expectedGeomCount = null;
- if (rawN !== undefined && rawN !== null && rawN !== "") {
- const parsedN = typeof rawN === "number" ? rawN : Number(rawN);
- // 0 表示不固定个数、支持多个要素,不做「元素个数」与 features 数量对齐拦截
- if (Number.isFinite(parsedN) && parsedN > 0) {
- expectedGeomCount = parsedN;
- }
- }
- const fc = this.jsonData;
- const features = fc && Array.isArray(fc.features) ? fc.features : [];
- if (expectedGeomCount !== null) {
- if (features.length > expectedGeomCount) {
- return {
- ok: false,
- message: `入参几何要素过多:当前 ${features.length} 个,场景要求 ${expectedGeomCount} 个(元素个数)`,
- };
- }
- if (features.length < expectedGeomCount) {
- return {
- ok: false,
- message: `入参几何要素不足:当前 ${features.length} 个,场景要求 ${expectedGeomCount} 个(元素个数),请绘制或渲染足够要素`,
- };
- }
- }
- for (let i = 0; i < features.length; i++) {
- const f = features[i];
- const g = f && f.geometry;
- if (!g || !g.type) {
- return {
- ok: false,
- message: `第 ${i + 1} 个几何要素缺少有效的 geometry`,
- };
- }
- const fk = this.featureGeometryCanonical(g.type);
- if (!fk) {
- return {
- ok: false,
- message: `第 ${i + 1} 个几何要素类型「${g.type}」不符合场景要求,请使用点、线或面`,
- };
- }
- if (!allowedCanonSet.has(fk)) {
- const allowedLabel = [...allowedCanonSet].join("、");
- return {
- ok: false,
- message: `第 ${i + 1} 个几何要素类型「${g.type}」不在场景允许的类型内,允许:${allowedLabel}`,
- };
- }
- }
- return { ok: true };
- },
- /** 场景切换后若当前绘制类型不在参数类型中,则关闭绘制避免“隐形”仍在绘制 */
- syncDrawToolWithElementTypes() {
- if (!this.currentTool) {
- return;
- }
- const t = this.currentTool;
- if (t === "point" && !this.ifHasType("point")) {
- this.deactivateDraw();
- } else if (t === "polyline" && !this.ifHasType("polyline")) {
- this.deactivateDraw();
- } else if (t === "polygon" && !this.ifHasType("polygon")) {
- this.deactivateDraw();
- }
- },
- 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;
- },
- syncGeometriesFromRenderedSource() {
- const src = this.currentRenderedSource || "input";
- const geojson = this.getSourceGeojsonData(src);
- if (!geojson) {
- return;
- }
- const list = [];
- if (geojson.features && Array.isArray(geojson.features)) {
- geojson.features.forEach((f) => {
- if (f && f.geometry && f.geometry.type && f.geometry.coordinates) {
- list.push({
- type: f.geometry.type,
- coordinates: JSON.parse(JSON.stringify(f.geometry.coordinates)),
- });
- }
- });
- } else if (geojson.geometry && geojson.geometry.type && geojson.geometry.coordinates) {
- list.push({
- type: geojson.geometry.type,
- coordinates: JSON.parse(JSON.stringify(geojson.geometry.coordinates)),
- });
- }
- if (list.length) {
- this.geometries = list;
- }
- },
- // 将用户输入或后台返回的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");
- },
- startEditGeometry() {
- const picked = this.pickedFeatureRef;
- if (!picked || !picked.entity) {
- this.$message({
- message: "请先选择要素",
- type: "warning",
- });
- return;
- }
- this.geometryEditMode = true;
- this.deactivateDraw();
- this.applyMapEditSelection(picked.entity);
- this.propertyDialog.visible = false;
- this.$message({
- message: "已进入节点编辑模式,可拖拽顶点或中点调整绘制",
- type: "success",
- });
- },
- reindexEntityFeatureRefs(source, deletedIndex) {
- this.drawnEntities.forEach((ent) => {
- if (!ent || !ent.__featureRef) return;
- if (
- ent.__featureRef.source === source &&
- typeof ent.__featureRef.featureIndex === "number" &&
- ent.__featureRef.featureIndex > deletedIndex
- ) {
- ent.__featureRef.featureIndex -= 1;
- }
- });
- },
- deleteSelectedFeature() {
- const sel = this.mapEditSelection || this.pickedFeatureRef;
- if (!sel || !sel.entity || sel.featureIndex < 0) {
- this.$message({
- message: "请先选择要素",
- type: "warning",
- });
- return;
- }
- const source = sel.source || this.currentRenderedSource || "input";
- const geojson = this.getSourceGeojsonData(source);
- if (!geojson || !geojson.features || !Array.isArray(geojson.features)) {
- this.$message({
- message: "当前数据不是 FeatureCollection,无法删除",
- type: "warning",
- });
- return;
- }
- const idx = sel.featureIndex;
- if (idx < 0 || idx >= geojson.features.length) {
- this.$message({
- message: "要素索引无效,删除失败",
- type: "error",
- });
- return;
- }
- geojson.features.splice(idx, 1);
- const toRemove = this.drawnEntities.filter(
- (ent) =>
- ent &&
- ent.__featureRef &&
- ent.__featureRef.source === source &&
- ent.__featureRef.featureIndex === idx
- );
- toRemove.forEach((ent) => {
- try {
- viewer.entities.remove(ent);
- } catch (e) {
- /* ignore */
- }
- });
- this.drawnEntities = this.drawnEntities.filter((ent) => !toRemove.includes(ent));
- this.reindexEntityFeatureRefs(source, idx);
- this.propertyDialog.visible = false;
- this.propertyDialog.list = [];
- this.propertyDialog.source = "";
- this.propertyDialog.propertiesRef = null;
- this.propertyDialog.featureIndex = -1;
- this.clearMapEditSelection();
- this.syncJsonEditorData(source);
- this.$message({
- message: "删除成功",
- type: "success",
- });
- },
- 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",
- });
- },
- /** 时空格式转换等场景:上传文件仅允许 geojson / csv / xlsx / zip */
- isAllowedSceneUploadFile(file) {
- const name = (file && file.name) || (file.raw && file.raw.name) || "";
- const lower = String(name).toLowerCase();
- return (
- lower.endsWith(".geojson") ||
- lower.endsWith(".csv") ||
- lower.endsWith(".xlsx") ||
- lower.endsWith(".zip")
- );
- },
- handleFileChange(file, fileList) {
- if (!this.isAllowedSceneUploadFile(file)) {
- this.$message.error("仅支持上传 .geojson、.csv、.xlsx、.zip 格式的文件,请重新选择");
- this.currentFile = null;
- this.fileList = [];
- this.$nextTick(() => {
- if (this.$refs.sceneFileUpload && this.$refs.sceneFileUpload.clearFiles) {
- this.$refs.sceneFileUpload.clearFiles();
- }
- });
- return;
- }
- this.currentFile = file;
- this.refreshImportColumnOptions(file);
- },
- uploadRemove() {
- this.currentFile = null;
- this.params.geocodeColumnKeyword = "";
- this.selectOptions.geocodeColumnKeyword = [];
- },
- parseCsvHeaderLine(line) {
- const out = [];
- let cur = "";
- let inq = false;
- for (let i = 0; i < line.length; i++) {
- const c = line[i];
- if (c === '"') {
- inq = !inq;
- continue;
- }
- if (!inq && c === ",") {
- out.push(cur.trim());
- cur = "";
- continue;
- }
- cur += c;
- }
- out.push(cur.trim());
- return out.filter((h) => h.length > 0);
- },
- async refreshImportColumnOptions(file) {
- this.params.geocodeColumnKeyword = "";
- this.selectOptions.geocodeColumnKeyword = [];
- const raw = file && (file.raw || file);
- if (!raw || !raw.name) {
- return;
- }
- const name = String(raw.name).toLowerCase();
- if (name.endsWith(".zip")) {
- this.$message.info("zip 包请在列名输入框中填写与各 xlsx 表头一致的列名");
- return;
- }
- try {
- if (name.endsWith(".csv")) {
- const text = await raw.text();
- const firstLine = (text.split(/\r?\n/).find((l) => l.trim().length > 0) || "").trim();
- if (!firstLine) {
- return;
- }
- const headers = this.parseCsvHeaderLine(firstLine);
- this.selectOptions.geocodeColumnKeyword = headers.map((h) => ({
- value: h,
- label: h,
- }));
- this.$message.success("已解析 CSV 表头,共 " + headers.length + " 列");
- } else if (name.endsWith(".xlsx") || name.endsWith(".xls")) {
- const buf = await raw.arrayBuffer();
- const wb = XLSX.read(buf, { type: "array" });
- const sheetName = wb.SheetNames[0];
- const sheet = wb.Sheets[sheetName];
- const rows = XLSX.utils.sheet_to_json(sheet, { header: 1, defval: "" });
- const headers = (rows[0] || []).map((c) => String(c).trim()).filter((c) => c.length > 0);
- this.selectOptions.geocodeColumnKeyword = headers.map((h) => ({
- value: h,
- label: h,
- }));
- this.$message.success("已解析 Excel 首行表头,共 " + headers.length + " 列");
- }
- } catch (e) {
- console.error(e);
- this.$message.warning("解析表头失败,可手动在「列名」中输入与表格一致的列名");
- }
- },
- handleChange(emit) {
- this.params.unit = "";
- this.params.keyType = "";
- this.params.geocodeColumnKeyword = "";
- this.selectOptions.geocodeColumnKeyword = [];
- this.fileList = [];
- 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();
- },
- resetPickDragState() {
- this._pickDrag = null;
- },
- /** @param {boolean} exitEditMode 为 false 时仅清除选中与高亮,用于在「节点编辑」内切换要素 */
- clearMapEditSelection(exitEditMode = true) {
- this.removeVertexHandleEntities();
- const prevEntity = this.mapEditSelection && this.mapEditSelection.entity;
- this.mapEditSelection = null;
- this.hoveredVertexIndex = -1;
- this.hoveredHandleEntity = null;
- this.clearHoveredFeatureStyle();
- if (exitEditMode) {
- this.geometryEditMode = false;
- this.pickedFeatureRef = null;
- }
- this.hideHoverHint();
- this.restoreEntityHighlight(prevEntity);
- this.setScreenSpaceCameraForDrag(true);
- if (viewer) {
- viewer.scene.requestRender();
- }
- },
- removeVertexHandleEntities() {
- if (!viewer || !this.vertexHandleEntities.length) {
- this.vertexHandleEntities = [];
- return;
- }
- this.vertexHandleEntities.forEach((e) => {
- try {
- viewer.entities.remove(e);
- } catch (err) {
- /* ignore */
- }
- });
- this.vertexHandleEntities = [];
- },
- restoreEntityHighlight(entIn) {
- const ent = entIn || (this.mapEditSelection && this.mapEditSelection.entity);
- if (!ent || !ent.__editBackup || !viewer) {
- return;
- }
- const b = ent.__editBackup;
- if (ent.point && b.point) {
- ent.point.pixelSize = b.point.pixelSize;
- ent.point.color = b.point.color;
- ent.point.outlineColor = b.point.outlineColor;
- ent.point.outlineWidth = b.point.outlineWidth;
- }
- if (ent.polyline && b.polyline) {
- ent.polyline.width = b.polyline.width;
- ent.polyline.material = b.polyline.material;
- }
- if (ent.polygon && b.polygon) {
- if (b.polygon.material != null) {
- ent.polygon.material = b.polygon.material;
- }
- if (b.polygon.outlineWidth != null) {
- ent.polygon.outlineWidth = b.polygon.outlineWidth;
- }
- if (b.polygon.outlineColor != null) {
- ent.polygon.outlineColor = b.polygon.outlineColor;
- }
- }
- delete ent.__editBackup;
- },
- applyEntityHighlight(entity, geometryType) {
- if (!entity || !viewer) {
- return;
- }
- if (!entity.__editBackup) {
- entity.__editBackup = {};
- if (entity.point) {
- const p = entity.point;
- entity.__editBackup.point = {
- pixelSize: p.pixelSize,
- color: p.color,
- outlineColor: p.outlineColor,
- outlineWidth: p.outlineWidth,
- };
- }
- if (entity.polyline) {
- const pl = entity.polyline;
- entity.__editBackup.polyline = {
- width: pl.width,
- material: pl.material,
- };
- }
- if (entity.polygon) {
- const pg = entity.polygon;
- entity.__editBackup.polygon = {
- material: pg.material,
- outlineWidth: pg.outlineWidth,
- outlineColor: pg.outlineColor,
- };
- }
- }
- const C = SkyScenery.Color;
- if (geometryType === "Point" && entity.point) {
- entity.point.pixelSize = 16;
- entity.point.color = C.YELLOW.withAlpha(0.95);
- entity.point.outlineColor = C.DEEPSKYBLUE;
- entity.point.outlineWidth = 3;
- } else if (geometryType === "LineString" && entity.polyline) {
- entity.polyline.width = 6;
- entity.polyline.material = C.CYAN.withAlpha(0.95);
- } else if (geometryType === "Polygon" && entity.polygon) {
- entity.polygon.outlineWidth = 4;
- entity.polygon.outlineColor = C.CYAN;
- entity.polygon.material = C.LIME.withAlpha(0.45);
- }
- },
- getVertexHandleStyle(mode, handleType = "vertex") {
- const C = SkyScenery.Color;
- if (handleType === "midpoint") {
- if (mode === "pressed") {
- return {
- pixelSize: 12,
- color: C.ORANGE,
- outlineColor: C.WHITE,
- outlineWidth: 3,
- };
- }
- if (mode === "hover") {
- return {
- pixelSize: 10,
- color: C.YELLOW,
- outlineColor: C.DARKBLUE,
- outlineWidth: 2,
- };
- }
- return {
- pixelSize: 7,
- color: C.RED.withAlpha(0.9),
- outlineColor: C.WHITE,
- outlineWidth: 1,
- };
- }
- if (mode === "pressed") {
- return {
- pixelSize: 15,
- color: C.ORANGE,
- outlineColor: C.WHITE,
- outlineWidth: 3,
- };
- }
- if (mode === "hover") {
- return {
- pixelSize: 12,
- color: C.YELLOW,
- outlineColor: C.DARKBLUE,
- outlineWidth: 2,
- };
- }
- return {
- pixelSize: 9,
- color: C.WHITE,
- outlineColor: C.DEEPSKYBLUE,
- outlineWidth: 2,
- };
- },
- applyVertexHandleStyles() {
- if (!this.vertexHandleEntities.length) {
- return;
- }
- const dragging = this._pickDrag && this._pickDrag.dragging;
- const dragHandle = dragging ? this._pickDrag.dragHandleEntity || null : null;
- this.vertexHandleEntities.forEach((h) => {
- if (!h || !h.point) {
- return;
- }
- let mode = "default";
- if (dragging && dragHandle && h === dragHandle) {
- mode = "pressed";
- } else if (this.hoveredHandleEntity && h === this.hoveredHandleEntity) {
- mode = "hover";
- }
- const s = this.getVertexHandleStyle(mode, h.__handleType || "vertex");
- h.point.pixelSize = s.pixelSize;
- h.point.color = s.color;
- h.point.outlineColor = s.outlineColor;
- h.point.outlineWidth = s.outlineWidth;
- });
- },
- applyPointSelectionVisualByHoverPress() {
- const sel = this.mapEditSelection;
- if (!sel || sel.geometryType !== "Point" || !sel.entity || !sel.entity.point) {
- return;
- }
- const ent = sel.entity;
- const dragging = this._pickDrag && this._pickDrag.dragging && this._pickDrag.geometryType === "Point";
- const C = SkyScenery.Color;
- if (dragging) {
- ent.point.pixelSize = 18;
- ent.point.color = C.ORANGE.withAlpha(0.98);
- ent.point.outlineColor = C.WHITE;
- ent.point.outlineWidth = 4;
- } else if (this.hoveredVertexIndex === 0) {
- ent.point.pixelSize = 17;
- ent.point.color = C.LIME.withAlpha(0.95);
- ent.point.outlineColor = C.DEEPSKYBLUE;
- ent.point.outlineWidth = 3;
- } else {
- this.applyEntityHighlight(ent, "Point");
- }
- },
- refreshVertexHandlesFromPositions(positions) {
- if (!positions || !this.vertexHandleEntities.length) {
- return;
- }
- this.vertexHandleEntities.forEach((h) => {
- const i = h.__vertexIndex;
- if (h.__handleType === "vertex" && i >= 0 && i < positions.length) {
- h.position = SkyScenery.Cartesian3.clone(positions[i], new SkyScenery.Cartesian3());
- } else if (h.__handleType === "midpoint") {
- const a = h.__insertAfter;
- const b = a + 1;
- if (a >= 0 && b < positions.length) {
- h.position = SkyScenery.Cartesian3.midpoint(
- positions[a],
- positions[b],
- new SkyScenery.Cartesian3()
- );
- }
- }
- });
- if (viewer) {
- viewer.scene.requestRender();
- }
- },
- hideHoverHint() {
- this.hoverHint.visible = false;
- this.hoverHint.text = "";
- },
- clearHoveredFeatureStyle() {
- const ent = this.hoveredFeatureEntity;
- if (!ent || !ent.__hoverBackup) {
- this.hoveredFeatureEntity = null;
- return;
- }
- const b = ent.__hoverBackup;
- if (ent.point && b.point) {
- ent.point.pixelSize = b.point.pixelSize;
- ent.point.color = b.point.color;
- ent.point.outlineColor = b.point.outlineColor;
- }
- if (ent.polyline && b.polyline) {
- ent.polyline.width = b.polyline.width;
- ent.polyline.material = b.polyline.material;
- }
- if (ent.polygon && b.polygon) {
- ent.polygon.outlineWidth = b.polygon.outlineWidth;
- ent.polygon.outlineColor = b.polygon.outlineColor;
- }
- delete ent.__hoverBackup;
- this.hoveredFeatureEntity = null;
- },
- applyHoveredFeatureStyle(entity) {
- if (!entity || (this.mapEditSelection && this.mapEditSelection.entity === entity)) {
- this.clearHoveredFeatureStyle();
- return;
- }
- if (this.hoveredFeatureEntity === entity) {
- return;
- }
- this.clearHoveredFeatureStyle();
- entity.__hoverBackup = {};
- const C = SkyScenery.Color;
- if (entity.point) {
- entity.__hoverBackup.point = {
- pixelSize: entity.point.pixelSize,
- color: entity.point.color,
- outlineColor: entity.point.outlineColor,
- };
- entity.point.pixelSize = 13;
- entity.point.color = C.CYAN.withAlpha(0.95);
- entity.point.outlineColor = C.WHITE;
- } else if (entity.polyline) {
- entity.__hoverBackup.polyline = {
- width: entity.polyline.width,
- material: entity.polyline.material,
- };
- entity.polyline.width = 5;
- entity.polyline.material = C.CYAN.withAlpha(0.92);
- } else if (entity.polygon) {
- entity.__hoverBackup.polygon = {
- outlineWidth: entity.polygon.outlineWidth,
- outlineColor: entity.polygon.outlineColor,
- };
- entity.polygon.outlineWidth = 3;
- entity.polygon.outlineColor = C.CYAN;
- }
- this.hoveredFeatureEntity = entity;
- },
- findNearestHandleByScreen(screenPosition, maxDistPx = 18) {
- if (!this.vertexHandleEntities || !this.vertexHandleEntities.length) {
- return null;
- }
- let best = null;
- let bestD = maxDistPx;
- this.vertexHandleEntities.forEach((h) => {
- if (!h || !h.position) return;
- const p = h.position.getValue ? h.position.getValue(viewer.clock.currentTime) : h.position;
- const d = this.screenDistanceToCartesian(screenPosition, p);
- if (d < bestD) {
- bestD = d;
- best = h;
- }
- });
- return best;
- },
- showHoverHint(screenPosition, text) {
- if (!screenPosition || !text) {
- this.hideHoverHint();
- return;
- }
- this.hoverHint.visible = true;
- this.hoverHint.text = text;
- this.hoverHint.left = screenPosition.x + 14;
- this.hoverHint.top = screenPosition.y + 16;
- },
- createVertexHandlesForSelection(entity, geometryType, positionsArray) {
- this.removeVertexHandleEntities();
- if (!viewer || !entity || !positionsArray || !positionsArray.length) {
- return;
- }
- const featureRef = entity.__featureRef;
- const C = SkyScenery.Color;
- let indices = positionsArray.map((_, i) => i);
- const n = positionsArray.length;
- if (
- geometryType === "Polygon" &&
- n > 2 &&
- SkyScenery.Cartesian3.distance(positionsArray[0], positionsArray[n - 1]) < 0.5
- ) {
- indices = indices.slice(0, -1);
- }
- indices.forEach((vertexIndex) => {
- const pos = positionsArray[vertexIndex];
- const h = viewer.entities.add({
- position: SkyScenery.Cartesian3.clone(pos, new SkyScenery.Cartesian3()),
- point: {
- show: true,
- color: C.WHITE,
- pixelSize: 9,
- outlineColor: C.DEEPSKYBLUE,
- outlineWidth: 2,
- disableDepthTestDistance: Number.POSITIVE_INFINITY,
- },
- });
- h.__vertexHandle = true;
- h.__handleType = "vertex";
- h.__vertexIndex = vertexIndex;
- h.__parentEntity = entity;
- h.__featureRef = featureRef;
- this.vertexHandleEntities.push(h);
- });
- // 在相邻顶点中间增加可插入顶点的中点柄
- const segCount = geometryType === "Polygon" ? indices.length : Math.max(0, indices.length - 1);
- for (let i = 0; i < segCount; i++) {
- const aIdx = indices[i];
- const bIdx = geometryType === "Polygon" ? indices[(i + 1) % indices.length] : indices[i + 1];
- if (aIdx == null || bIdx == null) continue;
- const a = positionsArray[aIdx];
- const b = positionsArray[bIdx];
- if (!a || !b) continue;
- const mid = SkyScenery.Cartesian3.midpoint(a, b, new SkyScenery.Cartesian3());
- const m = viewer.entities.add({
- position: mid,
- point: {
- show: true,
- color: C.RED.withAlpha(0.9),
- pixelSize: 7,
- outlineColor: C.WHITE,
- outlineWidth: 1,
- disableDepthTestDistance: Number.POSITIVE_INFINITY,
- },
- });
- m.__vertexHandle = true;
- m.__handleType = "midpoint";
- m.__insertAfter = aIdx;
- m.__vertexIndex = -1;
- m.__parentEntity = entity;
- m.__featureRef = featureRef;
- this.vertexHandleEntities.push(m);
- }
- this.applyVertexHandleStyles();
- },
- applyMapEditSelection(entity) {
- if (!this.geometryEditMode) {
- return;
- }
- if (!entity || !viewer || !entity.__featureRef) {
- return;
- }
- const g =
- this.getSourceGeojsonData(entity.__featureRef.source || "input") ||
- this.renderedGeojsonCache[entity.__featureRef.source];
- const fi = entity.__featureRef.featureIndex;
- if (!g || !g.features || fi < 0 || !g.features[fi] || !g.features[fi].geometry) {
- return;
- }
- const geom = g.features[fi].geometry;
- const gt = geom.type === "MultiPolygon" ? null : geom.type;
- if (!gt || gt === "MultiPolygon") {
- return;
- }
- if (this.mapEditSelection && this.mapEditSelection.entity === entity) {
- const now = viewer.clock.currentTime;
- const gType = this.mapEditSelection.geometryType;
- this.removeVertexHandleEntities();
- if (gType === "LineString" && entity.polyline && entity.polyline.positions) {
- const arr = entity.polyline.positions.getValue(now);
- if (arr && arr.length) {
- this.createVertexHandlesForSelection(entity, "LineString", arr);
- }
- } else if (gType === "Polygon" && entity.polygon && entity.polygon.hierarchy) {
- const hProp = entity.polygon.hierarchy;
- const hier =
- hProp && typeof hProp.getValue === "function" ? hProp.getValue(now) : hProp;
- const arr = hier && hier.positions;
- if (arr && arr.length) {
- this.createVertexHandlesForSelection(entity, "Polygon", arr);
- }
- }
- this.applyVertexHandleStyles();
- this.applyPointSelectionVisualByHoverPress();
- return;
- }
- this.clearMapEditSelection(false);
- this.mapEditSelection = {
- entity,
- geometryType: gt,
- source: entity.__featureRef.source || "input",
- featureIndex: fi,
- };
- this.applyEntityHighlight(entity, gt === "LineString" ? "LineString" : gt === "Polygon" ? "Polygon" : "Point");
- const now = viewer.clock.currentTime;
- if (gt === "LineString" && entity.polyline && entity.polyline.positions) {
- const arr = entity.polyline.positions.getValue(now);
- if (arr && arr.length) {
- this.createVertexHandlesForSelection(entity, "LineString", arr);
- }
- } else if (gt === "Polygon" && entity.polygon && entity.polygon.hierarchy) {
- const hProp = entity.polygon.hierarchy;
- const hier = hProp && typeof hProp.getValue === "function" ? hProp.getValue(now) : hProp;
- const arr = hier && hier.positions;
- if (arr && arr.length) {
- this.createVertexHandlesForSelection(entity, "Polygon", arr);
- }
- }
- this.hoveredVertexIndex = -1;
- this.applyVertexHandleStyles();
- this.applyPointSelectionVisualByHoverPress();
- viewer.scene.requestRender();
- },
- updateVertexHoverFromPick(screenPosition, precomputedNearHandle = null) {
- if (!this.mapEditSelection || !this.mapEditSelection.entity) {
- this.hoveredVertexIndex = -1;
- this.hoveredHandleEntity = null;
- this.hideHoverHint();
- return;
- }
- if (this._pickDrag && this._pickDrag.dragging) {
- this.hoveredVertexIndex = this._pickDrag.vertexIndex;
- this.hoveredHandleEntity = this._pickDrag.dragHandleEntity || null;
- this.applyVertexHandleStyles();
- this.applyPointSelectionVisualByHoverPress();
- this.showHoverHint(screenPosition, "可拖拽调整节点位置");
- return;
- }
- // 与按下逻辑一致:优先按屏幕距离命中顶点/中点柄。纯 pick 常会先拾取到面/线填充而非小点实体。
- let e =
- precomputedNearHandle ||
- (this.vertexHandleEntities && this.vertexHandleEntities.length
- ? this.findNearestHandleByScreen(screenPosition, 32)
- : null);
- if (!e) {
- const pickedObject = viewer.scene.pick(screenPosition);
- e = pickedObject && pickedObject.id && pickedObject.id.__vertexHandle ? pickedObject.id : null;
- }
- if (e && e.__vertexHandle) {
- this.hoveredHandleEntity = e;
- this.hoveredVertexIndex = typeof e.__vertexIndex === "number" ? e.__vertexIndex : -1;
- if (e.__handleType === "midpoint") {
- this.showHoverHint(screenPosition, "拖动可新增节点");
- } else {
- this.showHoverHint(screenPosition, "可拖拽调整节点位置");
- }
- } else if (this.mapEditSelection.geometryType === "Point") {
- const pickedPoint = viewer.scene.pick(screenPosition);
- const pe = pickedPoint && pickedPoint.id;
- if (pe === this.mapEditSelection.entity) {
- this.hoveredVertexIndex = 0;
- this.hoveredHandleEntity = null;
- this.showHoverHint(screenPosition, "可拖拽调整节点位置");
- } else {
- this.hoveredVertexIndex = -1;
- this.hoveredHandleEntity = null;
- this.hideHoverHint();
- }
- } else {
- this.hoveredVertexIndex = -1;
- this.hoveredHandleEntity = null;
- this.hideHoverHint();
- }
- this.applyVertexHandleStyles();
- this.applyPointSelectionVisualByHoverPress();
- if (viewer) {
- viewer.scene.requestRender();
- }
- },
- setScreenSpaceCameraForDrag(enable) {
- if (!viewer || !viewer.scene || !viewer.scene.screenSpaceCameraController) {
- return;
- }
- const c = viewer.scene.screenSpaceCameraController;
- c.enableInputs = enable;
- c.enableRotate = enable;
- c.enableTranslate = enable;
- c.enableZoom = enable;
- c.enableTilt = enable;
- c.enableLook = enable;
- },
- /** 拖动顶点/线/面或折线面绘制时启用连续渲染,结束后恢复 Example 中的 requestRenderMode */
- enterInteractiveSceneRender() {
- if (!viewer || !viewer.scene) {
- return;
- }
- if (this._interactiveRenderDepth === 0) {
- this._savedSceneRenderMode = {
- requestRenderMode: viewer.scene.requestRenderMode,
- maximumRenderTimeChange: viewer.scene.maximumRenderTimeChange,
- };
- viewer.scene.requestRenderMode = false;
- viewer.scene.maximumRenderTimeChange = 0;
- }
- this._interactiveRenderDepth++;
- },
- exitInteractiveSceneRender() {
- if (!viewer || !viewer.scene || this._interactiveRenderDepth <= 0) {
- return;
- }
- this._interactiveRenderDepth--;
- if (this._interactiveRenderDepth === 0 && this._savedSceneRenderMode) {
- viewer.scene.requestRenderMode = this._savedSceneRenderMode.requestRenderMode;
- viewer.scene.maximumRenderTimeChange = this._savedSceneRenderMode.maximumRenderTimeChange;
- this._savedSceneRenderMode = null;
- }
- },
- /** globe.pick 在快速移动时易失败;补充 pickPosition / 椭球拾取,保证几何与柄同步 */
- pickSurfaceCartesian(screenPosition) {
- if (!viewer || !viewer.scene || !viewer.camera || !screenPosition) {
- return null;
- }
- const ray = viewer.camera.getPickRay(screenPosition);
- if (!ray) {
- return null;
- }
- let cart = viewer.scene.globe.pick(ray, viewer.scene);
- if (cart) {
- return cart;
- }
- if (typeof viewer.scene.pickPosition === "function") {
- cart = viewer.scene.pickPosition(screenPosition);
- if (cart) {
- return cart;
- }
- }
- const ellipsoid = viewer.scene.globe && viewer.scene.globe.ellipsoid;
- if (ellipsoid && typeof viewer.camera.pickEllipsoid === "function") {
- return viewer.camera.pickEllipsoid(screenPosition, ellipsoid);
- }
- return null;
- },
- wgs84PointToScreen(cartesian) {
- if (!viewer || !cartesian) {
- return null;
- }
- const ST = SkyScenery.SceneTransforms;
- if (!ST || typeof ST.wgs84ToWindowCoordinates !== "function") {
- return null;
- }
- const win = new SkyScenery.Cartesian2();
- if (!ST.wgs84ToWindowCoordinates(viewer.scene, cartesian, win)) {
- return null;
- }
- return win;
- },
- screenDistanceToCartesian(screenPos, cartesian) {
- const win = this.wgs84PointToScreen(cartesian);
- if (!win) {
- return Infinity;
- }
- const dx = win.x - screenPos.x;
- const dy = win.y - screenPos.y;
- return Math.sqrt(dx * dx + dy * dy);
- },
- findClosestVertexIndexScreen(positions, screenPos, maxDistPx) {
- if (!positions || !positions.length) {
- return -1;
- }
- let best = -1;
- let bestD = maxDistPx;
- for (let i = 0; i < positions.length; i++) {
- const d = this.screenDistanceToCartesian(screenPos, positions[i]);
- if (d < bestD) {
- bestD = d;
- best = i;
- }
- }
- return best;
- },
- cloneCartesian3Array(arr) {
- return arr.map((p) => SkyScenery.Cartesian3.clone(p, new SkyScenery.Cartesian3()));
- },
- openFeaturePropertyDialogForEntity(entity) {
- 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.pickedFeatureRef = {
- entity,
- source: propertySource,
- featureIndex,
- };
- this.propertyDialog.source = propertySource;
- this.propertyDialog.propertiesRef = propertiesRef || entity.__featureProperties || {};
- this.propertyDialog.featureIndex = featureIndex;
- this.propertyDialog.list = propertyList;
- this.propertyDialog.visible = true;
- } else {
- this.pickedFeatureRef = {
- entity,
- source: propertySource,
- featureIndex,
- };
- this.propertyDialog.visible = false;
- this.propertyDialog.list = [];
- this.propertyDialog.source = "";
- this.propertyDialog.propertiesRef = null;
- this.propertyDialog.featureIndex = -1;
- }
- },
- commitDraggedGeometryToGeojson(st) {
- if (!st || !st.entity || st.featureIndex < 0) {
- return;
- }
- const g = this.getSourceGeojsonData(st.source);
- if (!g || !g.features || !g.features[st.featureIndex] || !g.features[st.featureIndex].geometry) {
- return;
- }
- const geom = g.features[st.featureIndex].geometry;
- const now = viewer.clock.currentTime;
- const toLonLat = (c3, origCoord) => {
- const c = SkyScenery.Cartographic.fromCartesian(c3);
- const lon = SkyScenery.Math.toDegrees(c.longitude);
- const lat = SkyScenery.Math.toDegrees(c.latitude);
- if (origCoord && origCoord.length > 2 && typeof origCoord[2] === "number") {
- return [lon, lat, origCoord[2]];
- }
- return [lon, lat];
- };
- if (geom.type === "Point" && st.entity.position) {
- const p = st.entity.position.getValue(now);
- if (!p) {
- return;
- }
- const orig = geom.coordinates;
- geom.coordinates = toLonLat(p, orig);
- return;
- }
- if (geom.type === "LineString" && st.workingPositions) {
- geom.coordinates = st.workingPositions.map((c3, i) => toLonLat(c3, geom.coordinates[i]));
- return;
- }
- if (geom.type === "Polygon" && st.workingPositions) {
- const ring = st.workingPositions.map((c3, i) => {
- const origRing = (geom.coordinates && geom.coordinates[0]) || [];
- return toLonLat(c3, origRing[i]);
- });
- if (ring.length > 1) {
- const a = ring[0];
- const b = ring[ring.length - 1];
- if (a[0] !== b[0] || a[1] !== b[1] || (a[2] !== undefined && b[2] !== undefined && a[2] !== b[2])) {
- ring.push([...a]);
- }
- }
- if (!geom.coordinates) {
- geom.coordinates = [];
- }
- geom.coordinates[0] = ring;
- }
- },
- syncPolygonEntityHierarchy(entity, positions) {
- if (!entity || !entity.polygon) {
- return;
- }
- const n = positions.length;
- if (n > 1) {
- const a = positions[0];
- const b = positions[n - 1];
- if (SkyScenery.Cartesian3.distance(a, b) < 0.5) {
- positions[n - 1] = SkyScenery.Cartesian3.clone(a, new SkyScenery.Cartesian3());
- }
- }
- entity.polygon.hierarchy = new SkyScenery.PolygonHierarchy(this.cloneCartesian3Array(positions));
- },
- tryBeginPickDrag(event) {
- if (this.currentTool || this.isDrawingHole) {
- return;
- }
- const nearHandle = this.geometryEditMode ? this.findNearestHandleByScreen(event.position, 32) : null;
- const pickedObject = nearHandle ? { id: nearHandle } : viewer.scene.pick(event.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;
- this.resetPickDragState();
- this.clearMapEditSelection();
- return;
- }
- const entity = pickedObject.id;
- if (!this.geometryEditMode) {
- if (!entity.__featureRef || typeof entity.__featureRef.featureIndex !== "number") {
- this.resetPickDragState();
- return;
- }
- this._pickDrag = {
- entity,
- source: entity.__featureRef.source || this.currentRenderedSource || "input",
- featureIndex: entity.__featureRef.featureIndex,
- selectOnly: true,
- noDrag: true,
- startScreen: new SkyScenery.Cartesian2(event.position.x, event.position.y),
- dragging: false,
- };
- return;
- }
- if (
- (entity && entity.__vertexHandle && entity.__parentEntity) ||
- (nearHandle && nearHandle.__vertexHandle && nearHandle.__parentEntity)
- ) {
- const handleEntity = nearHandle || entity;
- const parent = handleEntity.__parentEntity;
- if (!parent.__featureRef || typeof parent.__featureRef.featureIndex !== "number") {
- this.resetPickDragState();
- return;
- }
- const featureIndex = parent.__featureRef.featureIndex;
- const source = parent.__featureRef.source || this.currentRenderedSource || "input";
- const g = this.getSourceGeojsonData(source) || this.renderedGeojsonCache[source];
- if (!g || !g.features || featureIndex < 0 || !g.features[featureIndex]) {
- this.resetPickDragState();
- return;
- }
- const geom = g.features[featureIndex].geometry;
- if (!geom || geom.type === "MultiPolygon") {
- this.resetPickDragState();
- return;
- }
- // 避免每次点柄都 remove/recreate 顶点实体,防止与当前按下目标不同步、pick/hover 失效
- if (!this.mapEditSelection || this.mapEditSelection.entity !== parent) {
- this.applyMapEditSelection(parent);
- }
- const now = viewer.clock.currentTime;
- const vi = handleEntity.__vertexIndex;
- if (geom.type === "LineString" && parent.polyline && parent.polyline.positions) {
- const positions = parent.polyline.positions.getValue(now);
- if (!positions || !positions.length) {
- this.resetPickDragState();
- return;
- }
- let working = this.cloneCartesian3Array(positions);
- let dragIndex = vi;
- if (handleEntity.__handleType === "midpoint") {
- const after = handleEntity.__insertAfter;
- const insertAt = after + 1;
- const midPos =
- handleEntity.position && handleEntity.position.getValue
- ? handleEntity.position.getValue(now)
- : null;
- if (after < 0 || insertAt > working.length || !midPos) {
- this.resetPickDragState();
- return;
- }
- working.splice(insertAt, 0, SkyScenery.Cartesian3.clone(midPos, new SkyScenery.Cartesian3()));
- parent.polyline.positions = this.cloneCartesian3Array(working);
- this.refreshVertexHandlesFromPositions(working);
- dragIndex = insertAt;
- }
- if (dragIndex < 0 || dragIndex >= working.length) {
- this.resetPickDragState();
- return;
- }
- this._pickDrag = {
- entity: parent,
- source,
- featureIndex,
- geometryType: "LineString",
- vertexIndex: dragIndex,
- dragHandleEntity: handleEntity,
- workingPositions: working,
- startScreen: new SkyScenery.Cartesian2(event.position.x, event.position.y),
- cameraLocked: true,
- dragging: false,
- };
- this.setScreenSpaceCameraForDrag(false);
- this.applyVertexHandleStyles();
- this.applyPointSelectionVisualByHoverPress();
- return;
- }
- if (geom.type === "Polygon" && parent.polygon && parent.polygon.hierarchy) {
- const hProp = parent.polygon.hierarchy;
- const hier =
- hProp && typeof hProp.getValue === "function" ? hProp.getValue(now) : hProp;
- const positions = hier && hier.positions;
- if (!positions || !positions.length) {
- this.resetPickDragState();
- return;
- }
- let working = this.cloneCartesian3Array(positions);
- let dragIndex = vi;
- if (handleEntity.__handleType === "midpoint") {
- const after = handleEntity.__insertAfter;
- const insertAt = after + 1;
- const midPos =
- handleEntity.position && handleEntity.position.getValue
- ? handleEntity.position.getValue(now)
- : null;
- // insertAt === length 表示在末尾追加(闭合边上 v_last→v0 之间插点);用 >= 会误拒开环多边形
- if (after < 0 || insertAt > working.length || !midPos) {
- this.resetPickDragState();
- return;
- }
- working.splice(insertAt, 0, SkyScenery.Cartesian3.clone(midPos, new SkyScenery.Cartesian3()));
- this.syncPolygonEntityHierarchy(parent, working);
- this.refreshVertexHandlesFromPositions(working);
- dragIndex = insertAt;
- }
- if (dragIndex < 0 || dragIndex >= working.length) {
- this.resetPickDragState();
- return;
- }
- this._pickDrag = {
- entity: parent,
- source,
- featureIndex,
- geometryType: "Polygon",
- vertexIndex: dragIndex,
- dragHandleEntity: handleEntity,
- workingPositions: working,
- startScreen: new SkyScenery.Cartesian2(event.position.x, event.position.y),
- cameraLocked: true,
- dragging: false,
- };
- this.setScreenSpaceCameraForDrag(false);
- this.applyVertexHandleStyles();
- this.applyPointSelectionVisualByHoverPress();
- return;
- }
- this.resetPickDragState();
- return;
- }
- if (!entity.__featureRef || typeof entity.__featureRef.featureIndex !== "number") {
- this.resetPickDragState();
- return;
- }
- const featureIndex = entity.__featureRef.featureIndex;
- const source = entity.__featureRef.source || this.currentRenderedSource || "input";
- const g = this.getSourceGeojsonData(source) || this.renderedGeojsonCache[source];
- if (!g || !g.features || featureIndex < 0 || !g.features[featureIndex]) {
- this.resetPickDragState();
- return;
- }
- const geom = g.features[featureIndex].geometry;
- if (!geom || geom.type === "MultiPolygon") {
- this.resetPickDragState();
- return;
- }
- const now = viewer.clock.currentTime;
- const maxPx = 28;
- if (entity.point && entity.position) {
- const pos = entity.position.getValue(now);
- if (!pos) {
- this.resetPickDragState();
- return;
- }
- if (this.screenDistanceToCartesian(event.position, pos) > maxPx) {
- this.resetPickDragState();
- return;
- }
- this.applyMapEditSelection(entity);
- this._pickDrag = {
- entity,
- source,
- featureIndex,
- geometryType: "Point",
- vertexIndex: 0,
- dragHandleEntity: null,
- startScreen: new SkyScenery.Cartesian2(event.position.x, event.position.y),
- cameraLocked: true,
- dragging: false,
- };
- this.setScreenSpaceCameraForDrag(false);
- this.applyPointSelectionVisualByHoverPress();
- return;
- }
- if (entity.polyline && entity.polyline.positions) {
- const positions = entity.polyline.positions.getValue(now);
- if (!positions || !positions.length) {
- this.resetPickDragState();
- return;
- }
- const vi = this.findClosestVertexIndexScreen(positions, event.position, maxPx);
- if (vi < 0) {
- // 点在折线段上但未贴近任一顶点:仍选中并高亮,便于查看属性
- this.applyMapEditSelection(entity);
- this._pickDrag = {
- entity,
- source,
- featureIndex,
- geometryType: "LineString",
- vertexIndex: -1,
- selectOnly: true,
- startScreen: new SkyScenery.Cartesian2(event.position.x, event.position.y),
- dragging: false,
- };
- return;
- }
- this.applyMapEditSelection(entity);
- this._pickDrag = {
- entity,
- source,
- featureIndex,
- geometryType: "LineString",
- vertexIndex: vi,
- dragHandleEntity: this.vertexHandleEntities.find(
- (h) => h.__handleType === "vertex" && h.__vertexIndex === vi
- ),
- workingPositions: this.cloneCartesian3Array(positions),
- startScreen: new SkyScenery.Cartesian2(event.position.x, event.position.y),
- cameraLocked: true,
- dragging: false,
- };
- this.setScreenSpaceCameraForDrag(false);
- this.applyVertexHandleStyles();
- return;
- }
- if (entity.polygon && entity.polygon.hierarchy) {
- const hProp = entity.polygon.hierarchy;
- const hier =
- hProp && typeof hProp.getValue === "function" ? hProp.getValue(now) : hProp;
- const positions = hier && hier.positions;
- if (!positions || !positions.length) {
- this.resetPickDragState();
- return;
- }
- const vi = this.findClosestVertexIndexScreen(positions, event.position, maxPx);
- if (vi < 0) {
- // 点在面填充/边线上但未贴近顶点:仍选中高亮并显示全部顶点柄
- this.applyMapEditSelection(entity);
- this._pickDrag = {
- entity,
- source,
- featureIndex,
- geometryType: "Polygon",
- vertexIndex: -1,
- selectOnly: true,
- startScreen: new SkyScenery.Cartesian2(event.position.x, event.position.y),
- dragging: false,
- };
- return;
- }
- this.applyMapEditSelection(entity);
- this._pickDrag = {
- entity,
- source,
- featureIndex,
- geometryType: "Polygon",
- vertexIndex: vi,
- dragHandleEntity: this.vertexHandleEntities.find(
- (h) => h.__handleType === "vertex" && h.__vertexIndex === vi
- ),
- workingPositions: this.cloneCartesian3Array(positions),
- startScreen: new SkyScenery.Cartesian2(event.position.x, event.position.y),
- cameraLocked: true,
- dragging: false,
- };
- this.setScreenSpaceCameraForDrag(false);
- this.applyVertexHandleStyles();
- return;
- }
- this.resetPickDragState();
- },
- onPickDragMove(event) {
- if (!this.geometryEditMode) {
- const po = viewer.scene.pick(event.endPosition);
- const ent = po && po.id && po.id.__featureRef ? po.id : null;
- if (ent) {
- this.applyHoveredFeatureStyle(ent);
- } else {
- this.clearHoveredFeatureStyle();
- }
- this.hideHoverHint();
- return;
- }
- this.clearHoveredFeatureStyle();
- const st = this._pickDrag;
- const hoverNearHandle =
- this.vertexHandleEntities && this.vertexHandleEntities.length
- ? this.findNearestHandleByScreen(event.endPosition, 32)
- : null;
- if (st && st.entity && !st.selectOnly) {
- const dx = event.endPosition.x - st.startScreen.x;
- const dy = event.endPosition.y - st.startScreen.y;
- const movedPx = Math.sqrt(dx * dx + dy * dy);
- if (!st.dragging && movedPx > 1) {
- st.dragging = true;
- this.setScreenSpaceCameraForDrag(false);
- this.enterInteractiveSceneRender();
- }
- if (st.dragging) {
- const cart = this.pickSurfaceCartesian(event.endPosition);
- if (cart) {
- if (st.geometryType === "Point" && st.entity.position) {
- st.entity.position = cart;
- } else if (st.geometryType === "LineString" && st.workingPositions) {
- const prev = st.workingPositions[st.vertexIndex];
- const cPrev = SkyScenery.Cartographic.fromCartesian(prev);
- const cNew = SkyScenery.Cartographic.fromCartesian(cart);
- cNew.height = cPrev.height;
- st.workingPositions[st.vertexIndex] = SkyScenery.Cartesian3.fromRadians(
- cNew.longitude,
- cNew.latitude,
- cNew.height
- );
- st.entity.polyline.positions = st.workingPositions;
- this.refreshVertexHandlesFromPositions(st.workingPositions);
- } else if (st.geometryType === "Polygon" && st.workingPositions) {
- const prev = st.workingPositions[st.vertexIndex];
- const cPrev = SkyScenery.Cartographic.fromCartesian(prev);
- const cNew = SkyScenery.Cartographic.fromCartesian(cart);
- cNew.height = cPrev.height;
- st.workingPositions[st.vertexIndex] = SkyScenery.Cartesian3.fromRadians(
- cNew.longitude,
- cNew.latitude,
- cNew.height
- );
- const n = st.workingPositions.length;
- // 仅当 positions 已带「首尾重合」闭合点时才同步;开环多边形首尾是不同角点,不能互拷否则会吞掉顶点
- const ringClosedDuplicate =
- n > 2 &&
- SkyScenery.Cartesian3.distance(st.workingPositions[0], st.workingPositions[n - 1]) <
- 0.5;
- if (ringClosedDuplicate) {
- if (st.vertexIndex === 0) {
- st.workingPositions[n - 1] = SkyScenery.Cartesian3.clone(
- st.workingPositions[0],
- st.workingPositions[n - 1]
- );
- } else if (st.vertexIndex === n - 1) {
- st.workingPositions[0] = SkyScenery.Cartesian3.clone(
- st.workingPositions[n - 1],
- st.workingPositions[0]
- );
- }
- }
- this.syncPolygonEntityHierarchy(st.entity, st.workingPositions);
- this.refreshVertexHandlesFromPositions(st.workingPositions);
- }
- viewer.scene.requestRender();
- }
- }
- }
- // 相机默认先于自定义 handler 响应拖拽;在靠近顶点/中点或正在拖动节点时关闭相机输入,避免“拖地图”抢事件
- const vertexInteraction =
- !!(st && st.dragging) ||
- !!(st && st.cameraLocked && st.entity && !st.selectOnly) ||
- !!hoverNearHandle;
- this.setScreenSpaceCameraForDrag(!vertexInteraction);
- if (!(st && st.dragging)) {
- this.updateVertexHoverFromPick(event.endPosition, hoverNearHandle);
- } else {
- this.applyVertexHandleStyles();
- this.applyPointSelectionVisualByHoverPress();
- }
- },
- onPickDragUp(event) {
- const st = this._pickDrag;
- if (!st || !st.entity) {
- this.resetPickDragState();
- return;
- }
- const wasDraggingGeometry = st.dragging;
- const dx = event.position.x - st.startScreen.x;
- const dy = event.position.y - st.startScreen.y;
- const movedPx = Math.sqrt(dx * dx + dy * dy);
- if (st.dragging) {
- this.commitDraggedGeometryToGeojson(st);
- this.syncJsonEditorData(st.source);
- this.applyMapEditSelection(st.entity);
- } else if (movedPx <= 5) {
- this.openFeaturePropertyDialogForEntity(st.entity);
- }
- if (st.cameraLocked) {
- this.setScreenSpaceCameraForDrag(true);
- }
- if (wasDraggingGeometry) {
- this.exitInteractiveSceneRender();
- }
- this.resetPickDragState();
- },
- // 初始化地图要素拾取:单击打开属性;按住拖动顶点/点实时改坐标并同步 JSON
- initFeaturePickHandler() {
- if (this.featurePickHandler || !viewer || !viewer.canvas) {
- return;
- }
- const h = new SkyScenery.ScreenSpaceEventHandler(viewer.canvas);
- const SS = SkyScenery.ScreenSpaceEventType;
- h.setInputAction((e) => this.tryBeginPickDrag(e), SS.LEFT_DOWN);
- h.setInputAction((e) => this.onPickDragMove(e), SS.MOUSE_MOVE);
- h.setInputAction((e) => this.onPickDragUp(e), SS.LEFT_UP);
- this.featurePickHandler = h;
- },
- // 激活绘制工具
- activateDraw(type) {
- // 进入绘制工具时退出“更改绘制”模式,避免交互冲突
- if (this.geometryEditMode) {
- this.clearMapEditSelection();
- }
- // 取消镂空绘制模式
- this.isDrawingHole = false;
- // 如果已经是当前激活的工具,则取消激活
- if (this.currentTool === type) {
- this.deactivateDraw();
- return;
- }
- // 先取消之前的绘制模式
- this.deactivateDraw();
- // 设置当前工具
- this.currentTool = type;
- this.drawingMode = type;
- // 从当前渲染/编辑器数据同步几何,避免新绘制覆盖已有要素
- this.syncGeometriesFromRenderedSource();
- // 重置当前绘制状态
- this.resetDrawingState();
- switch (type) {
- case "point":
- this.drawPoint();
- break;
- case "polyline":
- this.drawPolyline();
- break;
- case "polygon":
- this.drawPolygon();
- break;
- }
- if (type === "polyline" || type === "polygon") {
- this.enterInteractiveSceneRender();
- }
- },
- // 重置绘制状态
- resetDrawingState() {
- this.currentPositions = [];
- this.currentEntity = null;
- // 移除临时预览实体
- if (this.tempEntity) {
- viewer.entities.remove(this.tempEntity);
- viewer.scene.requestRender();
- this.tempEntity = null;
- }
- },
- // 取消绘制模式
- deactivateDraw() {
- this.exitInteractiveSceneRender();
- 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 position = that.pickSurfaceCartesian(event.position);
- 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();
- const source = "input";
- const featureIndex = that.geometries.length - 1;
- entity.__featureRef = { source, featureIndex };
- entity.__featureProperties = {};
- console.log("绘制了点:", geometry);
- }
- }, SkyScenery.ScreenSpaceEventType.LEFT_CLICK);
- },
- // 绘制线
- drawPolyline() {
- const that = this;
- // 鼠标移动时更新临时线(实时渲染预览)
- this.handler.setInputAction(function (event) {
- if (that.currentPositions.length > 0) {
- const endPosition = that.pickSurfaceCartesian(event.endPosition);
- if (endPosition) {
- const last = that.currentPositions[that.currentPositions.length - 1];
- const pendingSeg = [last, endPosition];
- // 待落点使用虚线预览
- if (!that.tempEntity) {
- that.tempEntity = viewer.entities.add({
- polyline: {
- show: true,
- positions: pendingSeg,
- material: new SkyScenery.PolylineDashMaterialProperty({
- color: SkyScenery.Color.RED.withAlpha(0.9),
- dashLength: 10,
- }),
- width: 3,
- clampToGround: true,
- },
- });
- viewer.scene.requestRender();
- } else {
- that.tempEntity.polyline.positions = pendingSeg;
- viewer.scene.requestRender();
- }
- }
- }
- }, SkyScenery.ScreenSpaceEventType.MOUSE_MOVE);
- // 左键点击添加点
- this.handler.setInputAction(function (event) {
- const position = that.pickSurfaceCartesian(event.position);
- if (position) {
- that.currentPositions.push(position.clone());
- // 已落点使用实线
- if (that.currentPositions.length > 1) {
- if (!that.currentEntity) {
- that.currentEntity = viewer.entities.add({
- polyline: {
- show: true,
- positions: [...that.currentPositions],
- material: SkyScenery.Color.RED,
- width: 3,
- clampToGround: true,
- },
- });
- } else {
- that.currentEntity.polyline.positions = [...that.currentPositions];
- }
- 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,
- clampToGround: true,
- },
- });
- }
- 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();
- const source = "input";
- const featureIndex = that.geometries.length - 1;
- that.currentEntity.__featureRef = { source, featureIndex };
- that.currentEntity.__featureProperties = {};
- console.log("绘制了线:", geometry);
- // 取消当前绘制模式
- that.deactivateDraw();
- }
- }, SkyScenery.ScreenSpaceEventType.RIGHT_CLICK);
- },
- // 绘制面
- drawPolygon() {
- const that = this;
- // 鼠标移动时更新临时面(实时渲染预览)
- this.handler.setInputAction(function (event) {
- if (that.currentPositions.length > 0) {
- const endPosition = that.pickSurfaceCartesian(event.endPosition);
- if (endPosition) {
- const last = that.currentPositions[that.currentPositions.length - 1];
- const first = that.currentPositions[0];
- const pending = [last, endPosition, first];
- // 待落点边使用虚线
- if (!that.tempEntity) {
- that.tempEntity = viewer.entities.add({
- polyline: {
- show: true,
- positions: pending,
- material: new SkyScenery.PolylineDashMaterialProperty({
- color: SkyScenery.Color.RED.withAlpha(0.95),
- dashLength: 10,
- }),
- width: 3,
- clampToGround: true,
- },
- });
- viewer.scene.requestRender();
- } else {
- that.tempEntity.polyline.positions = pending;
- viewer.scene.requestRender();
- }
- }
- }
- }, SkyScenery.ScreenSpaceEventType.MOUSE_MOVE);
- // 左键点击添加点
- this.handler.setInputAction(function (event) {
- const position = that.pickSurfaceCartesian(event.position);
- 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 linePositions = [...that.currentPositions];
- if (!that.currentEntity || !that.currentEntity.polyline) {
- if (that.currentEntity) {
- viewer.entities.remove(that.currentEntity);
- }
- that.currentEntity = viewer.entities.add({
- polyline: {
- show: true,
- positions: linePositions,
- material: SkyScenery.Color.RED,
- width: 3,
- clampToGround: true,
- },
- });
- } else {
- that.currentEntity.polyline.positions = linePositions;
- }
- viewer.scene.requestRender();
- } else if (that.currentPositions.length > 2) {
- // 闭合多边形
- const closedPositions = [...that.currentPositions, that.currentPositions[0]];
- if (!that.currentEntity) {
- that.currentEntity = viewer.entities.add({
- polygon: {
- show: true,
- hierarchy: new SkyScenery.PolygonHierarchy(closedPositions),
- material: SkyScenery.Color.RED.withAlpha(0.35),
- outline: true,
- outlineColor: SkyScenery.Color.RED,
- outlineWidth: 2,
- },
- });
- } else if (that.currentEntity.polygon) {
- that.currentEntity.polygon.hierarchy = new SkyScenery.PolygonHierarchy(closedPositions);
- } else {
- // 第二点阶段是线实体,第三点开始改为面实体
- viewer.entities.remove(that.currentEntity);
- that.currentEntity = viewer.entities.add({
- polygon: {
- show: true,
- hierarchy: new SkyScenery.PolygonHierarchy(closedPositions),
- material: SkyScenery.Color.RED.withAlpha(0.35),
- outline: true,
- outlineColor: SkyScenery.Color.RED,
- 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();
- const source = "input";
- const featureIndex = this.geometries.length - 1;
- this.currentEntity.__featureRef = { source, featureIndex };
- this.currentEntity.__featureProperties = {};
- 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.enterInteractiveSceneRender();
- // 清除之前的事件
- this.handler.removeInputAction(SkyScenery.ScreenSpaceEventType.LEFT_CLICK);
- // 鼠标移动时更新临时镂空区域
- this.handler.setInputAction(function (event) {
- if (that.currentPositions.length > 0) {
- const endPosition = that.pickSurfaceCartesian(event.endPosition);
- 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 position = that.pickSurfaceCartesian(event.position);
- 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;
- const baseFeatures =
- this.jsonData && Array.isArray(this.jsonData.features) ? this.jsonData.features : [];
- let FeatureCollectionFeatures = [];
- geometriesToSend.forEach((item, idx) => {
- const base = baseFeatures[idx] || {};
- FeatureCollectionFeatures.push({
- type: "Feature",
- properties: base.properties ? JSON.parse(JSON.stringify(base.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
- if (!this.dmsServerItem || !this.dmsServerItem.apiUrl) {
- this.$message({
- message: "请先选择有效场景并加载接口信息",
- type: "warning",
- });
- return;
- }
- let requestData;
- this.backData = {};
- let requestUrl = this.dmsServerItem.apiUrl;
- if (this.ifHasType("point") || this.ifHasType("polyline") || this.ifHasType("polygon")) {
- const check = this.validateSendGeometryParams();
- if (!check.ok) {
- this.$message({
- message: check.message,
- type: "warning",
- });
- return;
- }
- 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]);
- }
- });
- requestData.append("token", localStorage.getItem("token"));
- }
- 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",
- });
- const downPath = that.pickDownFilePathFromResponse(res);
- if (downPath) {
- that.$nextTick(() => {
- that.downloadConvertedFile(downPath);
- });
- }
- } 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);
- });
- },
- /**
- * 从接口返回 JSON 中解析 downFilePath(支持 content 为对象或 JSON 字符串、或顶层字段)。
- */
- pickDownFilePathFromResponse(res) {
- if (!res || typeof res !== "object") {
- return null;
- }
- const tryVal = (v) => {
- if (typeof v !== "string") {
- return null;
- }
- const s = v.trim();
- if (!s || s.indexOf("未知文件") !== -1) {
- return null;
- }
- return s;
- };
- let p = tryVal(res.downFilePath);
- if (p) {
- return p;
- }
- const c = res.content;
- if (c && typeof c === "object") {
- p = tryVal(c.downFilePath);
- if (p) {
- return p;
- }
- }
- if (typeof c === "string") {
- try {
- const parsed = JSON.parse(c);
- if (parsed && typeof parsed === "object") {
- p = tryVal(parsed.downFilePath);
- if (p) {
- return p;
- }
- }
- } catch (e) {
- /* ignore */
- }
- }
- return null;
- },
- /** 根据后端返回的 downFilePath,请求 downloadFile 并触发浏览器下载(与「下载文件」场景一致) */
- downloadConvertedFile(serverFilePath) {
- const base =
- typeof systemConfig !== "undefined" && systemConfig.baseServicerPath
- ? systemConfig.baseServicerPath
- : "";
- const url = base + "/spatiotemporal_data_format_conversion/downloadFile";
- getFile(url, { filePath: serverFilePath })
- .then((blob) => {
- if (!(blob instanceof Blob) || blob.size === 0) {
- this.$message.warning(
- "转换文件下载失败(空流),请从返回 JSON 的 downFilePath 手动获取"
- );
- return;
- }
- const name = this.fileNameFromPath(serverFilePath);
- const href = window.URL.createObjectURL(blob);
- const link = document.createElement("a");
- link.href = href;
- link.download = name;
- link.style.display = "none";
- document.body.appendChild(link);
- link.click();
- document.body.removeChild(link);
- window.URL.revokeObjectURL(href);
- this.$message.success("已开始下载转换文件:" + name);
- })
- .catch((e) => {
- console.error(e);
- this.$message.error("转换文件下载失败");
- });
- },
- fileNameFromPath(fullPath) {
- if (!fullPath) {
- return "download";
- }
- const s = String(fullPath).replace(/\\/g, "/");
- const i = s.lastIndexOf("/");
- return i >= 0 ? s.slice(i + 1) : s;
- },
- // 清除所有绘制的元素,并更新geometries
- clearAll() {
- this.clearAllMap();
- this.changeGeometries();
- },
- // 清除所有绘制的元素
- clearAllMap() {
- this.removeVertexHandleEntities();
- this.mapEditSelection = null;
- this.hoveredVertexIndex = -1;
- this.hoveredHandleEntity = null;
- this.clearHoveredFeatureStyle();
- this.geometryEditMode = false;
- this.pickedFeatureRef = null;
- this.hideHoverHint();
- // 移除所有实体
- 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.resetPickDragState();
- this.setScreenSpaceCameraForDrag(true);
- // 取消当前绘制模式
- 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-footer {
- width: 100%;
- display: flex;
- align-items: center;
- justify-content: space-between;
- }
- .feature-property-actions {
- display: flex;
- align-items: center;
- gap: 12px;
- }
- .feature-property-key-input {
- width: 180px;
- }
- .map-edit-hover-hint {
- position: fixed;
- z-index: 3000;
- pointer-events: none;
- color: #4a4a4a;
- background: #fff;
- border-radius: 4px;
- padding: 7px 10px;
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.18);
- font-size: 12px;
- line-height: 1.2;
- transform-origin: left top;
- animation: mapHintPop 120ms ease-out;
- }
- @keyframes mapHintPop {
- from {
- transform: scale(0.92);
- opacity: 0.5;
- }
- to {
- transform: scale(1);
- opacity: 1;
- }
- }
- :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>
|