fix(line):line

This commit is contained in:
李正学 2018-11-19 12:06:42 +08:00
parent 673a6205c4
commit 6fb4a2e973
14 changed files with 143 additions and 149 deletions

View File

@ -1,8 +1,8 @@
import Material from '../../../geom/material/material' import Material from '../../../geom/material/material';
import picking_frag from './picking_frag.glsl'; import picking_frag from './picking_frag.glsl';
import picking_vert from './picking_vert.glsl'; import picking_vert from './picking_vert.glsl';
export default function PickingMaterial(options){ export default function PickingMaterial(options) {
const material = new Material({ const material = new Material({
uniforms: { uniforms: {
u_zoom: { value: options.u_zoom || 1 } u_zoom: { value: options.u_zoom || 1 }

View File

@ -6,7 +6,6 @@ import Base from './base';
import * as THREE from './three'; import * as THREE from './three';
import ColorUtil from '../attr/color-util'; import ColorUtil from '../attr/color-util';
import * as source from '../source/index'; import * as source from '../source/index';
import * as turfMeta from '@turf/meta';
import PickingMaterial from '../core/engine/picking/pickingMaterial'; import PickingMaterial from '../core/engine/picking/pickingMaterial';
import Attr from '../attr/index'; import Attr from '../attr/index';
import Util from '../util'; import Util from '../util';
@ -265,11 +264,11 @@ export default class Layer extends Base {
const { featureId } = e; const { featureId } = e;
const activeStyle = this.get('activedOptions'); const activeStyle = this.get('activedOptions');
const selectFeatureIds =this.layerSource.getSelectFeatureId(featureId); const selectFeatureIds = this.layerSource.getSelectFeatureId(featureId);
if (this.StyleData[selectFeatureIds[0]].hasOwnProperty('filter') && this.StyleData[selectFeatureIds[0]].filter === false) { return; } if (this.StyleData[selectFeatureIds[0]].hasOwnProperty('filter') && this.StyleData[selectFeatureIds[0]].filter === false) { return; }
const style = Util.assign({}, this.StyleData[featureId]); const style = Util.assign({}, this.StyleData[featureId]);
style.color = ColorUtil.toRGB(activeStyle.fill).map(e => e / 255); style.color = ColorUtil.toRGB(activeStyle.fill).map(e => e / 255);
this.updateStyle([featureId], style); this.updateStyle([ featureId ], style);
} }
@ -277,11 +276,11 @@ export default class Layer extends Base {
const attrOptions = this.get('attrOptions'); const attrOptions = this.get('attrOptions');
for (const type in attrOptions) { for (const type in attrOptions) {
if (attrOptions.hasOwnProperty(type)) { if (attrOptions.hasOwnProperty(type)) {
this._updateAttr(type) this._updateAttr(type);
} }
} }
} }
_updateAttr(type){ _updateAttr(type) {
const self = this; const self = this;
const attrs = this.get('attrs'); const attrs = this.get('attrs');
const attrOptions = this.get('attrOptions'); const attrOptions = this.get('attrOptions');
@ -423,7 +422,7 @@ export default class Layer extends Base {
* @param {*} overwrite * @param {*} overwrite
* @param {*} callback * @param {*} callback
*/ */
on(type,callback) { on(type, callback) {
this._addPickingEvents(); this._addPickingEvents();
super.on(type, callback); super.on(type, callback);
@ -437,7 +436,7 @@ export default class Layer extends Base {
removeFromPicking(object) { removeFromPicking(object) {
this.scene._engine._picking.remove(object); this.scene._engine._picking.remove(object);
} }
_addPickMesh(mesh){ _addPickMesh(mesh) {
this._pickingMesh = new THREE.Object3D(); this._pickingMesh = new THREE.Object3D();
this._visibleWithZoom(); this._visibleWithZoom();
this.scene.on('zoomchange', () => { this.scene.on('zoomchange', () => {
@ -446,9 +445,9 @@ export default class Layer extends Base {
this.addToPicking(this._pickingMesh); this.addToPicking(this._pickingMesh);
const pickmaterial = new PickingMaterial({ const pickmaterial = new PickingMaterial({
u_zoom:this.scene.getZoom() u_zoom: this.scene.getZoom()
}); });
pickmaterial.setDefinesvalue(this.type,true); pickmaterial.setDefinesvalue(this.type, true);
this._pickingMesh.onBeforeRender = () => { this._pickingMesh.onBeforeRender = () => {
const zoom = this.scene.getZoom(); const zoom = this.scene.getZoom();
this._pickingMesh.material.setUniformsValue('u_zoom', zoom); this._pickingMesh.material.setUniformsValue('u_zoom', zoom);
@ -462,19 +461,18 @@ export default class Layer extends Base {
} }
_addPickingEvents() { _addPickingEvents() {
// TODO: Find a way to properly remove this listener on destroy // TODO: Find a way to properly remove this listener on destroy
this.scene.on('pick', (e) => { this.scene.on('pick', e => {
// Re-emit click event from the layer // Re-emit click event from the layer
const { featureId, point2d, point3d, intersects } = e; const { featureId, point2d, point3d, intersects } = e;
if(intersects.length === 0) if (intersects.length === 0) { return; }
return;
const source = this.layerSource.get('data'); const source = this.layerSource.get('data');
const feature = source.features[featureId]; const feature = source.features[featureId];
const lnglat = this.scene.containerToLngLat(point2d); const lnglat = this.scene.containerToLngLat(point2d);
const target = { const target = {
feature, feature,
pixel:point2d, pixel: point2d,
lnglat:{lng:lnglat.lng,lat:lnglat.lat} lnglat: { lng: lnglat.lng, lat: lnglat.lat }
} };
this.emit('click', target); this.emit('click', target);
// this.emit('move', target); // this.emit('move', target);
}); });
@ -489,25 +487,25 @@ export default class Layer extends Base {
this.resetStyle(); this.resetStyle();
} }
this._activeIds = featureStyleId; this._activeIds = featureStyleId;
const pickingId =this.layerMesh.geometry.attributes.pickingId.array; const pickingId = this.layerMesh.geometry.attributes.pickingId.array;
const color = style.color; const color = style.color;
const colorAttr =this.layerMesh.geometry.attributes.a_color; const colorAttr = this.layerMesh.geometry.attributes.a_color;
const firstId = pickingId.indexOf(featureStyleId[0]+1); const firstId = pickingId.indexOf(featureStyleId[0] + 1);
for(let i = firstId;i<pickingId.length;i++){ for (let i = firstId; i < pickingId.length; i++) {
if(pickingId[i]==featureStyleId[0]+1){ if (pickingId[i] == featureStyleId[0] + 1) {
colorAttr.array[i*4+0]=color[0]; colorAttr.array[i * 4 + 0] = color[0];
colorAttr.array[i*4+1]=color[1]; colorAttr.array[i * 4 + 1] = color[1];
colorAttr.array[i*4+2]=color[2]; colorAttr.array[i * 4 + 2] = color[2];
colorAttr.array[i*4+3]=color[3]; colorAttr.array[i * 4 + 3] = color[3];
} else{ } else {
break; break;
} }
} }
colorAttr.needsUpdate =true colorAttr.needsUpdate = true;
return; return;
} }
_updateColor(){ _updateColor() {
this._updateMaping(); this._updateMaping();
@ -522,82 +520,82 @@ export default class Layer extends Base {
this._activeIds = null; // 清空选中元素 this._activeIds = null; // 清空选中元素
const colorAttr = this.layerMesh.geometry.attributes.a_color; const colorAttr = this.layerMesh.geometry.attributes.a_color;
const pickAttr = this.layerMesh.geometry.attributes.pickingId; const pickAttr = this.layerMesh.geometry.attributes.pickingId;
pickAttr.array.forEach((id,index)=>{ pickAttr.array.forEach((id, index) => {
id = Math.abs(id); id = Math.abs(id);
const color = [ ...this.StyleData[id-1].color ]; const color = [ ...this.StyleData[id - 1].color ];
id =Math.abs(id); id = Math.abs(id);
const item = filterData[id-1]; const item = filterData[id - 1];
if (item.hasOwnProperty('filter') && item.filter === false) { if (item.hasOwnProperty('filter') && item.filter === false) {
colorAttr.array[index*4+0]=0; colorAttr.array[index * 4 + 0] = 0;
colorAttr.array[index*4+1]=0; colorAttr.array[index * 4 + 1] = 0;
colorAttr.array[index*4+2]=0; colorAttr.array[index * 4 + 2] = 0;
colorAttr.array[index*4+3]=0; colorAttr.array[index * 4 + 3] = 0;
pickAttr.array[index] = -id; pickAttr.array[index] = -id;
} else { } else {
colorAttr.array[index*4+0]=color[0]; colorAttr.array[index * 4 + 0] = color[0];
colorAttr.array[index*4+1]=color[1]; colorAttr.array[index * 4 + 1] = color[1];
colorAttr.array[index*4+2]=color[2]; colorAttr.array[index * 4 + 2] = color[2];
colorAttr.array[index*4+3]=color[3]; colorAttr.array[index * 4 + 3] = color[3];
pickAttr.array[index] = id; pickAttr.array[index] = id;
} }
}) });
colorAttr.needsUpdate =true; colorAttr.needsUpdate = true;
pickAttr.needsUpdate =true; pickAttr.needsUpdate = true;
this._needUpdateFilter = false; this._needUpdateFilter = false;
this._needUpdateColor = false; this._needUpdateColor = false;
} }
_visibleWithZoom(){ _visibleWithZoom() {
const zoom =this.scene.getZoom(); const zoom = this.scene.getZoom();
const minZoom = this.get('minZoom'); const minZoom = this.get('minZoom');
const maxZoom = this.get('maxZoom'); const maxZoom = this.get('maxZoom');
// z-fighting // z-fighting
let offset = 0; let offset = 0;
if(this.type==='point'){ if (this.type === 'point') {
offset = 5; offset = 5;
} else if(this.type === 'polyline'){ } else if (this.type === 'polyline') {
offset = 2; offset = 2;
} }
this._object3D.position.z = offset * Math.pow(2,20-zoom); this._object3D.position.z = offset * Math.pow(2, 20 - zoom);
if(zoom<minZoom || zoom > maxZoom){ if (zoom < minZoom || zoom > maxZoom) {
this._object3D.visible =false; this._object3D.visible = false;
}else if(this.get('visible')){ } else if (this.get('visible')) {
this._object3D.visible =true; this._object3D.visible = true;
} }
} }
/** /**
* 重置高亮要素 * 重置高亮要素
*/ */
resetStyle() { resetStyle() {
const pickingId =this.layerMesh.geometry.attributes.pickingId.array; const pickingId = this.layerMesh.geometry.attributes.pickingId.array;
const colorAttr =this.layerMesh.geometry.attributes.a_color; const colorAttr = this.layerMesh.geometry.attributes.a_color;
this._activeIds.forEach((index,value) => { this._activeIds.forEach((index, value) => {
const color = this.StyleData[index].color; const color = this.StyleData[index].color;
const firstId = pickingId.indexOf(index+1); const firstId = pickingId.indexOf(index + 1);
for(let i = firstId;i<pickingId.length;i++){ for (let i = firstId; i < pickingId.length; i++) {
if(pickingId[i]==index+1){ if (pickingId[i] == index + 1) {
colorAttr.array[i*4+0]=color[0]; colorAttr.array[i * 4 + 0] = color[0];
colorAttr.array[i*4+1]=color[1]; colorAttr.array[i * 4 + 1] = color[1];
colorAttr.array[i*4+2]=color[2]; colorAttr.array[i * 4 + 2] = color[2];
colorAttr.array[i*4+3]=color[3]; colorAttr.array[i * 4 + 3] = color[3];
} }
} }
}) });
colorAttr.needsUpdate =true; colorAttr.needsUpdate = true;
} }
/** /**
* 销毁Layer对象 * 销毁Layer对象
*/ */
despose() { despose() {
this.destroy(); this.destroy();
if(this._object3D && this._object3D.children){ if (this._object3D && this._object3D.children) {
let child; let child;
for(let i =0;i<this._object3D.children.length;i++){ for (let i = 0; i < this._object3D.children.length; i++) {
child = this._object3D.children[i]; child = this._object3D.children[i];
if(!child){ if (!child) {
continue; continue;
} }
this.remove(child); this.remove(child);
if(child.geometry){ if (child.geometry) {
child.geometry.dispose(); child.geometry.dispose();
child.geometry = null; child.geometry = null;
} }
@ -612,7 +610,7 @@ export default class Layer extends Base {
} }
} }
} }
this._object3D =null; this._object3D = null;
this.scene = null; this.scene = null;
} }
} }

View File

@ -169,7 +169,7 @@ export default class BufferBase extends Base {
} }
_toPointShapeAttributes(polygon) { _toPointShapeAttributes(polygon) {
// Three components per vertex per face (3 x 3 = 9) // Three components per vertex per face (3 x 3 = 9)
const { style, indices, position, indexCount, shapes,sizes } = polygon; const { style, indices, position, indexCount, shapes, sizes } = polygon;
const vertices = new Float32Array(indexCount * 3); const vertices = new Float32Array(indexCount * 3);
const shapePositions = new Float32Array(indexCount * 3); const shapePositions = new Float32Array(indexCount * 3);
const a_size = new Float32Array(indexCount * 3); const a_size = new Float32Array(indexCount * 3);
@ -296,7 +296,7 @@ export default class BufferBase extends Base {
pickingIds, pickingIds,
shapePositions, shapePositions,
a_size, a_size,
faceUv: new Float32Array(polygon.faceUv), faceUv: new Float32Array(polygon.faceUv)
}; };

View File

@ -80,7 +80,7 @@ export default class LineBuffer extends BufferBase {
const properties = this.get('properties'); const properties = this.get('properties');
const { lineType } = this.get('style'); const { lineType } = this.get('style');
const positions = []; const positions = [];
const pickingIds =[]; const pickingIds = [];
const normal = []; const normal = [];
const miter = []; const miter = [];
const colors = []; const colors = [];

View File

@ -49,25 +49,24 @@ export default class PointBuffer extends BufferBase {
const type = this.get('type'); const type = this.get('type');
const positions = []; const positions = [];
const shapes = []; const shapes = [];
const sizes =[]; const sizes = [];
const uvs=[]; const uvs = [];
const positionsIndex = []; const positionsIndex = [];
let indexCount = 0; let indexCount = 0;
this.bufferStruct.style = properties; this.bufferStruct.style = properties;
coordinates.forEach((geo, index) => { coordinates.forEach((geo, index) => {
const m1 = new THREE.Matrix4();
let { size, shape } = properties[index]; let { size, shape } = properties[index];
let shapeType = 'extrude'; let shapeType = 'extrude';
if (type === '2d' || (type === '3d' && size[2] === 0)) { if (type === '2d' || (type === '3d' && size[2] === 0)) {
shapeType = 'fill'; shapeType = 'fill';
Util.isArray(size) || (size = [ size, size, 0 ]); Util.isArray(size) || (size = [ size, size, 0 ]);
} else{ } else {
Util.isArray(size) || (size = [ size, size, size ]); Util.isArray(size) || (size = [ size, size, size ]);
} }
if(regularShape[shape]==null) { if (regularShape[shape] == null) {
uvs.push(0,0,1,0,1,1,1,1,0,1,0,0) uvs.push(0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0);
shape='square'; shape = 'square';
} }
const vert = regularShape[shape](shapeType); const vert = regularShape[shape](shapeType);
shapes.push(vert.positions); shapes.push(vert.positions);

View File

@ -123,7 +123,7 @@ export function defaultLine(geo, index) {
export function Line(path, props, positionsIndex, dash = false) { export function Line(path, props, positionsIndex, dash = false) {
if (path.length === 1) path = path[0];// 面坐标转线坐标 if (path.length === 1) path = path[0];// 面坐标转线坐标
const positions = []; const positions = [];
const pickingIds =[]; const pickingIds = [];
const normal = []; const normal = [];
const miter = []; const miter = [];
const colors = []; const colors = [];
@ -133,7 +133,7 @@ export function Line(path, props, positionsIndex, dash = false) {
const sizes = []; const sizes = [];
let c = 0; let c = 0;
let index = positionsIndex; let index = positionsIndex;
const { size, color,id } = props; const { size, color, id } = props;
path.forEach((point, pointIndex, list) => { path.forEach((point, pointIndex, list) => {
const i = index; const i = index;
colors.push(...color); colors.push(...color);

View File

@ -5,9 +5,9 @@ import ImageBuffer from '../geom/buffer/image';
// import ImageGeometry from '../geom/bufferGeometry/image'; // import ImageGeometry from '../geom/bufferGeometry/image';
import ImageMaterial from '../geom/material/imageMaterial'; import ImageMaterial from '../geom/material/imageMaterial';
export default class imageLayer extends Layer { export default class imageLayer extends Layer {
source(data,cfg = {}) { source(data, cfg = {}) {
cfg.mapType = this.get('mapType'); cfg.mapType = this.get('mapType');
cfg.data =data; cfg.data = data;
this.layerSource = new imageSource(cfg); this.layerSource = new imageSource(cfg);
return this; return this;
} }

View File

@ -14,7 +14,7 @@ export default class LineLayer extends Layer {
const source = this.layerSource; const source = this.layerSource;
const StyleData = this.StyleData; const StyleData = this.StyleData;
const style = this.get('styleOptions'); const style = this.get('styleOptions');
const buffer =this._buffer = new LineBuffer({ const buffer = this._buffer = new LineBuffer({
coordinates: source.geoData, coordinates: source.geoData,
properties: StyleData, properties: StyleData,
shapeType: this.shapeType, shapeType: this.shapeType,

View File

@ -38,7 +38,7 @@ export default class PointLayer extends Layer {
return; return;
} }
const source = this.layerSource; const source = this.layerSource;
const { opacity, strokeWidth, stroke ,shape} = this.get('styleOptions'); const { opacity, strokeWidth, stroke, shape } = this.get('styleOptions');
this._buffer = new PointBuffer({ this._buffer = new PointBuffer({
type: this.shapeType, type: this.shapeType,
imagePos: this.scene.image.imagePos, imagePos: this.scene.image.imagePos,
@ -53,16 +53,15 @@ export default class PointLayer extends Layer {
u_zoom: this.scene.getZoom() u_zoom: this.scene.getZoom()
}); });
mtl.setDefinesvalue('SHAPE', true); mtl.setDefinesvalue('SHAPE', true);
if(shape=='radar') { if (shape === 'radar') {
mtl.fragmentShader = radar; mtl.fragmentShader = radar;
} }
if(shape=='warn') { if (shape === 'warn') {
mtl.fragmentShader = warn; mtl.fragmentShader = warn;
} }
} else { // sdf 绘制点 } else { // sdf 绘制点
mtl = new PointMaterial({ mtl = new PointMaterial({
u_opacity: opacity, u_opacity: opacity,
@ -83,15 +82,13 @@ export default class PointLayer extends Layer {
if (this.shapeType === 'image') { if (this.shapeType === 'image') {
geometry.addAttribute('uv', new THREE.Float32BufferAttribute(attributes.uvs, 2)); geometry.addAttribute('uv', new THREE.Float32BufferAttribute(attributes.uvs, 2));
geometry.addAttribute('a_size', new THREE.Float32BufferAttribute(attributes.sizes, 1)); geometry.addAttribute('a_size', new THREE.Float32BufferAttribute(attributes.sizes, 1));
} else if(this.shapeType=== undefined) } else if (this.shapeType === undefined) {
{
geometry.addAttribute('a_size', new THREE.Float32BufferAttribute(attributes.sizes, 1)); geometry.addAttribute('a_size', new THREE.Float32BufferAttribute(attributes.sizes, 1));
} } else { // 多边形面
else { // 多边形面
geometry.addAttribute('normal', new THREE.Float32BufferAttribute(attributes.normals, 3)); geometry.addAttribute('normal', new THREE.Float32BufferAttribute(attributes.normals, 3));
geometry.addAttribute('a_shape', new THREE.Float32BufferAttribute(attributes.shapePositions, 3)); geometry.addAttribute('a_shape', new THREE.Float32BufferAttribute(attributes.shapePositions, 3));
geometry.addAttribute('a_size', new THREE.Float32BufferAttribute(attributes.a_size, 3)); geometry.addAttribute('a_size', new THREE.Float32BufferAttribute(attributes.a_size, 3));
if(shape) { if (shape) {
geometry.addAttribute('faceUv', new THREE.Float32BufferAttribute(attributes.faceUv, 2)); geometry.addAttribute('faceUv', new THREE.Float32BufferAttribute(attributes.faceUv, 2));
} }
} }

View File

@ -10,23 +10,22 @@ export default class CSVSource extends Source {
const y = this.get('y'); const y = this.get('y');
const x1 = this.get('x1'); const x1 = this.get('x1');
const y1 = this.get('y1'); const y1 = this.get('y1');
const coords = this.get('coordinates') const coords = this.get('coordinates');
this.propertiesData = [];// 临时使用 this.propertiesData = [];// 临时使用
this.geoData = []; this.geoData = [];
let csvdata = data; let csvdata = data;
Util.isArray(csvdata) || (csvdata = csvParse(data)); Util.isArray(csvdata) || (csvdata = csvParse(data));
this.propertiesData = csvdata; this.propertiesData = csvdata;
csvdata.forEach((col, featureIndex) => { csvdata.forEach((col, featureIndex) => {
let coordinates = [];
if(col['coordinates']){ if (col.coordinates) {
coordinates = col['coordinates']; coordinates = col.coordinates;
} }
let coordinates = [ col[x], col[y] ]; let coordinates = [ col[x], col[y] ];
if (x1 && y1) { if (x1 && y1) {
coordinates = [[ col[x], col[y] ], [ col[x1], col[y1] ]]; coordinates = [[ col[x], col[y] ], [ col[x1], col[y1] ]];
} }
if(coords&& col['coords']) if (coords && col.coords) { coordinates = col.coords; }
coordinates =col['coords'];
col._id = featureIndex + 1; col._id = featureIndex + 1;
this._coordProject(coordinates); this._coordProject(coordinates);
this.geoData.push(this._coordProject(coordinates)); this.geoData.push(this._coordProject(coordinates));
@ -37,8 +36,8 @@ export default class CSVSource extends Source {
const data = this.get('data'); const data = this.get('data');
this.featureIndex = new FeatureIndex(data); this.featureIndex = new FeatureIndex(data);
} }
getSelectFeatureId(featureId){ getSelectFeatureId(featureId) {
return [featureId]; return [ featureId ];
} }
_getCoord(geo) { _getCoord(geo) {
if (geo.geometry) { if (geo.geometry) {

View File

@ -6,7 +6,7 @@ import FeatureIndex from '../geo/featureIndex';
export default class GeojsonSource extends Source { export default class GeojsonSource extends Source {
prepareData() { prepareData() {
this.type ='geojson'; this.type = 'geojson';
const data = this.get('data'); const data = this.get('data');
this.propertiesData = []; this.propertiesData = [];
this.geoData = []; this.geoData = [];
@ -21,10 +21,11 @@ export default class GeojsonSource extends Source {
const data = this.get('data'); const data = this.get('data');
this.featureIndex = new FeatureIndex(data); this.featureIndex = new FeatureIndex(data);
} }
getSelectFeatureId(featureId){ getSelectFeatureId(featureId) {
const data = this.get('data'); const data = this.get('data');
const selectFeatureIds =[]; const selectFeatureIds = [];
let featureStyleId = 0; let featureStyleId = 0;
/* eslint-disable */
turfMeta.flattenEach(data, (currentFeature, featureIndex, multiFeatureIndex) => { turfMeta.flattenEach(data, (currentFeature, featureIndex, multiFeatureIndex) => {
/* eslint-disable */ /* eslint-disable */
if (featureIndex === (featureId)) { if (featureIndex === (featureId)) {

View File

@ -2,7 +2,7 @@ import Source from '../core/source';
import { getImage } from '../util/ajax'; import { getImage } from '../util/ajax';
export default class ImageSource extends Source { export default class ImageSource extends Source {
prepareData() { prepareData() {
this.type='image'; this.type = 'image';
const extent = this.get('extent'); const extent = this.get('extent');
const lb = this._coorConvert(extent.slice(0, 2)); const lb = this._coorConvert(extent.slice(0, 2));
const tr = this._coorConvert(extent.slice(2, 4)); const tr = this._coorConvert(extent.slice(2, 4));

View File

@ -1,7 +1,7 @@
import Source from '../core/source'; import Source from '../core/source';
export default class RasterSource extends Source { export default class RasterSource extends Source {
prepareData() { prepareData() {
this.type='raster'; this.type = 'raster';
const extent = this.get('extent'); const extent = this.get('extent');
const lb = this._coorConvert(extent.slice(0, 2)); const lb = this._coorConvert(extent.slice(0, 2));
const tr = this._coorConvert(extent.slice(2, 4)); const tr = this._coorConvert(extent.slice(2, 4));