update: 右键新增航线、事件编辑、kmz文件编辑
| | |
| | | <li @click="numberControls(true, 100, 'globalHeight')">+100</li> |
| | | <li @click="numberControls(true, 10, 'globalHeight')">+10</li> |
| | | <li> |
| | | <span>{{ Number(folderSetting.globalHeight) }}</span> m |
| | | <span>{{ Math.round(folderSetting.globalHeight) }}</span> m |
| | | </li> |
| | | <li @click="numberControls(false, 100, 'globalHeight')">-100</li> |
| | | <li @click="numberControls(false, 10, 'globalHeight')">-10</li> |
| | |
| | | import useMapDraw from '/@/utils/cesium/use-map-draw' |
| | | import { getKmlParams } from '/@/utils/cesium/kmz' |
| | | import { cesiumOperation } from '/@/hooks/use-cesium-tsa' |
| | | import { message } from 'ant-design-vue' |
| | | import type { missionConfig, payloadParam as payloadParamConfig, Folder } from '/@/utils/cesium/types/kml' |
| | | const { appContext }: any = getCurrentInstance() |
| | | const global = appContext.config.globalProperties |
| | |
| | | if (missionConfigSetting[key]) { |
| | | missionConfigSetting[key] -= computeVal |
| | | } else { |
| | | if (folderSetting[key] - computeVal <= 0) { |
| | | return message.warn('飞行高度较低!!!!') |
| | | } |
| | | folderSetting[key] -= computeVal |
| | | } |
| | | } |
| | |
| | | </template> |
| | | 返回 |
| | | </a-button> |
| | | <div class="save-button"> |
| | | <div class="save-button" @click="saveWaylineFile"> |
| | | <SaveOutlined /> |
| | | </div> |
| | | <setting v-model:loadCompleted="loadCompleted"> |
| | | <template #show> |
| | | <a-button type="text" class="setting-btn"> |
| | | <div class="router-setting"> |
| | | <img |
| | | class="wayline-icon" |
| | | :src="getResource('wayline.png')" |
| | | alt="icon" /> |
| | | <img class="wayline-icon" :src="getResource('wayline.png')" alt="icon" /> |
| | | <span class="text">航线设置</span> |
| | | <CaretDownOutlined /> |
| | | </div> |
| | |
| | | </setting> |
| | | </div> |
| | | <div class="wayline-info"> |
| | | <div |
| | | class="info-box" |
| | | v-for="(item, index) in waylineDetails" |
| | | :key="index"> |
| | | <div class="info-box" v-for="(item, index) in waylineDetails" :key="index"> |
| | | <div class="title">{{ item.title }}</div> |
| | | <div class="info">{{ item.value }}</div> |
| | | </div> |
| | |
| | | <div class="right">{{ index + 1 }}</div> |
| | | </div> |
| | | <div class="graph-right"> |
| | | <div |
| | | v-for="(event, index) in item.eventList" |
| | | :key="index" |
| | | class="s-event-icon"> |
| | | <div v-for="(event, index) in item.eventList" :key="index" class="s-event-icon"> |
| | | <a-tooltip placement="top"> |
| | | <template #title> |
| | | {{ event.name }} |
| | |
| | | import _ from 'lodash' |
| | | import * as Cesium from 'cesium' |
| | | import setting from './components/setting.vue' |
| | | import useKmzTsa from '/@/utils/cesium/use-kmz-tsa' |
| | | import useKmzTsa, { kmlStr } from '/@/utils/cesium/use-kmz-tsa' |
| | | import { ref, reactive, defineEmits, defineProps, watch, onMounted } from 'vue' |
| | | import { ArrowLeftOutlined, CaretDownOutlined, SaveOutlined } from '@ant-design/icons-vue' |
| | | import { cesiumOperation } from '/@/hooks/use-cesium-tsa' |
| | | import { useMyStore } from '/@/store' |
| | | import ImageTrailMaterial from '/@/utils/cesium/ImageTrailMaterial' |
| | | import useMapDraw from '/@/utils/cesium/use-map-draw' |
| | | import useMapDraw, { kmlEntities as globalEntities } from '/@/utils/cesium/use-map-draw' |
| | | import { getKmlParams } from '/@/utils/cesium/kmz' |
| | | import { getHaeHeight } from '/@/utils/cesium/mapUtils' |
| | | const store = useMyStore() |
| | | const { appContext }: any = getCurrentInstance() |
| | | const global = appContext.config.globalProperties |
| | | |
| | | const { |
| | | removeAllPoint, |
| | | getEntityById, |
| | | addPolyline, |
| | | addRightClick, |
| | | removeRightClickEvent, |
| | | } = cesiumOperation() |
| | | const kmzUtils = useKmzTsa() |
| | | |
| | | const { removeAllPoint, getEntityById, addPolyline, removeAllDataSource, addRightClick, removeRightClickEvent } = |
| | | cesiumOperation() |
| | | |
| | | interface waylineDetails { |
| | | title: string |
| | |
| | | |
| | | const tragetPointArr = ref<tragetPoint[]>([]) |
| | | |
| | | let kmlDataSource: |
| | | | { entities: { values: { _children: any }[] }; show: boolean } |
| | | | null |
| | | | any = null |
| | | let kmlDataSource: { entities: { values: { _children: any }[] }; show: boolean } | null | any = null |
| | | |
| | | let mouseRightClickEvent: any = null |
| | | |
| | | // 绘制地图上的东西 |
| | | let mapDraw = null |
| | | let mapDraw: any = null |
| | | // 航线详情 |
| | | const waylineDetails = ref<waylineDetails[]>([]) |
| | | // 样式 |
| | |
| | | if (kmlDataSource) { |
| | | global.$viewer.dataSources.remove(kmlDataSource) |
| | | } |
| | | global.$viewer.dataSources |
| | | .add(Cesium.KmlDataSource.load(filePath.value, options)) |
| | | .then((res: any) => { |
| | | kmlDataSource = res |
| | | mapDraw = useMapDraw(kmlDataSource, [], filePath.value, global) |
| | | const { |
| | | drawWayline, |
| | | waylineDetails: details, |
| | | waylinePointsEvent, |
| | | kmlEntities: entities, |
| | | clearWaylineData |
| | | } = mapDraw |
| | | clearWaylineData() |
| | | drawWayline() |
| | | useKmzTsa().init(filePath.value).then(kmlRes => { |
| | | loadCompleted.value = true |
| | | }) |
| | | waylineDetails.value = details |
| | | tragetPointArr.value = waylinePointsEvent.value |
| | | kmlEntities.value = entities.value |
| | | if (!mouseRightClickEvent) { |
| | | // 添加右键事件 |
| | | addMapPointEvent() |
| | | } |
| | | global.$viewer.dataSources.add(Cesium.KmlDataSource.load(filePath.value, options)).then((res: any) => { |
| | | kmlDataSource = res |
| | | mapDraw = useMapDraw(kmlDataSource, [], filePath.value, global) |
| | | const { |
| | | drawWayline, |
| | | waylineDetails: details, |
| | | waylinePointsEvent, |
| | | kmlEntities: entities, |
| | | clearWaylineData, |
| | | } = mapDraw |
| | | clearWaylineData() |
| | | drawWayline() |
| | | kmzUtils.init(filePath.value).then((kmlRes) => { |
| | | loadCompleted.value = true |
| | | }) |
| | | waylineDetails.value = details |
| | | tragetPointArr.value = waylinePointsEvent.value |
| | | kmlEntities.value = entities.value |
| | | if (!mouseRightClickEvent) { |
| | | // 添加右键事件 |
| | | addMapPointEvent() |
| | | } |
| | | }) |
| | | } |
| | | |
| | | // const createPointOrPolyline = () => {} |
| | |
| | | const nextPoint = points[index + 1]?.position |
| | | if (prevPoint) { |
| | | // 获取中心点 |
| | | const centerPoint = Cesium.Cartesian3.lerp( |
| | | currentPoint, |
| | | prevPoint, |
| | | 0.5, |
| | | new Cesium.Cartesian3(), |
| | | ) |
| | | const centerPoint = Cesium.Cartesian3.lerp(currentPoint, prevPoint, 0.5, new Cesium.Cartesian3()) |
| | | // 获取两个点之间的距离 |
| | | let distance = Cesium.Cartesian3.distance(currentPoint, prevPoint) |
| | | distance = Math.round(distance) |
| | |
| | | } |
| | | if (nextPoint) { |
| | | // 获取中心点 |
| | | const centerPoint = Cesium.Cartesian3.lerp( |
| | | currentPoint, |
| | | nextPoint, |
| | | 0.5, |
| | | new Cesium.Cartesian3(), |
| | | ) |
| | | const centerPoint = Cesium.Cartesian3.lerp(currentPoint, nextPoint, 0.5, new Cesium.Cartesian3()) |
| | | // 获取两个点之间的距离 |
| | | let distance = Cesium.Cartesian3.distance(currentPoint, nextPoint) |
| | | distance = Math.round(distance) |
| | |
| | | title: `在${Number(selectPointIndex.value) + 1}号航点前插入`, |
| | | }, |
| | | ] |
| | | const defaultEvents = [ |
| | | { class: 'finally-point', title: '在最后航点新增航点' }, |
| | | ] |
| | | const events = |
| | | selectPointIndex.value !== null |
| | | ? [...defaultEvents, ...pointEvents] |
| | | : defaultEvents |
| | | const defaultEvents = [{ class: 'finally-point', title: '在最后航点新增航点' }] |
| | | const events = selectPointIndex.value !== null ? [...defaultEvents, ...pointEvents] : defaultEvents |
| | | popupDom = createPointPopupDom(events) |
| | | global.$viewer.container.appendChild(popupDom) |
| | | popupDom.style.transform = `translate3d(${x}px, ${y}px, 0)` |
| | |
| | | ` |
| | | return pointPopup |
| | | } |
| | | |
| | | // 给DOM添加点击事件 |
| | | const addMenuEvent = (dom: HTMLElement, position: Cesium.Cartesian2) => { |
| | | dom.addEventListener('click', (e: any) => { |
| | | removeCesiumChildDom(popupDom) |
| | | const className = e.target.className |
| | | const c3Position = global.$viewer.scene.globe.pick( |
| | | global.$viewer.camera.getPickRay(position), |
| | | global.$viewer.scene, |
| | | ) |
| | | const c3Position = global.$viewer.scene.globe.pick(global.$viewer.camera.getPickRay(position), global.$viewer.scene) |
| | | const c2Postion = ellipsoid.cartesianToCartographic(c3Position) |
| | | const longitude = Cesium.Math.toDegrees(c2Postion.longitude) |
| | | const latitude = Cesium.Math.toDegrees(c2Postion.latitude) |
| | | const height = 70 |
| | | const createPointPosition = Cesium.Cartesian3.fromDegrees( |
| | | longitude, |
| | | latitude, |
| | | // 获取全局高度 |
| | | const globalHeight: any = getKmlParams(kmlStr.value, true, { |
| | | name: 'globalHeight', |
| | | findRegx: '([\\s\\S]*?)', |
| | | }) |
| | | const height = Number(globalHeight[1]) |
| | | const createPointPosition = Cesium.Cartesian3.fromDegrees(longitude, latitude, height) |
| | | const entity: Cesium.Entity = global.$viewer.entities.add({ |
| | | position: createPointPosition, |
| | | }) |
| | | // 加入kml文件中 |
| | | const setting = { |
| | | Point: { |
| | | coordinates: `${longitude},${latitude}`, |
| | | }, |
| | | index: tragetPointArr.value.length, |
| | | height, |
| | | ) |
| | | let entity: Cesium.Entity | any = null |
| | | ellipsoidHeight: height, |
| | | } |
| | | if (className === 'finally-point') { |
| | | entity = global.$viewer.entities.add({ |
| | | position: createPointPosition, |
| | | label: { |
| | | text: 'ASL:70m', |
| | | font: '13px monospace', |
| | | showBackground: true, |
| | | horizontalOrigin: Cesium.HorizontalOrigin.CENTER, |
| | | verticalOrigin: Cesium.VerticalOrigin.BOTTOM, |
| | | disableDepthTestDistance: Number.POSITIVE_INFINITY, |
| | | pixelOffset: new Cesium.Cartesian2(0, -40), |
| | | }, |
| | | billboard: { |
| | | image: createBillboard(tragetPointArr.value.length + 1, '#61d396'), |
| | | pixelOffset: new Cesium.Cartesian2(0, -20), |
| | | }, |
| | | point: { |
| | | pixelSize: 20, |
| | | color: Cesium.Color.GHOSTWHITE, |
| | | outlineColor: Cesium.Color.BLACK, |
| | | }, |
| | | }) |
| | | const prePostion = |
| | | tragetPointArr.value[tragetPointArr.value.length - 1].position |
| | | const polylinePositions = [prePostion, createPointPosition] |
| | | addPolyline({ |
| | | polyline: { |
| | | positions: polylinePositions, |
| | | width: 7, |
| | | material: new ImageTrailMaterial({ |
| | | backgroundColor: Cesium.Color.fromBytes(96, 210, 149), |
| | | image: getResource('arrow-right.png'), |
| | | imageW: 7, |
| | | duration: 0, |
| | | animation: false, |
| | | }), |
| | | clampToGround: false, // 关闭贴地效果,保留高度 |
| | | }, |
| | | }) |
| | | addPolyline({ |
| | | polyline: { |
| | | positions: Cesium.Cartesian3.fromDegreesArrayHeights([ |
| | | longitude, |
| | | latitude, |
| | | height, |
| | | longitude, |
| | | latitude, |
| | | 0, |
| | | ]), |
| | | width: 1, |
| | | material: new Cesium.PolylineDashMaterialProperty({ |
| | | color: Cesium.Color.WHITE, |
| | | }), |
| | | }, |
| | | }) |
| | | removeAllDataSource() |
| | | removeAllPoint() |
| | | kmzUtils.writePoint(setting) |
| | | globalEntities.value.push(entity) |
| | | mapDraw.drawWayline(globalEntities.value, kmlStr.value) |
| | | tragetPointArr.value.push({ |
| | | position: createPointPosition, |
| | | eventList: [], |
| | | }) |
| | | } |
| | | if (className === 'add-prev-point') { |
| | | // console.log(1) |
| | | } |
| | | if (className === 'add-next-point') { |
| | | // const index = selectPointIndex.value |
| | | // globalEntities.value.splice(index, 0, entity) |
| | | // console.log() |
| | | } |
| | | // clearCesiumMap() |
| | | // createMapMarker(undefined, [...kmlEntities.value, entity]) |
| | |
| | | } |
| | | } |
| | | |
| | | // 保存文件 |
| | | const saveWaylineFile = () => { |
| | | kmzUtils.save() |
| | | } |
| | | |
| | | onMounted(() => { |
| | | // 清空画布 |
| | | clearCesiumMap() |
| | |
| | | import { mode } from 'crypto-js' |
| | | import JsZip from 'jszip' |
| | | |
| | | const noWmpl: string[] = ['Folder', 'Placemark', 'Point', 'coordinates'] |
| | | |
| | | /** |
| | | * @description: 读取kmz文件解析出kml文件 |
| | | * @param {string} filePath 文件地址 |
| | |
| | | .then((kmzZip) => { |
| | | // 通过文件名找到 KML 文件 |
| | | const kmlFile = kmzZip.file(/\.kml$/i)[0] |
| | | return kmlFile.async('text') |
| | | return { |
| | | file: kmzZip, |
| | | content: kmlFile.async('text') |
| | | } |
| | | }) |
| | | } |
| | | |
| | |
| | | if (Object.prototype.toString.call(settingParmas[key]) === '[object Object]') { |
| | | let parmaStr = '' |
| | | Object.keys(settingParmas[key]).forEach((v) => { |
| | | parmaStr += `<wpml:${v}>${settingParmas[key][v]}</wpml:${v}>` |
| | | const xml = noWmpl.includes(key) ? `<${v}>${settingParmas[key][v]}</${v}>` : `<wpml:${v}>${settingParmas[key][v]}</wpml:${v}>` |
| | | parmaStr += xml |
| | | }) |
| | | paramGroup += `<wpml:${key}>${parmaStr}</wpml:${key}>` |
| | | const xml = noWmpl.includes(key) ? `<${key}>${parmaStr}</${key}>` : `<wpml:${key}>${parmaStr}</wpml:${key}>` |
| | | paramGroup += xml |
| | | } else { |
| | | paramGroup += `<wpml:${key}>${settingParmas[key]}</wpml:${key}>` |
| | | const xml = noWmpl.includes(key) ? `<${key}>${settingParmas[key]}</${key}>` : `<wpml:${key}>${settingParmas[key]}</wpml:${key}>` |
| | | paramGroup += xml |
| | | } |
| | | }) |
| | | return paramGroup |
| | |
| | | import * as Cesium from 'cesium' |
| | | |
| | | export const getLngLatDistance = ( |
| | | lat1: number, |
| | | lng1: number, |
| | | lat2: number, |
| | | lng2: number, |
| | | ) => { |
| | | export const getLngLatDistance = (lat1: number, lng1: number, lat2: number, lng2: number) => { |
| | | const radLat1 = (lat1 * Math.PI) / 180.0 |
| | | const radLat2 = (lat2 * Math.PI) / 180.0 |
| | | const a = radLat1 - radLat2 |
| | |
| | | let s = |
| | | 2 * |
| | | Math.asin( |
| | | Math.sqrt( |
| | | Math.pow(Math.sin(a / 2), 2) + |
| | | Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(b / 2), 2), |
| | | ), |
| | | Math.sqrt(Math.pow(Math.sin(a / 2), 2) + Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(b / 2), 2)), |
| | | ) |
| | | s = s * 6378.137 // EARTH_RADIUS; |
| | | s = Math.round(s * 10000) / 10000 |
| | |
| | | ctx.closePath() |
| | | return billboard |
| | | } |
| | | |
| | | // 获取当前经纬度海拔 |
| | | export const getHaeHeight = (start: { longitude: number; latitude: number; height: number }, end: { longitude: number; latitude: number; height: number }) => { |
| | | const ellipsoid = Cesium.Ellipsoid.WGS84 // 选择合适的椭圆体模型 |
| | | // const { longitude, latitude, height } = start |
| | | // const { longitude: endLng, latitude: endLat, height: endHeight } = end |
| | | // const startC3Position = new Cesium.Cartographic(longitude, latitude, height) |
| | | // const endC3Position = new Cesium.Cartographic(endLng, endLat, endHeight) |
| | | // const startCartesianPosition = ellipsoid.cartographicToCartesian(startC3Position) |
| | | // const endCartesianPosition = ellipsoid.cartographicToCartesian(endC3Position) |
| | | // // 计算两个位置之间的距离 |
| | | // const distance = new Cesium.EllipsoidGeodesic(startCartesianPosition, endCartesianPosition, ellipsoid) |
| | | // return distance |
| | | const { longitude, latitude, height } = end |
| | | const cartographic = Cesium.Cartographic.fromDegrees(longitude, latitude, height) |
| | | const cartesianPosition = ellipsoid.cartographicToCartesian(cartographic) |
| | | const haeHeight = ellipsoid.cartesianToCartographic(cartesianPosition).height |
| | | return haeHeight |
| | | } |
| | |
| | | import { ref } from 'vue' |
| | | import { ref, getCurrentInstance } from 'vue' |
| | | import { analyzeKmzFile, getKmlParams, generateKmlFormat } from '/@/utils/cesium/kmz' |
| | | import { getLngLatAltitude } from './mapUtils' |
| | | import JSZIP from 'jszip' |
| | | import { saveAs } from 'file-saver' |
| | | |
| | | const JsZip = new JSZIP() |
| | | |
| | | const kmlStr = ref('') |
| | | const kmzFile = ref<any>(null) |
| | | |
| | | // 点位参数 |
| | | const placemark: any = { |
| | | Point: { |
| | | coordinates: '', |
| | | }, |
| | | index: 0, |
| | | executeHeight: 80, |
| | | height: 80, |
| | | waypointSpeed: 10, |
| | | waypointHeadingParam: { |
| | | waypointHeadingMode: 'followWayline', |
| | | waypointHeadingAngle: 0, |
| | | waypointPoiPoint: '0.000000,0.000000,0.000000', |
| | | waypointHeadingPathMode: 'followBadArc', |
| | | waypointHeadingPoiIndex: 0, |
| | | }, |
| | | waypointTurnParam: { |
| | | waypointTurnMode: 'toPointAndStopWithDiscontinuityCurvature', |
| | | waypointTurnDampingDist: 0.2, |
| | | }, |
| | | useGlobalHeight: 1, |
| | | useGlobalSpeed: 1, |
| | | useGlobalHeadingParam: 1, |
| | | useGlobalTurnParam: 1, |
| | | useStraightLine: 1, |
| | | actionGroup: {}, |
| | | } |
| | | |
| | | const useKmzTsa = () => { |
| | | const init = async (fileUrl: string, fileContent?: string) => { |
| | | const kmzRes: string = await analyzeKmzFile(fileUrl) |
| | | const fileRes = await analyzeKmzFile(fileUrl) |
| | | kmzFile.value = fileRes.file |
| | | const kmzRes = await fileRes.content |
| | | if (fileContent) { |
| | | kmlStr.value = fileContent |
| | | return false |
| | |
| | | kmlStr.value = str |
| | | } |
| | | |
| | | const write = (settingParmas: any = {}, name?: string, isWpml?: boolean = false) => {} |
| | | |
| | | const del = () => { |
| | | // const write = (settingParmas: any = {}, name: string) => { |
| | | // } |
| | | const writePoint = (settingParmas: any = {}) => { |
| | | Object.keys(settingParmas).forEach((key: string) => { |
| | | placemark[key] = settingParmas[key] |
| | | }) |
| | | const Placemark = `<Placemark>${generateKmlFormat(placemark)}</Placemark>` |
| | | const points: RegExpMatchArray = getKmlParams(kmlStr.value, false, { |
| | | name: 'Placemark', |
| | | findRegx: '([\\s\\S]*?)', |
| | | mode: 'g', |
| | | }) || [''] |
| | | const lastPoint = points[points.length - 1] |
| | | const index = kmlStr.value.indexOf(lastPoint) |
| | | const beforeStr = kmlStr.value.slice(0, index) |
| | | const afterStr = kmlStr.value.slice(index + lastPoint.length) |
| | | kmlStr.value = beforeStr + lastPoint + Placemark + afterStr |
| | | } |
| | | |
| | | const del = () => {} |
| | | |
| | | const edit = (settingParmas: any = {}, keyName?: string, isKeyWpml: boolean = false) => { |
| | | Object.keys(settingParmas).forEach((key: string) => { |
| | |
| | | const regxVal: any = getKmlParams(kmlStr.value, true, { |
| | | name: 'height', |
| | | findRegx: '([\\s\\S]*?)', |
| | | mode: 'g' |
| | | mode: 'g', |
| | | }) |
| | | regxVal.forEach((heightStr: string) => { |
| | | const rstr = generateKmlFormat({ height: settingParmas[key] }) |
| | |
| | | } |
| | | |
| | | const save = () => { |
| | | console.log(kmlStr.value) |
| | | JsZip.file('wpmz/template.kml', kmlStr.value) |
| | | kmzFile.value.file('wpmz/template.kml', kmlStr.value) |
| | | JsZip.generateAsync({ type: 'blob' }).then((content) => { |
| | | saveAs(content, '新建航线-' + new Date().toLocaleString() + '.kmz') |
| | | }) |
| | | } |
| | | |
| | | return { |
| | | init, |
| | | create, |
| | | write, |
| | | // write, |
| | | writePoint, |
| | | del, |
| | | edit, |
| | | save, |
| | |
| | | let cartesianArr: Cesium.Cartesian3[] = [] |
| | | |
| | | // 绘制路线 |
| | | const drawWayline = async (entities: Cesium.Entity[], kmlStr: string) => { |
| | | const drawWayline = async (entities?: Cesium.Entity[], kmlStr?: string) => { |
| | | console.log(kmlStr) |
| | | if (!entities && !kmlStr) { |
| | | dataSource.show = false |
| | | } |
| | |
| | | if (kmlStr) { |
| | | kmlRes = kmlStr |
| | | } else { |
| | | kmlRes = await analyzeKmzFile(fileUrl) |
| | | const fileRes = await analyzeKmzFile(fileUrl) |
| | | kmlRes = await fileRes.content |
| | | } |
| | | // 所有航点 |
| | | kmlPoints = getKmlParams(kmlRes, false, { |
| | |
| | | if (kmlStr) { |
| | | kmlRes = kmlStr |
| | | } else { |
| | | await analyzeKmzFile(fileUrl) |
| | | const fileRes = await analyzeKmzFile(fileUrl) |
| | | kmlRes = await fileRes.content |
| | | } |
| | | // 所有航点 |
| | | const points = getKmlParams(kmlRes, false, { |