Compare commits

...

3 Commits

Author SHA1 Message Date
徐景良 b631f44dc9 Merge branch 'main' of http://123.132.248.154:10000/HC_YFZX/CaiYuanYiTiHua 2024-09-09 09:11:40 +08:00
徐景良 f816fe9266 merge 2024-09-09 09:11:36 +08:00
徐景良 cc6034a042 merge 2024-08-31 17:41:03 +08:00
7 changed files with 793 additions and 179 deletions

View File

@ -146,7 +146,9 @@
"vxe-table-plugin-export-xlsx": "^3.1.0",
"xe-utils": "^3.5.14",
"xlsx": "^0.18.5",
"xml-js": "^1.6.11"
"xml-js": "^1.6.11",
"jszip":"^3.10.1",
"shpjs":"^6.1.0"
},
"devDependencies": {
"@commitlint/cli": "^18.4.4",

View File

@ -10,6 +10,7 @@
<div>云查询</div>
</div>
</div>
<!-- 图层控制 -->
<div class="layer-control-center" v-if="false">
<a-collapse v-model:activeKey="activeKey" accordion expandIconPosition="end" expandIcon="">
@ -24,7 +25,7 @@
item.name
}}</a-checkbox>
</p>
</a-collapse-panel>
</a-collapse-panel>
</a-collapse>
</div>
@ -46,9 +47,28 @@
<div class="draw-polygon" @click="LocationShow = true"></div>
</a-tooltip>
<a-popover placement="bottom" v-if="props.splitPlugin" v-model:visible="splitPanelVisible">
<template #content>
<div class="split-panel">
<p class="split-panel-item" @click="handlerDrawLineString()">线</p>
<p class="split-panel-item" @click="handlerDrawPolygon()"></p>
<a-upload
name="file"
:before-upload="handleImportShapeFileChange"
:showUploadList="false"
>
<p class="split-panel-item">导入图层分割</p>
</a-upload>
</div>
</template>
<div class="split-line" ></div>
</a-popover>
<a-tooltip>
<template #title>线分割图斑</template>
<div class="split-line" @click="handlerDrawLineString()" v-if="props.splitPlugin"></div>
</a-tooltip>
<a-tooltip>
@ -84,6 +104,7 @@
<a-button type="default" size="small" @click="handlerClearLocationItem"
><ClearOutlined />清空</a-button
>
&nbsp;
<a-button
type="default"
@ -91,18 +112,57 @@
v-if="props.splitPlugin"
@click="onHandlerSplitPolygon"
><SplitCellsOutlined />分割图斑</a-button>
<span style="float: right">
<CloseOutlined @click="handlerLocationClose" />
</span>
</div>
<a-empty v-if="locationArrays.length == 0" />
<!-- <a-empty v-if="locationArrays.length == 0" /> -->
<div class="location-item-list-coantienr">
<div class="location-item" v-for="(item, index) in locationArrays" :key="index">
<a-table :dataSource="locationArrays" size="small" :pagination="false" >
<a-table-column-group>
<a-table-column key="lng" title="经度(Y)" data-index="lng">
<template #default="{ record }">
<a-input
v-model:value="record.lng"
@chagne="handlerLocationChange"
size="small"
/>
</template>
</a-table-column>
<a-table-column key="lat" title="纬度(X)" data-index="lat">
<template #default="{record}">
<a-input
v-model:value="record.lat"
@chagne="handlerLocationChange"
size="small"
/>
</template>
</a-table-column>
<a-table-column key="operation" title="操作" data-index="operation" width="100px">
<template #default="{record,index}">
<a-button type="default" size="small" @click="handlerLocationFlyTo(index)"
><EnvironmentOutlined
/></a-button>
&nbsp;
<a-button type="default" size="small" @click="handlerLocationRemove(index)"
><DeleteOutlined
/></a-button>
</template>
</a-table-column>
</a-table-column-group>
</a-table>
<!-- <div class="location-item" v-for="(item, index) in locationArrays" :key="index">
<a-button type="default" size="small" @click="handlerLocationFlyTo(item)"
><EnvironmentOutlined
/></a-button>
@ -123,7 +183,7 @@
<a-button type="default" size="small" @click="handlerLocationRemove(index)"
><DeleteOutlined
/></a-button>
</div>
</div> -->
</div>
</div>
</div>
@ -177,6 +237,9 @@
import { polygonCut } from './lib/segmentation';
import { splitPolygonByLine, splitPolygonByFill } from './lib/splitpolygon';
// 线
import {chunkUtil} from './lib/chunkutil.ts';
//
import { userFormFileStore } from '@/store/modules/formFileUrl';
import { storeToRefs } from 'pinia';
@ -200,34 +263,101 @@
import { useCloudQueryStore } from '@/store/modules/cloudquery';
import { AddDroneTask } from '@/api/demo/cloudQuery.ts';
//
import proj4 from 'proj4'
// shp
import JSZip from 'jszip'
import shp from 'shpjs'
// zipzip
const parseZip = async(zip)=>{
// zip
const jsZip = new JSZip()
const zipData = await jsZip.loadAsync(zip)
// zip
const data = await zipData.generateAsync({ type: 'arraybuffer' })
return await shp(data)
}
//
const handleImportShapeFileChange = (e)=>{
parseZip(e).then(res=>{
let geojson = JSON.parse(JSON.stringify(res))
// shapgefile
handlerDetails(
res,
'shapefileSource',
'shapefileLayer',
{
lineStyle: { 'line-color': '#ff0000', 'line-width': 3 },
fillStyle: { 'fill-color': '#ff0000', 'fill-opacity': 0.1 },
}
)
Modal.confirm({
title: '是否确认分割图斑?',
onCancel() {
handlerDetails(
{type:"FeatureCollection",features:[]},
'shapefileSource',
'shapefileLayer',
{
lineStyle: { 'line-color': '#ff0000', 'line-width': 3 },
fillStyle: { 'fill-color': '#ff0000', 'fill-opacity': 0.1 },
}
)
},
async onOk() {
splitFeatureByFill(geojson.features[0].geometry.coordinates[0]);
handlerDetails(
{type:"FeatureCollection",features:[]},
'shapefileSource',
'shapefileLayer',
{
lineStyle: { 'line-color': '#ff0000', 'line-width': 3 },
fillStyle: { 'fill-color': '#ff0000', 'fill-opacity': 0.1 },
}
)
},
});
});
}
// shapefile
//
function GkToCGCS2000(lngLat){
try{
// CGCS2000 / 3-degree Gauss-Kruger zone 39
let from_system = "+proj=tmerc +lat_0=0 +lon_0=117 +k=1 +x_0=39500000 +y_0=0 +ellps=GRS80 +units=m +no_defs +type=crs"
try{ //
let from_system = "";
if(lngLat[0]>=37000000 && lngLat[0] < 38000000){
from_system = "+proj=tmerc +lat_0=0 +lon_0=111 +k=1 +x_0=37500000 +y_0=0 +ellps=GRS80 +units=m +no_defs +type=crs"
}if(lngLat[0]>=38000000 && lngLat[0] < 39000000){
from_system = "+proj=tmerc +lat_0=0 +lon_0=114 +k=1 +x_0=38500000 +y_0=0 +ellps=GRS80 +units=m +no_defs +type=crs"
}if(lngLat[0]>=39000000 && lngLat[0] < 40000000){ // CGCS2000 / 3-degree Gauss-Kruger zone 39
from_system = "+proj=tmerc +lat_0=0 +lon_0=117 +k=1 +x_0=39500000 +y_0=0 +ellps=GRS80 +units=m +no_defs +type=crs"
}else if(lngLat[0] >= 40000000){ // CGCS2000 / 3-degree Gauss-Kruger zone 40
from_system = "+proj=tmerc +lat_0=0 +lon_0=120 +k=1 +x_0=40500000 +y_0=0 +ellps=GRS80 +units=m +no_defs +type=crs"
}
// China Geodetic Coordinate System 2000
// let to_system = "+proj=longlat +ellps=GRS80 +no_defs +type=crs";
// WGS 84 -- WGS84 - World Geodetic System 1984,
let to_system = "+proj=longlat +datum=WGS84 +no_defs +type=crs";
// transform
let trasnformLnglat = proj4(from_system,to_system,lngLat);
return trasnformLnglat;
}catch(e){
return null;
}
}
const useCloudQuery = useCloudQueryStore();
const { VITE_GLOB_API_URL } = getAppEnvConfig();
@ -656,43 +786,63 @@
let splitPolygon = currentGeoJson.value;
try {
// let features = polygonCut(splitPolygon,splitLineString,0.1,"meters");
let features = splitPolygonByLine(splitLineString, splitPolygon);
splitPolygon?.geometry.coordinates[0].forEach((item,index)=>{
splitPolygon.geometry.coordinates[0][index] = [item[0],item[1]];
})
let features = splitPolygonByLine(splitLineString, splitPolygon);
let tempFeatures = JSON.parse(JSON.stringify(features))
let splitAfterFeatures = {
type: 'FeatureCollection',
features: [],
};
splitAfterFeatures.features = features;
emit('handlerSplitPolygon', features);
handlerDetails(splitAfterFeatures);
splitAfterFeatures.features = tempFeatures;
console.log("tempFeatures",tempFeatures)
//
emit('handlerSplitPolygon', tempFeatures);
//
// handlerDetails(splitAfterFeatures);
handlerUnDraw();
} catch (e) {
createMessage.warning('分割线起点、终点需要在图斑外');
console.log("error error",e);
createMessage.warning('分割线起点、终点需要在图斑外,多个图斑时需要点击选择需要分割的图斑!');
handlerUnDraw();
}
};
//
const splitFeatureByFill = (fill) => {
let splitLineString = {
let drawPolygon = {
type: 'Feature',
properties: {},
properties: {
id:null,
},
geometry: {
coordinates: [fill],
type: 'Polygon',
},
};
let splitPolygon = currentGeoJson.value;
// let features = polygonCut(splitPolygon,splitLineString,0.1,"meters");
let features = splitPolygonByFill(splitLineString, splitPolygon);
let features = splitPolygonByFill(drawPolygon, splitPolygon);
let tempFeatures = JSON.parse(JSON.stringify(features))
let splitAfterFeatures = {
type: 'FeatureCollection',
features: [],
};
splitAfterFeatures.features = [features];
handlerDetails(splitAfterFeatures);
splitAfterFeatures.features = tempFeatures;
emit('handlerSplitPolygon', tempFeatures);
handlerUnDraw();
};
//
@ -702,8 +852,10 @@
emit('mapDraw', 'Point', e);
});
};
const splitPanelVisible = ref<Boolean>(false);
//线
const handlerDrawLineString = () => {
splitPanelVisible.value = false;
mp.draw('LineString');
mp.on('LineString', function (e) {
Modal.confirm({
@ -725,13 +877,24 @@
//
const handlerDrawPolygon = () => {
splitPanelVisible.value = false;
mp.draw('Polygon');
mp.on('Polygon', function (e) {
let coordinates = [];
e?.forEach((item, index) => {
coordinates?.push([item.lng, item.lat]);
Modal.confirm({
title: '是否确认分割图斑?',
onCancel() {
handlerUnDraw();
},
async onOk() {
let coordinates = [];
e?.forEach((item, index) => {
coordinates?.push([item.lng, item.lat]);
});
coordinates.push(coordinates[0]);
splitFeatureByFill(coordinates);
handlerUnDraw();
},
});
splitFeatureByFill(coordinates);
emit('mapDraw', 'Polygon', e);
});
};
@ -1068,7 +1231,6 @@
};
const currentGeoJson = ref({});
const handlerDetails = (
geojson,
source = 'detailsSource',
@ -1078,26 +1240,30 @@
fillStyle: { 'fill-color': '#fcf003', 'fill-opacity': 0.1 },
},
) => {
let fillLayerName = layer + 'Fill';
let lineLayerName = layer + 'Line';
if (source == 'detailsSource') {
console.log('currentGeoJson', geojson);
if (source == 'detailsSource' || source == "splitPolygonSource") {
//
let coordinates = null;
if (geojson.features[0].geometry.type == 'MultiPolygon') {
if (geojson.features.length>0 && geojson.features[0].geometry.type == 'MultiPolygon') { //
//
coordinates = geojson.features[0].geometry.coordinates[0];
} else if (geojson.features[0].geometry.type == 'Polygon') {
} else if (geojson.features.length>0 && geojson.features[0].geometry.type == 'Polygon') { //
coordinates = geojson.features[0].geometry.coordinates;
}
// 2Mapbox
if (coordinates) {
if(
coordinates[0][coordinates[0].length - 1][0] ==
coordinates[0][coordinates[0].length - 2][0]
) {
if((coordinates[0][coordinates[0].length - 1][0] == coordinates[0][coordinates[0].length - 2][0]) && (coordinates[0][coordinates[0].length - 1][1] == coordinates[0][coordinates[0].length - 2][1])) {
coordinates[0]?.pop();
}
}
let singleFeature = {
type: 'Feature',
properties: {},
@ -1106,10 +1272,13 @@
type: 'Polygon',
},
};
currentGeoJson.value = singleFeature;
console.log('currentGeoJson', currentGeoJson.value);
}
// geojson使
currentGeoJson.value = singleFeature;
}
if (map.getSource(source)) {
map.getSource(source).setData(geojson);
} else {
@ -1132,10 +1301,14 @@
paint: style.lineStyle,
});
map.on('click', fillLayerName, function (e) {
if (e.features.length > 0) {
var feature = e.features[0];
emit('onFeatureClick', feature);
}
if(fillLayerName == 'detailsLayerFill' || fillLayerName == 'detailsLayerLine'){
}
});
}
};
@ -1149,46 +1322,31 @@
}
const LocationShow = ref<Boolean>(false);
const locationArrays = ref<LocationItem[]>([
// {"lat":3876411.9272,"lng":39624337.6595},
// {"lat":3876419.7281,"lng":39624444.1571},
// {"lat":3876419.8852,"lng":39624446.3011},
// {"lat":3876425.1275,"lng":39624517.8687},
// {"lat":3876425.2918,"lng":39624520.1122},
// {"lat":3876431.6438,"lng":39624606.8289},
// {"lat":3876412.7832,"lng":39624632.9065},
// {"lat":3876365.7135,"lng":39624644.6931},
// {"lat":3876325.9298,"lng":39624654.7128},
// {"lat":3876316.1075,"lng":39624657.1746},
// {"lat":3876246.0573,"lng":39624674.5737},
// {"lat":3876243.9623,"lng":39624675.0987},
// {"lat":3876243.8703,"lng":39624674.5037},
// {"lat":3876240.4283,"lng":39624652.4797},
// {"lat":3876233.6843,"lng":39624607.2316},
// {"lat":3876232.2352,"lng":39624597.5126},
// {"lat":3876222.6982,"lng":39624540.8865},
// {"lat":3876221.3592,"lng":39624531.5094},
// {"lat":3876215.2311,"lng":39624497.0364},
// {"lat":3876206.5951,"lng":39624498.5794},
// {"lat":3876189.579,"lng":39624390.1352},
// {"lat":3876189.002,"lng":39624386.4602},
// {"lat":3876186.4209,"lng":39624370.0092},
// {"lat":3876195.956,"lng":39624367.6201},
// {"lat":3876198.685,"lng":39624385.0072},
// {"lat":3876199.261,"lng":39624388.6822},
// {"lat":3876207.5961,"lng":39624441.8023},
// {"lat":3876209.7341,"lng":39624441.5433},
// {"lat":3876253.1836,"lng":39624436.2662},
// {"lat":3876258.4453,"lng":39624435.6273},
// {"lat":3876290.5054,"lng":39624431.7412},
// {"lat":3876299.4224,"lng":39624430.675},
// {"lat":3876303.8533,"lng":39624430.1453},
// {"lat":3876297.7186,"lng":39624342.1213},
// {"lat":3876299.3188,"lng":39624341.7203},
// {"lat":3876305.1635,"lng":39624340.2558},
// {"lat":3876387.1195,"lng":39624319.7203},
// {"lat":3876411.9272,"lng":39624337.6595}
]);
const locationColumns = ref([
{
title: '定位',
dataIndex: 'position',
key: 'position',
},
{
title: '经度',
dataIndex: 'lng',
key: 'lng',
},
{
title: '纬度',
dataIndex: 'lat',
key: 'lat',
},
{
title: '操作',
dataIndex: 'operation',
key: 'operation',
},
])
const locationArrays = ref<LocationItem[]>([]);
const locationDrawArrays = ref<LocationItem[]>();
const locationGeoJson = reactive({
point: {
type: 'FeatureCollection',
@ -1215,7 +1373,6 @@
// txt
const handleImportCoorinateChange = (e)=>{
console.log("woligehoulai",e);
const reader = new FileReader();
reader.readAsText(e);
reader.onload = function(text){
@ -1235,7 +1392,6 @@
}
locationArrays.value?.push(obj);
}
})
}
const handlerClearLocationItem = () => {
@ -1251,24 +1407,35 @@
};
const handlerLocationRemove = (index: number) => {
locationArrays.value.splice(index, 1);
locationDrawArrays.value?.splice(index, 1);
handlerLocationGeoJson();
};
const handlerLocationChange = (e) => {};
//
const handlerLocationFlyTo = (item: LocationItem) => {
if(item.lng > 180){
let coor = GkToCGCS2000([parseFloat(item.lng),parseFloat(item.lat)]);
item.lng = coor[0];
item.lat = coor[1];
}
if (item.lng && item.lat) {
handlerLocation([item.lng, item.lat]);
handlerLocationGeoJson();
}
const handlerLocationFlyTo = (clickIndex:number) => {
locationDrawArrays.value = [];
locationArrays.value?.forEach((location)=>{
let obj={...location}
console.log(obj);
locationDrawArrays.value?.push(obj)
})
locationDrawArrays.value?.forEach((item,index)=>{
//
if(item.lng > 180){
let coor = GkToCGCS2000([parseFloat(item.lng),parseFloat(item.lat)]);
item.lng = coor[0];
item.lat = coor[1];
}
if (item.lng && item.lat) {
handlerLocationGeoJson();
}
})
//
handlerLocation([locationDrawArrays.value[clickIndex].lng, locationDrawArrays.value[clickIndex].lat]);
};
// geojson
@ -1277,7 +1444,7 @@
locationGeoJson.polyline.features = [];
locationGeoJson.polygon.features = [];
locationArrays.value?.forEach((item, index) => {
locationDrawArrays.value?.forEach((item, index) => {
if (item.lng && item.lat) {
let feature = {
type: 'Feature',
@ -1289,7 +1456,8 @@
locationGeoJson.point.features.push(feature);
}
});
if (locationArrays.value?.length >= 2) {
if (locationDrawArrays.value?.length >= 2) {
let feature = {
type: 'Feature',
geometry: {
@ -1298,19 +1466,22 @@
},
};
locationArrays.value?.forEach((item, index) => {
locationDrawArrays.value?.forEach((item, index) => {
if (item.lng && item.lat) {
let coor = [parseFloat(item.lng), parseFloat(item.lat)];
feature.geometry.coordinates.push(coor);
}
});
feature.geometry.coordinates.push([
parseFloat(locationArrays.value[0].lng),
parseFloat(locationArrays.value[0].lat),
parseFloat(locationDrawArrays.value[0].lng),
parseFloat(locationDrawArrays.value[0].lat),
]);
locationGeoJson.polyline.features[0] = feature;
}
if (locationArrays.value?.length >= 3) {
if (locationDrawArrays.value?.length >= 3) {
let feature = {
type: 'Feature',
geometry: {
@ -1318,15 +1489,15 @@
coordinates: [[]],
},
};
locationArrays.value?.forEach((item, index) => {
locationDrawArrays.value?.forEach((item, index) => {
if (item.lng && item.lat) {
let coor = [parseFloat(item.lng), parseFloat(item.lat)];
feature.geometry.coordinates[0].push(coor);
}
});
feature.geometry.coordinates[0].push([
parseFloat(locationArrays.value[0].lng),
parseFloat(locationArrays.value[0].lat),
parseFloat(locationDrawArrays.value[0].lng),
parseFloat(locationDrawArrays.value[0].lat),
]);
locationGeoJson.polygon.features[0] = feature;
}
@ -1780,6 +1951,7 @@
position: absolute;
top: 48px;
right: 10px;
z-index:999999;
border-radius: 5px;
box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1);
.location-operation {
@ -1798,4 +1970,10 @@
}
}
}
.split-panel-item:hover{
cursor:pointer;
color:#999;
}
</style>

View File

@ -0,0 +1,142 @@
class chunkUtil {
constructor() {
this.turf = window.turf || "";
if (!this.turf) {
throw new Error("请确定turf已挂载至window");
}
}
clipPolygon(polygon, polyline) {
const types = typeof polygon;
const types1 = typeof polyline;
if (!polygon) {
throw console.error("请传入面");
}
if (!polyline) {
throw console.error("请传入线");
}
if ([types, types1].includes('string')) {
const polygonParse = JSON.parse(polygon);
const polylineParse = JSON.parse(polyline);
const isLon = !polygonParse && !polygonParse.features && !polygonParse.features.length;
const isLine = !polylineParse && !polylineParse.features && !polylineParse.features.length;
if (isLon || isLine) {
throw new Error("请确定为面与线都为json串格式");
}
return this._setFloors(polygonParse, polylineParse)
} else {
throw new Error("暂时只支持json串格式的转换如需支持json格式请与作者沟通。");
}
}
_setFloors(A, B) {
const { turf } = this;
if ([A.type, B.type].includes('FeatureCollection')) {
const geojsonA = A.features[0];
if (geojsonA.geometry.type !== 'Polygon') {
throw new Error("第一个值请传入面");
}
const polyline = turf.polygonToLine(geojsonA);
if (polyline.geometry.type === "LineString") {
const lines = B.features[0]
const singClip = this._singeClips(polyline, lines)
return singClip
}
}
}
_singeClips(A, B) {
const { turf } = this;
const C = turf.lineIntersect(A, B);
if (C.features.length !== 2) {
throw new Error("请确定线与面有两个相交点");
}
const L = B.geometry.coordinates.length;
const SP = turf.point(B.geometry.coordinates[0]);
const EP = turf.point(B.geometry.coordinates[L - 1]);
const P = turf.polygon([A.geometry.coordinates])
if (turf.booleanPointInPolygon(SP, P) || turf.booleanPointInPolygon(EP, P)) {
throw new Error("线起点或终点不能在面内部");
}
const PL = turf.lineSlice(C.features[0], C.features[1], A);
const CL = turf.lineSlice(C.features[0], C.features[1], B);
const RL = this.connectLine(PL, CL);
RL.geometry.coordinates.push(RL.geometry.coordinates[0]); //闭合
const RP1 = turf.lineToPolygon(RL);
const FL = this.isLines(turf.point(A.geometry.coordinates[0]), PL);
const pList = [];
if (FL) {
for (let i = 0; i < A.geometry.coordinates.length; i++) {
const coordinate = A.geometry.coordinates[i];
if (!this.isLines(turf.point(coordinate), PL)) {
pList.push(coordinate)
}
}
} else {
let sNum = 0;
let isStartPush = false;
for (let i = 0; i < A.geometry.coordinates.length; i++) {
const coordinate = A.geometry.coordinates[i];
if (!this.isLines(turf.point(coordinate), PL)) {
if (isStartPush) {
pList.push(coordinate)
} else {
sNum++;
}
} else {
isStartPush = true;
}
}
for (let i = 0; i < sNum; i++) {
pList.push(A.geometry.coordinates[i]);
}
}
const PL2 = turf.lineString(pList);
const RL2 = this.connectLine(PL2, CL);
RL2.geometry.coordinates.push(RL2.geometry.coordinates[0]);
const RP2 = turf.lineToPolygon(RL2);
return turf.featureCollection([RP1, RP2])
}
connectLine(l1, l2) {
const l2l = l2.geometry.coordinates.length;
const l1sp = l1.geometry.coordinates[0];
const l2sp = l2.geometry.coordinates[0];
const l2ep = l2.geometry.coordinates[l2l - 1];
const pList = [];
for (let i = 0; i < l1.geometry.coordinates.length; i++) {
const coordinate = l1.geometry.coordinates[i];
pList.push(coordinate);
}
if (this.turf.distance(l1sp, l2sp) < this.turf.distance(l1sp, l2ep)) {
l2.geometry.coordinates = l2.geometry.coordinates.reverse();
}
for (let i = 0; i < l2.geometry.coordinates.length; i++) {
const coordinate = l2.geometry.coordinates[i];
pList.push(coordinate);
}
return this.turf.lineString(pList)
}
isLines(P, L) {
for (let i = 0; i < L.geometry.coordinates.length; i++) {
const coordinate = L.geometry.coordinates[i];
if (P.geometry.coordinates[0] === coordinate[0] && P.geometry.coordinates[1] === coordinate[1]) {
return true
}
}
return false;
}
}
export { chunkUtil }

View File

@ -7,44 +7,77 @@ import { generateUUID } from '../src/tool';
* return
*/
export function splitPolygonByLine(line, outerPolygon) {
console.log("绘制的分割线数据",line);
// 处理被分割的面数据坐标小数点保留多少位
let truncatedSplitter = turf.truncate(turf.lineString(outerPolygon.geometry.coordinates[0]), {
precision: 7,
});
console.log("分割面转换为线数据",truncatedSplitter);
//求交点
let intersectCollection = turf.lineIntersect(line, truncatedSplitter);
console.log("与分割面的交点",intersectCollection);
// printLngLat(intersectCollection.geometry.coordinates);
// 交点小于2个 返回null
if (intersectCollection.features.length < 2) {
return null;
}
//将点合并成MultiPoint
let intersectCombined = turf.combine(intersectCollection).features[0];
console.log("分割后所有点数据",intersectCombined)
//分别获取切割线
let outerPieceCollection = turf.lineSplit(line, intersectCombined);
let splitterPieceCollection = turf.lineSplit(truncatedSplitter, intersectCombined);
console.log("切割线被分割后所有线段",outerPieceCollection);
console.log("切割面被分割后所有线段",splitterPieceCollection);
//将所有的线段放到一起
let pieceCollection = turf.featureCollection(
outerPieceCollection.features.concat(splitterPieceCollection.features),
);
console.log("所有分割线段",pieceCollection);
//使用turf将闭合线组成多边形
let polygonCollection = turf.polygonize(pieceCollection);
console.log("分割线合并面数据",polygonCollection)
//对多边形进行判断,切割外的多边形丢弃
let innerPolygons = polygonCollection.features.filter((polygon) => {
// 获取多边形质心 多边形不规则时有问题
// let center = turf.centroid(polygon);
// 获取多边形表面一点
let newcenter = turf.pointOnFeature(polygon);
// 求多边形内一点
// let innerCenter =
return turf.booleanWithin(newcenter, outerPolygon);
// 判断点是否在被分割图斑内
// 图斑中心点
let centerPoint = turf.point(newcenter.geometry.coordinates);
// 图斑面
let outPolygon = turf.polygon(outerPolygon.geometry.coordinates)
// return turf.booleanPointInPolygon(centerPoint, outPolygon);
// let innerPoint = getRandomPointInPolygon(outPolygon);
// 获取多边形边界
// let bbox = turf.bbox(outPolygon); // 获取多边形的边界框
// return turf.booleanPointInPolygon(randomPoint.features[0], outPolygon);
return turf.booleanWithin(centerPoint, outerPolygon);
});
// let innerPolygons = polygonCollection.features;
//处理镂空数据多处镂空数据会导致计算错误因为polygonize方法无法正常的返回数据
@ -63,71 +96,71 @@ export function splitPolygonByLine(line, outerPolygon) {
return diff;
});
}
innerPolygons.forEach((item, index) => {
innerPolygons[index].properties.id = generateUUID();
// console.log("innerPolygons_length",innerPolygons[index].geometry.coordinates[0].length);
// console.log("innerPolygons_array",innerPolygons[index].geometry.coordinates[0])
// console.log("innerPolygons_length",innerPolygons[index].geometry.coordinates[0].length);
// console.log("innerPolygons_string",JSON.stringify(innerPolygons[index].geometry.coordinates[0]));
});
// let arr = JSON.parse(JSON.stringify(innerPolygons))
// arr.forEach((item, index) => {
// arr[index].properties.id = generateUUID();
// if((arr[index].geometry.coordinates[0][0][0] == arr[index].geometry.coordinates[0][arr[index].geometry.coordinates[0].length-1][0]) && arr[index].geometry.coordinates[0][0][1] == arr[index].geometry.coordinates[0][arr[index].geometry.coordinates[0].length-1][1]){
// }else{
// arr[index].geometry.coordinates[0].push(arr[index].geometry.coordinates[0][0])
// console.log("coordinates789",arr);
// }
// });
return innerPolygons;
}
export function splitPolygonByFill(fill, outerPolygon) {
var polygon1 = turf.polygon(fill.geometry.coordinates);
// var polygon1 = turf.polygon(
// [
// [
// [
// 118.08246593377775,
// 35.22214906870923
// ],
// [
// 118.0824468053414,
// 35.2221025269492
// ],
// [
// 118.08255783344151,
// 35.22206277960647
// ],
// [
// 118.08258527859043,
// 35.22213378126961
// ],
// [
// 118.08246593377775,
// 35.22214906870923
// ]
// ]
// ]
// )
function getRandomPointInPolygon(polygon) {
const bbox = turf.bbox(polygon); // 获取多边形的边界框
let point;
do {
point = turf.randomPoint(1, {bbox: bbox}).features[0]; // 从边界框中生成一个随机点
} while (!turf.booleanPointInPolygon(point, polygon)); // 检查点是否在多边形内部
return point;
}
export function splitPolygonByFill(drawPolygon, outerPolygon) {
console.log("drawPolygon",drawPolygon);
console.log("outerPolygon",outerPolygon)
var polygon1 = turf.polygon(drawPolygon.geometry.coordinates)
var polygon2 = turf.polygon(outerPolygon.geometry.coordinates);
// var polygon2 = turf.polygon(
// [
// [
// [
// 118.082358,
// 35.222126
// ],
// [
// 118.082517,
// 35.222061
// ],
// [
// 118.082535,
// 35.222098
// ],
// [
// 118.082372,
// 35.222163
// ],
// [
// 118.082358,
// 35.222126
// ]
// ]
// ]
// )
var difference = turf.difference(polygon2,polygon1);
var intersection = turf.intersect(polygon2, polygon1);
var difference = turf.difference(polygon1, polygon2);
intersection.properties.id = generateUUID();
difference.properties.id = generateUUID();
let splitAfterFeatures = [intersection,difference];
// splitAfterFeatures?.forEach((item,index)=>{
// splitAfterFeatures[index].properties?.id = generateUUID();
// })
return splitAfterFeatures;
console.log('difference123', difference);
return difference;
}
export function printLngLat(arr){
arr.forEach((item,index)=>{
console.log(item[1]+","+item[0]+"\n");
})
}

View File

@ -29,7 +29,7 @@ const LINE_IS_DRAW_STYLE = {
};
const POLYGON_STYLE = {
'fill-color': '#FAFAD2',
'fill-opacity': 0.3,
'fill-opacity': 0.6,
};
function typeOf(obj: any): any {
@ -394,6 +394,13 @@ export class MP extends BaseMP {
_this._currentDrawLayer();
this.drawIsFinish();
}
if (this.drawModel === this.drawModelChoose.Polygon) {
_this.drawLocal.push(e.lngLat);
_this._currentDrawSource();
_this._currentDrawLayer();
this.drawIsFinish();
}
}
};
//右键点击

View File

@ -274,6 +274,9 @@
:style="{ 'border-color': currentPolygon == index ? '#408eff' : '#fff' }"
>
<div class="data-list-layout-div">
<div class="data-list-checked">
<a-checkbox v-model:checked="item.checked"></a-checkbox>
</div>
<div class="data-list-title-div">
<img
src="/positioning.png"
@ -369,7 +372,12 @@
/>
</div>
<div class="split-operation">
<a-button type="primary" :disabled="isSpliting" @click="reductionSplit"></a-button>
<!-- <a-button type="primary" :disabled="isSpliting" @click="mergeSplitResult"></a-button> -->
<!-- &nbsp;
<a-button type="error" :disabled="isSpliting" @click="deleteSplitResult"></a-button> -->
&nbsp;
<a-button type="primary" :disabled="isSpliting" @click="reductionSplit"></a-button>
&nbsp;
<a-button type="primary" :disabled="isSpliting" @click="saveSplitResult"></a-button>
</div>
@ -501,6 +509,7 @@
import { getLoadDroneCaseInfoDetail } from '@/api/tiankongdi/index.ts';
import ShowInfoModal from '@/views/demo/tiankongdi/curbspotcity/MapList/ShowInfoModal/index.vue';
import { dataProcessing ,dataProcessingCount} from '@/views/demo/tiankongdi/util.ts';
import { itemProps } from '@/components/Menu/src/props';
const MapboxComponent = ref();
const mapConfig = ref({});
@ -733,8 +742,15 @@
};
const splitPolygonForm = ref([]);
const splitAfterPolygon = ref([]);
const handlerSplitPolygon = (e) => {
splitPolygonForm.value = [];
// splitPolygonForm.value = [];
//
if(currentPolygon.value){
splitPolygonForm.value?.splice(currentPolygon.value,1);
splitAfterPolygon.value?.splice(currentPolygon.value,1);
}
e?.forEach((item, index) => {
let form = {
fid: null,
@ -751,10 +767,41 @@
guotukongjianguihua_area: null,
area: null,
geom: item,
checked:false,
};
splitPolygonForm.value?.push(form);
splitAfterPolygon.value?.push(form.geom)
});
console.log('splitPolygonForm', splitPolygonForm.value);
//
let splitAfterFeatures = {
type: 'FeatureCollection',
features: [],
};
splitPolygonForm.value?.forEach((item,idnex)=>{
splitAfterFeatures.features.push(item.geom);
})
// splitAfterFeatures.features = e;
MapboxComponent.value.handlerDetails(splitAfterFeatures);
//
let geoms = {
type: 'FeatureCollection',
features: [],
};
MapboxComponent.value.handlerDetails(geoms, 'splitPolygonSource', 'splitPolygonLayer', {
lineStyle: { 'line-color': '#408eff', 'line-width': 3 },
fillStyle: { 'fill-color': '#408eff', 'fill-opacity': 0.1 },
});
currentPolygon.value = null;
};
const handlerEditPolygonInfo = (e) => {
@ -787,7 +834,7 @@
};
//
const currentPolygon = ref<Number>();
const currentPolygon = ref(null);
const changeCurrentPolygon = (index, item) => {
let geoms = {
type: 'FeatureCollection',
@ -804,7 +851,6 @@
//
const onFeatureClick = (feature) => {
splitPolygonForm.value?.forEach((item, index) => {
console.log('item---', item);
if (item['geom']['properties']['id'] == feature.properties.id) {
changeCurrentPolygon(index, item);
}
@ -828,8 +874,141 @@
fillStyle: { 'fill-color': '#408eff', 'fill-opacity': 0.1 },
});
splitPolygonForm.value = [];
currentPolygon.value = null;
};
//
const deleteSplitResult = ()=>{
//
let deleteIndexs = [];
splitPolygonForm.value?.forEach((item,index)=>{
if(item.checked){
deleteIndexs.push(index);
}
})
if(deleteIndexs.length<1){
createMessage.error("请选择需要删除的图斑!");
return;
}
removeElementsByIndexes(splitAfterPolygon.value,deleteIndexs);
removeElementsByIndexes(splitPolygonForm.value,deleteIndexs);
//
let geoms = {
type: 'FeatureCollection',
features:splitAfterPolygon.value?splitAfterPolygon.value : [],
};
console.log("geoms123",geoms);
MapboxComponent.value.handlerDetails(geoms, 'detailsSource', 'detailsLayer', {
lineStyle: { 'line-color': '#fcf003', 'line-width': 3 },
fillStyle: { 'fill-color': '#fcf003', 'fill-opacity': 0.1 },
});
let checkedGeoms = {
type: 'FeatureCollection',
features: [],
};
console.log("geoms12345",checkedGeoms);
MapboxComponent.value.handlerDetails(checkedGeoms, 'splitPolygonSource', 'splitPolygonLayer', {
lineStyle: { 'line-color': '#408eff', 'line-width': 3 },
fillStyle: { 'fill-color': '#408eff', 'fill-opacity': 0.1 },
});
}
//
function removeElementsByIndexes(arr, indexes) {
//
indexes.sort((a, b) => b - a);
//
for (const index of indexes) {
arr.splice(index, 1);
}
}
//
const mergeSplitResult = ()=>{
let mergeArr = []
let deleteIndexs = [];
splitPolygonForm.value?.forEach((item,index)=>{
if(item.checked){
let turfPolygon = turf.polygon(splitAfterPolygon.value[index].geometry.coordinates);
mergeArr.push(turfPolygon);
deleteIndexs.push(index);
}
})
if(deleteIndexs.length!=2){
createMessage.error("请选择两个图斑进行合并!");
return;
}
removeElementsByIndexes(splitAfterPolygon.value,deleteIndexs);
removeElementsByIndexes(splitPolygonForm.value,deleteIndexs);
//
let union = turf.union(mergeArr[0],mergeArr[1]);
let polygon = {
fid: null,
unitname: '',
createdate: createtime,
caseno: case_no + ' 分割图斑',
countyname: countyname,
streetname: streetname,
isbuildname: '',
nongyongdi_area: null,
gengdi_area: null,
yongjiujibennongtian_area: null,
shengtaibaohuhongxian_area: null,
guotukongjianguihua_area: null,
area: null,
geom: union,
checked:false,
}
splitAfterPolygon.value?.push(union);
splitPolygonForm.value?.push(polygon);
//
let geoms = {
type: 'FeatureCollection',
features:splitAfterPolygon.value?splitAfterPolygon.value : [],
};
MapboxComponent.value.handlerDetails(geoms, 'detailsSource', 'detailsLayer', {
lineStyle: { 'line-color': '#fcf003', 'line-width': 3 },
fillStyle: { 'fill-color': '#fcf003', 'fill-opacity': 0.1 },
});
let checkedGeoms = {
type: 'FeatureCollection',
features: [],
};
MapboxComponent.value.handlerDetails(checkedGeoms, 'splitPolygonSource', 'splitPolygonLayer', {
lineStyle: { 'line-color': '#408eff', 'line-width': 3 },
fillStyle: { 'fill-color': '#408eff', 'fill-opacity': 0.1 },
});
}
//
const saveSplitResult = () => {
isSpliting.value = true;
@ -1006,7 +1185,14 @@
padding-bottom: 8px;
border-bottom: 1px solid #e5e5e5;
height: 45px;
gap:20px;
.data-list-checked{
width:28px;
height:28px;
margin-top:8px;
}
.data-list-title-div {
flex:auto;
display: flex;
align-items: center;
.map-mark {

View File

@ -0,0 +1,66 @@
// vite.config.ts
import { defineApplicationConfig } from "file:///E:/projects/%E8%B4%A2%E6%BA%90%E7%B3%BB%E7%BB%9F/CaiYuanYiTiHua/internal/vite-config/dist/index.mjs";
var vite_config_default = defineApplicationConfig({
overrides: {
optimizeDeps: {
include: [
"echarts/core",
"echarts/charts",
"echarts/components",
"echarts/renderers",
"qrcode",
"@iconify/iconify",
"ant-design-vue/es/locale/zh_CN",
"ant-design-vue/es/locale/en_US",
"@/../lib/vform/designer.umd.js"
]
},
build: {
/* 其他build生产打包配置省略 */
//...
target: "esnext",
commonjsOptions: {
include: /node_modules|lib/
//这里记得把lib目录加进来否则生产打包会报错
}
},
server: {
proxy: {
"/basic-api": {
target: "http://localhost:3000",
changeOrigin: true,
ws: true,
rewrite: (path) => path.replace(new RegExp(`^/basic-api`), "")
// only https
// secure: false
},
"/upload": {
target: "http://localhost:3300/upload",
changeOrigin: true,
ws: true,
rewrite: (path) => path.replace(new RegExp(`^/upload`), "")
}
},
warmup: {
clientFiles: ["./index.html", "./src/{views,components}/*"]
}
},
define: {
"process.env": {
BASE_URL: "/"
}
},
css: {
preprocessorOptions: {
less: {
javascriptEnabled: true
}
}
},
plugins: []
}
});
export {
vite_config_default as default
};
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsidml0ZS5jb25maWcudHMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbImNvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9kaXJuYW1lID0gXCJFOlxcXFxwcm9qZWN0c1xcXFxcdThEMjJcdTZFOTBcdTdDRkJcdTdFREZcXFxcQ2FpWXVhbllpVGlIdWFcIjtjb25zdCBfX3ZpdGVfaW5qZWN0ZWRfb3JpZ2luYWxfZmlsZW5hbWUgPSBcIkU6XFxcXHByb2plY3RzXFxcXFx1OEQyMlx1NkU5MFx1N0NGQlx1N0VERlxcXFxDYWlZdWFuWWlUaUh1YVxcXFx2aXRlLmNvbmZpZy50c1wiO2NvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9pbXBvcnRfbWV0YV91cmwgPSBcImZpbGU6Ly8vRTovcHJvamVjdHMvJUU4JUI0JUEyJUU2JUJBJTkwJUU3JUIzJUJCJUU3JUJCJTlGL0NhaVl1YW5ZaVRpSHVhL3ZpdGUuY29uZmlnLnRzXCI7aW1wb3J0IHsgZGVmaW5lQXBwbGljYXRpb25Db25maWcgfSBmcm9tICdAdmJlbi92aXRlLWNvbmZpZyc7XG5cbmV4cG9ydCBkZWZhdWx0IGRlZmluZUFwcGxpY2F0aW9uQ29uZmlnKHtcbiAgb3ZlcnJpZGVzOiB7XG4gICAgb3B0aW1pemVEZXBzOiB7XG4gICAgICBpbmNsdWRlOiBbXG4gICAgICAgICdlY2hhcnRzL2NvcmUnLFxuICAgICAgICAnZWNoYXJ0cy9jaGFydHMnLFxuICAgICAgICAnZWNoYXJ0cy9jb21wb25lbnRzJyxcbiAgICAgICAgJ2VjaGFydHMvcmVuZGVyZXJzJyxcbiAgICAgICAgJ3FyY29kZScsXG4gICAgICAgICdAaWNvbmlmeS9pY29uaWZ5JyxcbiAgICAgICAgJ2FudC1kZXNpZ24tdnVlL2VzL2xvY2FsZS96aF9DTicsXG4gICAgICAgICdhbnQtZGVzaWduLXZ1ZS9lcy9sb2NhbGUvZW5fVVMnLFxuICAgICAgICAnQC8uLi9saWIvdmZvcm0vZGVzaWduZXIudW1kLmpzJyxcbiAgICAgIF0sXG4gICAgfSxcbiAgICBidWlsZDoge1xuICAgICAgLyogXHU1MTc2XHU0RUQ2YnVpbGRcdTc1MUZcdTRFQTdcdTYyNTNcdTUzMDVcdTkxNERcdTdGNkVcdTc3MDFcdTc1NjUgKi9cbiAgICAgIC8vLi4uXG4gICAgICB0YXJnZXQ6ICdlc25leHQnLFxuICAgICAgY29tbW9uanNPcHRpb25zOiB7XG4gICAgICAgIGluY2x1ZGU6IC9ub2RlX21vZHVsZXN8bGliLywgLy9cdThGRDlcdTkxQ0NcdThCQjBcdTVGOTdcdTYyOEFsaWJcdTc2RUVcdTVGNTVcdTUyQTBcdThGREJcdTY3NjVcdUZGMENcdTU0MjZcdTUyMTlcdTc1MUZcdTRFQTdcdTYyNTNcdTUzMDVcdTRGMUFcdTYyQTVcdTk1MTlcdUZGMDFcdUZGMDFcbiAgICAgIH0sXG4gICAgfSxcbiAgICBzZXJ2ZXI6IHtcbiAgICAgIHByb3h5OiB7XG4gICAgICAgICcvYmFzaWMtYXBpJzoge1xuICAgICAgICAgIHRhcmdldDogJ2h0dHA6Ly9sb2NhbGhvc3Q6MzAwMCcsXG4gICAgICAgICAgY2hhbmdlT3JpZ2luOiB0cnVlLFxuICAgICAgICAgIHdzOiB0cnVlLFxuICAgICAgICAgIHJld3JpdGU6IChwYXRoKSA9PiBwYXRoLnJlcGxhY2UobmV3IFJlZ0V4cChgXi9iYXNpYy1hcGlgKSwgJycpLFxuICAgICAgICAgIC8vIG9ubHkgaHR0cHNcbiAgICAgICAgICAvLyBzZWN1cmU6IGZhbHNlXG4gICAgICAgIH0sXG4gICAgICAgICcvdXBsb2FkJzoge1xuICAgICAgICAgIHRhcmdldDogJ2h0dHA6Ly9sb2NhbGhvc3Q6MzMwMC91cGxvYWQnLFxuICAgICAgICAgIGNoYW5nZU9yaWdpbjogdHJ1ZSxcbiAgICAgICAgICB3czogdHJ1ZSxcbiAgICAgICAgICByZXdyaXRlOiAocGF0aCkgPT4gcGF0aC5yZXBsYWNlKG5ldyBSZWdFeHAoYF4vdXBsb2FkYCksICcnKSxcbiAgICAgICAgfSxcbiAgICAgIH0sXG4gICAgICB3YXJtdXA6IHtcbiAgICAgICAgY2xpZW50RmlsZXM6IFsnLi9pbmRleC5odG1sJywgJy4vc3JjL3t2aWV3cyxjb21wb25lbnRzfS8qJ10sXG4gICAgICB9LFxuICAgIH0sXG4gICAgZGVmaW5lOiB7XG4gICAgICAncHJvY2Vzcy5lbnYnOiB7XG4gICAgICAgIEJBU0VfVVJMOiAnLycsXG4gICAgICB9LFxuICAgIH0sXG4gICAgY3NzOiB7XG4gICAgICBwcmVwcm9jZXNzb3JPcHRpb25zOiB7XG4gICAgICAgIGxlc3M6IHtcbiAgICAgICAgICBqYXZhc2NyaXB0RW5hYmxlZDogdHJ1ZSxcbiAgICAgICAgfSxcbiAgICAgIH0sXG4gICAgfSxcbiAgICBwbHVnaW5zOiBbXSxcbiAgfSxcbn0pO1xuIl0sCiAgIm1hcHBpbmdzIjogIjtBQUF1VCxTQUFTLCtCQUErQjtBQUUvVixJQUFPLHNCQUFRLHdCQUF3QjtBQUFBLEVBQ3JDLFdBQVc7QUFBQSxJQUNULGNBQWM7QUFBQSxNQUNaLFNBQVM7QUFBQSxRQUNQO0FBQUEsUUFDQTtBQUFBLFFBQ0E7QUFBQSxRQUNBO0FBQUEsUUFDQTtBQUFBLFFBQ0E7QUFBQSxRQUNBO0FBQUEsUUFDQTtBQUFBLFFBQ0E7QUFBQSxNQUNGO0FBQUEsSUFDRjtBQUFBLElBQ0EsT0FBTztBQUFBO0FBQUE7QUFBQSxNQUdMLFFBQVE7QUFBQSxNQUNSLGlCQUFpQjtBQUFBLFFBQ2YsU0FBUztBQUFBO0FBQUEsTUFDWDtBQUFBLElBQ0Y7QUFBQSxJQUNBLFFBQVE7QUFBQSxNQUNOLE9BQU87QUFBQSxRQUNMLGNBQWM7QUFBQSxVQUNaLFFBQVE7QUFBQSxVQUNSLGNBQWM7QUFBQSxVQUNkLElBQUk7QUFBQSxVQUNKLFNBQVMsQ0FBQyxTQUFTLEtBQUssUUFBUSxJQUFJLE9BQU8sYUFBYSxHQUFHLEVBQUU7QUFBQTtBQUFBO0FBQUEsUUFHL0Q7QUFBQSxRQUNBLFdBQVc7QUFBQSxVQUNULFFBQVE7QUFBQSxVQUNSLGNBQWM7QUFBQSxVQUNkLElBQUk7QUFBQSxVQUNKLFNBQVMsQ0FBQyxTQUFTLEtBQUssUUFBUSxJQUFJLE9BQU8sVUFBVSxHQUFHLEVBQUU7QUFBQSxRQUM1RDtBQUFBLE1BQ0Y7QUFBQSxNQUNBLFFBQVE7QUFBQSxRQUNOLGFBQWEsQ0FBQyxnQkFBZ0IsNEJBQTRCO0FBQUEsTUFDNUQ7QUFBQSxJQUNGO0FBQUEsSUFDQSxRQUFRO0FBQUEsTUFDTixlQUFlO0FBQUEsUUFDYixVQUFVO0FBQUEsTUFDWjtBQUFBLElBQ0Y7QUFBQSxJQUNBLEtBQUs7QUFBQSxNQUNILHFCQUFxQjtBQUFBLFFBQ25CLE1BQU07QUFBQSxVQUNKLG1CQUFtQjtBQUFBLFFBQ3JCO0FBQUEsTUFDRjtBQUFBLElBQ0Y7QUFBQSxJQUNBLFNBQVMsQ0FBQztBQUFBLEVBQ1o7QUFDRixDQUFDOyIsCiAgIm5hbWVzIjogW10KfQo=