Merge pull request #398 from antvis/fix_draw_line

Fix draw line
This commit is contained in:
@thinkinggis 2020-06-10 16:15:45 +08:00 committed by GitHub
commit b55052afa1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 304 additions and 140 deletions

View File

@ -181,17 +181,17 @@ const dataLevel1: { [key: string]: any } = {
line: {
type: 'pbf',
url:
'//gw.alipayobjects.com/os/bmw-prod/561e2cfe-9460-42d1-a2f8-3fd2e1274c52.bin',
'https://gw.alipayobjects.com/os/bmw-prod/8bfbfe7e-bd0e-4bbe-84d8-629f4dc7abc4.bin',
},
cityLine: {
type: 'pbf',
url:
'//gw.alipayobjects.com/os/bmw-prod/561e2cfe-9460-42d1-a2f8-3fd2e1274c52.bin',
'https://gw.alipayobjects.com/os/bmw-prod/8bfbfe7e-bd0e-4bbe-84d8-629f4dc7abc4.bin',
},
provinceLine: {
type: 'pbf',
url:
'//gw.alipayobjects.com/os/bmw-prod/778ad7ba-5a3f-4ed6-a94a-b8ab8acae9d6.bin',
'https://gw.alipayobjects.com/os/bmw-prod/778ad7ba-5a3f-4ed6-a94a-b8ab8acae9d6.bin',
},
},
3: {

View File

@ -52,12 +52,23 @@ export default class CountryLayer extends BaseLayer {
const lineData = await this.fetchData(cfg);
const border1 = lineData.features.filter((feature: any) => {
const type = feature.properties.type;
return type === '1' || type === '4';
return type === '1';
});
// 香港 澳门
const border2 = lineData.features.filter((feature: any) => {
const type = feature.properties.type;
return type === '4';
});
const borderFc = {
type: 'FeatureCollection',
features: border1,
};
const borderFc2 = {
type: 'FeatureCollection',
features: border2,
};
const nationalBorder = lineData.features.filter((feature: any) => {
const type = feature.properties.type;
return type !== '1' && type !== '4';
@ -66,29 +77,29 @@ export default class CountryLayer extends BaseLayer {
type: 'FeatureCollection',
features: nationalBorder,
};
this.addNationBorder(nationalFc, borderFc);
this.addNationBorder(nationalFc, borderFc, borderFc2);
}
// 国界,省界
protected addFillLine(lineData: any) {
const border1 = lineData.features.filter((feature: any) => {
const type = feature.properties.type;
return type === '1' || type === '4';
});
const borderFc = {
type: 'FeatureCollection',
features: border1,
};
const nationalBorder = lineData.features.filter((feature: any) => {
const type = feature.properties.type;
return type !== '1' && type !== '4';
});
const nationalFc = {
type: 'FeatureCollection',
features: nationalBorder,
};
this.addNationBorder(nationalFc, borderFc);
}
// protected addFillLine(lineData: any) {
// const border1 = lineData.features.filter((feature: any) => {
// const type = feature.properties.type;
// return type === '1' || type === '4';
// });
// const borderFc = {
// type: 'FeatureCollection',
// features: border1,
// };
// const nationalBorder = lineData.features.filter((feature: any) => {
// const type = feature.properties.type;
// return type !== '1' && type !== '4';
// });
// const nationalFc = {
// type: 'FeatureCollection',
// features: nationalBorder,
// };
// this.addNationBorder(nationalFc, borderFc);
// }
private async loadData() {
const { depth } = this.options;
@ -102,7 +113,11 @@ export default class CountryLayer extends BaseLayer {
return [fillData, fillLabel];
}
// 省级行政区划
private async addNationBorder(boundaries: any, boundaries2: any) {
private async addNationBorder(
boundaries: any,
boundaries2: any,
boundaries3: any,
) {
const {
nationalStroke,
provinceStroke,
@ -149,17 +164,31 @@ export default class CountryLayer extends BaseLayer {
zIndex: zIndex + 0.1,
})
.source(boundaries2)
.size(nationalWidth)
.size(chinaNationalWidth)
.shape('line')
.color('gray')
.color(chinaNationalStroke)
.style({
lineType: 'dash',
dashArray: [2, 2],
});
// 添加澳门香港界限
const lineLayer3 = new LineLayer({
zIndex: zIndex + 0.1,
})
.source(boundaries3)
.size(provinceStrokeWidth)
.shape('line')
.color(provinceStroke)
.style({
lineType: 'dash',
dashArray: [4, 2, 2, 2],
});
this.scene.addLayer(lineLayer);
this.scene.addLayer(lineLayer2);
this.layers.push(lineLayer, lineLayer2);
this.scene.addLayer(lineLayer3);
this.layers.push(lineLayer, lineLayer2, lineLayer3);
}
// 市边界
private async addCityBorder(cfg: any) {

View File

@ -208,11 +208,10 @@ export default class Popup extends EventEmitter implements IPopup {
.split(' ')
.forEach((name) => this.container.classList.add(name));
}
this.container.addEventListener('mousedown', (e) => {
e.stopPropagation();
});
this.container.addEventListener('click', (e) => {
e.stopPropagation();
['mousemove', 'mousedown', 'mouseup', 'click'].forEach((type) => {
this.container.addEventListener(type, (e) => {
e.stopPropagation();
});
});
}
if (maxWidth && this.container.style.maxWidth !== maxWidth) {

View File

@ -58,6 +58,10 @@ export default abstract class DrawFeature extends DrawMode {
this.on(DrawEvent.CREATE, this.onDrawCreate);
this.on(DrawEvent.MODE_CHANGE, this.onModeChange);
document.addEventListener('keydown', this.addKeyDownEvent);
if (this.options.data && this.initData()) {
this.normalLayer.update(this.source.data);
this.normalLayer.enableSelect();
}
}
public abstract drawFinish(): void;
public setCurrentFeature(feature: Feature) {
@ -143,6 +147,9 @@ export default abstract class DrawFeature extends DrawMode {
protected abstract hideOtherLayer(): void;
protected abstract showOtherLayer(): void;
protected initData(): boolean {
return false;
}
private addDrawPopup(lnglat: ILngLat, dis: number) {
const popup = new Popup({

View File

@ -30,16 +30,39 @@ export default class DrawLine extends DrawPolygon {
this.pointFeatures = newPointFeture;
return this.currentFeature;
}
protected createFeature(points: ILngLat[]): Feature {
const pointfeatures = createPoint(this.points);
protected createFeature(
points: ILngLat[],
id?: string,
active: boolean = true,
): Feature {
const pointfeatures = createPoint(points);
this.pointFeatures = pointfeatures.features;
const feature = createLine(points, {
id: this.getUniqId(),
id: id || this.getUniqId(),
type: 'line',
active: true,
active,
pointFeatures: this.pointFeatures,
});
this.setCurrentFeature(feature as Feature);
return feature;
}
protected initData(): boolean {
const features: Feature[] = [];
this.source.data.features.forEach((feature) => {
if (feature.geometry.type === 'LineString') {
// @ts-ignore
const points = feature.geometry.coordinates.map((coord) => {
return {
lng: coord[0],
lat: coord[1],
};
});
features.push(
this.createFeature(points, feature?.properties?.id, false),
);
}
});
this.source.data.features = features;
return true;
}
}

View File

@ -57,15 +57,34 @@ export default class DrawPoint extends DrawFeature {
};
return this.currentFeature;
}
protected createFeature(p: ILngLat): Feature {
protected createFeature(
p: ILngLat,
id?: string,
active: boolean = true,
): Feature {
const feature = point([p.lng, p.lat], {
id: this.getUniqId(),
id: id || this.getUniqId(),
type: 'point',
active,
pointFeatures: [point([p.lng, p.lat])],
});
this.setCurrentFeature(feature as Feature);
return feature;
}
protected initData(): boolean {
const features: Feature[] = [];
this.source.data.features.forEach((feature) => {
if (feature.geometry.type === 'Point') {
const p = {
lng: feature.geometry.coordinates[0] as number,
lat: feature.geometry.coordinates[1] as number,
};
features.push(this.createFeature(p, feature?.properties?.id, false));
}
});
this.source.data.features = features;
return true;
}
protected editFeature(endPoint: ILngLat): void {
this.createFeature(endPoint);

View File

@ -29,11 +29,6 @@ export default class DrawPolygon extends DrawFeature {
this.type = 'polygon';
this.drawMidVertexLayer = new DrawMidVertex(this);
this.on(DrawEvent.MODE_CHANGE, this.addMidLayerEvent);
if (this.options.data) {
this.initData();
this.normalLayer.update(this.source.data);
this.normalLayer.enableSelect();
}
}
public enable() {
super.enable();
@ -240,6 +235,26 @@ export default class DrawPolygon extends DrawFeature {
break;
}
}
protected initData(): boolean {
const features: Feature[] = [];
this.source.data.features.forEach((feature) => {
if (feature.geometry.type === 'Polygon') {
const points = (feature.geometry.coordinates[0] as Position[]).map(
(coord) => {
return {
lng: coord[0],
lat: coord[1],
};
},
);
features.push(
this.createFeature(points.slice(1), feature?.properties?.id, false),
);
}
});
this.source.data.features = features;
return true;
}
private editPolygonVertex(id: number, vertex: ILngLat) {
const feature = this.currentFeature as Feature<Geometries, Properties>;
@ -259,26 +274,6 @@ export default class DrawPolygon extends DrawFeature {
featureCollection([this.currentFeature as Feature]),
);
}
private initData() {
const features: Feature[] = [];
this.source.data.features.forEach((feature) => {
if (feature.geometry.type === 'Polygon') {
const points = (feature.geometry.coordinates[0] as Position[]).map(
(coord) => {
return {
lng: coord[0],
lat: coord[1],
};
},
);
features.push(
this.createFeature(points.slice(1), feature?.properties?.id, false),
);
}
});
this.source.data.features = features;
}
}
/**
* draw

View File

@ -24,6 +24,9 @@ export default class ArcModel extends BaseModel {
lineType = 'solid',
dashArray = [10, 5],
} = this.layer.getLayerConfig() as ILineLayerStyleOptions;
if (dashArray.length === 2) {
dashArray.push(0, 0);
}
return {
u_opacity: opacity || 1,
segmentNumber: 30,

View File

@ -23,6 +23,9 @@ export default class Arc3DModel extends BaseModel {
lineType = 'solid',
dashArray = [10, 5],
} = this.layer.getLayerConfig() as ILineLayerStyleOptions;
if (dashArray.length === 2) {
dashArray.push(0, 0);
}
return {
u_opacity: opacity || 1,
segmentNumber: 30,

View File

@ -25,6 +25,9 @@ export default class GreatCircleModel extends BaseModel {
lineType = 'solid',
dashArray = [10, 5],
} = this.layer.getLayerConfig() as Partial<ILineLayerStyleOptions>;
if (dashArray.length === 2) {
dashArray.push(0, 0);
}
return {
u_opacity: opacity || 1,
segmentNumber: 30,

View File

@ -22,8 +22,11 @@ export default class LineModel extends BaseModel {
const {
opacity,
lineType = 'solid',
dashArray = [10, 5],
dashArray = [10, 5, 0, 0],
} = this.layer.getLayerConfig() as ILineLayerStyleOptions;
if (dashArray.length === 2) {
dashArray.push(0, 0);
}
return {
u_opacity: opacity || 1.0,
u_line_type: lineStyleObj[lineType],

View File

@ -13,8 +13,8 @@ varying vec2 v_normal;
varying float v_distance_ratio;
uniform float u_line_type: 0.0;
uniform vec2 u_dash_array: [10.0, 5.];
varying vec2 v_dash_array;
uniform vec4 u_dash_array: [10.0, 5., 0, 0];
varying vec4 v_dash_array;
#pragma include "projection"
#pragma include "project"

View File

@ -13,9 +13,8 @@ varying vec4 v_color;
varying vec2 v_normal;
varying float v_distance_ratio;
uniform float u_line_type: 0.0;
uniform vec2 u_dash_array: [10.0, 5.];
varying vec2 v_dash_array;
uniform vec4 u_dash_array: [10.0, 5., 0, 0];
varying vec4 v_dash_array;
#pragma include "projection"
#pragma include "project"

View File

@ -6,7 +6,7 @@ uniform float u_opacity;
uniform float u_blur : 0.9;
uniform float u_line_type: 0.0;
varying vec2 v_normal;
varying vec2 v_dash_array;
varying vec4 v_dash_array;
varying float v_distance_ratio;
varying vec4 v_color;
@ -21,7 +21,12 @@ void main() {
// float blur = smoothstep(1.0, u_blur, length(v_normal.xy));
gl_FragColor.a *= u_opacity;
if(u_line_type == LineTypeDash) {
gl_FragColor.a *= (1.0- step(v_dash_array.x, mod(v_distance_ratio, v_dash_array.x +v_dash_array.y)));
float flag = 0.;
float dashLength = mod(v_distance_ratio, v_dash_array.x + v_dash_array.y + v_dash_array.z + v_dash_array.w);
if(dashLength < v_dash_array.x || (dashLength > (v_dash_array.x + v_dash_array.y) && dashLength < v_dash_array.x + v_dash_array.y + v_dash_array.z)) {
flag = 1.;
}
gl_FragColor.a *=flag;
}
if(u_aimate.x == Animate) {

View File

@ -13,8 +13,8 @@ varying vec2 v_normal;
varying float v_distance_ratio;
uniform float u_line_type: 0.0;
uniform vec2 u_dash_array: [10.0, 5.];
varying vec2 v_dash_array;
uniform vec4 u_dash_array: [10.0, 5., 0, 0];
varying vec4 v_dash_array;
#pragma include "projection"
#pragma include "project"

View File

@ -13,8 +13,8 @@ varying vec2 v_normal;
varying float v_distance_ratio;
uniform float u_line_type: 0.0;
uniform vec2 u_dash_array: [10.0, 5.];
varying vec2 v_dash_array;
uniform vec4 u_dash_array: [10.0, 5., 0, 0];
varying vec4 v_dash_array;
#pragma include "projection"
#pragma include "project"
#pragma include "picking"

View File

@ -13,8 +13,8 @@ varying vec2 v_normal;
varying float v_distance_ratio;
uniform float u_line_type: 0.0;
uniform vec2 u_dash_array: [10.0, 5.];
varying vec2 v_dash_array;
uniform vec4 u_dash_array: [10.0, 5., 0, 0];
varying vec4 v_dash_array;
#pragma include "projection"
#pragma include "project"
#pragma include "picking"

View File

@ -12,7 +12,7 @@ varying vec2 v_normal;
uniform float u_dash_offset : 0.0;
uniform float u_dash_ratio : 0.1;
varying float v_distance_ratio;
varying vec2 v_dash_array;
varying vec4 v_dash_array;
varying float v_side;
@ -35,7 +35,13 @@ void main() {
}
// dash line
if(u_line_type == LineTypeDash) {
gl_FragColor.a *=(1.0- step(v_dash_array.x, mod(v_distance_ratio, v_dash_array.x +v_dash_array.y)));
float flag = 0.;
float dashLength = mod(v_distance_ratio, v_dash_array.x + v_dash_array.y + v_dash_array.z + v_dash_array.w);
if(dashLength < v_dash_array.x || (dashLength > (v_dash_array.x + v_dash_array.y) && dashLength < v_dash_array.x + v_dash_array.y + v_dash_array.z)) {
flag = 1.;
}
gl_FragColor.a *=flag;
// gl_FragColor.a *=(1.0- step(v_dash_array.x, mod(v_distance_ratio, dashLength)));
}
gl_FragColor = filterColor(gl_FragColor);

View File

@ -14,14 +14,14 @@ attribute float a_Distance;
uniform mat4 u_ModelMatrix;
uniform float u_line_type: 0.0;
uniform vec2 u_dash_array: [10.0, 5.];
uniform vec4 u_dash_array: [10.0, 5.0, 0];
uniform vec4 u_aimate: [ 0, 2., 1.0, 0.2 ];
#pragma include "projection"
#pragma include "picking"
varying vec4 v_color;
varying vec2 v_dash_array;
varying vec4 v_dash_array;
varying vec2 v_normal;
varying float v_distance_ratio;
varying float v_side;

View File

@ -27,6 +27,7 @@ import mapboxgl, { IControl, Map } from 'mapbox-gl';
import 'mapbox-gl/dist/mapbox-gl.css';
import { IMapboxInstance } from '../../typings/index';
import Viewport from './Viewport';
window.mapboxgl = mapboxgl;
const EventMap: {
[key: string]: any;
} = {
@ -239,10 +240,11 @@ export default class MapboxService
lnglat: [number, number],
altitude: number,
): IMercator {
const { x = 0, y = 0, z = 0 } = mapboxgl.MercatorCoordinate.fromLngLat(
lnglat,
altitude,
);
const {
x = 0,
y = 0,
z = 0,
} = window.mapboxgl.MercatorCoordinate.fromLngLat(lnglat, altitude);
return { x, y, z };
}
public getModelMatrix(
@ -252,7 +254,7 @@ export default class MapboxService
scale: [number, number, number] = [1, 1, 1],
origin: IMercator = { x: 0, y: 0, z: 0 },
): number[] {
const modelAsMercatorCoordinate = mapboxgl.MercatorCoordinate.fromLngLat(
const modelAsMercatorCoordinate = window.mapboxgl.MercatorCoordinate.fromLngLat(
lnglat,
altitude,
);
@ -302,7 +304,7 @@ export default class MapboxService
*/
// 判断全局 mapboxgl 对象的加载
if (!mapInstance && !mapboxgl) {
if (!mapInstance && !window.mapboxgl) {
// 用户有时传递进来的实例是继承于 mapbox 实例化的,不一定是 mapboxgl 对象。
this.logger.error(this.configService.getSceneWarninfo('SDK'));
}
@ -310,16 +312,16 @@ export default class MapboxService
if (
token === MAPBOX_API_KEY &&
style !== 'blank' &&
!mapboxgl.accessToken &&
!window.mapboxgl.accessToken &&
!mapInstance // 如果用户传递了 mapInstance应该不去干预实例的 accessToken。
) {
this.logger.warn(this.configService.getSceneWarninfo('MapToken'));
}
// 判断是否设置了 accessToken
if (!mapInstance && !mapboxgl.accessToken) {
if (!mapInstance && !window.mapboxgl.accessToken) {
// 用户有时传递进来的实例是继承于 mapbox 实例化的,不一定是 mapboxgl 对象。
mapboxgl.accessToken = token;
window.mapboxgl.accessToken = token;
}
if (mapInstance) {
@ -329,7 +331,7 @@ export default class MapboxService
} else {
this.$mapContainer = this.creatAmapContainer(id);
// @ts-ignore
this.map = new mapboxgl.Map({
this.map = new window.mapboxgl.Map({
container: this.$mapContainer,
style: this.getMapStyle(style),
attributionControl,

View File

@ -26,21 +26,16 @@ export default class Country extends React.Component {
scene.on('loaded', () => {
const Layer = new CountryLayer(scene, {
data: [],
geoDataLevel: 2,
depth: 2,
stroke: '#fff',
provinceStroke: '#783D2D',
cityStroke: '#EBCCB4',
coastlineWidth: 0.5,
nationalWidth: 0.5,
fill: {
color: {
field: 'NAME_CHN',
values: [
'#feedde',
'#fdd0a2',
'#fdae6b',
'#fd8d3c',
'#e6550d',
'#a63603',
],
values: ['#D92568', '#E3507E', '#FC7AAB', '#F1D3E5', '#F2EEFF'],
},
},
popup: {

View File

@ -16,36 +16,37 @@ export default class Circle extends React.Component {
map: new Mapbox({
pitch: 0,
style: 'light',
center: [113.775374, 28.31067],
zoom: 12,
center: [79.8046875, 52.482780222078226],
zoom: 4,
}),
});
this.scene = scene;
const linneData = {
type: 'FeatureCollection',
features: [
{
type: 'Feature',
properties: {},
geometry: {
type: 'LineString',
coordinates: [
[79.8046875, 52.482780222078226],
[110.74218749999999, 36.87962060502676],
[111.4453125, 19.973348786110602],
[112.8515625, 9.795677582829743],
[95.2734375, -6.664607562172573],
[82.265625, -14.264383087562637],
[74.53125, -25.799891182088306],
[68.203125, -30.145127183376115],
[41.484375, -16.63619187839765],
],
},
},
],
};
const line = scene.on('loaded', () => {
const drawLine = new DrawLine(scene);
const drawLine = new DrawLine(scene, {
data: {
type: 'FeatureCollection',
features: [
{
type: 'Feature',
properties: {},
geometry: {
type: 'LineString',
coordinates: [
[79.8046875, 52.482780222078226],
[110.74218749999999, 36.87962060502676],
[111.4453125, 19.973348786110602],
[112.8515625, 9.795677582829743],
[95.2734375, -6.664607562172573],
[82.265625, -14.264383087562637],
[74.53125, -25.799891182088306],
[68.203125, -30.145127183376115],
[41.484375, -16.63619187839765],
],
},
},
],
},
});
drawLine.enable();
});
}

View File

@ -23,7 +23,37 @@ export default class Circle extends React.Component {
this.scene = scene;
scene.on('loaded', () => {
const drawPoint = new DrawPoint(scene);
const drawPoint = new DrawPoint(scene, {
data: {
type: 'FeatureCollection',
features: [
{
type: 'Feature',
properties: {},
geometry: {
type: 'Point',
coordinates: [88.9453125, 53.330872983017066],
},
},
{
type: 'Feature',
properties: {},
geometry: {
type: 'Point',
coordinates: [109.3359375, 28.613459424004414],
},
},
{
type: 'Feature',
properties: {},
geometry: {
type: 'Point',
coordinates: [97.734375, 35.460669951495305],
},
},
],
},
});
drawPoint.enable();
});
}

View File

@ -40,7 +40,7 @@ export default class Polygon3D extends React.Component {
const layer = new PolygonLayer({})
.source(await response.json())
.shape('extrude')
.size('h20', [100, 120, 160, 200, 260, 500])
.size('h20', [10, 12, 16, 20, 26, 50])
.active({ color: 'blue' })
.color('h20', [
'#816CAD',

View File

@ -1,5 +1,5 @@
import { LineLayer, Scene } from '@antv/l7';
import { Mapbox, GaodeMap } from '@antv/l7-maps';
import { GaodeMap, Mapbox } from '@antv/l7-maps';
import * as React from 'react';
export default class DashLineDemo extends React.Component {
@ -46,7 +46,8 @@ export default class DashLineDemo extends React.Component {
].reverse(),
)
.style({
// lineType: 'dash',
lineType: 'dash',
dashArray: [4, 2, 2, 2],
opacity: 1.0,
});

View File

@ -114,7 +114,7 @@ export default class TextLayerDemo extends React.Component {
const scene = new Scene({
id: 'map',
map: new Mapbox({
map: new GaodeMap({
center: [120.19382669582967, 30.258134],
pitch: 0,
style: 'blank',
@ -127,10 +127,11 @@ export default class TextLayerDemo extends React.Component {
type: 'FeatureCollection',
features: [],
})
.shape('fill')
.shape('extrude')
.scale('childrenNum', {
type: 'quantile',
})
.size('childrenNum', [10, 100000000])
// .color('red')
.color('childrenNum', [
'#D92568',

View File

@ -1,5 +1,5 @@
import { LineLayer, PolygonLayer, Scene } from '@antv/l7';
import { GaodeMap } from '@antv/l7-maps';
import { GaodeMap, Mapbox } from '@antv/l7-maps';
import * as React from 'react';
function convertRGB2Hex(rgb: number[]) {
@ -27,17 +27,57 @@ export default class MultiPolygon extends React.Component {
map: new GaodeMap({
pitch: 0,
style: 'dark',
center: [121.775374, 31.31067],
zoom: 5,
center: [118.70796203613281, 31.84956532831343],
zoom: 12,
}),
});
const data = await response.json();
// console.log(data.features[5]);
// data.features = data.features.slice(6);
const layer = new LineLayer()
.source(data)
.shape('line')
.size(1)
const layer = new PolygonLayer()
.source({
type: 'FeatureCollection',
features: [
{
type: 'Feature',
properties: {},
geometry: {
type: 'Polygon',
coordinates: [
[
[118.70796203613281, 31.84956532831343],
[118.67019653320312, 31.783049527817784],
[118.70384216308594, 31.757947795369688],
[118.7944793701172, 31.79647323968844],
[118.78829956054686, 31.85073184447357],
[118.70796203613281, 31.84956532831343],
],
],
},
},
{
type: 'Feature',
properties: {},
geometry: {
type: 'Polygon',
coordinates: [
[
[116.96044921875, 29.38217507514529],
[114.41162109375, 30.315987718557867],
[114.78515624999999, 28.43971381702788],
[114.93896484374999, 27.0982539061379],
[116.98242187499999, 27.01998400798257],
[119.20166015625, 28.091366281406945],
[119.17968749999999, 29.38217507514529],
[116.96044921875, 29.38217507514529],
],
],
},
},
],
})
.shape('extrude')
.size(10)
.color('red')
.style({
opacity: 1.0,