diff --git a/demos/01_point_circle.html b/demos/01_point_circle.html index bb80671de8..7121179c29 100644 --- a/demos/01_point_circle.html +++ b/demos/01_point_circle.html @@ -10,6 +10,9 @@ point_circle @@ -55,15 +58,15 @@ scene.on('loaded', () => { .shape('2d:circle') .size('value', [ 2, 80]) // default 1 //.size('value', [ 10, 300]) // default 1 - .active(false) + .active(true) .filter('value', field_8 => { return field_8 * 1 > 500; }) - .color('type', colorObj.red) + .color('type', colorObj.blue) .style({ stroke: 'rgb(255,255,255)', strokeWidth: 1, - opacity: 0.9 + opacity: 1. }) .render(); diff --git a/demos/01_point_column.html b/demos/01_point_column.html index 8e1abda83c..fd7d33ae02 100644 --- a/demos/01_point_column.html +++ b/demos/01_point_column.html @@ -34,9 +34,11 @@ scene.on('loaded', () => { zIndex: 2 }) .source(data.list, { - type: 'array', - x: 'j', - y: 'w', + parser:{ + type: 'json', + x: 'j', + y: 'w', + } }) .shape('cylinder') .size('t',(level)=> { diff --git a/demos/01_point_distribute.html b/demos/01_point_distribute.html index 18a714ab75..a3d1bd8f73 100644 --- a/demos/01_point_distribute.html +++ b/demos/01_point_distribute.html @@ -34,9 +34,11 @@ scene.on('loaded', () => { zIndex: 2 }) .source(data, { - type: 'csv', - y: 'lat', - x: 'lng' + parser:{ + type: 'csv', + y: 'lat', + x: 'lng' + } }) .size(1.0) .color('#0D408C') diff --git a/demos/02_point_circle.html b/demos/02_point_circle.html new file mode 100644 index 0000000000..cc95ad57d6 --- /dev/null +++ b/demos/02_point_circle.html @@ -0,0 +1,77 @@ + + + + + + + + + + point_circle + + + + +
+ + + + + + + + diff --git a/demos/04_image.html b/demos/04_image.html index 3f0773c96e..5acb717137 100644 --- a/demos/04_image.html +++ b/demos/04_image.html @@ -33,8 +33,11 @@ const scene = new L7.Scene({ scene.on('loaded', () => { const imageLayer = scene.ImageLayer(). source('https://gw.alipayobjects.com/zos/rmsportal/FnHFeFklTzKDdUESRNDv.jpg',{ - - extent: [ 121.1680, 30.2828, 121.3840, 30.4219 ] + parser:{ + type:'image', + extent: [ 121.1680, 30.2828, 121.3840, 30.4219 ] + } + }) .style({ opacity:1.0, diff --git a/demos/05_raster_dem.html b/demos/05_raster_dem.html index 0589eed6b4..fb560be45b 100644 --- a/demos/05_raster_dem.html +++ b/demos/05_raster_dem.html @@ -48,13 +48,14 @@ scene.on('loaded', () => { const layer = scene.RasterLayer({ zIndex: 2 }). source(values, { - type: 'raster', - width: n, - height: m, - min: 0, - max: 8000, - extent: [ 73.482190241, 3.82501784112, 135.106618732, 57.6300459963 ] - + parser: { + type: 'raster', + width: n, + height: m, + min: 0, + max: 8000, + extent: [ 73.482190241, 3.82501784112, 135.106618732, 57.6300459963 ] + } }) .style({ rampColors: { diff --git a/demos/grid.html b/demos/grid.html new file mode 100644 index 0000000000..94c2000bb9 --- /dev/null +++ b/demos/grid.html @@ -0,0 +1,74 @@ + + + + + + + + + point_circle + + + + +
+ + + + + + + + diff --git a/package.json b/package.json index cbaf70578a..3ee6803cde 100755 --- a/package.json +++ b/package.json @@ -107,6 +107,7 @@ "lodash": "^4.17.5", "polyline-normals": "^2.0.2", "rbush": "^2.0.2", + "simple-statistics": "^7.0.1", "three": "^0.96.0", "venn.js": "^0.2.20", "viewport-mercator-project": "^5.2.0", diff --git a/src/core/engine/picking/picking.js b/src/core/engine/picking/picking.js index e4fb4c2ee1..e70912ac16 100755 --- a/src/core/engine/picking/picking.js +++ b/src/core/engine/picking/picking.js @@ -42,7 +42,7 @@ class Picking { // this._world._container.addEventListener('mousemove', this._onWorldMove.bind(this), false); } pickdata(event) { - const point = { x: event.clientX, y: event.clientY, type: event.type }; + const point = { x: event.offsetX, y: event.offsetY, type: event.type }; const normalisedPoint = { x: 0, y: 0 }; normalisedPoint.x = (point.x / this._width) * 2 - 1; normalisedPoint.y = -(point.y / this._height) * 2 + 1; @@ -53,7 +53,6 @@ class Picking { // if (event.button !== 0) { // return; // } - const point = { x: event.clientX, y: event.clientY, type: event.type }; const normalisedPoint = { x: 0, y: 0 }; normalisedPoint.x = (point.x / this._width) * 2 - 1; diff --git a/src/core/layer.js b/src/core/layer.js index 50f2858587..e4d4467ab9 100644 --- a/src/core/layer.js +++ b/src/core/layer.js @@ -5,7 +5,7 @@ import Base from './base'; import * as THREE from './three'; import ColorUtil from '../attr/color-util'; -import * as source from '../source/index'; +import source from './source'; import PickingMaterial from '../core/engine/picking/pickingMaterial'; import Attr from '../attr/index'; import Util from '../util'; @@ -106,12 +106,10 @@ export default class Layer extends Base { this._object3D.visible = this.get('visible'); } source(data, cfg = {}) { - const dataType = this._getDataType(data); - const { type = dataType } = cfg; cfg.data = data; cfg.mapType = this.get('mapType'); - this.layerSource = new source[type](cfg); + this.layerSource = new source(cfg); // this.scene.workerPool.runTask({ // command: 'geojson', // data: cfg @@ -279,10 +277,10 @@ export default class Layer extends Base { const { featureId } = e; if (featureId < 0) return; 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; } - const style = Util.assign({}, this.StyleData[featureId]); + if (this.layerData[featureId].hasOwnProperty('filter') && this.layerData[featureId].filter === false) { return; } + const style = Util.assign({}, this.layerData[featureId]); style.color = ColorUtil.toRGB(activeStyle.fill).map(e => e / 255); this.updateStyle([ featureId ], style); } @@ -321,7 +319,7 @@ export default class Layer extends Base { _updateSize(zoom) { const sizeOption = this.get('attrOptions').size; const fields = parseFields(sizeOption.field); - const data = this.layerSource.propertiesData; + const data = this.layerSource.data.dataArray; if (!this.zoomSizeCache) this.zoomSizeCache = {}; if (!this.zoomSizeCache[zoom]) { this.zoomSizeCache[zoom] = []; @@ -339,7 +337,8 @@ export default class Layer extends Base { const self = this; const attrs = self.get('attrs'); const mappedData = []; - const data = this.layerSource.propertiesData; + // const data = this.layerSource.propertiesData; + const data = this.layerSource.data.dataArray; for (let i = 0; i < data.length; i++) { const record = data[i]; const newRecord = {}; @@ -362,18 +361,17 @@ export default class Layer extends Base { } } } + newRecord.coordinates = record.coordinates; mappedData.push(newRecord); } - - this.StyleData = mappedData; - return mappedData; + this.layerData = mappedData; } // 更新地图映射 _updateMaping() { const self = this; const attrs = self.get('attrs'); - const data = this.layerSource.propertiesData; + const data = this.layerSource.data.dataArray; for (let i = 0; i < data.length; i++) { const record = data[i]; for (const attrName in attrs) { @@ -385,10 +383,10 @@ export default class Layer extends Base { for (let j = 0; j < values.length; j++) { const val = values[j]; const name = names[j]; - this.StyleData[i][name] = (Util.isArray(val) && val.length === 1) ? val[0] : val; // 只有一个值时返回第一个属性值 + this.layerData[i][name] = (Util.isArray(val) && val.length === 1) ? val[0] : val; // 只有一个值时返回第一个属性值 } } else { - this.StyleData[i][names[0]] = values.length === 1 ? values[0] : values; + this.layerData[i][names[0]] = values.length === 1 ? values[0] : values; } attr.neadUpdate = true; @@ -468,6 +466,7 @@ export default class Layer extends Base { pickingMesh.material.setUniformsValue('u_zoom', zoom); }; this._pickingMesh.add(pickingMesh); + } _setPickingId() { this._pickingId = this.getPickingId(); @@ -532,13 +531,13 @@ export default class Layer extends Base { */ _updateFilter(object) { this._updateMaping(); - const filterData = this.StyleData; + const filterData = this.layerData; this._activeIds = null; // 清空选中元素 const colorAttr = object.geometry.attributes.a_color; const pickAttr = object.geometry.attributes.pickingId; pickAttr.array.forEach((id, index) => { id = Math.abs(id); - const color = [ ...this.StyleData[id - 1].color ]; + const color = [ ...this.layerData[id - 1].color ]; id = Math.abs(id); const item = filterData[id - 1]; if (item.hasOwnProperty('filter') && item.filter === false) { @@ -584,7 +583,7 @@ export default class Layer extends Base { const pickingId = this.layerMesh.geometry.attributes.pickingId.array; const colorAttr = this.layerMesh.geometry.attributes.a_color; this._activeIds.forEach(index => { - const color = this.StyleData[index].color; + const color = this.layerData[index].color; const firstId = pickingId.indexOf(index + 1); for (let i = firstId; i < pickingId.length; i++) { if (pickingId[i] === index + 1) { diff --git a/src/core/source.js b/src/core/source.js index 3a61aead88..fdb37fa2de 100644 --- a/src/core/source.js +++ b/src/core/source.js @@ -2,16 +2,19 @@ * @Author: ThinkGIS * @Date: 2018-06-08 11:19:06 * @Last Modified by: mikey.zhaopeng - * @Last Modified time: 2018-11-01 11:50:43 + * @Last Modified time: 2019-02-25 20:58:08 */ import Base from './base'; const Controller = require('./controller/index'); import { aProjectFlat } from '../geo/project'; +import { getTransform, getParser } from '../source'; export default class Source extends Base { getDefaultCfg() { return { data: null, defs: {}, + parser: {}, + transforms: [], scales: { }, options: {} @@ -19,26 +22,40 @@ export default class Source extends Base { } constructor(cfg) { super(cfg); - + const transform = this.get('transforms'); + this._transforms = transform || []; this._initControllers(); - this.prepareData(); + // 数据解析 + this._excuteParser(); + // 数据转换 统计,聚合,分类 + this._executeTrans(); + // 坐标转换 + this._projectCoords(); } - - // 标准化数据 - prepareData() { + _excuteParser() { + const parser = this.get('parser'); + const { type = 'geojson' } = parser; const data = this.get('data'); - this.propertiesData = [];// 临时使用 - this.geoData = []; - - data.coordinates.forEach(geo => { - const coord = this._coordProject(geo); - this.geoData.push(coord); - this.propertiesData.push([]); + this.data = getParser(type)(data, parser); + } + /** + * 数据统计 + */ + _executeTrans() { + const trans = this._transforms; + trans.forEach(tran => { + const { type } = tran; + this.data = getTransform(type)(this.data, tran); + }); + this._transforms = trans; + } + _projectCoords() { + this.data.dataArray.forEach(data => { + data.coordinates = this._coordProject(data.coordinates); }); } - createScale(field) { - const data = this.propertiesData; + const data = this.data.dataArray; const scales = this.get('scales'); let scale = scales[field]; const scaleController = this.get('scaleController'); @@ -87,5 +104,10 @@ export default class Source extends Base { return [ ll.x, -ll.y, geo[2] || 0 ]; } + getSelectFeature(featureId) { + const data = this.get('data'); + // 如果是GeoJSON 数据返回原数 + return data.features ? data.features[featureId] : this.data.dataArray[featureId]; + } } diff --git a/src/geom/buffer/heatmap/grid.js b/src/geom/buffer/heatmap/grid.js new file mode 100644 index 0000000000..af089e408b --- /dev/null +++ b/src/geom/buffer/heatmap/grid.js @@ -0,0 +1,32 @@ +export default function gridBuffer(layerData) { + const attribute = { + vertices: [], + miter: [], + colors: [], + pickingIds: [] + }; + layerData.forEach(element => { + const { color, id } = element; + const [ x, y, z ] = element.coordinates; + attribute.vertices.push(x, y, z); + attribute.miter.push(-1, -1); + attribute.vertices.push(x, y, z); + attribute.miter.push(1, 1); + attribute.vertices.push(x, y, z); + attribute.miter.push(-1, 1); + attribute.vertices.push(x, y, z); + attribute.miter.push(-1, -1); + attribute.vertices.push(x, y, z); + attribute.miter.push(1, -1); + attribute.vertices.push(x, y, z); + attribute.miter.push(1, 1); + attribute.colors.push(...color); + attribute.colors.push(...color); + attribute.colors.push(...color); + attribute.colors.push(...color); + attribute.colors.push(...color); + attribute.colors.push(...color); + attribute.pickingIds.push(id, id, id, id, id, id); + }); + return attribute; +} diff --git a/src/geom/buffer/image.js b/src/geom/buffer/image.js index 70b52aed51..262151b149 100644 --- a/src/geom/buffer/image.js +++ b/src/geom/buffer/image.js @@ -4,8 +4,9 @@ import * as THREE from '../../core/three'; export default class ImageBuffer extends BufferBase { geometryBuffer() { - const coordinates = this.get('coordinates'); - const images = this.get('image'); + const layerData = this.get('layerData'); + const coordinates = layerData[0].coordinates; + const images = layerData[0].images; const positions = [ ...coordinates[0], coordinates[1][0], coordinates[0][1], 0, ...coordinates[1], diff --git a/src/geom/buffer/line.js b/src/geom/buffer/line.js index a73006f07c..880ac2dc95 100644 --- a/src/geom/buffer/line.js +++ b/src/geom/buffer/line.js @@ -3,8 +3,7 @@ import { lineShape } from '../shape'; export default class LineBuffer extends BufferBase { geometryBuffer() { - const coordinates = this.get('coordinates'); - const properties = this.get('properties'); + const layerData = this.get('layerData'); const shapeType = this.shapeType = this.get('shapeType'); const positions = []; const positionsIndex = []; @@ -16,16 +15,16 @@ export default class LineBuffer extends BufferBase { this.attributes = this._getArcLineAttributes(); return; } - coordinates.forEach((geo, index) => { - const props = properties[index]; - const attrData = this._getShape(geo, props, index); + layerData.forEach((item, index) => { + const props = item; + const attrData = this._getShape(item.coordinates, props, index); positions.push(...attrData.positions); positionsIndex.push(...attrData.indexes); if (attrData.hasOwnProperty('instances')) { instances.push(...attrData.instances); } }); - this.bufferStruct.style = properties; + this.bufferStruct.style = layerData; this.bufferStruct.verts = positions; this.bufferStruct.indexs = positionsIndex; if (instances.length > 0) { @@ -50,17 +49,16 @@ export default class LineBuffer extends BufferBase { } _getArcLineAttributes() { - const coordinates = this.get('coordinates'); - const properties = this.get('properties'); + const layerData = this.get('layerData'); const positions = []; const colors = []; const indexArray = []; const sizes = []; const instances = []; - coordinates.forEach((geo, index) => { - const props = properties[index]; + layerData.forEach(item => { + const props = item; const positionCount = positions.length / 3; - const attrData = this._getShape(geo, props, positionCount); + const attrData = this._getShape(item.coordinates, props, positionCount); positions.push(...attrData.positions); colors.push(...attrData.colors); indexArray.push(...attrData.indexArray); @@ -76,8 +74,7 @@ export default class LineBuffer extends BufferBase { }; } _getMeshLineAttributes() { - const coordinates = this.get('coordinates'); - const properties = this.get('properties'); + const layerData = this.get('layerData'); const { lineType } = this.get('style'); const positions = []; const pickingIds = []; @@ -87,10 +84,10 @@ export default class LineBuffer extends BufferBase { const indexArray = []; const sizes = []; const attrDistance = []; - coordinates.forEach((geo, index) => { - const props = properties[index]; + layerData.forEach(item => { + const props = item; const positionCount = positions.length / 3; - const attr = lineShape.Line(geo, props, positionCount, (lineType !== 'soild')); + const attr = lineShape.Line(item.coordinates, props, positionCount, (lineType !== 'soild')); positions.push(...attr.positions); normal.push(...attr.normal); miter.push(...attr.miter); diff --git a/src/geom/buffer/point/fillBuffer.js b/src/geom/buffer/point/fillBuffer.js index 84d257c3ee..7179959827 100644 --- a/src/geom/buffer/point/fillBuffer.js +++ b/src/geom/buffer/point/fillBuffer.js @@ -3,7 +3,7 @@ import * as THREE from '../../../core/three'; import * as polygonShape from '../../shape/polygon'; import * as polygonPath from '../../shape/path'; import Util from '../../../util'; -export default function fillBuffer(coordinates, properties) { +export default function fillBuffer(layerData) { const attribute = { vertices: [], normals: [], @@ -14,8 +14,8 @@ export default function fillBuffer(coordinates, properties) { faceUv: [] }; - coordinates.forEach((geo, index) => { - let { size, shape, color, id } = properties[index]; + layerData.forEach(item => { + let { size, shape, color, id, coordinates } = item; let polygon = null; const path = polygonPath[shape](); if (pointShape['2d'].indexOf(shape) !== -1) { @@ -27,7 +27,7 @@ export default function fillBuffer(coordinates, properties) { } else { throw new Error('Invalid shape type: ' + shape); } - toPointShapeAttributes(polygon, geo, { size, shape, color, id }, attribute); + toPointShapeAttributes(polygon, coordinates, { size, shape, color, id }, attribute); }); return attribute; diff --git a/src/geom/buffer/point/imageBuffer.js b/src/geom/buffer/point/imageBuffer.js index 6bc37b052a..383134b6d9 100644 --- a/src/geom/buffer/point/imageBuffer.js +++ b/src/geom/buffer/point/imageBuffer.js @@ -1,4 +1,4 @@ -export default function ImageBuffer(coordinates, properties, opt) { +export default function ImageBuffer(layerData, opt) { const attributes = { vertices: [], colors: [], @@ -7,10 +7,10 @@ export default function ImageBuffer(coordinates, properties, opt) { pickingIds: [], uv: [] }; - coordinates.forEach((pos, index) => { - const { color, size, id, shape } = properties[index]; + layerData.forEach(item => { + const { color, size, id, shape, coordinates } = item; const { x, y } = opt.imagePos[shape]; - attributes.vertices.push(...pos); + attributes.vertices.push(...coordinates); attributes.colors.push(...color); attributes.pickingIds.push(id); attributes.sizes.push(size * window.devicePixelRatio); // diff --git a/src/geom/buffer/point/normalBuffer.js b/src/geom/buffer/point/normalBuffer.js index d35fd4217b..aaa50ee424 100644 --- a/src/geom/buffer/point/normalBuffer.js +++ b/src/geom/buffer/point/normalBuffer.js @@ -1,13 +1,13 @@ -export default function NormalBuffer(coordinates, properties) { +export default function NormalBuffer(layerData) { const attributes = { vertices: [], colors: [], sizes: [], pickingIds: [] }; - coordinates.forEach((pos, index) => { - const { color, size, id } = properties[index]; - attributes.vertices.push(...pos); + layerData.forEach(item => { + const { color, size, id, coordinates} = item; + attributes.vertices.push(...coordinates); attributes.colors.push(...color); attributes.pickingIds.push(id); attributes.sizes.push(size); diff --git a/src/geom/buffer/point/strokeBuffer.js b/src/geom/buffer/point/strokeBuffer.js index c71726cbd6..eff757e3c6 100644 --- a/src/geom/buffer/point/strokeBuffer.js +++ b/src/geom/buffer/point/strokeBuffer.js @@ -3,7 +3,7 @@ import * as polygonShape from '../../shape/polygon'; import * as lineShape from '../../shape/line'; import { pointShape } from '../../../global'; import Util from '../../../util'; -export default function StrokeBuffer(coordinates, properties, style) { +export default function StrokeBuffer(layerData, style) { const attribute = { shapes: [], normal: [], @@ -15,8 +15,8 @@ export default function StrokeBuffer(coordinates, properties, style) { colors: [] }; const { stroke, strokeWidth } = style; - coordinates.forEach((geo, index) => { - let { size, shape, id } = properties[index]; + layerData.forEach(item => { + let { size, shape, id, coordinates } = item; const path = polygonPath[shape](); const positionsIndex = attribute.miter.length; let polygon = null; @@ -33,7 +33,7 @@ export default function StrokeBuffer(coordinates, properties, style) { } else { throw new Error('Invalid shape type: ' + shape); } - polygonLineBuffer(polygon, geo, size, attribute); + polygonLineBuffer(polygon, coordinates, size, attribute); }); return attribute; diff --git a/src/geom/buffer/point/textBuffer.js b/src/geom/buffer/point/textBuffer.js index 0bbab7d759..1d68acb3b1 100644 --- a/src/geom/buffer/point/textBuffer.js +++ b/src/geom/buffer/point/textBuffer.js @@ -9,7 +9,7 @@ const metrics = { family: 'ios9', size: 24 }; -export default function TextBuffer(coordinates, properties, style) { +export default function TextBuffer(layerData, style) { EventEmitter.call(this); const attributes = { originPoints: [], @@ -21,7 +21,7 @@ export default function TextBuffer(coordinates, properties, style) { const { textOffset = [ 0, 0 ] } = style; const chars = []; const textChars = {}; - properties.forEach(element => { + layerData.forEach(element => { let text = element.shape || ''; text = text.toString(); for (let j = 0; j < text.length; j++) { @@ -33,9 +33,9 @@ export default function TextBuffer(coordinates, properties, style) { } }); loadTextInfo(chars, (chars, texture) => { - properties.forEach((element, index) => { + layerData.forEach(element => { const size = element.size; - const pos = coordinates[index]; + const pos = layerData.coordinates; const pen = { x: textOffset[0], y: textOffset[1] }; let text = element.shape || ''; text = text.toString(); diff --git a/src/geom/buffer/polygon.js b/src/geom/buffer/polygon.js index c90172802a..60c585d872 100644 --- a/src/geom/buffer/polygon.js +++ b/src/geom/buffer/polygon.js @@ -3,22 +3,21 @@ import BufferBase from './bufferBase'; export default class PolygonBuffer extends BufferBase { geometryBuffer() { - const coordinates = this.get('coordinates'); - const properties = this.get('properties'); + const layerData = this.get('layerData'); const shape = this.get('shape'); const positions = []; const faceUv = []; const sizes = []; const positionsIndex = []; let indexCount = 0; - this.bufferStruct.style = properties; - const isExtrude = properties[0].hasOwnProperty('size'); + this.bufferStruct.style = layerData; + const isExtrude = layerData[0].hasOwnProperty('size'); // indices, normals, colors, UVs - coordinates.forEach((geo, index) => { - const heightValue = properties[index].size; - let extrudeData = polygonShape[shape](geo); + layerData.forEach(item => { + const heightValue = item.size; + let extrudeData = polygonShape[shape](item.coordinates); if (isExtrude && shape === 'extrude') { - extrudeData = polygonShape.extrude(geo); + extrudeData = polygonShape.extrude(item.coordinates); extrudeData.positions = extrudeData.positions.map(pos => { pos[2] *= heightValue; return pos; @@ -48,7 +47,7 @@ export default class PolygonBuffer extends BufferBase { this.bufferStruct.indices = positionsIndex; this.bufferStruct.position = positions; this.bufferStruct.indexCount = indexCount; - this.bufferStruct.style = properties; + this.bufferStruct.style = layerData; this.bufferStruct.faceUv = faceUv; this.bufferStruct.sizes = sizes; if (shape !== 'line') { diff --git a/src/geom/buffer/raster.js b/src/geom/buffer/raster.js index 32fa14a3ba..9f62192699 100644 --- a/src/geom/buffer/raster.js +++ b/src/geom/buffer/raster.js @@ -3,8 +3,8 @@ import { colorScales } from '../../attr/colorscales'; import * as THREE from '../../core/three'; export class RasterBuffer extends BufferBase { geometryBuffer() { - const coordinates = this.get('coordinates'); - + const layerData = this.get('layerData'); + const { coordinates, width, data, height } = layerData.dataArray[0]; const positions = [ ...coordinates[0], coordinates[1][0], coordinates[0][1], 0, @@ -14,9 +14,8 @@ export class RasterBuffer extends BufferBase { coordinates[0][0], coordinates[1][1], 0 ]; const imgPosUv = [ 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0 ]; - const raster = this.get('raster'); const size = this.get('size'); - const texture = new THREE.DataTexture(new Float32Array(raster.data), raster.width, raster.height, THREE.LuminanceFormat, THREE.FloatType); + const texture = new THREE.DataTexture(new Float32Array(data), width, height, THREE.LuminanceFormat, THREE.FloatType); texture.generateMipmaps = true; texture.needsUpdate = true; const colors = this.get('rampColors'); @@ -28,7 +27,7 @@ export class RasterBuffer extends BufferBase { this.bufferStruct.u_extent = [ coordinates[0][0], coordinates[0][1], coordinates[1][0], coordinates[1][1] ]; this.bufferStruct.u_colorTexture = colorTexture; // 颜色表‘= - const triangles = this._buildTriangles(raster, size, this.bufferStruct.u_extent); + const triangles = this._buildTriangles(width, height, size, this.bufferStruct.u_extent); const attributes = { vertices: new Float32Array(triangles.vertices), uvs: new Float32Array(triangles.uvs), @@ -78,9 +77,8 @@ export class RasterBuffer extends BufferBase { texture1.needsUpdate = true; return texture1; } - _buildTriangles(raster, size = 1, extent) { + _buildTriangles(width, height, size = 1, extent) { // const extent = [ 73.482190241, 3.82501784112, 135.106618732, 57.6300459963 ] - const { width, height } = raster; const indices = []; const vertices = []; const uvs = []; diff --git a/src/geom/buffer/text.js b/src/geom/buffer/text.js index 099f6d217f..5f906352b4 100644 --- a/src/geom/buffer/text.js +++ b/src/geom/buffer/text.js @@ -13,12 +13,11 @@ export default class TextBuffer extends BufferBase { family: 'ios9', size: 24 }; - const coordinates = this.get('coordinates'); - const properties = this.get('properties'); + const layerData = this.get('layerData'); const { textOffset = [ 0, 0 ] } = this.get('style'); const chars = []; const textChars = {}; - properties.forEach(element => { + layerData.forEach(element => { let text = element.shape || ''; text = text.toString(); for (let j = 0; j < text.length; j++) { @@ -39,9 +38,9 @@ export default class TextBuffer extends BufferBase { const originPoints = []; const textSizes = []; const textOffsets = []; - properties.forEach((element, index) => { + layerData.forEach(element => { const size = element.size; - const pos = coordinates[index]; + const pos = element.coordinates; // const pen = { x: pos[0] - dimensions.advance / 2, y: pos[1] }; const pen = { x: textOffset[0], y: textOffset[1] }; let text = element.shape || ''; @@ -53,7 +52,7 @@ export default class TextBuffer extends BufferBase { this._drawGlyph(pos, text[i], pen, size, colors, textureElements, originPoints, textSizes, textOffsets, color); } }); - this.bufferStruct.style = properties; + this.bufferStruct.style = layerData; this.attributes = { originPoints, textSizes, diff --git a/src/geom/material/grid.js b/src/geom/material/grid.js new file mode 100644 index 0000000000..65db262fcf --- /dev/null +++ b/src/geom/material/grid.js @@ -0,0 +1,31 @@ +import grid_frag from '../shader/grid_frag.glsl'; +import grid_vert from '../shader/grid_vert.glsl'; +import Material from './material'; + + +export default class GridMaterial extends Material { + getDefaultParameters() { + return { + uniforms: { + u_opacity: { value: 1.0 }, + u_time: { value: 0 }, + u_xOffset: { value: 0.01 }, + u_yOffset: { value: 0.01 }, + u_coverage: { value: 0.8 } + }, + defines: { + + } + }; + } + constructor(_uniforms, _defines, parameters) { + super(parameters); + const { uniforms, defines } = this.getDefaultParameters(); + this.uniforms = Object.assign(uniforms, this.setUniform(_uniforms)); + this.type = 'GridMaterial'; + this.defines = Object.assign(defines, _defines); + this.vertexShader = grid_vert; + this.fragmentShader = grid_frag; + this.transparent = true; + } +} diff --git a/src/geom/shader/grid_frag.glsl b/src/geom/shader/grid_frag.glsl new file mode 100644 index 0000000000..042dde511e --- /dev/null +++ b/src/geom/shader/grid_frag.glsl @@ -0,0 +1,8 @@ + precision highp float; + uniform float u_opacity; + varying vec4 v_color; + void main() { + vec4 color = v_color; + gl_FragColor = color; + gl_FragColor.a =color.a*u_opacity; +} \ No newline at end of file diff --git a/src/geom/shader/grid_vert.glsl b/src/geom/shader/grid_vert.glsl new file mode 100644 index 0000000000..90c68a3b70 --- /dev/null +++ b/src/geom/shader/grid_vert.glsl @@ -0,0 +1,15 @@ +precision highp float; +attribute vec2 miter; +attribute vec4 a_color; +uniform float u_xOffset; +uniform float u_yOffset; +uniform float u_coverage; +varying vec4 v_color; + +void main() { + mat4 matModelViewProjection = projectionMatrix * modelViewMatrix; + v_color = a_color; + float x = position.x + miter.x * u_xOffset * u_coverage; + float y = position.y + miter.y * u_yOffset * u_coverage; + gl_Position = matModelViewProjection * vec4(x, y, position.z, 1.0); +} \ No newline at end of file diff --git a/src/layer/heatmap.js b/src/layer/heatmap.js new file mode 100644 index 0000000000..f9971a55da --- /dev/null +++ b/src/layer/heatmap.js @@ -0,0 +1,28 @@ +import Layer from '../core/layer'; +import gridBuffer from '../geom/buffer/heatmap/grid'; +import DrawGrid from './render/heatmap/gird'; + +export default class HeatMapLayer extends Layer { + shape(type) { + this.shapeType = type; + return this; + } + render() { + this._prepareRender(); + return this; + } + _prepareRender() { + this.init(); + this.type = 'heatmap'; + const style = this.get('styleOptions'); + const { xOffset, yOffset } = this.layerSource.data; + this._buffer = new gridBuffer(this.layerData); + const config = { + ...style, + xOffset, + yOffset + }; + const girdMesh = new DrawGrid(this._buffer, config); + this.add(girdMesh); + } +} diff --git a/src/layer/imageLayer.js b/src/layer/imageLayer.js index f3092da117..92e9a60b59 100644 --- a/src/layer/imageLayer.js +++ b/src/layer/imageLayer.js @@ -1,26 +1,19 @@ import Layer from '../core/layer'; import * as THREE from '../core/three'; -import imageSource from '../source/imageSource'; import ImageBuffer from '../geom/buffer/image'; // import ImageGeometry from '../geom/bufferGeometry/image'; import ImageMaterial from '../geom/material/imageMaterial'; export default class imageLayer extends Layer { - source(data, cfg = {}) { - cfg.mapType = this.get('mapType'); - cfg.data = data; - this.layerSource = new imageSource(cfg); - return this; - } render() { this.init(); this.type = 'image'; const source = this.layerSource; const { opacity } = this.get('styleOptions'); // 加载 完成事件 - source.on('imageLoaded', () => { + source.data.images.then(images => { + this.layerData[0].images = images; const buffer = new ImageBuffer({ - coordinates: source.geoData, - image: source.image + layerData: this.layerData }); this.initGeometry(buffer.attributes); const material = new ImageMaterial({ diff --git a/src/layer/index.js b/src/layer/index.js index 5e8fef27c6..1c2cad2fdf 100644 --- a/src/layer/index.js +++ b/src/layer/index.js @@ -4,17 +4,14 @@ import PointLayer from './pointLayer'; import LineLayer from './lineLayer'; import ImageLayer from './imageLayer'; import RasterLayer from './rasterLayer'; +import HeatMapLayer from './heatmap'; registerLayer('PolygonLayer', PolygonLayer); registerLayer('PointLayer', PointLayer); registerLayer('LineLayer', LineLayer); registerLayer('ImageLayer', ImageLayer); registerLayer('RasterLayer', RasterLayer); +registerLayer('HeatMapLayer', HeatMapLayer); export { LAYER_MAP } from './factory'; -export { default as PolygonLayer } from './polygonLayer'; -export { default as PointLayer } from './pointLayer'; -export { default as LineLayer } from './lineLayer'; -export { default as ImageLayer } from './imageLayer'; -export { default as RasterLayer } from './rasterLayer'; diff --git a/src/layer/lineLayer.js b/src/layer/lineLayer.js index 4d40755100..c83c3f0a88 100644 --- a/src/layer/lineLayer.js +++ b/src/layer/lineLayer.js @@ -12,11 +12,10 @@ export default class LineLayer extends Layer { this.type = 'polyline'; this.init(); const source = this.layerSource; - const StyleData = this.StyleData; + const layerData = this.layerData; const style = this.get('styleOptions'); const buffer = this._buffer = new LineBuffer({ - coordinates: source.geoData, - properties: StyleData, + layerData, shapeType: this.shapeType, style }); diff --git a/src/layer/pointLayer.js b/src/layer/pointLayer.js index a21a80b4a7..02acff004f 100644 --- a/src/layer/pointLayer.js +++ b/src/layer/pointLayer.js @@ -46,12 +46,12 @@ export default class PointLayer extends Layer { case 'fill' :// 填充图形 { if (fill !== 'none') { // 是否填充 - const attributes = PointBuffer.FillBuffer(source.geoData, this.StyleData, style); + const attributes = PointBuffer.FillBuffer(this.layerData, style); const meshfill = drawPoint.DrawFill(attributes, this.get('styleOptions')); this.add(meshfill); } if (stroke !== 'none') { // 是否绘制边界 - const lineAttribute = PointBuffer.StrokeBuffer(source.geoData, this.StyleData, style); + const lineAttribute = PointBuffer.StrokeBuffer(this.layerData, style); const meshStroke = drawPoint.DrawStroke(lineAttribute, this.get('styleOptions')); this.add(meshStroke, 'line'); } @@ -59,14 +59,14 @@ export default class PointLayer extends Layer { } case 'image':// 绘制图片标注 { - const imageAttribute = PointBuffer.ImageBuffer(source.geoData, this.StyleData, { imagePos: this.scene.image.imagePos }); + const imageAttribute = PointBuffer.ImageBuffer(this.layerData, { imagePos: this.scene.image.imagePos }); const imageMesh = drawPoint.DrawImage(imageAttribute, { ...style, texture: this.scene.image.texture }); this.add(imageMesh); break; } case 'normal' : // 原生点 { - const normalAttribute = PointBuffer.NormalBuffer(source.geoData, this.StyleData, style); + const normalAttribute = PointBuffer.NormalBuffer(this.layerData, style); const normalPointMesh = drawPoint.DrawNormal(normalAttribute, style); this.add(normalPointMesh); break; @@ -78,11 +78,11 @@ export default class PointLayer extends Layer { _getShape() { let shape = null; - if (!this.StyleData[0].hasOwnProperty('shape')) { + if (!this.layerData[0].hasOwnProperty('shape')) { return 'normal'; } - for (let i = 0; i < this.StyleData.length; i++) { - shape = this.StyleData[i].shape; + for (let i = 0; i < this.layerData.length; i++) { + shape = this.layerData[i].shape; if (shape !== undefined) { break; } @@ -103,8 +103,7 @@ export default class PointLayer extends Layer { const styleOptions = this.get('styleOptions'); const buffer = new TextBuffer({ type: this.shapeType, - coordinates: source.geoData, - properties: this.StyleData, + layerData: this.layerData, style: this.get('styleOptions') }); diff --git a/src/layer/polygonLayer.js b/src/layer/polygonLayer.js index 53e7c9903e..ba37ed3b87 100644 --- a/src/layer/polygonLayer.js +++ b/src/layer/polygonLayer.js @@ -21,11 +21,9 @@ export default class PolygonLayer extends Layer { _prepareRender() { this.init(); this.type = 'polygon'; - const source = this.layerSource; this._buffer = new PolygonBuffer({ shape: this.shape, - coordinates: source.geoData, - properties: this.StyleData + layerData: this.layerData }); this.add(this._getLayerRender()); } diff --git a/src/layer/rasterLayer.js b/src/layer/rasterLayer.js index 7a68dc8530..cb5d9f41a9 100644 --- a/src/layer/rasterLayer.js +++ b/src/layer/rasterLayer.js @@ -1,16 +1,10 @@ import Layer from '../core/layer'; import * as THREE from '../core/three'; -import RasterSource from '../source/rasterSource'; import RasterMaterial from '../geom/material/rasterMaterial'; import { RasterBuffer } from '../geom/buffer/raster'; export default class RasterLayer extends Layer { - source(data, cfg = {}) { - cfg.mapType = this.get('mapType'); - cfg.data = data; - this.layerSource = new RasterSource(cfg); - return this; - } + render() { this.type = 'raster'; this.init(); @@ -18,18 +12,18 @@ export default class RasterLayer extends Layer { // 加载 完成事件 const styleOptions = this.get('styleOptions'); const buffer = new RasterBuffer({ - coordinates: source.geoData, - raster: source.rasterData, + layerData: source.data, rampColors: styleOptions.rampColors }); this.initGeometry(buffer.attributes); + const rasterConfig = source.data.dataArray[0]; const material = new RasterMaterial({ u_texture: buffer.bufferStruct.u_raster, u_colorTexture: buffer.bufferStruct.u_colorTexture, u_opacity: 1.0, u_extent: buffer.bufferStruct.u_extent, - u_min: source.rasterData.min, - u_max: source.rasterData.max, + u_min: rasterConfig.min, + u_max: rasterConfig.max, u_dimension: buffer.attributes.dimension }); diff --git a/src/layer/render/heatmap/gird.js b/src/layer/render/heatmap/gird.js new file mode 100644 index 0000000000..5aa4b07796 --- /dev/null +++ b/src/layer/render/heatmap/gird.js @@ -0,0 +1,21 @@ +import * as THREE from '../../../core/three'; +import GridMaterial from '../../../geom/material/grid'; +export default function DrawGrid(attributes, style) { + const { opacity, xOffset, yOffset, coverage } = style; + const geometry = new THREE.BufferGeometry(); + geometry.addAttribute('position', new THREE.Float32BufferAttribute(attributes.vertices, 3)); + geometry.addAttribute('miter', new THREE.Float32BufferAttribute(attributes.miter, 2)); + geometry.addAttribute('a_color', new THREE.Float32BufferAttribute(attributes.colors, 4)); + geometry.addAttribute('pickingId', new THREE.Float32BufferAttribute(attributes.pickingIds, 1)); + const material = new GridMaterial({ + u_opacity: opacity, + u_xOffset: xOffset, + u_yOffset: yOffset, + u_coverage: coverage + }, { + SHAPE: false + }); + const gridMesh = new THREE.Mesh(geometry, material); + return gridMesh; +} + diff --git a/src/source/csvSource.js b/src/source/csvSource.js deleted file mode 100644 index e5ce0b9834..0000000000 --- a/src/source/csvSource.js +++ /dev/null @@ -1,69 +0,0 @@ -import Source from '../core/source'; -import FeatureIndex from '../geo/featureIndex'; -import { csvParse } from 'd3-dsv'; -export default class CSVSource extends Source { - prepareData() { - this.type = 'csv'; - const data = this.get('data'); - const x = this.get('x'); - const y = this.get('y'); - const x1 = this.get('x1'); - const y1 = this.get('y1'); - const coords = this.get('coordinates'); - this.propertiesData = [];// 临时使用 - this.geoData = []; - let csvdata = data; - Array.isArray(csvdata) || (csvdata = csvParse(data)); - this.propertiesData = csvdata; - csvdata.forEach((col, featureIndex) => { - let coordinates = []; - if (col.coordinates) { - coordinates = col.coordinates; - } - if (x && y) { coordinates = [ col[x], col[y] ]; } // 点数据 - if (x1 && y1) { // 弧线 或者线段 - coordinates = [[ col[x], col[y] ], [ col[x1], col[y1] ]]; - } - if (coords && col.coords) { coordinates = col.coords; } - col._id = featureIndex + 1; - this._coordProject(coordinates); - this.geoData.push(this._coordProject(coordinates)); - }); - } - - featureIndex() { - const data = this.get('data'); - this.featureIndex = new FeatureIndex(data); - } - getSelectFeatureId(featureId) { - return [ featureId ]; - } - getSelectFeature(featureId) { - return this.propertiesData[featureId]; - - } - _getCoord(geo) { - if (geo.geometry) { - // GeoJSON feature - geo = geo.geometry.coordinates; - } else if (geo.coordinates) { - // GeoJSON geometry - geo = geo.coordinates; - } - return geo; - } - _coordProject(geo) { - if (Array.isArray(geo[0][0])) { - return geo.map(coor => { - return this._coordProject(coor); - }); - } - if (!Array.isArray(geo[0])) { - return this._coorConvert(geo); - } - return geo.map(coor => { - return this._coorConvert(coor); - }); - } - -} diff --git a/src/source/factory.js b/src/source/factory.js new file mode 100644 index 0000000000..37629b9e99 --- /dev/null +++ b/src/source/factory.js @@ -0,0 +1,11 @@ + +const TRANSFORMS = {}; +const PARSERS = {}; +export const getParser = type => PARSERS[type]; +export const registerParser = (type, parserFunction) => { + PARSERS[type] = parserFunction; +}; +export const getTransform = type => TRANSFORMS[type]; +export const registerTransform = (type, transFunction) => { + TRANSFORMS[type] = transFunction; +}; diff --git a/src/source/geojsonSource.js b/src/source/geojsonSource.js deleted file mode 100644 index df38e3fd13..0000000000 --- a/src/source/geojsonSource.js +++ /dev/null @@ -1,47 +0,0 @@ -import Source from '../core/source'; -import * as turfMeta from '@turf/meta'; -import { default as cleanCoords } from '@turf/clean-coords'; -import { getCoords } from '@turf/invariant'; -import FeatureIndex from '../geo/featureIndex'; - -export default class GeojsonSource extends Source { - prepareData() { - this.type = 'geojson'; - const data = this.get('data'); - this.propertiesData = []; - this.geoData = []; - turfMeta.flattenEach(data, (currentFeature, featureIndex) => { - const coord = getCoords(cleanCoords(currentFeature)); - this.geoData.push(this._coordProject(coord)); - currentFeature.properties._id = featureIndex + 1; - this.propertiesData.push(currentFeature.properties); - }); - } - featureIndex() { - const data = this.get('data'); - this.featureIndex = new FeatureIndex(data); - } - getSelectFeatureId(featureId) { - const data = this.get('data'); - const selectFeatureIds = []; - let featureStyleId = 0; - /* eslint-disable */ - turfMeta.flattenEach(data, (currentFeature, featureIndex, multiFeatureIndex) => { - /* eslint-disable */ - if (featureIndex === (featureId)) { - selectFeatureIds.push(featureStyleId); - } - featureStyleId++; - if (featureIndex > featureId) { - return; - } - }); - return selectFeatureIds; - - } - getSelectFeature(featureId){ - const data = this.get('data'); - return data.features[featureId]; - } - -} diff --git a/src/source/imageSource.js b/src/source/imageSource.js deleted file mode 100644 index 137f5a0772..0000000000 --- a/src/source/imageSource.js +++ /dev/null @@ -1,37 +0,0 @@ -import Source from '../core/source'; -import { getImage } from '../util/ajax'; -export default class ImageSource extends Source { - prepareData() { - this.type = 'image'; - const extent = this.get('extent'); - const lb = this._coorConvert(extent.slice(0, 2)); - const tr = this._coorConvert(extent.slice(2, 4)); - this.geoData = [ lb, tr ]; - this.propertiesData = []; - this._loadData(); - } - _loadData() { - const url = this.get('data'); - this.image = []; - if (typeof (url) === 'string') { - getImage({ url }, (err, img) => { - this.image = img; - this.emit('imageLoaded'); - }); - } else { - const imageCount = url.length; - let imageindex = 0; - url.forEach(item => { - getImage({ url: item }, (err, img) => { - imageindex++; - this.image.push(img); - if (imageindex === imageCount) { - this.emit('imageLoaded'); - } - - }); - }); - - } - } -} diff --git a/src/source/index.js b/src/source/index.js index ea6b260c09..307819cc1f 100644 --- a/src/source/index.js +++ b/src/source/index.js @@ -1,5 +1,23 @@ -export { default as geojson } from './geojsonSource'; -export { default as csv } from './csvSource'; -export { default as array } from './csvSource'; -export { default as basic } from '../core/source'; -export { default as imageSource } from './imageSource'; +// source parser + +import geojson from './parser/geojson'; +import image from './parser/image'; +import csv from './parser/csv'; +import json from './parser/json'; +import raster from './parser/raster'; + +import { registerTransform, registerParser } from './factory'; +import { aggregatorToGrid } from './transform/grid-aggregator'; +import { map } from './transform/map'; + +registerParser('geojson', geojson); +registerParser('image', image); +registerParser('csv', csv); +registerParser('json', json); +registerParser('raster', raster); +// 注册transform + +registerTransform('grid', aggregatorToGrid); +registerTransform('map', map); + +export { getTransform, registerTransform, getParser, registerParser } from './factory'; diff --git a/src/source/parser/csv.js b/src/source/parser/csv.js new file mode 100644 index 0000000000..f727d18209 --- /dev/null +++ b/src/source/parser/csv.js @@ -0,0 +1,23 @@ +import { csvParse } from 'd3-dsv'; +export default function csv(data, cfg) { + const { x, y, x1, y1 } = cfg; + const csvdata = csvParse(data); + const resultdata = []; + csvdata.forEach((col, featureIndex) => { + let coordinates = []; + if (x && y) { coordinates = [ col[x], col[y] ]; } // 点数据 + if (x1 && y1) { // 弧线 或者线段 + coordinates = [[ col[x], col[y] ], [ col[x1], col[y1] ]]; + } + col._id = featureIndex + 1; + const dataItem = { + ...col, + coordinates + + }; + resultdata.push(dataItem); + }); + return { + dataArray: resultdata + }; +} diff --git a/src/source/parser/geojson.js b/src/source/parser/geojson.js new file mode 100644 index 0000000000..6385fa0d42 --- /dev/null +++ b/src/source/parser/geojson.js @@ -0,0 +1,19 @@ +import * as turfMeta from '@turf/meta'; +import { default as cleanCoords } from '@turf/clean-coords'; +import { getCoords } from '@turf/invariant'; + +export default function geoJSON(data) { + const resultData = []; + turfMeta.flattenEach(data, (currentFeature, featureIndex) => { // 多个polygon 拆成一个 + const coord = getCoords(cleanCoords(currentFeature)); + const dataItem = { + ...currentFeature.properties, + coordinates: coord, + _id: featureIndex + 1 + }; + resultData.push(dataItem); + }); + return { + dataArray: resultData + }; +} diff --git a/src/source/parser/image.js b/src/source/parser/image.js new file mode 100644 index 0000000000..e4a6f99ddf --- /dev/null +++ b/src/source/parser/image.js @@ -0,0 +1,41 @@ +import { getImage } from '../../util/ajax'; +export default function image(data, cfg) { + const { extent } = cfg; + + const images = new Promise(resolve => { + loadData(data, res => { + resolve(res); + }); + }); + const resultData = { + images, + _id: 1, + dataArray: [{ coordinates: [[ extent[0], extent[1] ], [ extent[2], extent[3] ]] }] + }; + return resultData; +} +function loadData(data, done) { + const url = data; + let image = []; + if (typeof (url) === 'string') { + getImage({ url }, (err, img) => { + image = img; + done(image); + }); + } else { + const imageCount = url.length; + let imageindex = 0; + url.forEach(item => { + getImage({ url: item }, (err, img) => { + imageindex++; + image.push(img); + if (imageindex === imageCount) { + done(image); + } + + }); + }); + + } + return image; +} diff --git a/src/source/parser/json.js b/src/source/parser/json.js new file mode 100644 index 0000000000..37128bebce --- /dev/null +++ b/src/source/parser/json.js @@ -0,0 +1,21 @@ +export default function json(data, cfg) { + const { x, y, x1, y1 } = cfg; + const resultdata = []; + data.forEach((col, featureIndex) => { + let coordinates = []; + if (x && y) { coordinates = [ col[x], col[y] ]; } // 点数据 + if (x1 && y1) { // 弧线 或者线段 + coordinates = [[ col[x], col[y] ], [ col[x1], col[y1] ]]; + } + col._id = featureIndex + 1; + const dataItem = { + ...col, + coordinates + + }; + resultdata.push(dataItem); + }); + return { + dataArray: resultdata + }; +} diff --git a/src/source/parser/raster.js b/src/source/parser/raster.js new file mode 100644 index 0000000000..7d33903a23 --- /dev/null +++ b/src/source/parser/raster.js @@ -0,0 +1,15 @@ +export default function raster(data, cfg) { + const { extent, width, height, min, max } = cfg; + const resultData = { + _id: 1, + dataArray: [ + { + data, + width, + height, + min, + max, + coordinates: [[ extent[0], extent[1] ], [ extent[2], extent[3] ]] }] + }; + return resultData; +} diff --git a/src/source/rainSource.js b/src/source/rainSource.js deleted file mode 100644 index 6808878742..0000000000 --- a/src/source/rainSource.js +++ /dev/null @@ -1,31 +0,0 @@ -import imageSource from './imageSource'; -export class RainSource extends imageSource { - prepareData() { - const extent = this.get('extent'); - const lb = this._coorConvert(extent.slice(0, 2)); - const tr = this._coorConvert(extent.slice(2, 4)); - this.extent = [ lb, tr ]; - this.propertiesData = []; - this._genaratePoints(); - this._loadData(); - } - _genaratePoints() { - const numParticles = 512 * 512; - - const particleRes = this.particleRes = Math.ceil(Math.sqrt(numParticles)); - const numPoints = particleRes * particleRes; - const particleState = []; - const particleState0 = new Uint8ClampedArray(numPoints * 4); - const particleState1 = new Uint8ClampedArray(numPoints * 4); - const emptyPixels = new Uint8ClampedArray(numPoints * 4); - for (let i = 0; i < particleState0.length; i++) { - particleState0[i] = Math.floor(Math.random() * 256); // randomize the initial particle positions - } - this.particleIndices = new Float32Array(numPoints); - for (let i = 0; i < numPoints; i++) this.particleIndices[i] = i; - this.particleImage0 = new ImageData(particleState0, particleRes, particleRes); - this.particleImage1 = new ImageData(particleState1, particleRes, particleRes); - this.backgroundImage = new ImageData(emptyPixels, particleRes, particleRes); - this.geoData = particleState; - } -} diff --git a/src/source/rasterSource.js b/src/source/rasterSource.js deleted file mode 100644 index 2439422dce..0000000000 --- a/src/source/rasterSource.js +++ /dev/null @@ -1,20 +0,0 @@ -import Source from '../core/source'; -export default class RasterSource extends Source { - prepareData() { - this.type = 'raster'; - const extent = this.get('extent'); - const lb = this._coorConvert(extent.slice(0, 2)); - const tr = this._coorConvert(extent.slice(2, 4)); - this.geoData = [ lb, tr ]; - this.propertiesData = []; - this.rasterData = { - data: this.get('data'), - width: this.get('width'), - height: this.get('height'), - min: this.get('min'), - max: this.get('max') - }; - - } - -} diff --git a/src/source/transform/grid-aggregator.js b/src/source/transform/grid-aggregator.js new file mode 100644 index 0000000000..d2ace3476c --- /dev/null +++ b/src/source/transform/grid-aggregator.js @@ -0,0 +1,101 @@ +/** + * 生成四边形热力图 + */ +import * as statistics from './statistics'; + +const R_EARTH = 6378000; + +/** + * 计算方格密度图 + * @param {*} data 经纬度数据 和属性数据 + * @param {*} size 半径大小 单位 km + * @return + */ +export function aggregatorToGrid(data, option) { + const dataArray = data.dataArray; + const { size = 10 } = option; + const { gridHash, gridOffset } = _pointsGridHash(dataArray, size); + const layerData = _getGridLayerDataFromGridHash(gridHash, gridOffset, option); + return { + xOffset: gridOffset.xOffset / 360 * (256 << 20) / 2, + yOffset: gridOffset.xOffset / 360 * (256 << 20) / 2, + dataArray: layerData + }; +} + +function _pointsGridHash(dataArray, size) { + let latMin = Infinity; + let latMax = -Infinity; + let pLat; + for (let index = 0; index < dataArray.length; index++) { + const point = dataArray[index]; + pLat = point.coordinates[1]; + if (Number.isFinite(pLat)) { + latMin = pLat < latMin ? pLat : latMin; + latMax = pLat > latMax ? pLat : latMax; + } + + } + // const centerLat = (latMin + latMax) / 2; + const centerLat = 34.54083; + const gridOffset = _calculateGridLatLonOffset(size, centerLat); + if (gridOffset.xOffset <= 0 || gridOffset.yOffset <= 0) { + return { gridHash: {}, gridOffset }; + } + const gridHash = {}; + for (let index = 0; index < dataArray.length; index++) { + const point = dataArray[index]; + const lat = point.coordinates[1]; + const lng = point.coordinates[0]; + + if (Number.isFinite(lat) && Number.isFinite(lng)) { + const latIdx = Math.floor((lat + 90) / gridOffset.yOffset); + const lonIdx = Math.floor((lng + 180) / gridOffset.xOffset); + const key = `${latIdx}-${lonIdx}`; + + gridHash[key] = gridHash[key] || { count: 0, points: [] }; + gridHash[key].count += 1; + gridHash[key].points.push(point); + } + } + + return { gridHash, gridOffset }; +} +// 计算网格偏移量 +function _calculateGridLatLonOffset(cellSize, latitude) { + const yOffset = _calculateLatOffset(cellSize); + const xOffset = _calculateLonOffset(latitude, cellSize); + return { yOffset, xOffset }; +} + +function _calculateLatOffset(dy) { + return (dy / R_EARTH) * (180 / Math.PI); +} + +function _calculateLonOffset(lat, dx) { + return ((dx / R_EARTH) * (180 / Math.PI)) / Math.cos((lat * Math.PI) / 180); +} +function _getGridLayerDataFromGridHash(gridHash, gridOffset, option) { + return Object.keys(gridHash).reduce((accu, key, i) => { + const idxs = key.split('-'); + const latIdx = parseInt(idxs[0], 10); + const lonIdx = parseInt(idxs[1], 10); + const item = {}; + if (option.field && option.method) { + const columns = getColumn(gridHash[key].points, option.field); + item[option.method] = statistics[option.method](columns); + } + Object.assign(item, { + _id: i + 1, + coordinates: [ -180 + gridOffset.xOffset * lonIdx, -90 + gridOffset.yOffset * latIdx ], + count: gridHash[key].count + }); + accu.push(item); + return accu; + }, []); +} +function getColumn(data, columnName) { + return data.map(item => { + return item[columnName]; + }); +} diff --git a/src/source/transform/map.js b/src/source/transform/map.js new file mode 100644 index 0000000000..47c66e1005 --- /dev/null +++ b/src/source/transform/map.js @@ -0,0 +1,7 @@ +export function map(data, options) { + const { callback } = options; + if (callback) { + data.dataArray = data.dataArray.map(callback); + } + return data; +} diff --git a/src/source/transform/statistics.js b/src/source/transform/statistics.js new file mode 100644 index 0000000000..2a7a317846 --- /dev/null +++ b/src/source/transform/statistics.js @@ -0,0 +1,10 @@ +/* 支持 'max', 'mean', 'median', 'min', 'mode', 'product', 'standardDeviation', + * 'sum', 'sumSimple', 'variance', 'count', 'distinct' + */ +export { default as min } from 'simple-statistics/src/min'; +export { default as max } from 'simple-statistics/src/max'; +export { default as mean } from 'simple-statistics/src/mean'; +export { default as sum } from 'simple-statistics/src/sum'; +export { default as median } from 'simple-statistics/src/median'; +export { default as standardDeviation } from 'simple-statistics/src/standard_deviation'; +