refactor(src) file name
|
@ -8,9 +8,7 @@ node_modules/
|
|||
demos/assets/
|
||||
demos/index.html
|
||||
demos/*
|
||||
<<<<<<< HEAD
|
||||
rollup/*
|
||||
webpack/*
|
||||
=======
|
||||
src/core/three.js
|
||||
>>>>>>> master
|
||||
testdemo/*
|
|
@ -74,4 +74,5 @@ demos/data
|
|||
demos/image
|
||||
.vscode
|
||||
demos/hexagon.html
|
||||
demos/model
|
||||
demos/model
|
||||
testdemo
|
|
@ -16,6 +16,6 @@ $ npm run dev
|
|||
$ npm run demos
|
||||
```
|
||||
visit online demo
|
||||
http://site.alipay.net/datavis/L7/demos/index.html
|
||||
https://antv.alipay.com/zh-cn/l7/1.x/demo/index.html
|
||||
|
||||
## How to Contribute
|
||||
|
|
|
@ -0,0 +1,106 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<meta name="geometry" content="diagram">
|
||||
<link rel="stylesheet" href="./assets/common.css">
|
||||
<link rel="stylesheet" href="./assets/info.css">
|
||||
<title>point_circle</title>
|
||||
<style>
|
||||
#map { position:absolute; top:0; bottom:0; width:100%; }
|
||||
.amap-maps {
|
||||
cursor: auto !important
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="map"></div>
|
||||
<script src="https://webapi.amap.com/maps?v=1.4.8&key=15cd8a57710d40c9b7c0e3cc120f1200&plugin=Map3D"></script>
|
||||
<script src="./assets/jquery-3.2.1.min.js"></script>
|
||||
<script src="./assets/dat.gui.min.js"></script>
|
||||
<script src="../build/L7.js"></script>
|
||||
<script>
|
||||
var radius = 0.2;
|
||||
|
||||
function pointOnCircle(angle) {
|
||||
return {
|
||||
"type": "FeatureCollection",
|
||||
"features": [
|
||||
{
|
||||
"type": "Feature",
|
||||
"properties": {},
|
||||
"geometry": {
|
||||
"type": "Point",
|
||||
"coordinates": [
|
||||
120.19382669582967 + Math.cos(angle) * radius,
|
||||
30.258134 + Math.sin(angle) * radius
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
console.log(pointOnCircle(10))
|
||||
const scene = new L7.Scene({
|
||||
id: 'map',
|
||||
mapStyle: 'light', // 样式URL
|
||||
center: [ 120.19382669582967, 30.258134 ],
|
||||
pitch: 0,
|
||||
zoom: 2,
|
||||
maxZoom:20,
|
||||
hash:true,
|
||||
minZoom:0,
|
||||
});
|
||||
window.scene = scene;
|
||||
scene.on('loaded', () => {
|
||||
const circleLayer = scene.PointLayer({
|
||||
zIndex: 0,
|
||||
})
|
||||
.source(pointOnCircle(0))
|
||||
.shape('circle')
|
||||
.size(10) // default 1
|
||||
.active(true)
|
||||
.color('#2894E0')
|
||||
.style({
|
||||
stroke: 'rgb(255,255,255)',
|
||||
strokeWidth: 1,
|
||||
opacity: 0.8
|
||||
})
|
||||
.render();
|
||||
|
||||
function animateMarker(timestamp) {
|
||||
|
||||
circleLayer.setData(pointOnCircle(timestamp / 1000));
|
||||
requestAnimationFrame(animateMarker);
|
||||
}
|
||||
//animateMarker(0);
|
||||
|
||||
/**
|
||||
const layerText = scene.PointLayer({
|
||||
zIndex: 3
|
||||
})
|
||||
.source(circleLayer.layerSource)
|
||||
.shape('point_count', 'text')
|
||||
.active(true)
|
||||
.size('point_count', [ 0, 16]) // default 1
|
||||
.color('#f00')
|
||||
.style({
|
||||
stroke: '#999',
|
||||
strokeWidth: 0,
|
||||
opacity: 1.0
|
||||
})
|
||||
.render();
|
||||
console.log(layerText);
|
||||
|
||||
});
|
||||
**/
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -24,55 +24,79 @@
|
|||
<script src="../build/L7.js"></script>
|
||||
<script>
|
||||
const colorObj ={
|
||||
blue: ["#E8FCFF", "#CFF6FF", "#A1E9ff", "#65CEF7", "#3CB1F0", "#2894E0", "#1772c2", "#105CB3", "#0D408C", "#002466"].reverse(),
|
||||
blue: ["#E8FCFF", "#CFF6FF", "#A1E9ff", "#65CEF7", "#3CB1F0", "#2894E0", "#1772c2", "#105CB3", "#0D408C", "#002466"],
|
||||
red: ["#FFF4F2", "#FFDFDB", "#FAADAA", "#F77472", "#F04850", "#D63147", "#BD223E", "#A81642", "#820C37", "#5C0023"].reverse(),
|
||||
orange:["#FFF7EB", "#FFECD4", "#FAD09D", "#F7B16A", "#F08D41", "#DB6C2C", "#C2491D", "#AD2B11", "#871D0C", "#610800"].reverse(),
|
||||
green:["#FAFFF0", "#EBF7D2", "#C8E695", "#A5D660", "#7DC238", "#59A616", "#3F8C0B", "#237804", "#125200", "#082B00"].reverse(),
|
||||
yellow:["#FFFFE8", "#FFFECC", "#FAF896", "#F7E463", "#F0CE3A", "#DBB125", "#C29117", "#AD7410", "#87500C", "#613000"].reverse(),
|
||||
purple:["#FCF2FF", "#F5DEFF", "#DDB3F2", "#BE7BE3", "#9B4ECF", "#7737B3", "#5B2899", "#411C85", "#270F5E", "#100338"].reverse()
|
||||
}
|
||||
|
||||
var mapinstance = new AMap.Map('map',{
|
||||
center: [ 120.19382669582967, 30.258134 ],
|
||||
viewMode: '3D',
|
||||
pitch: 0,
|
||||
zoom: 12,
|
||||
maxZoom:20,
|
||||
minZoom:0,
|
||||
});
|
||||
|
||||
const scene = new L7.Scene({
|
||||
id: 'map',
|
||||
mapStyle: 'light', // 样式URL
|
||||
mapStyle: 'dark', // 样式URL
|
||||
map:mapinstance,
|
||||
center: [ 120.19382669582967, 30.258134 ],
|
||||
pitch: 0,
|
||||
zoom: 12,
|
||||
maxZoom:14,
|
||||
minZoom:11,
|
||||
maxZoom:20,
|
||||
minZoom:0,
|
||||
});
|
||||
window.scene = scene;
|
||||
scene.on('loaded', () => {
|
||||
$.get('https://gw.alipayobjects.com/os/rmsportal/epnZEheZeDgsiSjSPcCv.json', data => {
|
||||
const circleLayer = scene.PointLayer({
|
||||
zIndex: 2
|
||||
zIndex: 0,
|
||||
})
|
||||
.source(data,{
|
||||
scale:{
|
||||
min:0,
|
||||
max:1000,
|
||||
type:'linear'
|
||||
}
|
||||
isCluster:true
|
||||
})
|
||||
.shape('2d:circle')
|
||||
.size('value', [ 2, 80]) // default 1
|
||||
// .shape('circle')
|
||||
.shape('point_count', [ 'circle', 'triangle', 'hexagon' ])
|
||||
// .shape('triangle')
|
||||
// .shape('square')
|
||||
// .shape('hexagon')
|
||||
// .shape('octogon')
|
||||
// .shape('hexagram')
|
||||
// .shape('pentagon')
|
||||
.size('point_count', [ 5, 40]) // default 1
|
||||
//.size('value', [ 10, 300]) // default 1
|
||||
.active(true)
|
||||
.filter('value', field_8 => {
|
||||
return field_8 * 1 > 500;
|
||||
})
|
||||
.color('type', colorObj.blue)
|
||||
.color('point_count',["#002466","#105CB3","#2894E0","#CFF6FF","#FFF5B8","#FFAB5C","#F27049","#730D1C"])
|
||||
.style({
|
||||
stroke: 'rgb(255,255,255)',
|
||||
strokeWidth: 1,
|
||||
opacity: 1.
|
||||
strokeWidth: 2,
|
||||
opacity: 1
|
||||
})
|
||||
.render();
|
||||
|
||||
circleLayer.on('click',(e)=>{
|
||||
console.log(e);
|
||||
})
|
||||
window.circleLayer = circleLayer;
|
||||
const layerText = scene.PointLayer({
|
||||
zIndex: 3
|
||||
})
|
||||
.source(circleLayer.layerSource)
|
||||
.shape('point_count', 'text')
|
||||
.active(false)
|
||||
.filter('point_count',(p)=>{
|
||||
return p > 50
|
||||
})
|
||||
.size('point_count', [ 5, 20]) // default 1
|
||||
.color('#fff')
|
||||
.style({
|
||||
stroke: '#999',
|
||||
strokeWidth: 1,
|
||||
opacity: 1.0
|
||||
})
|
||||
.render();
|
||||
console.log(layerText);
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
|
|
@ -30,7 +30,7 @@ const scene = new L7.Scene({
|
|||
});
|
||||
scene.on('loaded', () => {
|
||||
$.get('https://gw.alipayobjects.com/os/rmsportal/oVTMqfzuuRFKiDwhPSFL.json', data => {
|
||||
scene.PointLayer({
|
||||
window.layer = scene.PointLayer({
|
||||
zIndex: 2
|
||||
})
|
||||
.source(data.list, {
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<meta name="geometry" content="diagram">
|
||||
<link rel="stylesheet" href="./assets/common.css">
|
||||
<link rel="stylesheet" href="./assets/info.css">
|
||||
<title>animateLine</title>
|
||||
<style>
|
||||
#map { position:absolute; top:0; bottom:0; width:100%; }
|
||||
.amap-maps {
|
||||
cursor: auto !important
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="map"></div>
|
||||
<script src="https://webapi.amap.com/maps?v=1.4.8&key=15cd8a57710d40c9b7c0e3cc120f1200&plugin=Map3D"></script>
|
||||
<script src="./assets/jquery-3.2.1.min.js"></script>
|
||||
<script src="./assets/dat.gui.min.js"></script>
|
||||
<script src="../build/L7.js"></script>
|
||||
<script>
|
||||
var geojson = {
|
||||
"type": "FeatureCollection",
|
||||
"features": [{
|
||||
"type": "Feature",
|
||||
"geometry": {
|
||||
"type": "LineString",
|
||||
"coordinates": [
|
||||
[0, 0],
|
||||
[0.001, 0.0001]
|
||||
]
|
||||
}
|
||||
} ]
|
||||
};
|
||||
var speedFactor = 30; // number of frames per longitude degree
|
||||
var animation; // to store and cancel the animation
|
||||
var startTime = 0;
|
||||
var progress = 0; // progress = timestamp - startTime
|
||||
var resetTime = false; // indicator of whether time reset is needed for the animation
|
||||
var pauseButton = document.getElementById('pause');
|
||||
const scene = new L7.Scene({
|
||||
id: 'map',
|
||||
mapStyle: 'light', // 样式URL
|
||||
center: [ 120.19382669582967, 30.258134 ],
|
||||
pitch: 0,
|
||||
zoom: 2,
|
||||
maxZoom:20,
|
||||
minZoom:0,
|
||||
});
|
||||
scene.on('loaded', () => {
|
||||
const linelayer = scene.LineLayer({
|
||||
zIndex: 2
|
||||
})
|
||||
.source(geojson)
|
||||
.size([2,1])
|
||||
.shape('line')
|
||||
.color( "#2894E0")
|
||||
.render();
|
||||
startTime = performance.now();
|
||||
animateLine(0);
|
||||
function animateLine(timestamp) {
|
||||
if (resetTime) {
|
||||
// resume previous progress
|
||||
startTime = performance.now() - progress;
|
||||
resetTime = false;
|
||||
} else {
|
||||
progress = timestamp - startTime;
|
||||
}
|
||||
|
||||
// restart if it finishes a loop
|
||||
if (progress > speedFactor * 360) {
|
||||
startTime = timestamp;
|
||||
geojson.features[0].geometry.coordinates = [
|
||||
[0, 0],
|
||||
[0.001, 0.0001]
|
||||
];
|
||||
} else {
|
||||
var x = progress / speedFactor;
|
||||
// draw a sine wave with some math.
|
||||
var y = Math.sin(x * Math.PI / 90) * 40;
|
||||
// append new coordinates to the lineString
|
||||
|
||||
geojson.features[0].geometry.coordinates.push([x, y]);
|
||||
// then update the map
|
||||
linelayer.setData(geojson);
|
||||
}
|
||||
// Request the next frame of the animation.
|
||||
animation = requestAnimationFrame(animateLine);
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -30,8 +30,7 @@ const scene = new L7.Scene({
|
|||
zoom: 14.82,
|
||||
});
|
||||
scene.on('loaded', () => {
|
||||
$.get('./data/contour.geojson', data => {
|
||||
// data.features = data.features.slice(0,1);
|
||||
$.get('https://gw.alipayobjects.com/os/rmsportal/ZVfOvhVCzwBkISNsuKCc.json', data => {
|
||||
scene.LineLayer({
|
||||
zIndex: 2
|
||||
})
|
||||
|
|
|
@ -59,6 +59,7 @@ $.getJSON('https://gw.alipayobjects.com/os/rmsportal/kwUdcXnxQtexeGRvTGtA.json',
|
|||
})
|
||||
.render();
|
||||
});
|
||||
|
||||
$.getJSON('https://gw.alipayobjects.com/os/rmsportal/dzpMOiLYBKxpdmsgBLoE.json', contourData => {
|
||||
const landlayer = scene.LineLayer(
|
||||
{zIndex:2}
|
||||
|
@ -72,6 +73,7 @@ $.getJSON('https://gw.alipayobjects.com/os/rmsportal/dzpMOiLYBKxpdmsgBLoE.json',
|
|||
})
|
||||
.render();
|
||||
});
|
||||
|
||||
$.getJSON('https://gw.alipayobjects.com/os/rmsportal/opYqFyDGyGUAUXkLUhBV.json', city => {
|
||||
var makerLayer = scene.PointLayer({
|
||||
zIndex: 4
|
||||
|
@ -81,7 +83,6 @@ $.getJSON('https://gw.alipayobjects.com/os/rmsportal/opYqFyDGyGUAUXkLUhBV.json',
|
|||
.shape('image:local')
|
||||
.color('#0D408C')
|
||||
.render();
|
||||
|
||||
var makerText = scene.PointLayer({
|
||||
zIndex: 8,
|
||||
minZoom:5,
|
||||
|
@ -96,6 +97,7 @@ $.getJSON('https://gw.alipayobjects.com/os/rmsportal/opYqFyDGyGUAUXkLUhBV.json',
|
|||
strokeWidth: 4,
|
||||
})
|
||||
.render();
|
||||
return
|
||||
})
|
||||
|
||||
})
|
||||
|
|
|
@ -94,9 +94,9 @@ scene.on('loaded', () => {
|
|||
.style({
|
||||
opacity: 1.0
|
||||
})
|
||||
.render();
|
||||
//.render();
|
||||
|
||||
|
||||
console.log(citylayer);
|
||||
citylayer.on('click',(e)=>{
|
||||
$("#info").css({'left': e.pixel.x,'top':e.pixel.y, display:'block'});
|
||||
$("#info").html(`<p>${e.feature.properties.area || e.feature.properties.name }<span">${e.feature.properties.pm2_5_24h || 0}</span></p>`);
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<meta name="geometry" content="diagram">
|
||||
<link rel="stylesheet" href="./assets/common.css">
|
||||
<link rel="stylesheet" href="./assets/info.css">
|
||||
|
||||
<title>hexagon demo</title>
|
||||
<style>
|
||||
body {margin: 0;}
|
||||
#map { position:absolute; top:0; bottom:0; width:100%; }
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="map"></div>
|
||||
<div id ="info" class ="tooltip" style="display:none"></div>
|
||||
<div class='info-panel top-right'>
|
||||
<p>
|
||||
<label>min</label><input name="minaqi" type="range" step="1" min="0" max="200" value=0> <label>0</label>
|
||||
</p>
|
||||
<p>
|
||||
<label>max</label><input name="maxaqi" type="range" step="1" min="0" max="300" value=300><label>300</label>
|
||||
</p>
|
||||
<p><label>color</label><select>
|
||||
<option value ="default">default</option>
|
||||
<option value ="blue">blue</option>
|
||||
<option value ="red">red</option>
|
||||
<option value="orange">orange</option>
|
||||
<option value="green">green</option>
|
||||
<option value="yellow">yellow</option>
|
||||
<option value="purple">purple</option>
|
||||
</select> </p>
|
||||
<div>
|
||||
|
||||
<script src="https://webapi.amap.com/maps?v=1.4.8&key=15cd8a57710d40c9b7c0e3cc120f1200&plugin=Map3D"></script>
|
||||
<script src="./assets/jquery-3.2.1.min.js"></script>
|
||||
<script src="./assets/dat.gui.min.js"></script>
|
||||
<script src="../build/L7.js"></script>
|
||||
<script>
|
||||
const colorObj ={
|
||||
blue: ["#E8FCFF", "#CFF6FF", "#98E3FA", "#65CEF7", "#3CB4F0", "#2894E0", "#1A76C7", "#105CB3", "#0D408C", "#002466"],
|
||||
red: ["#FFF4F2", "#FFDFDB", "#FAADAA", "#F77472", "#F04850", "#D63147", "#BD223E", "#A81642", "#820C37", "#5C0023"],
|
||||
orange:["#FFF7EB", "#FFECD4", "#FAD09D", "#F7B16A", "#F08D41", "#DB6C2C", "#C2491D", "#AD2B11", "#871D0C", "#610800"],
|
||||
green:["#FAFFF0", "#EBF7D2", "#C8E695", "#A5D660", "#7DC238", "#59A616", "#3F8C0B", "#237804", "#125200", "#082B00"],
|
||||
yellow:["#FFFFE8", "#FFFECC", "#FAF896", "#F7E463", "#F0CE3A", "#DBB125", "#C29117", "#AD7410", "#87500C", "#613000"],
|
||||
purple:["#FCF2FF", "#F5DEFF", "#DDB3F2", "#BE7BE3", "#9B4ECF", "#7737B3", "#5B2899", "#411C85", "#270F5E", "#100338"],
|
||||
}
|
||||
|
||||
const scene = new L7.Scene({
|
||||
id: 'map',
|
||||
mapStyle: 'dark', // 样式URL
|
||||
center: [104.838088,34.075889 ],
|
||||
pitch: 0,
|
||||
zoom: 4.5,
|
||||
|
||||
});
|
||||
window.scene = scene;
|
||||
scene.on('loaded', () => {
|
||||
var colors = ["#FFF5B8","#FFDC7D","#FFAB5C","#F27049","#D42F31","#730D1C"];
|
||||
$.getJSON('https://gw.alipayobjects.com/os/basement_prod/77497aa8-8dd0-4a0c-bf3b-3bb55c5d453c.json', city => {
|
||||
|
||||
const citylayer = scene.PolygonLayer()
|
||||
.source(city)
|
||||
.color('#F27049')
|
||||
.shape('fill')
|
||||
.active(true)
|
||||
.style({
|
||||
opacity: 0.8
|
||||
})
|
||||
.render();
|
||||
|
||||
const citylayer2 = scene.PolygonLayer()
|
||||
.source(city)
|
||||
.shape('line')
|
||||
.color('#fff')
|
||||
.style({
|
||||
opacity: 1.0
|
||||
})
|
||||
.render();
|
||||
|
||||
console.log(citylayer);
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -26,9 +26,9 @@ const scene = new L7.Scene({
|
|||
id: 'map',
|
||||
viewMode: '3D',
|
||||
mapStyle: 'amap://styles/ba3e9759545cd618392ef073c0dfda8c?isPublic=true', // 样式URL
|
||||
center: [ 110.770672, 34.159869 ],
|
||||
center: [ 110.770672, 44.159869 ],
|
||||
pitch: 0,
|
||||
zoom: 4
|
||||
zoom: 3
|
||||
});
|
||||
|
||||
scene.on('loaded', () => {
|
||||
|
@ -42,10 +42,9 @@ scene.on('loaded', () => {
|
|||
const blob = this.response;
|
||||
const tiff = GeoTIFF.parse(blob);
|
||||
const image = tiff.getImage();
|
||||
const values = image.readRasters()[0];
|
||||
const values = image.readRasters()[0].values();
|
||||
const m = image.getHeight();
|
||||
const n = image.getWidth();
|
||||
|
||||
const layer = scene.RasterLayer({ zIndex: 2 }).
|
||||
source(values, {
|
||||
parser: {
|
||||
|
@ -64,11 +63,7 @@ scene.on('loaded', () => {
|
|||
}
|
||||
})
|
||||
.render();
|
||||
setTimeout(()=>{
|
||||
layer.style({
|
||||
rampColors: 'viridis',
|
||||
}).render();
|
||||
},3000)
|
||||
console.log(layer);
|
||||
|
||||
}
|
||||
};
|
||||
|
|
|
@ -25,22 +25,23 @@ const scene = new L7.Scene({
|
|||
mapStyle: 'dark', // 样式URL
|
||||
center: [ 120.19382669582967, 30.258134 ],
|
||||
pitch: 0,
|
||||
zoom: 3
|
||||
zoom: 1
|
||||
});
|
||||
window.scene = scene;
|
||||
scene.on('loaded', () => {
|
||||
$.get('./data/provincePoint.geojson', data => {
|
||||
$.get('./data/provincePoint.json', data => {
|
||||
scene.PointLayer({
|
||||
zIndex: 2
|
||||
})
|
||||
.source(data)
|
||||
.shape('name', 'text')
|
||||
.active(true)
|
||||
.size(12) // default 1
|
||||
.color('#fff')
|
||||
.color('name')
|
||||
.style({
|
||||
stroke: '#999',
|
||||
strokeWidth: 2,
|
||||
opacity: 0.85
|
||||
strokeWidth: 0,
|
||||
opacity: 1.0
|
||||
})
|
||||
.render();
|
||||
});
|
||||
|
|
|
@ -32,35 +32,35 @@ const scene = new L7.Scene({
|
|||
});
|
||||
window.scene = scene;
|
||||
scene.on('loaded', () => {
|
||||
$.get('https://gw.alipayobjects.com/os/rmsportal/XHMbjQwrSrajvLLvMPbK.json', data => {
|
||||
scene.PolygonLayer({
|
||||
zIndex: 0
|
||||
})
|
||||
.source(data)
|
||||
.shape('fill')
|
||||
.active({fill:'blue'})
|
||||
.color('rgb(79,174,234)')
|
||||
.render();
|
||||
});
|
||||
$.get('https://gw.alipayobjects.com/os/rmsportal/VifgwJEyBIXnDrjCwWdK.json', data => {
|
||||
scene.PolygonLayer({
|
||||
zIndex: 0
|
||||
})
|
||||
.source(data)
|
||||
.shape('fill')
|
||||
.color('rgb(156,194,116)')
|
||||
.render();
|
||||
});
|
||||
$.get('https://gw.alipayobjects.com/os/rmsportal/ZseLNWMOPGrgqQYfvtli.json', data => {
|
||||
scene.LineLayer({
|
||||
zIndex: 2
|
||||
})
|
||||
.source(data)
|
||||
.shape('line')
|
||||
.size([3,0])
|
||||
.color('rgb(79,174,234)')
|
||||
.render();
|
||||
});
|
||||
// $.get('https://gw.alipayobjects.com/os/rmsportal/XHMbjQwrSrajvLLvMPbK.json', data => {
|
||||
// scene.PolygonLayer({
|
||||
// zIndex: 0
|
||||
// })
|
||||
// .source(data)
|
||||
// .shape('fill')
|
||||
// .active({fill:'blue'})
|
||||
// .color('rgb(79,174,234)')
|
||||
// .render();
|
||||
// });
|
||||
// $.get('https://gw.alipayobjects.com/os/rmsportal/VifgwJEyBIXnDrjCwWdK.json', data => {
|
||||
// scene.PolygonLayer({
|
||||
// zIndex: 0
|
||||
// })
|
||||
// .source(data)
|
||||
// .shape('fill')
|
||||
// .color('rgb(156,194,116)')
|
||||
// .render();
|
||||
// });
|
||||
// $.get('https://gw.alipayobjects.com/os/rmsportal/ZseLNWMOPGrgqQYfvtli.json', data => {
|
||||
// scene.LineLayer({
|
||||
// zIndex: 2
|
||||
// })
|
||||
// .source(data)
|
||||
// .shape('line')
|
||||
// .size([3,0])
|
||||
// .color('rgb(79,174,234)')
|
||||
// .render();
|
||||
// });
|
||||
|
||||
$.get('https://gw.alipayobjects.com/os/rmsportal/ggFwDClGjjvpSMBIrcEx.json', data => {
|
||||
citylayer = scene.PolygonLayer({
|
||||
|
@ -69,6 +69,24 @@ scene.on('loaded', () => {
|
|||
.source(data)
|
||||
.shape('extrude')
|
||||
.active({fill:'red'})
|
||||
.style({
|
||||
lights: [
|
||||
{
|
||||
type: 'directional',
|
||||
direction: [ 1, 10.5, 12 ],
|
||||
ambient: [ 0.2, 0.2, 0.2 ],
|
||||
diffuse: 'red',
|
||||
specular: [ 0.1, 0.1, 0.1 ]
|
||||
},
|
||||
{
|
||||
type: 'directional',
|
||||
direction: [ 1, -10.5, 12 ],
|
||||
ambient: [ 0.2, 0.2, 0.2 ],
|
||||
diffuse: 'green',
|
||||
specular: [ 0.1, 0.1, 0.1 ]
|
||||
},
|
||||
]
|
||||
})
|
||||
.size('floor',[10,2000])
|
||||
.color('rgba(242,246,250,0.96)')
|
||||
.render();
|
||||
|
|
|
@ -41,7 +41,7 @@ scene.on('loaded', () => {
|
|||
features.push(greatCircle);
|
||||
}
|
||||
var fc = turf.featureCollection(features);
|
||||
scene.LineLayer({
|
||||
const layer = scene.LineLayer({
|
||||
zIndex: 2
|
||||
})
|
||||
.source(fc)
|
||||
|
@ -51,6 +51,7 @@ scene.on('loaded', () => {
|
|||
})
|
||||
//.animate({enable:true})
|
||||
.render();
|
||||
console.log(layer);
|
||||
/**
|
||||
scene.LineLayer({
|
||||
zIndex: 2
|
||||
|
|
|
@ -0,0 +1,173 @@
|
|||
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.ColorHash = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
|
||||
/**
|
||||
* BKDR Hash (modified version)
|
||||
*
|
||||
* @param {String} str string to hash
|
||||
* @returns {Number}
|
||||
*/
|
||||
var BKDRHash = function(str) {
|
||||
var seed = 131;
|
||||
var seed2 = 137;
|
||||
var hash = 0;
|
||||
// make hash more sensitive for short string like 'a', 'b', 'c'
|
||||
str += 'x';
|
||||
// Note: Number.MAX_SAFE_INTEGER equals 9007199254740991
|
||||
var MAX_SAFE_INTEGER = parseInt(9007199254740991 / seed2);
|
||||
for(var i = 0; i < str.length; i++) {
|
||||
if(hash > MAX_SAFE_INTEGER) {
|
||||
hash = parseInt(hash / seed2);
|
||||
}
|
||||
hash = hash * seed + str.charCodeAt(i);
|
||||
}
|
||||
return hash;
|
||||
};
|
||||
|
||||
module.exports = BKDRHash;
|
||||
|
||||
},{}],2:[function(require,module,exports){
|
||||
var BKDRHash = require('./bkdr-hash');
|
||||
|
||||
/**
|
||||
* Convert RGB Array to HEX
|
||||
*
|
||||
* @param {Array} RGBArray - [R, G, B]
|
||||
* @returns {String} 6 digits hex starting with #
|
||||
*/
|
||||
var RGB2HEX = function(RGBArray) {
|
||||
var hex = '#';
|
||||
RGBArray.forEach(function(value) {
|
||||
if (value < 16) {
|
||||
hex += 0;
|
||||
}
|
||||
hex += value.toString(16);
|
||||
});
|
||||
return hex;
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert HSL to RGB
|
||||
*
|
||||
* @see {@link http://zh.wikipedia.org/wiki/HSL和HSV色彩空间} for further information.
|
||||
* @param {Number} H Hue ∈ [0, 360)
|
||||
* @param {Number} S Saturation ∈ [0, 1]
|
||||
* @param {Number} L Lightness ∈ [0, 1]
|
||||
* @returns {Array} R, G, B ∈ [0, 255]
|
||||
*/
|
||||
var HSL2RGB = function(H, S, L) {
|
||||
H /= 360;
|
||||
|
||||
var q = L < 0.5 ? L * (1 + S) : L + S - L * S;
|
||||
var p = 2 * L - q;
|
||||
|
||||
return [H + 1/3, H, H - 1/3].map(function(color) {
|
||||
if(color < 0) {
|
||||
color++;
|
||||
}
|
||||
if(color > 1) {
|
||||
color--;
|
||||
}
|
||||
if(color < 1/6) {
|
||||
color = p + (q - p) * 6 * color;
|
||||
} else if(color < 0.5) {
|
||||
color = q;
|
||||
} else if(color < 2/3) {
|
||||
color = p + (q - p) * 6 * (2/3 - color);
|
||||
} else {
|
||||
color = p;
|
||||
}
|
||||
return Math.round(color * 255);
|
||||
});
|
||||
};
|
||||
|
||||
function isArray(o) {
|
||||
return Object.prototype.toString.call(o) === '[object Array]';
|
||||
}
|
||||
|
||||
/**
|
||||
* Color Hash Class
|
||||
*
|
||||
* @class
|
||||
*/
|
||||
var ColorHash = function(options) {
|
||||
options = options || {};
|
||||
|
||||
var LS = [options.lightness, options.saturation].map(function(param) {
|
||||
param = param || [0.35, 0.5, 0.65]; // note that 3 is a prime
|
||||
return isArray(param) ? param.concat() : [param];
|
||||
});
|
||||
|
||||
this.L = LS[0];
|
||||
this.S = LS[1];
|
||||
|
||||
if (typeof options.hue === 'number') {
|
||||
options.hue = {min: options.hue, max: options.hue};
|
||||
}
|
||||
if (typeof options.hue === 'object' && !isArray(options.hue)) {
|
||||
options.hue = [options.hue];
|
||||
}
|
||||
if (typeof options.hue === 'undefined') {
|
||||
options.hue = [];
|
||||
}
|
||||
this.hueRanges = options.hue.map(function (range) {
|
||||
return {
|
||||
min: typeof range.min === 'undefined' ? 0 : range.min,
|
||||
max: typeof range.max === 'undefined' ? 360: range.max
|
||||
};
|
||||
});
|
||||
|
||||
this.hash = options.hash || BKDRHash;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the hash in [h, s, l].
|
||||
* Note that H ∈ [0, 360); S ∈ [0, 1]; L ∈ [0, 1];
|
||||
*
|
||||
* @param {String} str string to hash
|
||||
* @returns {Array} [h, s, l]
|
||||
*/
|
||||
ColorHash.prototype.hsl = function(str) {
|
||||
var H, S, L;
|
||||
var hash = this.hash(str);
|
||||
|
||||
if (this.hueRanges.length) {
|
||||
var range = this.hueRanges[hash % this.hueRanges.length];
|
||||
var hueResolution = 727; // note that 727 is a prime
|
||||
H = ((hash / this.hueRanges.length) % hueResolution) * (range.max - range.min) / hueResolution + range.min;
|
||||
} else {
|
||||
H = hash % 359; // note that 359 is a prime
|
||||
}
|
||||
hash = parseInt(hash / 360);
|
||||
S = this.S[hash % this.S.length];
|
||||
hash = parseInt(hash / this.S.length);
|
||||
L = this.L[hash % this.L.length];
|
||||
|
||||
return [H, S, L];
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the hash in [r, g, b].
|
||||
* Note that R, G, B ∈ [0, 255]
|
||||
*
|
||||
* @param {String} str string to hash
|
||||
* @returns {Array} [r, g, b]
|
||||
*/
|
||||
ColorHash.prototype.rgb = function(str) {
|
||||
var hsl = this.hsl(str);
|
||||
return HSL2RGB.apply(this, hsl);
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the hash in hex
|
||||
*
|
||||
* @param {String} str string to hash
|
||||
* @returns {String} hex with #
|
||||
*/
|
||||
ColorHash.prototype.hex = function(str) {
|
||||
var rgb = this.rgb(str);
|
||||
return RGB2HEX(rgb);
|
||||
};
|
||||
|
||||
module.exports = ColorHash;
|
||||
|
||||
},{"./bkdr-hash":1}]},{},[2])(2)
|
||||
});
|
After Width: | Height: | Size: 81 KiB |
Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 7.2 KiB |
Before Width: | Height: | Size: 564 KiB After Width: | Height: | Size: 122 KiB |
Before Width: | Height: | Size: 8.9 KiB After Width: | Height: | Size: 7.2 KiB |
Before Width: | Height: | Size: 214 KiB After Width: | Height: | Size: 194 KiB |
After Width: | Height: | Size: 89 KiB |
Before Width: | Height: | Size: 528 KiB After Width: | Height: | Size: 29 KiB |
Before Width: | Height: | Size: 8.9 KiB After Width: | Height: | Size: 69 KiB |
Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 7.2 KiB |
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 7.2 KiB |
Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 82 KiB |
Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 8.9 KiB After Width: | Height: | Size: 71 KiB |
After Width: | Height: | Size: 7.2 KiB |
After Width: | Height: | Size: 73 KiB |
After Width: | Height: | Size: 7.2 KiB |
After Width: | Height: | Size: 73 KiB |
|
@ -6,7 +6,7 @@
|
|||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<meta name="geometry" content="diagram">
|
||||
<link rel="stylesheet" href="./assets/common.css">
|
||||
<title>point_circle</title>
|
||||
<title>dashline demo</title>
|
||||
<style>
|
||||
#map { position:absolute; top:0; bottom:0; width:100%; }
|
||||
</style>
|
||||
|
@ -20,38 +20,36 @@
|
|||
<script src="../build/L7.js"></script>
|
||||
<script>
|
||||
|
||||
const color1 = [ 'rgba(37, 140, 249, 0.8)', 'rgba(14, 241, 242, 0.8)', 'rgba(255, 255, 255, 0.8)' ];
|
||||
const scene = new L7.Scene({
|
||||
id: 'map',
|
||||
mapStyle: 'dark', // 样式URL
|
||||
center: [104.838088,34.075889 ],
|
||||
pitch: 0,
|
||||
zoom: 9,
|
||||
minZoom: 8,
|
||||
rotation:0
|
||||
center: [ 102.602992, 23.107329],
|
||||
pitch: 15,
|
||||
zoom: 14.82,
|
||||
});
|
||||
scene.on('loaded', () => {
|
||||
scene.image.addImage('local', 'https://gw.alipayobjects.com/zos/rmsportal/xZXhTxbglnuTmZEwqQrE.png');
|
||||
$.get('https://gw.alipayobjects.com/os/rmsportal/oVTMqfzuuRFKiDwhPSFL.json', data => {
|
||||
scene.PointLayer({
|
||||
$.get('https://gw.alipayobjects.com/os/rmsportal/ZVfOvhVCzwBkISNsuKCc.json', data => {
|
||||
scene.LineLayer({
|
||||
zIndex: 2
|
||||
})
|
||||
.source(data.list, {
|
||||
type: 'array',
|
||||
x: 'j',
|
||||
y: 'w',
|
||||
.source(data)
|
||||
.size('ELEV',(value)=>{
|
||||
return [2,(value-1000)*7];
|
||||
})
|
||||
.shape('m','text')
|
||||
.size(20)
|
||||
.color('#fff')
|
||||
.active(true)
|
||||
.shape('line')
|
||||
.style({
|
||||
stroke:'#fff',
|
||||
strokeWidth:1.0,
|
||||
opacity:1.0,
|
||||
lineType: 'dash',
|
||||
dashArray: 200,
|
||||
dashOffset: 0.2,
|
||||
dashRatio: 0.5
|
||||
})
|
||||
.color('ELEV',["#E8FCFF", "#CFF6FF", "#A1E9ff", "#65CEF7", "#3CB1F0", "#2894E0", "#1772c2", "#105CB3", "#0D408C", "#002466"].reverse())
|
||||
.render();
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
</html>
|
|
@ -29,7 +29,7 @@ const scene = new L7.Scene({
|
|||
});
|
||||
scene.on('loaded', () => {
|
||||
$.get('https://gw.alipayobjects.com/os/basement_prod/7359a5e9-3c5e-453f-b207-bc892fb23b84.csv', data => {
|
||||
var layer = scene.HeatMapLayer({
|
||||
var layer = scene.HeatmapLayer({
|
||||
zIndex: 2
|
||||
})
|
||||
.source(data, {
|
||||
|
@ -59,7 +59,8 @@ scene.on('loaded', () => {
|
|||
}
|
||||
]
|
||||
})
|
||||
.shape('gird')
|
||||
.shape('grid')
|
||||
.active({fill:'red'})
|
||||
.style({
|
||||
coverage: 0.8
|
||||
})
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<meta name="geometry" content="diagram">
|
||||
<link rel="stylesheet" href="./assets/common.css">
|
||||
<title>heatmap</title>
|
||||
<style>
|
||||
#map { position:absolute; top:0; bottom:0; width:100%; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="map"></div>
|
||||
<script src="https://webapi.amap.com/maps?v=1.4.8&key=f28fca5384129d180ad82915156a9baf&plugin=Map3D"></script>
|
||||
<script src="./assets/jquery-3.2.1.min.js"></script>
|
||||
<script src="./assets/dat.gui.min.js"></script>
|
||||
<script src="../build/L7.js"></script>
|
||||
<script>
|
||||
const scene = new L7.Scene({
|
||||
id: 'map',
|
||||
mapStyle: 'amap://styles/c9f1d10cae34f8ab05e425462c5a58d7', // 样式URL
|
||||
center: [ -155, 60 ],
|
||||
pitch: 0,
|
||||
zoom: 4.5
|
||||
});
|
||||
|
||||
window.scene = scene;
|
||||
scene.on('loaded', () => {
|
||||
$.get('https://gw.alipayobjects.com/os/basement_prod/08c6ea00-dc5f-4bb0-b0b5-52bde5edf0a3.json', data => {
|
||||
scene.HeatmapLayer({
|
||||
zIndex: 2
|
||||
})
|
||||
.source(data)
|
||||
.size('mag', [ 0, 1 ]) // weight映射通道
|
||||
.style({
|
||||
intensity: 10,
|
||||
radius: 10,
|
||||
opacity:1,
|
||||
rampColors: {
|
||||
colors: [ '#ffda45ff', '#fde725ff', '#ffc934ff', '#ffb824ff', '#ffb824ff', '#fa8100ff' ],
|
||||
positions: [ 0, 0.2, 0.4, 0.6, 0.9, 1.0 ]
|
||||
}
|
||||
})
|
||||
.render();
|
||||
/*scene.PointLayer({
|
||||
zIndex: 2
|
||||
})
|
||||
.source(data)
|
||||
.shape('2d:circle')
|
||||
.size(2) // weight映射通道
|
||||
.color('red')
|
||||
.render();*/
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -29,7 +29,7 @@ const scene = new L7.Scene({
|
|||
});
|
||||
scene.on('loaded', () => {
|
||||
$.get('https://gw.alipayobjects.com/os/basement_prod/7359a5e9-3c5e-453f-b207-bc892fb23b84.csv', data => {
|
||||
var layer = scene.HeatMapLayer({
|
||||
var layer = scene.HeatmapLayer({
|
||||
zIndex: 2
|
||||
})
|
||||
.source(data, {
|
||||
|
|
|
@ -39,7 +39,7 @@ scene.on('loaded', () => {
|
|||
//.color('#F08D41')
|
||||
.color('#ff893a')
|
||||
.animate({enable:true})
|
||||
.render();
|
||||
//.render();
|
||||
});
|
||||
$.get('https://gw.alipayobjects.com/os/rmsportal/vmvAxgsEwbpoSWbSYvix.json', data => {
|
||||
buildLayer = scene.PolygonLayer({
|
||||
|
@ -55,7 +55,23 @@ scene.on('loaded', () => {
|
|||
baseColor:'rgb(16,16,16)',
|
||||
windowColor:'rgb(30,60,89)',
|
||||
//brightColor:'rgb(155,217,255)'
|
||||
brightColor:'rgb(255,176,38)'
|
||||
brightColor:'rgb(255,176,38)',
|
||||
lights: [
|
||||
{
|
||||
type: 'directional',
|
||||
direction: [ 1, 10.5, 12 ],
|
||||
ambient: [ 0.2, 0.2, 0.2 ],
|
||||
diffuse: 'red',
|
||||
specular: [ 0.1, 0.1, 0.1 ]
|
||||
},
|
||||
{
|
||||
type: 'directional',
|
||||
direction: [ 1, -10.5, 12 ],
|
||||
ambient: [ 0.2, 0.2, 0.2 ],
|
||||
diffuse: 'green',
|
||||
specular: [ 0.1, 0.1, 0.1 ]
|
||||
},
|
||||
]
|
||||
})
|
||||
.render();
|
||||
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<meta name="geometry" content="diagram">
|
||||
<link rel="stylesheet" href="./assets/common.css">
|
||||
<link rel="stylesheet" href="./assets/info.css">
|
||||
|
||||
<title>hexagon demo</title>
|
||||
<style>
|
||||
body {margin: 0;}
|
||||
#map { position:absolute; top:0; bottom:0; width:100%; }
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="map"></div>
|
||||
<script src="https://webapi.amap.com/maps?v=1.4.8&key=15cd8a57710d40c9b7c0e3cc120f1200&plugin=Map3D"></script>
|
||||
<script src="./assets/jquery-3.2.1.min.js"></script>
|
||||
<script src="./assets/dat.gui.min.js"></script>
|
||||
<script src="../build/L7.js"></script>
|
||||
<script>
|
||||
|
||||
const scene = new L7.Scene({
|
||||
id: 'map',
|
||||
mapStyle: 'light', // 样式URL
|
||||
center: [104.838088,34.075889 ],
|
||||
pitch: 0,
|
||||
hash:true,
|
||||
zoom: 3,
|
||||
|
||||
});
|
||||
window.scene = scene;
|
||||
scene.on('loaded', () => {
|
||||
scene.ImageTileLayer({
|
||||
zIndex:4
|
||||
})
|
||||
.source('http://t1.tianditu.com/DataServer?T=cva_w&X={x}&Y={y}&L={z}&tk=174705aebfe31b79b3587279e211cb9a')
|
||||
.render();
|
||||
|
||||
$.getJSON('https://gw.alipayobjects.com/os/rmsportal/JToMOWvicvJOISZFCkEI.json', city => {
|
||||
const citylayer = scene.PolygonLayer(
|
||||
{
|
||||
zIndex:0
|
||||
}
|
||||
)
|
||||
.source(city)
|
||||
.active(false)
|
||||
.color('pm2_5_24h',["#FFF5B8","#FFDC7D","#FFAB5C","#F27049","#D42F31","#730D1C"])
|
||||
.shape('fill')
|
||||
.style({
|
||||
opacity: 1.0
|
||||
})
|
||||
.render();
|
||||
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<meta name="geometry" content="diagram">
|
||||
<link rel="stylesheet" href="./assets/common.css">
|
||||
<link rel="stylesheet" href="./assets/info.css">
|
||||
|
||||
<title>hexagon demo</title>
|
||||
<style>
|
||||
body {margin: 0;}
|
||||
#map { position:absolute; top:0; bottom:0; width:100%; }
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="map"></div>
|
||||
<script src="https://webapi.amap.com/maps?v=1.4.8&key=15cd8a57710d40c9b7c0e3cc120f1200&plugin=Map3D"></script>
|
||||
<script src="./assets/jquery-3.2.1.min.js"></script>
|
||||
<script src="./assets/dat.gui.min.js"></script>
|
||||
<script src="../build/L7.js"></script>
|
||||
<script>
|
||||
|
||||
const scene = new L7.Scene({
|
||||
id: 'map',
|
||||
mapStyle: 'dark', // 样式URL
|
||||
center: [116.5909,39.9225 ],
|
||||
pitch: 0,
|
||||
hash:true,
|
||||
zoom: 14,
|
||||
|
||||
});
|
||||
window.scene = scene;
|
||||
scene.on('loaded', () => {
|
||||
|
||||
const layer = scene.VectorTileLayer({
|
||||
zIndex:0,
|
||||
layerType:'point'
|
||||
})
|
||||
//.source('https://pre-lbs-show.taobao.com/gettile?x={x}&y={y}&z={z}&pipeId=pipe_vt_test')
|
||||
|
||||
// http://alipay-rmsdeploy-image.cn-hangzhou.alipay.aliyun-inc.com/thinkgis/tile/point/{z}/{x}/{y}.pbf
|
||||
// https://mvt.amap.com/district/CHN2/8/203/105/4096?key=
|
||||
.source('http://alipay-rmsdeploy-image.cn-hangzhou.alipay.aliyun-inc.com/thinkgis/tile/point2/{z}/{x}/{y}.pbf',{
|
||||
parser:{
|
||||
type: 'mvt',
|
||||
sourceLayer:'layer',
|
||||
// idField:'adcode',
|
||||
maxZoom: 14,
|
||||
minZoom: 13,
|
||||
}
|
||||
})
|
||||
.scale({
|
||||
total:{
|
||||
min:0,
|
||||
max:100000,
|
||||
type:'log'
|
||||
}
|
||||
})
|
||||
.shape('normal')
|
||||
.size(1)
|
||||
.active({fill:'red'})
|
||||
.color('total', ['#d53e4f','#f46d43','#fdae61','#fee08b','#ffffbf','#e6f598','#abdda4','#66c2a5','#3288bd'].reverse())
|
||||
//.color('#0D408C')
|
||||
.style({
|
||||
stroke: 'rgba(255,255,255,0.8)',
|
||||
strokeWidth: 1,
|
||||
strokeOpacity:0.6,
|
||||
opacity: 1
|
||||
})
|
||||
.render(
|
||||
);
|
||||
layer.on('mousemove',(feature)=>{
|
||||
console.log(feature);
|
||||
})
|
||||
console.log(layer);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<meta name="geometry" content="diagram">
|
||||
<link rel="stylesheet" href="./assets/common.css">
|
||||
<link rel="stylesheet" href="./assets/info.css">
|
||||
|
||||
<title>hexagon demo</title>
|
||||
<style>
|
||||
body {margin: 0;}
|
||||
#map { position:absolute; top:0; bottom:0; width:100%; }
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="map"></div>
|
||||
<script src="https://webapi.amap.com/maps?v=1.4.8&key=15cd8a57710d40c9b7c0e3cc120f1200&plugin=Map3D"></script>
|
||||
<script src="./assets/color-hash.js"></script>
|
||||
<script src="./assets/jquery-3.2.1.min.js"></script>
|
||||
<script src="./assets/dat.gui.min.js"></script>
|
||||
<script src="../build/L7.js"></script>
|
||||
<script>
|
||||
|
||||
const scene = new L7.Scene({
|
||||
id: 'map',
|
||||
mapStyle: 'dark', // 样式URL
|
||||
center: [116.5909,39.9225 ],
|
||||
pitch: 0,
|
||||
hash:true,
|
||||
zoom: 4,
|
||||
|
||||
});
|
||||
window.scene = scene;
|
||||
var colorHash = new ColorHash();
|
||||
|
||||
|
||||
scene.on('loaded', () => {
|
||||
|
||||
const layer = scene.VectorTileLayer({
|
||||
zIndex:0,
|
||||
layerType:'polygon'
|
||||
})
|
||||
.source('http://alipay-rmsdeploy-image.cn-hangzhou.alipay.aliyun-inc.com/thinkgis/tile/china/province/{z}/{x}/{y}.pbf',{
|
||||
parser:{
|
||||
type: 'mvt',
|
||||
sourceLayer:'layer',
|
||||
idField:'code',
|
||||
maxZoom: 5,
|
||||
}
|
||||
})
|
||||
.scale({
|
||||
total:{
|
||||
type:'linear',
|
||||
min:0,
|
||||
max:5000000
|
||||
}
|
||||
})
|
||||
.shape('fill')
|
||||
.size(2)
|
||||
.active(false)
|
||||
.color('total', ['#ffffe5','#fff7bc','#fee391','#fec44f','#fe9929','#ec7014','#cc4c02','#993404','#662506'])
|
||||
.style({
|
||||
opacity:1.0
|
||||
})
|
||||
.render();
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<meta name="geometry" content="diagram">
|
||||
<link rel="stylesheet" href="./assets/common.css">
|
||||
<link rel="stylesheet" href="./assets/info.css">
|
||||
|
||||
<title>hexagon demo</title>
|
||||
<style>
|
||||
body {margin: 0;}
|
||||
#map { position:absolute; top:0; bottom:0; width:100%; }
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="map"></div>
|
||||
<script src="https://webapi.amap.com/maps?v=1.4.8&key=15cd8a57710d40c9b7c0e3cc120f1200&plugin=Map3D"></script>
|
||||
<script src="./assets/jquery-3.2.1.min.js"></script>
|
||||
<script src="./assets/dat.gui.min.js"></script>
|
||||
<script src="../build/L7.js"></script>
|
||||
<script>
|
||||
|
||||
const scene = new L7.Scene({
|
||||
id: 'map',
|
||||
mapStyle: 'light', // 样式URL
|
||||
center: [116.5909,39.9225 ],
|
||||
pitch: 0,
|
||||
hash:true,
|
||||
zoom: 16,
|
||||
|
||||
});
|
||||
window.scene = scene;
|
||||
scene.on('loaded', () => {
|
||||
const layer = scene.VectorTileLayer({
|
||||
zIndex:0,
|
||||
layerType:'point'
|
||||
})
|
||||
.source('http://alipay-rmsdeploy-image.cn-hangzhou.alipay.aliyun-inc.com/thinkgis/tile/china/all_point/{z}/{x}/{y}.pbf',{
|
||||
parser:{
|
||||
type: 'mvt',
|
||||
sourceLayer:'layer',
|
||||
maxZoom:14,
|
||||
}
|
||||
})
|
||||
.scale({
|
||||
bc_grade:{
|
||||
type:'cat',
|
||||
values:[1, 2 ,3, 4]
|
||||
},
|
||||
open_mode:{
|
||||
type:'cat',
|
||||
values:['线上','线下','自助']
|
||||
}
|
||||
})
|
||||
.active(false)
|
||||
// 'circle', 'triangle', 'square', 'pentagon', 'hexagon', 'octogon', 'hexagram', 'rhombus', 'vesica'
|
||||
.shape('open_mode', ['circle','hexagon','hexagram'])
|
||||
.size('bc_grade', [2,15])
|
||||
.color('bc_grade', ['#ffffcc','#d9f0a3','#addd8e','#78c679','#31a354','#006837'])
|
||||
.style({
|
||||
stroke: '#fff',
|
||||
strokeWidth: 1.0,
|
||||
strokeOpacity:1.,
|
||||
})
|
||||
.render(
|
||||
);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
32
package.json
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@antv/l7",
|
||||
"version": "1.0.3",
|
||||
"version": "1.2.0-beta.0",
|
||||
"description": "Large-scale WebGL-powered Geospatial Data Visualization",
|
||||
"main": "build/l7.js",
|
||||
"browser": "build/l7.js",
|
||||
|
@ -21,9 +21,9 @@
|
|||
"author": "https://github.com/orgs/antvis/people",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@babel/cli": "~7.0.0",
|
||||
"@babel/core": "~7.0.0",
|
||||
"@babel/preset-env": "~7.1.0",
|
||||
"@babel/cli": "^7.4.3",
|
||||
"@babel/core": "~7.4.3",
|
||||
"@babel/preset-env": "~7.4.3",
|
||||
"babel-eslint": "~8.0.3",
|
||||
"babel-loader": "~8.0.0",
|
||||
"babel-plugin-transform-remove-strict-mode": "~0.0.2",
|
||||
|
@ -48,6 +48,7 @@
|
|||
"open": "~0.0.5",
|
||||
"parseurl": "~1.3.2",
|
||||
"pre-commit": "~1.2.2",
|
||||
"rollup": "^1.16.2",
|
||||
"rollup-plugin-buble": "^0.19.6",
|
||||
"rollup-plugin-commonjs": "^9.2.1",
|
||||
"rollup-plugin-node-resolve": "^4.0.1",
|
||||
|
@ -58,11 +59,14 @@
|
|||
"torchjs": "~2.1.0",
|
||||
"uglify-js": "~3.1.10",
|
||||
"webpack": "~4.29.6",
|
||||
"webpack-cli": "^3.2.3",
|
||||
"worker-loader": "^2.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
"build-dev": "rollup -c --environment BUILD:dev && npm run demos-web",
|
||||
"watch-dev": "rollup -c --environment BUILD:dev --watch",
|
||||
"build-dev": "rollup -c --environment BUILD:dev && npm run demos-web",
|
||||
"watch-dev": "rollup -c --environment BUILD:dev --watch & npm run demos-web ",
|
||||
"build-prod": "rollup -c --environment BUILD:production",
|
||||
"build-prod-min": "rollup -c --environment BUILD:production,MINIFY:true",
|
||||
"build": "webpack",
|
||||
"build-lib": "babel src --out-dir lib",
|
||||
"bundler": "electron ./bundler/app.js",
|
||||
|
@ -83,8 +87,8 @@
|
|||
"mkdir-dist": "node ./bin/mkdir-dist.js",
|
||||
"prepublishOnly": "npm run build-lib && npm run dist",
|
||||
"screenshot": "node ./bin/screenshot.js",
|
||||
"start": "npm run dev",
|
||||
"test": "torch --compile-opts ./.torch.compile.opts.js --compile --renderer --recursive test/unit",
|
||||
"start": "npm run watch-dev",
|
||||
"test": "torch --compile-opts ./.torch.compile.opts.js --compile --renderer --recursive test/unit",
|
||||
"test-all": "npm run test && npm run test-bugs",
|
||||
"test-bugs": "torch --compile --renderer --recursive test/bugs",
|
||||
"test-bugs-live": "torch --compile --interactive --watch --recursive test/bugs",
|
||||
|
@ -94,14 +98,17 @@
|
|||
},
|
||||
"pre-commit": {
|
||||
"run": [
|
||||
"lint"
|
||||
"lint",
|
||||
"test"
|
||||
],
|
||||
"silent": false
|
||||
},
|
||||
"dependencies": {
|
||||
"@antv/g": "^3.1.3",
|
||||
"@antv/util": "~1.2.5",
|
||||
"@antv/geo-coord": "^1.0.8",
|
||||
"@antv/util": "~2.0.1",
|
||||
"@mapbox/tiny-sdf": "^1.1.0",
|
||||
"@mapbox/vector-tile": "^1.3.1",
|
||||
"@turf/bbox": "^6.0.1",
|
||||
"@turf/clean-coords": "^6.0.1",
|
||||
"@turf/invariant": "^6.1.2",
|
||||
|
@ -111,10 +118,13 @@
|
|||
"earcut": "^2.1.3",
|
||||
"fecha": "^2.3.3",
|
||||
"gl-matrix": "^2.4.1",
|
||||
"gl-vec2": "^1.3.0",
|
||||
"lodash": "^4.17.5",
|
||||
"polyline-normals": "^2.0.2",
|
||||
"pbf": "^3.2.0",
|
||||
"polyline-miter-util": "^1.0.1",
|
||||
"rbush": "^2.0.2",
|
||||
"simple-statistics": "^7.0.1",
|
||||
"supercluster": "^6.0.1",
|
||||
"three": "^0.101.1",
|
||||
"venn.js": "^0.2.20",
|
||||
"viewport-mercator-project": "^5.2.0",
|
||||
|
|
|
@ -24,11 +24,11 @@ const config = [
|
|||
indent: false,
|
||||
chunkFileNames: 'shared.js'
|
||||
},
|
||||
experimentalCodeSplitting: true,
|
||||
treeshake: production,
|
||||
// experimentalCodeSplitting: true,
|
||||
treeshake: false,
|
||||
plugins: [
|
||||
glsl(
|
||||
[ './src/geom/shader/*.glsl', './src/core/engine/picking/*.glsl' ],
|
||||
[ './src/geom/shader/*.glsl', './src/core/engine/picking/*.glsl', './src/geom/shader/**/*.glsl' ],
|
||||
production
|
||||
),
|
||||
minified ? terser() : false,
|
||||
|
|
|
@ -84,6 +84,9 @@ const ColorUtil = {
|
|||
const rgba = this.toRGB(str);
|
||||
return rgba.map(v => v / 255);
|
||||
},
|
||||
colorArray2RGBA(arr) {
|
||||
return `rgba(${arr[0] * 255},${arr[1] * 255},${arr[2] * 255},${arr[3]})`;
|
||||
},
|
||||
color2RGBA(str) {
|
||||
return this.color2Arr(str);
|
||||
},
|
||||
|
|
|
@ -42,10 +42,11 @@ class Size extends Base {
|
|||
_scaling(scale, v) {
|
||||
if (scale.type === 'identity') {
|
||||
return v;
|
||||
} else if (scale.type === 'linear') {
|
||||
const percent = scale.scale(v);
|
||||
return this.getLinearValue(percent);
|
||||
}
|
||||
const percent = scale.scale(v);
|
||||
return this.getLinearValue(percent);
|
||||
|
||||
// else if (scale.type === 'linear') {
|
||||
}
|
||||
|
||||
getLinearValue(percent) {
|
||||
|
|
|
@ -0,0 +1,227 @@
|
|||
import TinySDF from '@mapbox/tiny-sdf';
|
||||
import { buildMapping } from '../../util/font-util';
|
||||
import * as THREE from '../../core/three';
|
||||
import LRUCache from '../../util/lru-cache';
|
||||
export const DEFAULT_CHAR_SET = getDefaultCharacterSet();
|
||||
export const DEFAULT_FONT_FAMILY = 'sans-serif';
|
||||
export const DEFAULT_FONT_WEIGHT = 'normal';
|
||||
export const DEFAULT_FONT_SIZE = 24;
|
||||
export const DEFAULT_BUFFER = 3;
|
||||
export const DEFAULT_CUTOFF = 0.25;
|
||||
export const DEFAULT_RADIUS = 8;
|
||||
const MAX_CANVAS_WIDTH = 1024;
|
||||
const BASELINE_SCALE = 0.9;
|
||||
const HEIGHT_SCALE = 1.2;
|
||||
const CACHE_LIMIT = 3;
|
||||
const cache = new LRUCache(CACHE_LIMIT);
|
||||
|
||||
const VALID_PROPS = [
|
||||
'fontFamily',
|
||||
'fontWeight',
|
||||
'characterSet',
|
||||
'fontSize',
|
||||
'sdf',
|
||||
'buffer',
|
||||
'cutoff',
|
||||
'radius'
|
||||
];
|
||||
|
||||
function getDefaultCharacterSet() {
|
||||
const charSet = [];
|
||||
for (let i = 32; i < 128; i++) {
|
||||
charSet.push(String.fromCharCode(i));
|
||||
}
|
||||
return charSet;
|
||||
}
|
||||
|
||||
function setTextStyle(ctx, fontFamily, fontSize, fontWeight) {
|
||||
ctx.font = `${fontWeight} ${fontSize}px ${fontFamily}`;
|
||||
ctx.fillStyle = '#000';
|
||||
ctx.textBaseline = 'baseline';
|
||||
ctx.textAlign = 'left';
|
||||
}
|
||||
function getNewChars(key, characterSet) {
|
||||
const cachedFontAtlas = cache.get(key);
|
||||
if (!cachedFontAtlas) {
|
||||
return characterSet;
|
||||
}
|
||||
|
||||
const newChars = [];
|
||||
const cachedMapping = cachedFontAtlas.mapping;
|
||||
let cachedCharSet = Object.keys(cachedMapping);
|
||||
cachedCharSet = new Set(cachedCharSet);
|
||||
|
||||
let charSet = characterSet;
|
||||
if (charSet instanceof Array) {
|
||||
charSet = new Set(charSet);
|
||||
}
|
||||
|
||||
charSet.forEach(char => {
|
||||
if (!cachedCharSet.has(char)) {
|
||||
newChars.push(char);
|
||||
}
|
||||
});
|
||||
|
||||
return newChars;
|
||||
}
|
||||
|
||||
function populateAlphaChannel(alphaChannel, imageData) {
|
||||
// populate distance value from tinySDF to image alpha channel
|
||||
for (let i = 0; i < alphaChannel.length; i++) {
|
||||
imageData.data[4 * i + 3] = alphaChannel[i];
|
||||
}
|
||||
}
|
||||
|
||||
export default class FontAtlasManager {
|
||||
constructor() {
|
||||
|
||||
// font settings
|
||||
this.props = {
|
||||
fontFamily: DEFAULT_FONT_FAMILY,
|
||||
fontWeight: DEFAULT_FONT_WEIGHT,
|
||||
characterSet: DEFAULT_CHAR_SET,
|
||||
fontSize: DEFAULT_FONT_SIZE,
|
||||
buffer: DEFAULT_BUFFER,
|
||||
// sdf only props
|
||||
// https://github.com/mapbox/tiny-sdf
|
||||
sdf: true,
|
||||
cutoff: DEFAULT_CUTOFF,
|
||||
radius: DEFAULT_RADIUS
|
||||
};
|
||||
|
||||
// key is used for caching generated fontAtlas
|
||||
this._key = null;
|
||||
this._texture = new THREE.Texture();
|
||||
}
|
||||
|
||||
get texture() {
|
||||
return this._texture;
|
||||
}
|
||||
|
||||
get mapping() {
|
||||
const data = cache.get(this._key);
|
||||
return data && data.mapping;
|
||||
}
|
||||
|
||||
get scale() {
|
||||
return HEIGHT_SCALE;
|
||||
}
|
||||
|
||||
get fontAtlas() {
|
||||
return this._fontAtlas;
|
||||
}
|
||||
|
||||
setProps(props = {}) {
|
||||
VALID_PROPS.forEach(prop => {
|
||||
if (prop in props) {
|
||||
this.props[prop] = props[prop];
|
||||
}
|
||||
});
|
||||
|
||||
// update cache key
|
||||
const oldKey = this._key;
|
||||
this._key = this._getKey();
|
||||
|
||||
const charSet = getNewChars(this._key, this.props.characterSet);
|
||||
const cachedFontAtlas = cache.get(this._key);
|
||||
|
||||
// if a fontAtlas associated with the new settings is cached and
|
||||
// there are no new chars
|
||||
if (cachedFontAtlas && charSet.length === 0) {
|
||||
// update texture with cached fontAtlas
|
||||
if (this._key !== oldKey) {
|
||||
this._updateTexture(cachedFontAtlas);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// update fontAtlas with new settings
|
||||
const fontAtlas = this._generateFontAtlas(this._key, charSet, cachedFontAtlas);
|
||||
this._fontAtlas = fontAtlas;
|
||||
this._updateTexture(fontAtlas);
|
||||
|
||||
// update cache
|
||||
cache.set(this._key, fontAtlas);
|
||||
}
|
||||
|
||||
_updateTexture({ data: canvas }) {
|
||||
this._texture = new THREE.CanvasTexture(canvas);
|
||||
this._texture.wrapS = THREE.ClampToEdgeWrapping;
|
||||
this._texture.wrapT = THREE.ClampToEdgeWrapping;
|
||||
this._texture.minFilter = THREE.LinearFilter;
|
||||
this._texture.flipY = false;
|
||||
this._texture.needUpdate = true;
|
||||
}
|
||||
|
||||
_generateFontAtlas(key, characterSet, cachedFontAtlas) {
|
||||
const { fontFamily, fontWeight, fontSize, buffer, sdf, radius, cutoff } = this.props;
|
||||
let canvas = cachedFontAtlas && cachedFontAtlas.data;
|
||||
if (!canvas) {
|
||||
canvas = document.createElement('canvas');
|
||||
canvas.width = MAX_CANVAS_WIDTH;
|
||||
}
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
setTextStyle(ctx, fontFamily, fontSize, fontWeight);
|
||||
|
||||
// 1. build mapping
|
||||
const { mapping, canvasHeight, xOffset, yOffset } = buildMapping(
|
||||
Object.assign(
|
||||
{
|
||||
getFontWidth: char => ctx.measureText(char).width,
|
||||
fontHeight: fontSize * HEIGHT_SCALE,
|
||||
buffer,
|
||||
characterSet,
|
||||
maxCanvasWidth: MAX_CANVAS_WIDTH
|
||||
},
|
||||
cachedFontAtlas && {
|
||||
mapping: cachedFontAtlas.mapping,
|
||||
xOffset: cachedFontAtlas.xOffset,
|
||||
yOffset: cachedFontAtlas.yOffset
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
// 2. update canvas
|
||||
// copy old canvas data to new canvas only when height changed
|
||||
if (canvas.height !== canvasHeight) {
|
||||
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
||||
canvas.height = canvasHeight;
|
||||
ctx.putImageData(imageData, 0, 0);
|
||||
}
|
||||
setTextStyle(ctx, fontFamily, fontSize, fontWeight);
|
||||
|
||||
// 3. layout characters
|
||||
if (sdf) {
|
||||
const tinySDF = new TinySDF(fontSize, buffer, radius, cutoff, fontFamily, fontWeight);
|
||||
// used to store distance values from tinySDF
|
||||
// tinySDF.size equals `fontSize + buffer * 2`
|
||||
const imageData = ctx.getImageData(0, 0, tinySDF.size, tinySDF.size);
|
||||
|
||||
for (const char of characterSet) {
|
||||
populateAlphaChannel(tinySDF.draw(char), imageData);
|
||||
ctx.putImageData(imageData, mapping[char].x - buffer, mapping[char].y - buffer);
|
||||
}
|
||||
} else {
|
||||
for (const char of characterSet) {
|
||||
ctx.fillText(char, mapping[char].x, mapping[char].y + fontSize * BASELINE_SCALE);
|
||||
}
|
||||
}
|
||||
return {
|
||||
xOffset,
|
||||
yOffset,
|
||||
mapping,
|
||||
data: canvas,
|
||||
width: canvas.width,
|
||||
height: canvas.height
|
||||
};
|
||||
}
|
||||
|
||||
_getKey() {
|
||||
const { fontFamily, fontWeight, fontSize, buffer, sdf, radius, cutoff } = this.props;
|
||||
if (sdf) {
|
||||
return `${fontFamily} ${fontWeight} ${fontSize} ${buffer} ${radius} ${cutoff}`;
|
||||
}
|
||||
return `${fontFamily} ${fontWeight} ${fontSize} ${buffer}`;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
import { buildIconMaping } from '../../util/font-util';
|
||||
import * as THREE from '../../../../core/three';
|
||||
const BUFFER = 3;
|
||||
const MAX_CANVAS_WIDTH = 1024;
|
||||
export default class IconManager {
|
||||
constructor() {
|
||||
this._getIcon = null;
|
||||
this._mapping = {};
|
||||
this._autoPacking = false;
|
||||
this.iconData = {};
|
||||
this._canvas = document.createElement('canvas');
|
||||
this._texture = new THREE.Texture(this._canvas);
|
||||
this.ctx = this._canvas.getContext('2d');
|
||||
}
|
||||
getTexture() {
|
||||
return this._texture;
|
||||
}
|
||||
|
||||
_updateIconAtlas() {
|
||||
this._canvas.width = MAX_CANVAS_WIDTH;
|
||||
this._canvas.height = this._canvasHeigth;
|
||||
for (const key in this.mapping) {
|
||||
const icon = this.mapping[key];
|
||||
const { x, y, image } = icon;
|
||||
this.ctx.drawImage(image, x, y, this.imageWidth, this.imageWidth);
|
||||
}
|
||||
this.texture.magFilter = THREE.LinearFilter;
|
||||
this.texture.minFilter = THREE.LinearFilter;
|
||||
this.texture.needsUpdate = true;
|
||||
}
|
||||
|
||||
addImage(id, opt) {
|
||||
this._loadImage(opt).then(image => {
|
||||
this.iconData.push({ id, image });
|
||||
const { mapping, canvasHeight } = buildIconMaping(this.iconData, BUFFER, MAX_CANVAS_WIDTH);
|
||||
this._mapping = mapping;
|
||||
this._canvasHeigth = canvasHeight;
|
||||
});
|
||||
}
|
||||
_loadImage(url) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const image = new Image();
|
||||
image.onload = () => {
|
||||
resolve(image);
|
||||
};
|
||||
image.onerror = function() {
|
||||
reject(new Error('Could not load image at ' + url));
|
||||
};
|
||||
image.src = url;
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
import Util from '../../util';
|
||||
import { updateObjecteUniform } from '../../util/object3d-util';
|
||||
export default class BufferController {
|
||||
constructor(cfg) {
|
||||
// defs 列定义
|
||||
Util.assign(this, cfg);
|
||||
if (!this.mesh) this.mesh = this.layer;
|
||||
}
|
||||
|
||||
_updateColorAttributes() {
|
||||
const filterData = this.mesh.layerData;
|
||||
const colorKey = {};
|
||||
for (let e = 0; e < filterData.length; e++) {
|
||||
const item = filterData[e];
|
||||
colorKey[item.id] = item.color;
|
||||
}
|
||||
this.layer._activeIds = null; // 清空选中元素xwxw
|
||||
const colorAttr = this.mesh.mesh.geometry.attributes.a_color;
|
||||
const pickAttr = this.mesh.mesh.geometry.attributes.pickingId;
|
||||
pickAttr.array.forEach((id, index) => {
|
||||
let newId = Math.abs(id);
|
||||
let item = null;
|
||||
let color = null;
|
||||
if (this.mesh.layerSource.data.featureKeys) { // hash数据映射
|
||||
newId = this.mesh.layerSource.data.featureKeys[newId].index;
|
||||
item = filterData[newId];
|
||||
color = colorKey[item.id];
|
||||
} else {
|
||||
item = filterData[newId - 1];
|
||||
color = colorKey[newId];
|
||||
}
|
||||
|
||||
if (item.hasOwnProperty('filter') && item.filter === false) {
|
||||
colorAttr.array[index * 4 + 0] = 0;
|
||||
colorAttr.array[index * 4 + 1] = 0;
|
||||
colorAttr.array[index * 4 + 2] = 0;
|
||||
colorAttr.array[index * 4 + 3] = 0;
|
||||
pickAttr.array[index] = -id; // 通过Id数据过滤 id<0 不显示
|
||||
} else {
|
||||
colorAttr.array[index * 4 + 0] = color[0];
|
||||
colorAttr.array[index * 4 + 1] = color[1];
|
||||
colorAttr.array[index * 4 + 2] = color[2];
|
||||
colorAttr.array[index * 4 + 3] = color[3];
|
||||
pickAttr.array[index] = id;
|
||||
}
|
||||
});
|
||||
colorAttr.needsUpdate = true;
|
||||
pickAttr.needsUpdate = true;
|
||||
}
|
||||
_updateStyle(option) {
|
||||
const newOption = { };
|
||||
for (const key in option) {
|
||||
newOption['u_' + key] = option[key];
|
||||
}
|
||||
updateObjecteUniform(this.mesh._object3D, newOption);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
import Util from '../../util';
|
||||
export default class EventContoller {
|
||||
constructor(cfg) {
|
||||
Util.assign(this, cfg);
|
||||
}
|
||||
_init() {
|
||||
this.layer.scene.on('pick-' + this.layer.layerId, e => {
|
||||
let { featureId, point2d, type } = e;
|
||||
if (featureId < 0 && this._activeIds !== null) {
|
||||
type = 'mouseleave';
|
||||
}
|
||||
this._activeIds = featureId;
|
||||
// TODO 瓦片图层获取选中数据信息
|
||||
const lnglat = this.layer.scene.containerToLngLat(point2d);
|
||||
const { feature, style } = this.layer.getSelectFeature(featureId, lnglat);
|
||||
// const style = this.layerData[featureId - 1];
|
||||
const target = {
|
||||
featureId,
|
||||
feature,
|
||||
style,
|
||||
pixel: point2d,
|
||||
type,
|
||||
lnglat: { lng: lnglat.lng, lat: lnglat.lat }
|
||||
};
|
||||
if (featureId >= 0 || this._activeIds >= 0) { // 拾取到元素,或者离开元素
|
||||
this.layer.emit(type, target);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
_initMapEvent() {
|
||||
|
||||
}
|
||||
}
|
|
@ -1,4 +1,14 @@
|
|||
import Scale from './scale';
|
||||
import Mapping from './mapping';
|
||||
import Picking from './pick';
|
||||
import Interaction from './interaction';
|
||||
import Event from './event';
|
||||
import Buffer from './buffer';
|
||||
export default {
|
||||
Scale
|
||||
Scale,
|
||||
Mapping,
|
||||
Picking,
|
||||
Interaction,
|
||||
Event,
|
||||
Buffer
|
||||
};
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
import Util from '../../util';
|
||||
import { getInteraction } from '../../interaction/index';
|
||||
export default class InteractionController {
|
||||
constructor(cfg) {
|
||||
// defs 列定义
|
||||
Util.assign(this, cfg);
|
||||
}
|
||||
// interaction 方法
|
||||
clearAllInteractions() {
|
||||
const interactions = this.layer.get('interactions');
|
||||
Util.each(interactions, (interaction, key) => {
|
||||
interaction.destory();
|
||||
delete interactions[key];
|
||||
});
|
||||
return this;
|
||||
}
|
||||
clearInteraction(type) {
|
||||
const interactions = this.layer.get('interactions');
|
||||
if (interactions[type]) {
|
||||
interactions[type].destory();
|
||||
delete interactions[type];
|
||||
}
|
||||
return this;
|
||||
}
|
||||
addInteraction(type, cfg = {}) {
|
||||
cfg.layer = this.layer;
|
||||
const Ctor = getInteraction(type);
|
||||
const interaction = new Ctor(cfg);
|
||||
this._setInteraction(type, interaction);
|
||||
return this;
|
||||
}
|
||||
_setInteraction(type, interaction) {
|
||||
const interactions = this.layer.get('interactions');
|
||||
if (interactions[type]) {
|
||||
interactions[type].destory();
|
||||
}
|
||||
interactions[type] = interaction;
|
||||
}
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
|
||||
export class layerControl {
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
|
||||
import { getMap } from '../../map';
|
||||
import Base from '../base';
|
||||
export default class MapContorller extends Base {
|
||||
constructor(cfg, engine, scene) {
|
||||
super(cfg);
|
||||
this._engine = engine;
|
||||
this.scene = scene;
|
||||
}
|
||||
_init() {
|
||||
const mapType = this.get('mapType');
|
||||
const mapCfg = this.get('mapCfg');
|
||||
this.map = new getMap(mapType)(mapCfg);
|
||||
this.map('mapLoad', this._mapload.bind(this));
|
||||
}
|
||||
_mapload() {
|
||||
this.map.asyncCamera(this._engine);
|
||||
this.emit('loaded');
|
||||
}
|
||||
_bindMapMethod() {
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,188 @@
|
|||
import Util from '../../util';
|
||||
import Global from '../../global';
|
||||
import ScaleController from './scale';
|
||||
import Attr from '../../attr/index';
|
||||
export default class Mapping {
|
||||
/** 初始化mapping
|
||||
* 初始化mapping
|
||||
* @param {*} cfg 配置
|
||||
* @param {*} cfg.layer layer对象
|
||||
* @param {*} cfg.mesh mesh对象
|
||||
*/
|
||||
constructor(cfg) {
|
||||
Util.assign(this, cfg);
|
||||
if (!this.mesh) this.mesh = this.layer;
|
||||
this._init();
|
||||
}
|
||||
_init() {
|
||||
this._initControllers();
|
||||
this._initTileAttrs();
|
||||
this._mapping();
|
||||
}
|
||||
update() {
|
||||
this.mesh.set('scales', {});
|
||||
this._initTileAttrs();
|
||||
this._updateMaping();
|
||||
}
|
||||
_initControllers() {
|
||||
const scalesOption = this.layer.get('scaleOptions');
|
||||
const scaleController = new ScaleController({
|
||||
defs: {
|
||||
...scalesOption
|
||||
}
|
||||
});
|
||||
this.mesh.set('scaleController', scaleController);
|
||||
}
|
||||
_createScale(field) {
|
||||
const scales = this.mesh.get('scales');
|
||||
this._initControllers(); // scale更新
|
||||
let scale = scales[field];
|
||||
if (!scale) {
|
||||
scale = this.createScale(field);
|
||||
scales[field] = scale;
|
||||
}
|
||||
return scale;
|
||||
}
|
||||
createScale(field) {
|
||||
const data = this.mesh.layerSource.data.dataArray;
|
||||
const scales = this.mesh.get('scales');
|
||||
let scale = scales[field];
|
||||
const scaleController = this.mesh.get('scaleController');
|
||||
if (!scale) {
|
||||
scale = scaleController.createScale(field, data);
|
||||
scales[field] = scale;
|
||||
}
|
||||
return scale;
|
||||
}
|
||||
// 获取属性映射的值
|
||||
_getAttrValues(attr, record) {
|
||||
const scales = attr.scales;
|
||||
const params = [];
|
||||
for (let i = 0; i < scales.length; i++) {
|
||||
const scale = scales[i];
|
||||
const field = scale.field;
|
||||
if (scale.type === 'identity') {
|
||||
params.push(scale.value);
|
||||
} else {
|
||||
params.push(record[field]);
|
||||
}
|
||||
}
|
||||
const indexZoom = params.indexOf('zoom');
|
||||
indexZoom !== -1 ? params[indexZoom] = attr.zoom : null;
|
||||
const values = attr.mapping(...params);
|
||||
return values;
|
||||
}
|
||||
_mapping() {
|
||||
const attrs = this.mesh.get('attrs');
|
||||
const mappedData = [];
|
||||
const data = this.mesh.layerSource.data.dataArray;
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
const record = data[i];
|
||||
const newRecord = {};
|
||||
newRecord.id = data[i]._id;
|
||||
for (const k in attrs) {
|
||||
if (attrs.hasOwnProperty(k)) {
|
||||
const attr = attrs[k];
|
||||
const names = attr.names;
|
||||
const values = this._getAttrValues(attr, record);
|
||||
if (names.length > 1) { // position 之类的生成多个字段的属性
|
||||
for (let j = 0; j < values.length; j++) {
|
||||
const val = values[j];
|
||||
const name = names[j];
|
||||
newRecord[name] = (Util.isArray(val) && val.length === 1) ? val[0] : val; // 只有一个值时返回第一个属性值
|
||||
}
|
||||
} else {
|
||||
newRecord[names[0]] = values.length === 1 ? values[0] : values;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
newRecord.coordinates = record.coordinates;
|
||||
mappedData.push(newRecord);
|
||||
}
|
||||
// 通过透明度过滤数据
|
||||
if (attrs.hasOwnProperty('filter')) {
|
||||
mappedData.forEach(item => {
|
||||
if (item.filter === false) {
|
||||
(item.color[3] = 0);
|
||||
item.id = -item.id;
|
||||
}
|
||||
});
|
||||
}
|
||||
this.mesh.layerData = mappedData;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新数据maping
|
||||
* @param {*} layerSource 数据源
|
||||
* @param {*} layer map
|
||||
*/
|
||||
_updateMaping() {
|
||||
const attrs = this.mesh.get('attrs');
|
||||
|
||||
const data = this.mesh.layerSource.data.dataArray;
|
||||
const layerData = this.mesh.layerData;
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
const record = data[i];
|
||||
for (const attrName in attrs) {
|
||||
if (attrs.hasOwnProperty(attrName) && attrs[attrName].neadUpdate) {
|
||||
const attr = attrs[attrName];
|
||||
const names = attr.names;
|
||||
const values = this._getAttrValues(attr, record);
|
||||
if (names.length > 1) { // position 之类的生成多个字段的属性
|
||||
for (let j = 0; j < values.length; j++) {
|
||||
const val = values[j];
|
||||
const name = names[j];
|
||||
layerData[i][name] = (Util.isArray(val) && val.length === 1) ? val[0] : val; // 只有一个值时返回第一个属性值
|
||||
}
|
||||
} else {
|
||||
layerData[i][names[0]] = values.length === 1 ? values[0] : values;
|
||||
|
||||
}
|
||||
attr.neadUpdate = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
_initTileAttrs() {
|
||||
const attrOptions = this.layer.get('attrOptions');
|
||||
for (const type in attrOptions) {
|
||||
if (attrOptions.hasOwnProperty(type)) {
|
||||
this._updateTileAttr(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
_updateTileAttr(type) {
|
||||
const self = this;
|
||||
const attrs = this.mesh.get('attrs');
|
||||
const attrOptions = this.layer.get('attrOptions');
|
||||
const option = attrOptions[type];
|
||||
option.neadUpdate = true;
|
||||
const className = Util.upperFirst(type);
|
||||
const fields = this._parseFields(option.field);
|
||||
const scales = [];
|
||||
for (let i = 0; i < fields.length; i++) {
|
||||
const field = fields[i];
|
||||
const scale = self._createScale(field);
|
||||
|
||||
if (type === 'color' && Util.isNil(option.values)) { // 设置 color 的默认色值
|
||||
option.values = Global.colors;
|
||||
}
|
||||
scales.push(scale);
|
||||
}
|
||||
option.scales = scales;
|
||||
const attr = new Attr[className](option);
|
||||
attrs[type] = attr;
|
||||
}
|
||||
_parseFields(field) {
|
||||
if (Util.isArray(field)) {
|
||||
return field;
|
||||
}
|
||||
if (Util.isString(field)) {
|
||||
return field.split('*');
|
||||
}
|
||||
return [ field ];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
import Util from '../../util';
|
||||
import * as THREE from '../three';
|
||||
import pickingFragmentShader from '../engine/picking/picking_frag.glsl';
|
||||
import { updateObjecteUniform, destoryObject } from '../../util/object3d-util';
|
||||
export default class PickContoller {
|
||||
constructor(cfg) {
|
||||
Util.assign(this, cfg);
|
||||
this.pickObject3D = new THREE.Object3D();
|
||||
this.addToPicking(this.pickObject3D);
|
||||
}
|
||||
getPickingId() {
|
||||
return this.layer.scene._engine._picking.getNextId();
|
||||
}
|
||||
addToPicking(object) {
|
||||
object.name = this.layer.layerId;
|
||||
this.layer.scene._engine._picking.add(object);
|
||||
}
|
||||
removePickingObject(object) {
|
||||
this.layer.scene._engine._picking.remove(object);
|
||||
}
|
||||
removePickingMesh(mesh) {
|
||||
this.pickObject3D.remove(mesh);
|
||||
destoryObject(mesh);
|
||||
}
|
||||
removePickMeshByName(name) {
|
||||
for (let i = 0; i < this.pickObject3D.children.length; i++) {
|
||||
if (this.pickObject3D.children[i].name === name) {
|
||||
this.removePickingMesh(this.pickObject3D.children[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
removeAllMesh() {
|
||||
this.pickObject3D.children.forEach(element => {
|
||||
|
||||
this.pickObject3D.remove(element);
|
||||
destoryObject(element);
|
||||
});
|
||||
}
|
||||
addPickMesh(mesh) {
|
||||
const pickmaterial = mesh.material.clone();
|
||||
pickmaterial.fragmentShader = pickingFragmentShader;
|
||||
const pickingMesh = new THREE[mesh.type](mesh.geometry, pickmaterial);
|
||||
pickingMesh.name = mesh.name;
|
||||
pickingMesh.onBeforeRender = () => {
|
||||
const zoom = this.layer.scene.getZoom();
|
||||
updateObjecteUniform(pickingMesh, { u_zoom: zoom });
|
||||
};
|
||||
this.pickObject3D.add(pickingMesh);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,149 @@
|
|||
// jscs:disable
|
||||
/* eslint-disable */
|
||||
|
||||
import * as THREE from '../three';
|
||||
import CopyShader from './copy-shader';
|
||||
import ShaderPass from './shader-pass';
|
||||
import MaskPass, {ClearMaskPass} from './mask-pass';
|
||||
|
||||
/**
|
||||
* @author alteredq / http://alteredqualia.com/
|
||||
*/
|
||||
|
||||
var EffectComposer = function ( renderer, renderTarget ) {
|
||||
|
||||
this.renderer = renderer;
|
||||
|
||||
if ( renderTarget === undefined ) {
|
||||
|
||||
var pixelRatio = renderer.getPixelRatio();
|
||||
|
||||
var width = Math.floor( renderer.context.canvas.width / pixelRatio ) || 1;
|
||||
var height = Math.floor( renderer.context.canvas.height / pixelRatio ) || 1;
|
||||
var parameters = { minFilter: THREE.LinearFilter, magFilter: THREE.LinearFilter, format: THREE.RGBAFormat, stencilBuffer: false };
|
||||
|
||||
renderTarget = new THREE.WebGLRenderTarget( width, height, parameters );
|
||||
|
||||
}
|
||||
|
||||
this.renderTarget1 = renderTarget;
|
||||
this.renderTarget2 = renderTarget.clone();
|
||||
|
||||
this.writeBuffer = this.renderTarget1;
|
||||
this.readBuffer = this.renderTarget2;
|
||||
|
||||
this.passes = [];
|
||||
|
||||
if ( CopyShader === undefined )
|
||||
console.error( "EffectComposer relies on THREE.CopyShader" );
|
||||
|
||||
this.copyPass = new ShaderPass( CopyShader );
|
||||
|
||||
};
|
||||
|
||||
EffectComposer.prototype = {
|
||||
|
||||
swapBuffers: function() {
|
||||
|
||||
var tmp = this.readBuffer;
|
||||
this.readBuffer = this.writeBuffer;
|
||||
this.writeBuffer = tmp;
|
||||
|
||||
},
|
||||
visible:true,
|
||||
type:'composer',
|
||||
addPass: function ( pass ) {
|
||||
|
||||
this.passes.push( pass );
|
||||
|
||||
},
|
||||
|
||||
insertPass: function ( pass, index ) {
|
||||
|
||||
this.passes.splice( index, 0, pass );
|
||||
|
||||
},
|
||||
|
||||
render: function ( delta ) {
|
||||
|
||||
this.writeBuffer = this.renderTarget1;
|
||||
this.readBuffer = this.renderTarget2;
|
||||
|
||||
var maskActive = false;
|
||||
|
||||
var pass, i, il = this.passes.length;
|
||||
for ( i = 0; i < il; i ++ ) {
|
||||
|
||||
pass = this.passes[ i ];
|
||||
|
||||
if ( ! pass.enabled ) continue;
|
||||
|
||||
pass.render( this.renderer, this.writeBuffer, this.readBuffer, delta, maskActive );
|
||||
|
||||
if ( pass.needsSwap ) {
|
||||
|
||||
if ( maskActive ) {
|
||||
|
||||
var context = this.renderer.context;
|
||||
|
||||
context.stencilFunc( context.NOTEQUAL, 1, 0xffffffff );
|
||||
|
||||
this.copyPass.render( this.renderer, this.writeBuffer, this.readBuffer, delta );
|
||||
|
||||
context.stencilFunc( context.EQUAL, 1, 0xffffffff );
|
||||
|
||||
}
|
||||
|
||||
this.swapBuffers();
|
||||
|
||||
}
|
||||
|
||||
if ( pass instanceof MaskPass ) {
|
||||
|
||||
maskActive = true;
|
||||
|
||||
} else if ( pass instanceof ClearMaskPass ) {
|
||||
|
||||
maskActive = false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
reset: function ( renderTarget ) {
|
||||
|
||||
if ( renderTarget === undefined ) {
|
||||
|
||||
renderTarget = this.renderTarget1.clone();
|
||||
|
||||
var pixelRatio = this.renderer.getPixelRatio();
|
||||
|
||||
renderTarget.setSize(
|
||||
Math.floor( this.renderer.context.canvas.width / pixelRatio ),
|
||||
Math.floor( this.renderer.context.canvas.height / pixelRatio )
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
this.renderTarget1.dispose();
|
||||
this.renderTarget1 = renderTarget;
|
||||
this.renderTarget2.dispose();
|
||||
this.renderTarget2 = renderTarget.clone();
|
||||
|
||||
this.writeBuffer = this.renderTarget1;
|
||||
this.readBuffer = this.renderTarget2;
|
||||
|
||||
},
|
||||
|
||||
setSize: function ( width, height ) {
|
||||
|
||||
this.renderTarget1.setSize( width, height );
|
||||
this.renderTarget2.setSize( width, height );
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
export default EffectComposer;
|
|
@ -0,0 +1,53 @@
|
|||
// jscs:disable
|
||||
/* eslint-disable */
|
||||
|
||||
import * as THREE from '../three';
|
||||
|
||||
/**
|
||||
* @author alteredq / http://alteredqualia.com/
|
||||
*
|
||||
* Full-screen textured quad shader
|
||||
*/
|
||||
|
||||
var CopyShader = {
|
||||
|
||||
uniforms: {
|
||||
|
||||
"tDiffuse": { type: "t", value: null },
|
||||
"opacity": { type: "f", value: 1.0 }
|
||||
|
||||
},
|
||||
|
||||
vertexShader: [
|
||||
|
||||
"varying vec2 vUv;",
|
||||
|
||||
"void main() {",
|
||||
|
||||
"vUv = uv;",
|
||||
"gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
|
||||
|
||||
"}"
|
||||
|
||||
].join( "\n" ),
|
||||
|
||||
fragmentShader: [
|
||||
|
||||
"uniform float opacity;",
|
||||
|
||||
"uniform sampler2D tDiffuse;",
|
||||
|
||||
"varying vec2 vUv;",
|
||||
|
||||
"void main() {",
|
||||
|
||||
"vec4 texel = texture2D( tDiffuse, vUv );",
|
||||
"gl_FragColor = opacity * texel;",
|
||||
|
||||
"}"
|
||||
|
||||
].join( "\n" )
|
||||
|
||||
};
|
||||
|
||||
export default CopyShader;
|
|
@ -0,0 +1,21 @@
|
|||
import EffectComposer from './composer';
|
||||
|
||||
export default function(renderer, container) {
|
||||
const composer = new EffectComposer(renderer);
|
||||
|
||||
const updateSize = function() {
|
||||
// TODO: Re-enable this when perf issues can be solved
|
||||
//
|
||||
// Rendering double the resolution of the screen can be really slow
|
||||
// var pixelRatio = window.devicePixelRatio;
|
||||
const pixelRatio = 1;
|
||||
|
||||
composer.setSize(container.clientWidth * pixelRatio, container.clientHeight * pixelRatio);
|
||||
};
|
||||
|
||||
window.addEventListener('resize', updateSize, false);
|
||||
updateSize();
|
||||
|
||||
return composer;
|
||||
}
|
||||
|
|
@ -10,26 +10,31 @@ export default class Engine extends EventEmitter {
|
|||
this._scene = Scene;
|
||||
this._camera = new Camera(container).camera;
|
||||
this._renderer = new Renderer(container).renderer;
|
||||
this._world = world;
|
||||
this._world = world;// 地图场景实例
|
||||
// for MapBox
|
||||
this.world = new THREE.Group();
|
||||
this._scene.add(this.world);
|
||||
this._picking = Picking(this._world, this._renderer, this._camera, this._scene);
|
||||
this._picking = Picking(this._world, this._renderer, this._camera);
|
||||
this.clock = new THREE.Clock();
|
||||
this.composerLayers = [];
|
||||
}
|
||||
_initPostProcessing() {
|
||||
|
||||
this.composerLayers.forEach(layer => {
|
||||
layer.visible && layer.render();
|
||||
});
|
||||
}
|
||||
update() {
|
||||
|
||||
this._renderer.clear();
|
||||
this._renderer.render(this._scene, this._camera);
|
||||
|
||||
this._initPostProcessing();
|
||||
}
|
||||
destroy() {
|
||||
|
||||
}
|
||||
// 渲染第三方Scene对象
|
||||
renderScene(scene) {
|
||||
this._renderer.render(scene, this._camera);
|
||||
}
|
||||
run() {
|
||||
|
||||
this.update();
|
||||
this.engineID = requestAnimationFrame(this.run.bind(this));
|
||||
}
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
// jscs:disable
|
||||
/* eslint-disable */
|
||||
|
||||
import * as THREE from '../three';
|
||||
|
||||
/**
|
||||
* @author alteredq / http://alteredqualia.com/
|
||||
*/
|
||||
|
||||
var MaskPass = function ( scene, camera ) {
|
||||
|
||||
this.scene = scene;
|
||||
this.camera = camera;
|
||||
|
||||
this.enabled = true;
|
||||
this.clear = true;
|
||||
this.needsSwap = false;
|
||||
|
||||
this.inverse = false;
|
||||
|
||||
};
|
||||
|
||||
MaskPass.prototype = {
|
||||
|
||||
render: function ( renderer, writeBuffer, readBuffer, delta ) {
|
||||
|
||||
var context = renderer.context;
|
||||
|
||||
// don't update color or depth
|
||||
|
||||
context.colorMask( false, false, false, false );
|
||||
context.depthMask( false );
|
||||
|
||||
// set up stencil
|
||||
|
||||
var writeValue, clearValue;
|
||||
|
||||
if ( this.inverse ) {
|
||||
|
||||
writeValue = 0;
|
||||
clearValue = 1;
|
||||
|
||||
} else {
|
||||
|
||||
writeValue = 1;
|
||||
clearValue = 0;
|
||||
|
||||
}
|
||||
|
||||
context.enable( context.STENCIL_TEST );
|
||||
context.stencilOp( context.REPLACE, context.REPLACE, context.REPLACE );
|
||||
context.stencilFunc( context.ALWAYS, writeValue, 0xffffffff );
|
||||
context.clearStencil( clearValue );
|
||||
|
||||
// draw into the stencil buffer
|
||||
|
||||
renderer.render( this.scene, this.camera, readBuffer, this.clear );
|
||||
renderer.render( this.scene, this.camera, writeBuffer, this.clear );
|
||||
|
||||
// re-enable update of color and depth
|
||||
|
||||
context.colorMask( true, true, true, true );
|
||||
context.depthMask( true );
|
||||
|
||||
// only render where stencil is set to 1
|
||||
|
||||
context.stencilFunc( context.EQUAL, 1, 0xffffffff ); // draw if == 1
|
||||
context.stencilOp( context.KEEP, context.KEEP, context.KEEP );
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
var ClearMaskPass = function () {
|
||||
|
||||
this.enabled = true;
|
||||
|
||||
};
|
||||
|
||||
ClearMaskPass.prototype = {
|
||||
|
||||
render: function ( renderer, writeBuffer, readBuffer, delta ) {
|
||||
|
||||
var context = renderer.context;
|
||||
|
||||
context.disable( context.STENCIL_TEST );
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
export default MaskPass;
|
||||
export {ClearMaskPass as ClearMaskPass};
|
|
@ -3,13 +3,10 @@ import * as THREE from '../../three';
|
|||
let nextId = 1;
|
||||
|
||||
class Picking {
|
||||
constructor(world, renderer, camera, scene) {
|
||||
constructor(world, renderer, camera) {
|
||||
this._world = world;
|
||||
this._renderer = renderer;
|
||||
this._camera = camera;
|
||||
this._raycaster = new THREE.Raycaster();
|
||||
this.scene = scene;
|
||||
this._raycaster.linePrecision = 10;
|
||||
this._pickingScene = PickingScene;
|
||||
this.world = new THREE.Group();
|
||||
this._pickingScene.add(this.world);
|
||||
|
@ -49,13 +46,11 @@ class Picking {
|
|||
this._height = size.height;
|
||||
this._pickingTexture.setSize(this._width, this._height);
|
||||
this._pixelBuffer = new Uint8Array(4 * this._width * this._height);
|
||||
|
||||
this._needUpdate = true;
|
||||
}
|
||||
_update(point) {
|
||||
|
||||
const texture = this._pickingTexture;
|
||||
this._renderer.render(this._pickingScene, this._camera, this._pickingTexture);
|
||||
this._renderer.render(this._pickingScene, this._camera, texture);
|
||||
this.pixelBuffer = new Uint8Array(4);
|
||||
this._renderer.readRenderTargetPixels(texture, point.x, this._height - point.y, 1, 1, this.pixelBuffer);
|
||||
|
||||
|
@ -66,8 +61,23 @@ class Picking {
|
|||
index === id ? object.visible = true : object.visible = false;
|
||||
});
|
||||
}
|
||||
_layerIsVisable(object) {
|
||||
const layers = this._world.getLayers();
|
||||
let isVisable = false;
|
||||
for (let i = 0; i < layers.length; i++) {
|
||||
const layer = layers[i];
|
||||
if (object.name === layer.layerId) {
|
||||
isVisable = layer.get('visible');
|
||||
break;
|
||||
}
|
||||
}
|
||||
return isVisable;
|
||||
}
|
||||
_pickAllObject(point, normalisedPoint) {
|
||||
this.world.children.forEach((object, index) => {
|
||||
if (!this._layerIsVisable(object)) {
|
||||
return;
|
||||
}
|
||||
this._filterObject(index);
|
||||
const item = this._pick(point, normalisedPoint, object.name);
|
||||
item.type = point.type;
|
||||
|
@ -76,9 +86,9 @@ class Picking {
|
|||
|
||||
});
|
||||
}
|
||||
_updateRender() {
|
||||
this._renderer.render(this._pickingScene, this._camera, this._pickingTexture);
|
||||
}
|
||||
// _updateRender() {
|
||||
// this._renderer.render(this._pickingScene, this._camera, this._pickingTexture);
|
||||
// }
|
||||
|
||||
_pick(point, normalisedPoint, layerId) {
|
||||
this._update(point);
|
||||
|
@ -87,21 +97,11 @@ class Picking {
|
|||
id = -999;
|
||||
// return;
|
||||
}
|
||||
this._raycaster.setFromCamera(normalisedPoint, this._camera);
|
||||
|
||||
const intersects = this._raycaster.intersectObjects(this._pickingScene.children, true);
|
||||
const _point2d = { x: point.x, y: point.y };
|
||||
|
||||
let _point3d;
|
||||
if (intersects.length > 0) {
|
||||
_point3d = intersects[0].point;
|
||||
}
|
||||
const item = {
|
||||
layerId,
|
||||
featureId: id - 1,
|
||||
point2d: _point2d,
|
||||
point3d: _point3d,
|
||||
intersects
|
||||
featureId: id,
|
||||
point2d: _point2d
|
||||
};
|
||||
return item;
|
||||
|
||||
|
@ -135,8 +135,6 @@ class Picking {
|
|||
this._world._container.removeEventListener(event[0], event[1], false);
|
||||
});
|
||||
|
||||
this._world.off('move', this._onWorldMove);
|
||||
|
||||
if (this._pickingScene.children) {
|
||||
// Remove everything else in the layer
|
||||
let child;
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import Material from '../../../geom/material/material';
|
||||
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) {
|
||||
const material = new Material({
|
||||
uniforms: {
|
||||
u_zoom: { value: options.u_zoom || 1 }
|
||||
},
|
||||
vertexShader: picking_vert,
|
||||
vertexShader: options.vs,
|
||||
fragmentShader: picking_frag,
|
||||
transparent: false
|
||||
});
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
// jscs:disable
|
||||
/* eslint-disable */
|
||||
|
||||
import * as THREE from '../three';
|
||||
|
||||
/**
|
||||
* @author alteredq / http://alteredqualia.com/
|
||||
*/
|
||||
|
||||
var RenderPass = function ( scene, camera, overrideMaterial, clearColor, clearAlpha ) {
|
||||
|
||||
this.scene = scene;
|
||||
this.camera = camera;
|
||||
|
||||
this.overrideMaterial = overrideMaterial;
|
||||
|
||||
this.clearColor = clearColor;
|
||||
this.clearAlpha = ( clearAlpha !== undefined ) ? clearAlpha : 1;
|
||||
|
||||
this.oldClearColor = new THREE.Color();
|
||||
this.oldClearAlpha = 1;
|
||||
|
||||
this.enabled = true;
|
||||
this.clear = false;
|
||||
this.needsSwap = false;
|
||||
|
||||
};
|
||||
|
||||
RenderPass.prototype = {
|
||||
|
||||
render: function ( renderer, writeBuffer, readBuffer, delta ) {
|
||||
|
||||
this.scene.overrideMaterial = this.overrideMaterial;
|
||||
if ( this.clearColor ) {
|
||||
|
||||
this.oldClearColor.copy( renderer.getClearColor() );
|
||||
this.oldClearAlpha = renderer.getClearAlpha();
|
||||
|
||||
renderer.setClearColor( this.clearColor, this.clearAlpha );
|
||||
|
||||
}
|
||||
|
||||
renderer.render( this.scene, this.camera, readBuffer, this.clear );
|
||||
|
||||
|
||||
if ( this.clearColor ) {
|
||||
|
||||
renderer.setClearColor( this.oldClearColor, this.oldClearAlpha );
|
||||
|
||||
}
|
||||
|
||||
this.scene.overrideMaterial = null;
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
export default RenderPass;
|
|
@ -9,7 +9,8 @@ export default class Renderer {
|
|||
initRender() {
|
||||
this.renderer = new THREE.WebGLRenderer({
|
||||
antialias: true,
|
||||
alpha: true
|
||||
alpha: true,
|
||||
autoClear: false
|
||||
});
|
||||
this.renderer.setClearColor(0xff0000, 0.0);
|
||||
this.pixelRatio = window.devicePixelRatio;
|
||||
|
|
|
@ -1,41 +0,0 @@
|
|||
import * as THREE from '../three';
|
||||
|
||||
export class RenderPass {
|
||||
constructor(cfg) {
|
||||
this.scene;
|
||||
this.camera = cfg.camera;
|
||||
this.renderer = cfg.renderer;
|
||||
this.clearColor = cfg.clear.clearColor;
|
||||
this.clearAlpha = cfg.clear.clearAlpha;
|
||||
this._init(cfg);
|
||||
}
|
||||
|
||||
_init() {
|
||||
this.scene = new THREE.Scene();
|
||||
const parameters = { minFilter: THREE.NearestFilter,
|
||||
magFilter: THREE.NearestFilter,
|
||||
format: THREE.RGBAFormat,
|
||||
stencilBuffer: false,
|
||||
depthBuffer: false
|
||||
};
|
||||
const size = this.renderer.getSize();
|
||||
this.pass = new THREE.WebGLRenderTarget(size.width, size.height, parameters);
|
||||
this.originClearColor = this.renderer.getClearColor();
|
||||
this.originClearAlpha = this.renderer.getClearAlpha();
|
||||
this.texture = this.pass.texture;
|
||||
}
|
||||
|
||||
add(mesh) {
|
||||
this.scene.add(mesh);
|
||||
}
|
||||
|
||||
remove(mesh) {
|
||||
this.scene.remove(mesh);
|
||||
}
|
||||
|
||||
render() {
|
||||
this.renderer.setClearColor(this.clearColor, this.clearAlpha);
|
||||
this.renderer.render(this.scene, this.camera, this.pass, true); // this.pass,true
|
||||
this.renderer.setClearColor(this.clearColor, this.clearAlpha);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
// jscs:disable
|
||||
/* eslint-disable */
|
||||
|
||||
import * as THREE from '../three';
|
||||
|
||||
/**
|
||||
* @author alteredq / http://alteredqualia.com/
|
||||
*/
|
||||
|
||||
var ShaderPass = function( shader, textureID ) {
|
||||
|
||||
this.textureID = ( textureID !== undefined ) ? textureID : "tDiffuse";
|
||||
|
||||
if ( shader instanceof THREE.ShaderMaterial ) {
|
||||
|
||||
this.uniforms = shader.uniforms;
|
||||
|
||||
this.material = shader;
|
||||
|
||||
}
|
||||
else if ( shader ) {
|
||||
|
||||
this.uniforms = THREE.UniformsUtils.clone( shader.uniforms );
|
||||
|
||||
this.material = new THREE.ShaderMaterial( {
|
||||
|
||||
defines: shader.defines || {},
|
||||
uniforms: this.uniforms,
|
||||
vertexShader: shader.vertexShader,
|
||||
fragmentShader: shader.fragmentShader
|
||||
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
this.renderToScreen = false;
|
||||
|
||||
this.enabled = true;
|
||||
this.needsSwap = true;
|
||||
this.clear = true;
|
||||
|
||||
|
||||
this.camera = new THREE.OrthographicCamera( - 1, 1, 1, - 1, 0, 1 );
|
||||
this.scene = new THREE.Scene();
|
||||
|
||||
this.quad = new THREE.Mesh( new THREE.PlaneBufferGeometry( 2, 2 ), null );
|
||||
this.scene.add( this.quad );
|
||||
|
||||
};
|
||||
|
||||
ShaderPass.prototype = {
|
||||
|
||||
render: function( renderer, writeBuffer, readBuffer, delta ) {
|
||||
if ( this.uniforms[ this.textureID ] ) {
|
||||
this.uniforms[ this.textureID ].value = readBuffer.texture;
|
||||
|
||||
}
|
||||
renderer.autoClear = false;
|
||||
this.quad.material = this.material;
|
||||
|
||||
if ( this.renderToScreen ) {
|
||||
renderer.render( this.scene, this.camera );
|
||||
|
||||
} else {
|
||||
|
||||
renderer.render( this.scene, this.camera, writeBuffer, this.clear );
|
||||
|
||||
}
|
||||
renderer.autoClear = true;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
export default ShaderPass;
|
|
@ -28,14 +28,11 @@ export default class LoadImage extends EventEmitter {
|
|||
if (typeof opt === 'string') {
|
||||
getImage({ url: opt }, (err, img) => {
|
||||
img.id = id;
|
||||
|
||||
this.images.push(img);
|
||||
this.ctx.drawImage(img, x, y, this.imageWidth, this.imageWidth);
|
||||
|
||||
this.texture.magFilter = THREE.LinearFilter;
|
||||
this.texture.minFilter = THREE.LinearFilter;
|
||||
this.texture.needsUpdate = true;
|
||||
|
||||
if (this.images.length === this.imagesCount) {
|
||||
this.emit('imageLoaded');
|
||||
}
|
||||
|
|
|
@ -5,9 +5,10 @@
|
|||
import Base from './base';
|
||||
import * as THREE from './three';
|
||||
import ColorUtil from '../attr/color-util';
|
||||
import Controller from './controller/index';
|
||||
import source from './source';
|
||||
import PickingMaterial from '../core/engine/picking/pickingMaterial';
|
||||
import Attr from '../attr/index';
|
||||
import diff from '../util/diff';
|
||||
import { updateObjecteUniform } from '../util/object3d-util';
|
||||
import Util from '../util';
|
||||
import Global from '../global';
|
||||
let id = 1;
|
||||
|
@ -20,7 +21,6 @@ function parseFields(field) {
|
|||
}
|
||||
return [ field ];
|
||||
}
|
||||
|
||||
export default class Layer extends Base {
|
||||
getDefaultCfg() {
|
||||
return {
|
||||
|
@ -30,8 +30,11 @@ export default class Layer extends Base {
|
|||
minZoom: 0,
|
||||
maxZoom: 22,
|
||||
rotation: 0,
|
||||
option: {},
|
||||
attrOptions: {
|
||||
},
|
||||
scaleOptions: {},
|
||||
preScaleOptions: null,
|
||||
scales: {},
|
||||
attrs: {},
|
||||
// 样式配置项
|
||||
|
@ -46,7 +49,10 @@ export default class Layer extends Base {
|
|||
// 选中时的配置项
|
||||
selectedOptions: null,
|
||||
// active 时的配置项
|
||||
activedOptions: null,
|
||||
activedOptions: {
|
||||
fill: [ 1.0, 0, 0, 1.0 ]
|
||||
},
|
||||
interactions: {},
|
||||
animateOptions: {
|
||||
enable: false
|
||||
}
|
||||
|
@ -60,6 +66,7 @@ export default class Layer extends Base {
|
|||
this._pickObject3D = new THREE.Object3D();
|
||||
this._object3D.visible = this.get('visible');
|
||||
this._object3D.renderOrder = this.get('zIndex') || 0;
|
||||
this._mapEventHandlers = [];
|
||||
const layerId = this._getUniqueId();
|
||||
this.layerId = layerId;
|
||||
this._activeIds = null;
|
||||
|
@ -76,27 +83,40 @@ export default class Layer extends Base {
|
|||
* @param {*} type mesh类型是区别是填充还是边线
|
||||
*/
|
||||
add(object, type = 'fill') {
|
||||
type === 'fill' ? this.layerMesh = object : this.layerLineMesh = object;
|
||||
|
||||
this._visibleWithZoom();
|
||||
this._zoomchangeHander = this._visibleWithZoom.bind(this);
|
||||
this.scene.on('zoomchange', this._zoomchangeHander);
|
||||
|
||||
object.onBeforeRender = () => {
|
||||
const zoom = this.scene.getZoom();
|
||||
object.material.setUniformsValue('u_time', this.scene._engine.clock.getElapsedTime());
|
||||
object.material.setUniformsValue('u_zoom', zoom);
|
||||
this._preRender();
|
||||
|
||||
};
|
||||
// 更新
|
||||
if (this._needUpdateFilter) {
|
||||
this._updateFilter(object);
|
||||
// composer合图层绘制
|
||||
if (object.type === 'composer') {
|
||||
this._object3D = object;
|
||||
this.scene._engine.composerLayers.push(object);
|
||||
setTimeout(() => this.scene._engine.update(), 500);
|
||||
return;
|
||||
}
|
||||
type === 'fill' ? this.layerMesh = object : this.layerLineMesh = object;
|
||||
this._visibleWithZoom();
|
||||
object.onBeforeRender = () => { // 每次渲染前改变状态
|
||||
const zoom = this.scene.getZoom();
|
||||
updateObjecteUniform(this._object3D, {
|
||||
u_time: this.scene._engine.clock.getElapsedTime(),
|
||||
u_zoom: zoom
|
||||
});
|
||||
this.preRender();
|
||||
};
|
||||
|
||||
object.onAfterRender = () => { // 每次渲染后改变状态
|
||||
this.afterRender();
|
||||
};
|
||||
this._object3D.add(object);
|
||||
if (type === 'fill') { this._addPickMesh(object); }
|
||||
if (type === 'fill') {
|
||||
this.get('pickingController').addPickMesh(object);
|
||||
}
|
||||
setTimeout(() => this.scene._engine.update(), 500);
|
||||
}
|
||||
remove(object) {
|
||||
if (object.type === 'composer') {
|
||||
this.scene._engine.composerLayers = this.scene._engine.composerLayers.filter(layer => {
|
||||
return (layer !== object);
|
||||
});
|
||||
return;
|
||||
}
|
||||
this._object3D.remove(object);
|
||||
}
|
||||
_getUniqueId() {
|
||||
|
@ -107,20 +127,20 @@ export default class Layer extends Base {
|
|||
this._object3D.visible = this.get('visible');
|
||||
}
|
||||
source(data, cfg = {}) {
|
||||
if (data instanceof source) {
|
||||
this.layerSource = data;
|
||||
return this;
|
||||
}
|
||||
cfg.data = data;
|
||||
cfg.mapType = this.scene.mapType;
|
||||
cfg.zoom = this.scene.getZoom();
|
||||
this.layerSource = new source(cfg);
|
||||
// this.scene.workerPool.runTask({
|
||||
// command: 'geojson',
|
||||
// data: cfg
|
||||
// }).then(data => {
|
||||
// this.scene.workerPool.runTask(cfg).then(data => {
|
||||
// console.log(data);
|
||||
// });
|
||||
|
||||
return this;
|
||||
}
|
||||
color(field, values) {
|
||||
this._needUpdateColor = true;// 标识颜色是否需要更新
|
||||
this._createAttrOption('color', field, values, Global.colors);
|
||||
return this;
|
||||
}
|
||||
|
@ -133,6 +153,16 @@ export default class Layer extends Base {
|
|||
this._createAttrOption('size', field, values, Global.size);
|
||||
return this;
|
||||
}
|
||||
scale(field, cfg) {
|
||||
const options = this.get('scaleOptions');
|
||||
const scaleDefs = options;
|
||||
if (Util.isObject(field)) {
|
||||
Util.mix(scaleDefs, field);
|
||||
} else {
|
||||
scaleDefs[field] = cfg;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
shape(field, values) {
|
||||
if (field.split(':').length === 2) {
|
||||
this.shapeType = field.split(':')[0];
|
||||
|
@ -152,12 +182,13 @@ export default class Layer extends Base {
|
|||
active(enable, cfg) {
|
||||
if (enable === false) {
|
||||
this.set('allowActive', false);
|
||||
} else if (Util.isObject(enable)) {
|
||||
} else if (Util.isObject(enable) && enable.fill) {
|
||||
this.set('allowActive', true);
|
||||
if (enable.fill) enable.fill = ColorUtil.color2RGBA(enable.fill);
|
||||
this.set('activedOptions', enable);
|
||||
} else {
|
||||
this.set('allowActive', true);
|
||||
this.set('activedOptions', cfg || { fill: Global.activeColor });
|
||||
this.set('activedOptions', cfg || { fill: ColorUtil.color2RGBA(Global.activeColor) });
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
@ -187,11 +218,12 @@ export default class Layer extends Base {
|
|||
this.set('styleOptions', styleOptions);
|
||||
return this;
|
||||
}
|
||||
|
||||
filter(field, values) {
|
||||
this._needUpdateFilter = true;
|
||||
this._createAttrOption('filter', field, values, true);
|
||||
return this;
|
||||
}
|
||||
|
||||
animate(field, cfg) {
|
||||
let animateOptions = this.get('animateOptions');
|
||||
if (!animateOptions) {
|
||||
|
@ -213,6 +245,10 @@ export default class Layer extends Base {
|
|||
}
|
||||
texture() {
|
||||
|
||||
}
|
||||
fitBounds() {
|
||||
const extent = this.layerSource.data.extent;
|
||||
this.scene.fitBounds(extent);
|
||||
}
|
||||
hide() {
|
||||
this._visible(false);
|
||||
|
@ -222,11 +258,16 @@ export default class Layer extends Base {
|
|||
this._visible(true);
|
||||
return this;
|
||||
}
|
||||
setData(data, cfg) {
|
||||
this.layerSource.setData(data, cfg);
|
||||
this.repaint();
|
||||
}
|
||||
_createScale(field) {
|
||||
// TODO scale更新
|
||||
const scales = this.get('scales');
|
||||
let scale = scales[field];
|
||||
if (!scale) {
|
||||
scale = this.layerSource.createScale(field);
|
||||
scale = this.createScale(field);
|
||||
scales[field] = scale;
|
||||
}
|
||||
return scale;
|
||||
|
@ -253,68 +294,133 @@ export default class Layer extends Base {
|
|||
}
|
||||
this._setAttrOptions(attrName, attrCfg);
|
||||
}
|
||||
_initControllers() {
|
||||
const mappingCtr = new Controller.Mapping({ layer: this });
|
||||
const pickCtr = new Controller.Picking({ layer: this });
|
||||
const interactionCtr = new Controller.Interaction({ layer: this });
|
||||
this.set('mappingController', mappingCtr);
|
||||
this.set('pickingController', pickCtr);
|
||||
this.set('interacionController', interactionCtr);
|
||||
}
|
||||
|
||||
render() {
|
||||
this.init();
|
||||
this.scene._engine.update();
|
||||
return this;
|
||||
}
|
||||
// 重绘 度量, 映射,顶点构建
|
||||
repaint() {
|
||||
this.set('scales', {});
|
||||
const mappingCtr = new Controller.Mapping({ layer: this });
|
||||
this.set('mappingController', mappingCtr);
|
||||
// this._initAttrs();
|
||||
// this._mapping();
|
||||
this.redraw();
|
||||
}
|
||||
// 初始化图层
|
||||
init() {
|
||||
this._initAttrs();
|
||||
this._scaleByZoom();
|
||||
this._mapping();
|
||||
|
||||
const activeHander = this._addActiveFeature.bind(this);
|
||||
const resetHander = this._resetStyle.bind(this);
|
||||
this._initControllers();
|
||||
// this._initAttrs();
|
||||
this._updateDraw();
|
||||
}
|
||||
_initInteraction() {
|
||||
if (this.get('allowActive')) {
|
||||
|
||||
this.on('mousemove', activeHander);
|
||||
this.on('mouseleave', resetHander);
|
||||
|
||||
} else {
|
||||
this.off('mousemove', activeHander);
|
||||
this.off('mouseleave', resetHander);
|
||||
this.get('interacionController').addInteraction('active');
|
||||
}
|
||||
}
|
||||
_initMapEvent() {
|
||||
// zoomchange mapmove resize
|
||||
const EVENT_TYPES = [ 'zoomchange', 'dragend' ];
|
||||
Util.each(EVENT_TYPES, type => {
|
||||
const handler = Util.wrapBehavior(this, `${type}`);
|
||||
this.map.on(`${type}`, handler);
|
||||
this._mapEventHandlers.push({ type, handler });
|
||||
});
|
||||
}
|
||||
clearMapEvent() {
|
||||
const eventHandlers = this._mapEventHandlers;
|
||||
Util.each(eventHandlers, eh => {
|
||||
this.map.off(eh.type, eh.handler);
|
||||
});
|
||||
}
|
||||
zoomchange(ev) {
|
||||
// 地图缩放等级变化
|
||||
this._visibleWithZoom(ev);
|
||||
}
|
||||
dragend() {
|
||||
|
||||
}
|
||||
resize() {
|
||||
}
|
||||
|
||||
setActive(id, color) {
|
||||
this._activeIds = id;
|
||||
if (!Array.isArray(color)) {
|
||||
color = ColorUtil.color2RGBA(color);
|
||||
}
|
||||
updateObjecteUniform(this._object3D, {
|
||||
u_activeColor: color,
|
||||
u_activeId: id
|
||||
});
|
||||
this.scene._engine.update();
|
||||
}
|
||||
|
||||
_addActiveFeature(e) {
|
||||
const { featureId } = e;
|
||||
if (featureId < 0) return;
|
||||
const activeStyle = this.get('activedOptions');
|
||||
// const selectFeatureIds = this.layerSource.getSelectFeatureId(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);
|
||||
this._activeIds = featureId;
|
||||
updateObjecteUniform(this._object3D, { u_activeId: featureId });
|
||||
}
|
||||
|
||||
|
||||
_initAttrs() {
|
||||
const attrOptions = this.get('attrOptions');
|
||||
for (const type in attrOptions) {
|
||||
if (attrOptions.hasOwnProperty(type)) {
|
||||
this._updateAttr(type);
|
||||
}
|
||||
_setPreOption() {
|
||||
const nextAttrs = this.get('attrOptions');
|
||||
const nextStyle = this.get('styleOptions');
|
||||
this.set('preAttrOptions', Util.clone(nextAttrs));
|
||||
this.set('preStyleOption', Util.clone(nextStyle));
|
||||
}
|
||||
_updateDraw() {
|
||||
const preAttrs = this.get('preAttrOptions');
|
||||
const nextAttrs = this.get('attrOptions');
|
||||
const preStyle = this.get('preStyleOption');
|
||||
const nextStyle = this.get('styleOptions');
|
||||
if (preAttrs === undefined && preStyle === undefined) { // 首次渲染
|
||||
// this._mapping();
|
||||
this._setPreOption();
|
||||
this._scaleByZoom();
|
||||
this._initInteraction();
|
||||
this._initMapEvent();
|
||||
this.draw();
|
||||
return;
|
||||
}
|
||||
}
|
||||
_updateAttr(type) {
|
||||
const self = this;
|
||||
const attrs = this.get('attrs');
|
||||
const attrOptions = this.get('attrOptions');
|
||||
const option = attrOptions[type];
|
||||
option.neadUpdate = true;
|
||||
const className = Util.upperFirst(type);
|
||||
const fields = parseFields(option.field);
|
||||
const scales = [];
|
||||
for (let i = 0; i < fields.length; i++) {
|
||||
const field = fields[i];
|
||||
const scale = self._createScale(field);
|
||||
|
||||
if (type === 'color' && Util.isNil(option.values)) { // 设置 color 的默认色值
|
||||
option.values = Global.colors;
|
||||
}
|
||||
scales.push(scale);
|
||||
if (!Util.isEqual(preAttrs.color, nextAttrs.color)) {
|
||||
this._updateAttributes(this.layerMesh);
|
||||
}
|
||||
option.scales = scales;
|
||||
const attr = new Attr[className](option);
|
||||
attrs[type] = attr;
|
||||
// 更新数据过滤 filter
|
||||
if (!Util.isEqual(preAttrs.filter, nextAttrs.filter)) {
|
||||
// 更新color;
|
||||
this._updateAttributes(this.layerMesh);
|
||||
}
|
||||
// 更新Size
|
||||
if (!Util.isEqual(preAttrs.size, nextAttrs.size)) {
|
||||
// 更新color;
|
||||
this._updateSize();
|
||||
}
|
||||
// 更新形状
|
||||
if (!Util.isEqual(preAttrs.shape, nextAttrs.shape)) {
|
||||
// 更新color;
|
||||
this._updateShape();
|
||||
}
|
||||
if (!Util.isEqual(preStyle, nextStyle)) {
|
||||
// 判断新增,修改,删除
|
||||
const newStyle = {};
|
||||
Util.each(diff(preStyle, nextStyle), ({ type, key, value }) => {
|
||||
(type !== 'remove') && (newStyle[key] = value);
|
||||
// newStyle[key] = type === 'remove' ? null : value;
|
||||
});
|
||||
this._updateStyle(newStyle);
|
||||
}
|
||||
this._setPreOption();
|
||||
}
|
||||
|
||||
_updateSize(zoom) {
|
||||
const sizeOption = this.get('attrOptions').size;
|
||||
const fields = parseFields(sizeOption.field);
|
||||
|
@ -332,95 +438,12 @@ export default class Layer extends Base {
|
|||
}
|
||||
this.emit('sizeUpdated', this.zoomSizeCache[zoom]);
|
||||
}
|
||||
_mapping() {
|
||||
const self = this;
|
||||
const attrs = self.get('attrs');
|
||||
const mappedData = [];
|
||||
// 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 = {};
|
||||
newRecord.id = data[i]._id;
|
||||
for (const k in attrs) {
|
||||
if (attrs.hasOwnProperty(k)) {
|
||||
const attr = attrs[k];
|
||||
attr.needUpdate = false;
|
||||
const names = attr.names;
|
||||
const values = self._getAttrValues(attr, record);
|
||||
if (names.length > 1) { // position 之类的生成多个字段的属性
|
||||
for (let j = 0; j < values.length; j++) {
|
||||
const val = values[j];
|
||||
const name = names[j];
|
||||
newRecord[name] = (Util.isArray(val) && val.length === 1) ? val[0] : val; // 只有一个值时返回第一个属性值
|
||||
}
|
||||
} else {
|
||||
newRecord[names[0]] = values.length === 1 ? values[0] : values;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
newRecord.coordinates = record.coordinates;
|
||||
mappedData.push(newRecord);
|
||||
_updateStyle(option) {
|
||||
const newOption = { };
|
||||
for (const key in option) {
|
||||
newOption['u_' + key] = option[key];
|
||||
}
|
||||
this.layerData = mappedData;
|
||||
}
|
||||
// 更新地图映射
|
||||
_updateMaping() {
|
||||
const self = this;
|
||||
const attrs = self.get('attrs');
|
||||
|
||||
const data = this.layerSource.data.dataArray;
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
const record = data[i];
|
||||
for (const attrName in attrs) {
|
||||
if (attrs.hasOwnProperty(attrName) && attrs[attrName].neadUpdate) {
|
||||
const attr = attrs[attrName];
|
||||
const names = attr.names;
|
||||
const values = self._getAttrValues(attr, record);
|
||||
if (names.length > 1) { // position 之类的生成多个字段的属性
|
||||
for (let j = 0; j < values.length; j++) {
|
||||
const val = values[j];
|
||||
const name = names[j];
|
||||
this.layerData[i][name] = (Util.isArray(val) && val.length === 1) ? val[0] : val; // 只有一个值时返回第一个属性值
|
||||
}
|
||||
} else {
|
||||
this.layerData[i][names[0]] = values.length === 1 ? values[0] : values;
|
||||
|
||||
}
|
||||
attr.neadUpdate = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 获取属性映射的值
|
||||
_getAttrValues(attr, record) {
|
||||
const scales = attr.scales;
|
||||
const params = [];
|
||||
for (let i = 0; i < scales.length; i++) {
|
||||
const scale = scales[i];
|
||||
const field = scale.field;
|
||||
if (scale.type === 'identity') {
|
||||
params.push(scale.value);
|
||||
} else {
|
||||
params.push(record[field]);
|
||||
}
|
||||
}
|
||||
const indexZoom = params.indexOf('zoom');
|
||||
indexZoom !== -1 ? params[indexZoom] = attr.zoom : null;
|
||||
const values = attr.mapping(...params);
|
||||
return values;
|
||||
}
|
||||
|
||||
// temp
|
||||
_getDataType(data) {
|
||||
if (data.hasOwnProperty('type')) {
|
||||
const type = data.type;
|
||||
if (type === 'FeatureCollection') {
|
||||
return 'geojson';
|
||||
}
|
||||
}
|
||||
return 'basic';
|
||||
updateObjecteUniform(this._object3D, newOption);
|
||||
}
|
||||
_scaleByZoom() {
|
||||
if (this._zoomScale) {
|
||||
|
@ -430,106 +453,52 @@ export default class Layer extends Base {
|
|||
});
|
||||
}
|
||||
}
|
||||
// on(type, callback) {
|
||||
|
||||
// this._addPickingEvents();
|
||||
// super.on(type, callback);
|
||||
// }
|
||||
getPickingId() {
|
||||
return this.scene._engine._picking.getNextId();
|
||||
}
|
||||
addToPicking(object) {
|
||||
this.scene._engine._picking.add(object);
|
||||
}
|
||||
removeFromPicking(object) {
|
||||
this.scene._engine._picking.remove(object);
|
||||
}
|
||||
_addPickMesh(mesh) {
|
||||
this._pickingMesh = new THREE.Object3D();
|
||||
this._pickingMesh.name = this.layerId;
|
||||
// this._visibleWithZoom();
|
||||
// this.scene.on('zoomchange', () => {
|
||||
// this._visibleWithZoom();
|
||||
// });
|
||||
|
||||
this.addToPicking(this._pickingMesh);
|
||||
const pickmaterial = new PickingMaterial({
|
||||
u_zoom: this.scene.getZoom()
|
||||
});
|
||||
|
||||
const pickingMesh = new THREE[mesh.type](mesh.geometry, pickmaterial);
|
||||
pickingMesh.name = this.layerId;
|
||||
pickmaterial.setDefinesvalue(this.type, true);
|
||||
pickingMesh.onBeforeRender = () => {
|
||||
const zoom = this.scene.getZoom();
|
||||
pickingMesh.material.setUniformsValue('u_zoom', zoom);
|
||||
};
|
||||
this._pickingMesh.add(pickingMesh);
|
||||
|
||||
}
|
||||
_setPickingId() {
|
||||
this._pickingId = this.getPickingId();
|
||||
}
|
||||
_initEvents() {
|
||||
this.scene.on('pick-' + this.layerId, e => {
|
||||
const { featureId, point2d, type } = e;
|
||||
if (featureId < -100 && this._activeIds !== null) {
|
||||
this.emit('mouseleave');
|
||||
return;
|
||||
}
|
||||
const feature = this.layerSource.getSelectFeature(featureId);
|
||||
let { featureId, point2d, type } = e;
|
||||
// TODO 瓦片图层获取选中数据信息
|
||||
const lnglat = this.scene.containerToLngLat(point2d);
|
||||
let feature = null;
|
||||
let style = null;
|
||||
if (featureId !== -999) {
|
||||
const res = this.getSelectFeature(featureId, lnglat);
|
||||
feature = res.feature;
|
||||
style = res.style;
|
||||
}
|
||||
const target = {
|
||||
featureId,
|
||||
feature,
|
||||
style,
|
||||
pixel: point2d,
|
||||
type,
|
||||
lnglat: { lng: lnglat.lng, lat: lnglat.lat }
|
||||
};
|
||||
if (featureId >= 0) {
|
||||
if (featureId >= 0) { // 拾取到元素,或者离开元素
|
||||
this.emit(type, target);
|
||||
}
|
||||
if (featureId < 0 && this._activeIds >= 0) {
|
||||
type = 'mouseleave';
|
||||
this.emit(type, target);
|
||||
}
|
||||
this._activeIds = featureId;
|
||||
|
||||
});
|
||||
}
|
||||
/**
|
||||
* 更新active操作
|
||||
* @param {*} featureStyleId 需要更新的要素Id
|
||||
* @param {*} style 更新的要素样式
|
||||
*/
|
||||
updateStyle(featureStyleId, style) {
|
||||
if (this._activeIds) {
|
||||
this._resetStyle();
|
||||
}
|
||||
this._activeIds = featureStyleId;
|
||||
const pickingId = this.layerMesh.geometry.attributes.pickingId.array;
|
||||
const color = style.color;
|
||||
const colorAttr = this.layerMesh.geometry.attributes.a_color;
|
||||
const firstId = pickingId.indexOf(featureStyleId[0] + 1);
|
||||
for (let i = firstId; i < pickingId.length; i++) {
|
||||
if (pickingId[i] === featureStyleId[0] + 1) {
|
||||
colorAttr.array[i * 4 + 0] = color[0];
|
||||
colorAttr.array[i * 4 + 1] = color[1];
|
||||
colorAttr.array[i * 4 + 2] = color[2];
|
||||
colorAttr.array[i * 4 + 3] = color[3];
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
colorAttr.needsUpdate = true;
|
||||
return;
|
||||
}
|
||||
|
||||
_updateColor() {
|
||||
|
||||
this._updateMaping();
|
||||
|
||||
getSelectFeature(featureId) {
|
||||
const feature = this.layerSource.getSelectFeature(featureId);
|
||||
const style = this.layerData[featureId - 1];
|
||||
return {
|
||||
feature,
|
||||
style
|
||||
};
|
||||
}
|
||||
/**
|
||||
* 用于过滤数据
|
||||
* @param {*} object 需要过滤的mesh
|
||||
* @param {*} object 更新颜色和数据过滤
|
||||
*/
|
||||
_updateFilter(object) {
|
||||
this._updateMaping();
|
||||
_updateAttributes(object) {
|
||||
this.get('mappingController').update();
|
||||
const filterData = this.layerData;
|
||||
this._activeIds = null; // 清空选中元素
|
||||
const colorAttr = object.geometry.attributes.a_color;
|
||||
|
@ -557,6 +526,7 @@ export default class Layer extends Base {
|
|||
pickAttr.needsUpdate = true;
|
||||
}
|
||||
_visibleWithZoom() {
|
||||
if (this._object3D === null) return;
|
||||
const zoom = this.scene.getZoom();
|
||||
const minZoom = this.get('minZoom');
|
||||
const maxZoom = this.get('maxZoom');
|
||||
|
@ -564,43 +534,56 @@ export default class Layer extends Base {
|
|||
let offset = 0;
|
||||
if (this.type === 'point') {
|
||||
offset = 5;
|
||||
} else if (this.type === 'polyline') {
|
||||
this.shapeType = 'text' && (offset = 10);
|
||||
|
||||
} else if (this.type === 'polyline' || this.type === 'line') {
|
||||
offset = 2;
|
||||
} else if (this.type === 'polygon') {
|
||||
offset = 1;
|
||||
}
|
||||
this._object3D.position.z = offset * Math.pow(2, 20 - zoom);
|
||||
if (zoom < minZoom || zoom > maxZoom) {
|
||||
this._object3D.position && (this._object3D.position.z = offset * Math.pow(2, 20 - zoom));
|
||||
if (zoom < minZoom || zoom >= maxZoom) {
|
||||
this._object3D.visible = false;
|
||||
} else if (this.get('visible')) {
|
||||
this._object3D.visible = true;
|
||||
}
|
||||
}
|
||||
|
||||
// 重新构建mesh
|
||||
redraw() {
|
||||
this._object3D.children.forEach(child => {
|
||||
this._object3D.remove(child);
|
||||
});
|
||||
this.get('pickingController').removeAllMesh();
|
||||
this.draw();
|
||||
}
|
||||
// 更新mesh
|
||||
updateDraw() {
|
||||
|
||||
}
|
||||
|
||||
styleCfg() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置高亮要素
|
||||
*/
|
||||
_resetStyle() {
|
||||
|
||||
const pickingId = this.layerMesh.geometry.attributes.pickingId.array;
|
||||
const colorAttr = this.layerMesh.geometry.attributes.a_color;
|
||||
this._activeIds.forEach(index => {
|
||||
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) {
|
||||
colorAttr.array[i * 4 + 0] = color[0];
|
||||
colorAttr.array[i * 4 + 1] = color[1];
|
||||
colorAttr.array[i * 4 + 2] = color[2];
|
||||
colorAttr.array[i * 4 + 3] = color[3];
|
||||
}
|
||||
}
|
||||
});
|
||||
colorAttr.needsUpdate = true;
|
||||
this._activeIds = null;
|
||||
updateObjecteUniform(this._object3D, { u_activeId: 0 });
|
||||
}
|
||||
/**
|
||||
* 销毁Layer对象
|
||||
*/
|
||||
destroy() {
|
||||
this.removeAllListeners();
|
||||
this.clearAllInteractions();
|
||||
this.clearMapEvent();
|
||||
if (this._object3D.type === 'composer') {
|
||||
this.remove(this._object3D);
|
||||
return;
|
||||
}
|
||||
if (this._object3D && this._object3D.children) {
|
||||
let child;
|
||||
for (let i = 0; i < this._object3D.children.length; i++) {
|
||||
|
@ -625,13 +608,55 @@ export default class Layer extends Base {
|
|||
child = null;
|
||||
}
|
||||
}
|
||||
this.layerMesh.geometry = null;
|
||||
this.layerMesh.material.dispose();
|
||||
this.layerMesh.material = null;
|
||||
if (this._pickingMesh) {
|
||||
this._pickingMesh.children[0].geometry = null;
|
||||
this._pickingMesh.children[0].material.dispose();
|
||||
this._pickingMesh.children[0].material = null;
|
||||
}
|
||||
this._buffer = null;
|
||||
this._object3D = null;
|
||||
this.scene._engine._scene.remove(this._object3D);
|
||||
this.layerData.length = 0;
|
||||
this.layerSource = null;
|
||||
this.scene._engine._picking.remove(this._pickingMesh);
|
||||
this.scene.off('zoomchange', this._zoomchangeHander);
|
||||
this.destroyed = true;
|
||||
}
|
||||
_preRender() {
|
||||
|
||||
/**
|
||||
* 获取图例配置项
|
||||
* @param {*} field 字段
|
||||
* @param {*} type 图例类型 color, size
|
||||
* @return {*} 图例配置项
|
||||
*/
|
||||
getLegendCfg(field, type = 'color') {
|
||||
// todo heatmap
|
||||
if (this.type === 'heatmap' && this.shapeType === 'heatmap') {
|
||||
return this.get('styleOptions').rampColors;
|
||||
}
|
||||
const scales = this.get('scales');
|
||||
const scale = scales[field];
|
||||
const colorAttrs = this.get('attrs')[type];
|
||||
const lengendCfg = {};
|
||||
if (scale) {
|
||||
const ticks = scale.ticks;
|
||||
lengendCfg.value = ticks;
|
||||
lengendCfg.type = scale.type;
|
||||
const values = ticks.map(value => {
|
||||
const v = this._getAttrValues(colorAttrs, { [field]: value });
|
||||
return type === 'color' ? ColorUtil.colorArray2RGBA(v) : v;
|
||||
});
|
||||
lengendCfg[type] = values;
|
||||
}
|
||||
return lengendCfg;
|
||||
}
|
||||
preRender() {
|
||||
|
||||
}
|
||||
|
||||
afterRender() {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,10 +2,12 @@ import Engine from './engine';
|
|||
import { LAYER_MAP } from '../layer';
|
||||
import Base from './base';
|
||||
import LoadImage from './image';
|
||||
// import WorkerPool from './worker';
|
||||
import FontAtlasManager from '../geom/buffer/point/text/font-manager';
|
||||
import WorkerPool from '../worker/worker_pool';
|
||||
// import { MapProvider } from '../map/AMap';
|
||||
import { getMap } from '../map/index';
|
||||
import Global from '../global';
|
||||
import { getInteraction } from '../interaction/index';
|
||||
import { compileBuiltinModules } from '../geom/shader';
|
||||
export default class Scene extends Base {
|
||||
getDefaultCfg() {
|
||||
|
@ -16,13 +18,16 @@ export default class Scene extends Base {
|
|||
this._initMap();
|
||||
// this._initAttribution(); // 暂时取消,后面作为组件去加载
|
||||
this.addImage();
|
||||
this.fontAtlasManager = new FontAtlasManager();
|
||||
this._layers = [];
|
||||
this.animateCount = 0;
|
||||
}
|
||||
|
||||
_initEngine(mapContainer) {
|
||||
this._engine = new Engine(mapContainer, this);
|
||||
this._engine.run();
|
||||
// this.workerPool = new WorkerPool();
|
||||
this.registerMapEvent();
|
||||
// this._engine.run();
|
||||
this.workerPool = new WorkerPool();
|
||||
compileBuiltinModules();
|
||||
}
|
||||
// 为pickup场景添加 object 对象
|
||||
|
@ -35,14 +40,20 @@ export default class Scene extends Base {
|
|||
const MapProvider = getMap(this.mapType);
|
||||
const Map = new MapProvider(this._attrs);
|
||||
Map.mixMap(this);
|
||||
this._container = document.getElementById(Map.container);
|
||||
this._container = Map.container;
|
||||
// const Map = new MapProvider(this.mapContainer, this._attrs);
|
||||
Map.on('mapLoad', () => {
|
||||
this._initEngine(Map.renderDom);
|
||||
this.map = Map.map;
|
||||
this._initEngine(Map.renderDom);
|
||||
Map.asyncCamera(this._engine);
|
||||
this.initLayer();
|
||||
this._registEvents();
|
||||
const hash = this.get('hash');
|
||||
if (hash) {
|
||||
const Ctor = getInteraction('hash');
|
||||
const interaction = new Ctor({ layer: this });
|
||||
interaction._onHashChange();
|
||||
}
|
||||
this.emit('loaded');
|
||||
});
|
||||
|
||||
|
@ -63,17 +74,8 @@ export default class Scene extends Base {
|
|||
}
|
||||
off(type, hander) {
|
||||
if (this.map) { this.map.off(type, hander); }
|
||||
|
||||
super.off(type, hander);
|
||||
}
|
||||
_initAttribution() {
|
||||
const message = '<a href="http://antv.alipay.com/zh-cn/index.html title="Large-scale WebGL-powered Geospatial Data Visualization">AntV | L7 </a>';
|
||||
const element = document.createElement('div');
|
||||
|
||||
element.innerHTML = message;
|
||||
element.style.cssText += 'position: absolute; pointer-events:none;background: rgba(255, 255, 255, 0.7);font-size: 11px;z-index:100; padding:4px;bottom: 0;right:0px;';
|
||||
this._container.appendChild(element);
|
||||
}
|
||||
addImage() {
|
||||
this.image = new LoadImage();
|
||||
}
|
||||
|
@ -103,7 +105,9 @@ export default class Scene extends Base {
|
|||
this._container.addEventListener(event, e => {
|
||||
// 要素拾取
|
||||
e.pixel || (e.pixel = e.point);
|
||||
this._engine._picking.pickdata(e);
|
||||
requestAnimationFrame(() => {
|
||||
this._engine._picking.pickdata(e);
|
||||
});
|
||||
}, false);
|
||||
});
|
||||
}
|
||||
|
@ -115,5 +119,31 @@ export default class Scene extends Base {
|
|||
layer.destroy();
|
||||
layer = null;
|
||||
}
|
||||
startAnimate() {
|
||||
if (this.animateCount === 0) {
|
||||
this.unRegsterMapEvent();
|
||||
this._engine.run();
|
||||
}
|
||||
this.animateCount++;
|
||||
}
|
||||
stopAnimate() {
|
||||
if (this.animateCount === 1) {
|
||||
this._engine.stop();
|
||||
this.registerMapEvent();
|
||||
}
|
||||
this.animateCount++;
|
||||
}
|
||||
// 地图状态变化时更新可视化渲染
|
||||
registerMapEvent() {
|
||||
this._updateRender = () => this._engine.update();
|
||||
this.map.on('mousemove', this._updateRender);
|
||||
this.map.on('mapmove', this._updateRender);
|
||||
this.map.on('camerachange', this._updateRender);
|
||||
}
|
||||
unRegsterMapEvent() {
|
||||
this.map.off('mousemove', this._updateRender);
|
||||
this.map.off('mapmove', this._updateRender);
|
||||
this.map.off('camerachange', this._updateRender);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
|
||||
import Base from './base';
|
||||
import Controller from './controller/index';
|
||||
import { getTransform, getParser } from '../source';
|
||||
import { cluster, formatData } from '../source/transform/cluster';
|
||||
import { extent, tranfrormCoord } from '../util/geo';
|
||||
import { clone } from '@antv/util';
|
||||
import { getMap } from '../map/index';
|
||||
export default class Source extends Base {
|
||||
getDefaultCfg() {
|
||||
|
@ -10,8 +11,8 @@ export default class Source extends Base {
|
|||
defs: {},
|
||||
parser: {},
|
||||
transforms: [],
|
||||
scales: {
|
||||
},
|
||||
scaledefs: {},
|
||||
scales: {},
|
||||
options: {}
|
||||
};
|
||||
}
|
||||
|
@ -19,19 +20,49 @@ export default class Source extends Base {
|
|||
super(cfg);
|
||||
const transform = this.get('transforms');
|
||||
this._transforms = transform || [];
|
||||
this._initControllers();
|
||||
const mapType = this.get('mapType') || 'AMap';
|
||||
this.projectFlat = getMap(mapType).project;
|
||||
// 数据解析
|
||||
this._init();
|
||||
|
||||
}
|
||||
_init() {
|
||||
this._excuteParser();
|
||||
const isCluster = this.get('isCluster') || false;
|
||||
isCluster && this._executeCluster();
|
||||
// 数据转换 统计,聚合,分类
|
||||
this._executeTrans();
|
||||
// 坐标转换
|
||||
this._projectCoords();
|
||||
}
|
||||
setData(data, cfg = {}) {
|
||||
Object.assign(this._attrs, cfg);
|
||||
const transform = this.get('transforms');
|
||||
this._transforms = transform || [];
|
||||
this.set('data', data);
|
||||
this._init();
|
||||
}
|
||||
// 数据更新
|
||||
updateTransfrom(cfg) {
|
||||
const { transforms } = cfg;
|
||||
this._transforms = transforms;
|
||||
this.data = clone(this.originData);
|
||||
this._executeTrans();
|
||||
this._projectCoords();
|
||||
}
|
||||
|
||||
_excuteParser() {
|
||||
const parser = this.get('parser');
|
||||
const { type = 'geojson' } = parser;
|
||||
const data = this.get('data');
|
||||
this.data = getParser(type)(data, parser);
|
||||
this.originData = getParser(type)(data, parser);
|
||||
// this.data = {
|
||||
// dataArray: clone(this.originData.dataArray)
|
||||
// }; // TODO 关闭数据备份
|
||||
this.data = this.originData;
|
||||
if (this.data !== null) {
|
||||
this.data.extent = extent(this.data.dataArray);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 数据统计
|
||||
|
@ -40,36 +71,42 @@ export default class Source extends Base {
|
|||
const trans = this._transforms;
|
||||
trans.forEach(tran => {
|
||||
const { type } = tran;
|
||||
this.data = getTransform(type)(this.data, tran);
|
||||
const data = getTransform(type)(this.data, tran);
|
||||
Object.assign(this.data, data);
|
||||
});
|
||||
this._transforms = trans;
|
||||
}
|
||||
transform(option) {
|
||||
const data = getTransform(option.type)(this.data, option);
|
||||
Object.assign(this.data, data);
|
||||
}
|
||||
_executeCluster() {
|
||||
const clusterCfg = this.get('Cluster') || {};
|
||||
const zoom = this.get('zoom');
|
||||
clusterCfg.zoom = Math.floor(zoom);
|
||||
this.set('cluster', clusterCfg);
|
||||
const clusterData = cluster(this.data, clusterCfg);
|
||||
this.data = clusterData.data;
|
||||
this.pointIndex = clusterData.pointIndex;
|
||||
}
|
||||
updateCusterData(zoom, bbox) {
|
||||
const clusterPoint = this.pointIndex.getClusters(bbox, zoom);
|
||||
this.data.dataArray = formatData(clusterPoint);
|
||||
const clusterCfg = this.get('Cluster') || {};
|
||||
clusterCfg.zoom = Math.floor(zoom);
|
||||
clusterCfg.bbox = bbox;
|
||||
this.set('cluster', clusterCfg);
|
||||
this._projectCoords();
|
||||
}
|
||||
_projectCoords() {
|
||||
this.data.dataArray.forEach(data => {
|
||||
data.coordinates = this._coordProject(data.coordinates);
|
||||
});
|
||||
}
|
||||
createScale(field) {
|
||||
const data = this.data.dataArray;
|
||||
const scales = this.get('scales');
|
||||
let scale = scales[field];
|
||||
const scaleController = this.get('scaleController');
|
||||
if (!scale) {
|
||||
scale = scaleController.createScale(field, data);
|
||||
scales[field] = scale;
|
||||
if (this.data === null) {
|
||||
return;
|
||||
}
|
||||
return scale;
|
||||
}
|
||||
_initControllers() {
|
||||
const defs = this.get('defs');
|
||||
const mapType = this.get('mapType');
|
||||
this.projectFlat = getMap(mapType).project;
|
||||
const scaleController = new Controller.Scale({
|
||||
defs
|
||||
this.data.dataArray.forEach(data => {
|
||||
// data.coordinates = this._coordProject(data.coordinates);
|
||||
data.coordinates = tranfrormCoord(data.coordinates, this._coorConvert.bind(this));
|
||||
});
|
||||
this.set('scaleController', scaleController);
|
||||
}
|
||||
|
||||
_getCoord(geo) {
|
||||
if (geo.geometry) {
|
||||
// GeoJSON feature
|
||||
|
@ -81,7 +118,6 @@ export default class Source extends Base {
|
|||
return geo;
|
||||
}
|
||||
_coordProject(geo) {
|
||||
|
||||
if (Array.isArray(geo[0][0])) {
|
||||
return geo.map(coor => {
|
||||
return this._coordProject(coor);
|
||||
|
@ -89,22 +125,41 @@ export default class Source extends Base {
|
|||
}
|
||||
if (!Array.isArray(geo[0])) {
|
||||
return this._coorConvert(geo);
|
||||
|
||||
}
|
||||
return geo.map(coor => {
|
||||
return this._coorConvert(coor);
|
||||
});
|
||||
}
|
||||
_coorConvert(geo) {
|
||||
|
||||
const ll = this.projectFlat(geo);
|
||||
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];
|
||||
// 颜色编码从1开始,要素索引从0开始,所以颜色转变要素需要减1
|
||||
const isCluster = this.get('isCluster') || false;
|
||||
return (data.features && !isCluster)
|
||||
? data.features[featureId - 1]
|
||||
: this.data.dataArray[featureId - 1];
|
||||
}
|
||||
getSeletFeatureIndex(featureId) {
|
||||
const data = this.get('data');
|
||||
if (!data.features) {
|
||||
return featureId - 1;
|
||||
}
|
||||
let featureIndex = 0;
|
||||
for (let i = 0; i < this.data.dataArray.length; i++) {
|
||||
const item = this.data.dataArray[i];
|
||||
if (item._id === featureId) {
|
||||
break;
|
||||
}
|
||||
featureIndex++;
|
||||
}
|
||||
return featureIndex;
|
||||
}
|
||||
destroy() {
|
||||
this.data = null;
|
||||
this.originData = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -12,8 +12,13 @@ export { Mesh } from 'three/src/objects/Mesh.js';
|
|||
export { Texture } from 'three/src/textures/Texture.js';
|
||||
export { WebGLRenderTarget } from 'three/src/renderers/WebGLRenderTarget.js';
|
||||
export { PerspectiveCamera } from 'three/src/cameras/PerspectiveCamera.js';
|
||||
export { OrthographicCamera } from 'three/src/cameras/OrthographicCamera.js';
|
||||
export { BufferGeometry } from 'three/src/core/BufferGeometry.js';
|
||||
export { InstancedBufferGeometry } from 'three/src/core/InstancedBufferGeometry';
|
||||
export { PlaneBufferGeometry } from 'three/src/geometries/PlaneGeometry.js';
|
||||
export { BoxBufferGeometry } from 'three/src/geometries/BoxGeometry.js';
|
||||
export { Raycaster } from 'three/src/core/Raycaster.js';
|
||||
export { UniformsUtils } from 'three/src/renderers/shaders/UniformsUtils.js';
|
||||
export { Matrix4 } from 'three/src/math/Matrix4.js';
|
||||
export { Matrix3 } from 'three/src/math/Matrix3.js';
|
||||
export { Line } from 'three/src/objects/Line.js';
|
||||
|
@ -22,6 +27,7 @@ export { Vector3 } from 'three/src/math/Vector3.js';
|
|||
export { Vector2 } from 'three/src/math/Vector2.js';
|
||||
export { ShaderMaterial } from 'three/src/materials/ShaderMaterial.js';
|
||||
export { DataTexture } from 'three/src/textures/DataTexture.js';
|
||||
export { Color } from 'three/src/math/Color.js';
|
||||
export {
|
||||
Float64BufferAttribute,
|
||||
Float32BufferAttribute,
|
||||
|
@ -35,4 +41,5 @@ export {
|
|||
BufferAttribute
|
||||
} from 'three/src/core/BufferAttribute.js';
|
||||
|
||||
export { InstancedBufferAttribute } from 'three/src/core/InstancedBufferAttribute'
|
||||
// export * from '../../build/three.js';
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
|
||||
export const GeomBase = {
|
||||
color: 'updateDraw',
|
||||
size: 'repaint',
|
||||
filter: 'updateDraw',
|
||||
layer: '',
|
||||
pickable: true,
|
||||
setLayer(layer) {
|
||||
this.layer = layer;
|
||||
this.style = layer.get('styleOption');
|
||||
},
|
||||
getShape(type) {
|
||||
return type;
|
||||
},
|
||||
draw() {
|
||||
const shape = this.getShape();
|
||||
this.Mesh = shape.Mesh();
|
||||
},
|
||||
// 更新geometry buffer;
|
||||
updateDraw() {
|
||||
|
||||
},
|
||||
repaint() {
|
||||
|
||||
}
|
||||
};
|
||||
export const shapeBae = {
|
||||
geometryBuffer() {
|
||||
},
|
||||
|
||||
geometry() {},
|
||||
|
||||
material() {},
|
||||
|
||||
mesh() {
|
||||
|
||||
}
|
||||
};
|
|
@ -0,0 +1,101 @@
|
|||
import BufferBase from '../bufferBase';
|
||||
import { colorScales } from '../../../attr/colorscales';
|
||||
import * as THREE from '../../../core/three';
|
||||
|
||||
|
||||
export default class HeatmapBuffer extends BufferBase {
|
||||
geometryBuffer() {
|
||||
const data = this.get('data');
|
||||
const positions = [];
|
||||
const dirs = [];
|
||||
const weights = [];
|
||||
// const indices = [];
|
||||
|
||||
// 组织顶点数据
|
||||
data.forEach(d => {
|
||||
// const totalIndex = index * 4;
|
||||
const coord = d.coordinates;
|
||||
const weight = d.size;
|
||||
const dir = this._addDir(-1, 1);
|
||||
const dir1 = this._addDir(1, 1);
|
||||
const dir2 = this._addDir(-1, -1);
|
||||
const dir3 = this._addDir(1, -1);
|
||||
positions.push(...coord, ...coord, ...coord, ...coord, ...coord, ...coord);
|
||||
dirs.push(...dir, ...dir2, ...dir3, ...dir1, ...dir, ...dir3);
|
||||
weights.push(weight, weight, weight, weight, weight, weight);
|
||||
// indices.push(totalIndex, totalIndex + 2, totalIndex + 3, totalIndex, totalIndex + 3, totalIndex + 1);
|
||||
});
|
||||
|
||||
this.attributes = {
|
||||
vertices: positions,
|
||||
// indices,
|
||||
dirs,
|
||||
weights
|
||||
};
|
||||
}
|
||||
|
||||
_addVertex(position, dirX, dirY) {
|
||||
const x = (position[0] * 2) + ((dirX + 1) / 2);
|
||||
const y = (position[1] * 2) + ((dirY + 1) / 2);
|
||||
const z = position[2];
|
||||
return [ x, y, z ];
|
||||
}
|
||||
|
||||
_addDir(dirX, dirY) {
|
||||
const x = (dirX + 1) / 2;
|
||||
const y = (dirY + 1) / 2;
|
||||
return [ x, y ];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export function createColorRamp(colors) {
|
||||
const colorImageData = getColorRamp(colors);
|
||||
const colorTexture = getTexture(colorImageData);
|
||||
return colorTexture;
|
||||
}
|
||||
|
||||
function getColorRamp(name) {
|
||||
let colorscale = name;
|
||||
const canvas = document.createElement('canvas');
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
canvas.width = 1;
|
||||
canvas.height = 256;
|
||||
const gradient = ctx.createLinearGradient(0, 0, 0, 256);
|
||||
let data = null;
|
||||
if (typeof (colorscale) === 'string') {
|
||||
colorscale = colorScales[name];
|
||||
}
|
||||
if (Object.prototype.toString.call(colorscale) === '[object Object]') {
|
||||
const min = colorscale.positions[0];
|
||||
const max = colorscale.positions[colorscale.positions.length - 1];
|
||||
|
||||
for (let i = 0; i < colorscale.colors.length; ++i) {
|
||||
const value = (colorscale.positions[i] - min) / (max - min);
|
||||
gradient.addColorStop(value, colorscale.colors[i]);
|
||||
}
|
||||
ctx.fillStyle = gradient;
|
||||
ctx.fillRect(0, 0, 1, 256);
|
||||
data = new Uint8ClampedArray(ctx.getImageData(0, 0, 1, 256).data);
|
||||
}
|
||||
if (Object.prototype.toString.call(colorscale) === '[object Uint8Array]') {
|
||||
data = ctx.createImageData(1, 256);
|
||||
}
|
||||
|
||||
return new ImageData(data, 1, 256);
|
||||
|
||||
}
|
||||
|
||||
function getTexture(image) {
|
||||
const texture = new THREE.Texture(image);
|
||||
texture.wrapS = THREE.ClampToEdgeWrapping;
|
||||
texture.wrapT = THREE.ClampToEdgeWrapping;
|
||||
texture.magFilter = THREE.NearestFilter;
|
||||
texture.minFilter = THREE.NearestFilter;
|
||||
texture.format = THREE.RGBAFormat;
|
||||
texture.type = THREE.UnsignedByteType;
|
||||
texture.needsUpdate = true;
|
||||
return texture;
|
||||
}
|
||||
|
|
@ -23,7 +23,7 @@ export default class ImageBuffer extends BufferBase {
|
|||
const uv = [ 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0 ];
|
||||
const texture = new THREE.Texture(image);
|
||||
texture.magFilter = THREE.LinearFilter;
|
||||
texture.minFilter = THREE.LinearFilter;
|
||||
texture.minFilter = THREE.LinearMipMapLinearFilter;
|
||||
texture.needsUpdate = true;
|
||||
const attributes = {
|
||||
vertices: new Float32Array(positions),
|
||||
|
|
|
@ -75,7 +75,7 @@ export default class LineBuffer extends BufferBase {
|
|||
}
|
||||
_getMeshLineAttributes() {
|
||||
const layerData = this.get('layerData');
|
||||
const { lineType } = this.get('style');
|
||||
const { dashArray } = this.get('style');
|
||||
const positions = [];
|
||||
const pickingIds = [];
|
||||
const normal = [];
|
||||
|
@ -84,10 +84,11 @@ export default class LineBuffer extends BufferBase {
|
|||
const indexArray = [];
|
||||
const sizes = [];
|
||||
const attrDistance = [];
|
||||
const attrDashArray = [];
|
||||
layerData.forEach(item => {
|
||||
const props = item;
|
||||
const positionCount = positions.length / 3;
|
||||
const attr = lineShape.Line(item.coordinates, props, positionCount, (lineType !== 'soild'));
|
||||
const attr = lineShape.Line(item.coordinates, props, positionCount, dashArray);
|
||||
positions.push(...attr.positions);
|
||||
normal.push(...attr.normal);
|
||||
miter.push(...attr.miter);
|
||||
|
@ -96,6 +97,7 @@ export default class LineBuffer extends BufferBase {
|
|||
sizes.push(...attr.sizes);
|
||||
attrDistance.push(...attr.attrDistance);
|
||||
pickingIds.push(...attr.pickingIds);
|
||||
attrDashArray.push(...attr.dashArray);
|
||||
});
|
||||
return {
|
||||
positions,
|
||||
|
@ -105,22 +107,26 @@ export default class LineBuffer extends BufferBase {
|
|||
indexArray,
|
||||
pickingIds,
|
||||
sizes,
|
||||
attrDistance
|
||||
attrDistance,
|
||||
attrDashArray
|
||||
};
|
||||
}
|
||||
|
||||
_toAttributes(bufferStruct) {
|
||||
const vertCount = bufferStruct.verts.length;
|
||||
const vertices = new Float32Array(vertCount * 3);
|
||||
const pickingIds = new Float32Array(vertCount);
|
||||
const inposs = new Float32Array(vertCount * 4);
|
||||
const colors = new Float32Array(vertCount * 4);
|
||||
for (let i = 0; i < vertCount; i++) {
|
||||
const index = bufferStruct.indexs[i];
|
||||
const color = bufferStruct.style[index].color;
|
||||
const id = bufferStruct.style[index].id;
|
||||
vertices[i * 3] = bufferStruct.verts[i][0];
|
||||
vertices[i * 3 + 1] = bufferStruct.verts[i][1];
|
||||
vertices[i * 3 + 2] = bufferStruct.verts[i][2];
|
||||
colors[i * 4] = color[0];
|
||||
pickingIds[i] = id;
|
||||
colors[i * 4 + 1] = color[1];
|
||||
colors[i * 4 + 2] = color[2];
|
||||
colors[i * 4 + 3] = color[3];
|
||||
|
@ -133,6 +139,7 @@ export default class LineBuffer extends BufferBase {
|
|||
|
||||
}
|
||||
return {
|
||||
pickingIds,
|
||||
vertices,
|
||||
colors,
|
||||
inposs
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
import { packUint8ToFloat } from '../../../util/vertex-compress';
|
||||
import Global from '../../../global';
|
||||
const { pointShape } = Global;
|
||||
|
||||
const LEFT_SHIFT17 = 131072.0;
|
||||
// const LEFT_SHIFT18 = 262144.0;
|
||||
// const LEFT_SHIFT19 = 524288.0;
|
||||
// const LEFT_SHIFT20 = 1048576.0;
|
||||
const LEFT_SHIFT21 = 2097152.0;
|
||||
// const LEFT_SHIFT22 = 4194304.0;
|
||||
const LEFT_SHIFT23 = 8388608.0;
|
||||
// const LEFT_SHIFT24 = 16777216.0;
|
||||
|
||||
export default function circleBuffer(layerData) {
|
||||
const index = [];
|
||||
const aPosition = [];
|
||||
const aPackedData = [];
|
||||
|
||||
layerData.forEach(({ size = 0, color, id, coordinates, shape }, i) => {
|
||||
|
||||
const shapeIndex = pointShape['2d'].indexOf(shape) || 0;
|
||||
|
||||
if (isNaN(size)) {
|
||||
size = 0;
|
||||
}
|
||||
|
||||
// pack color(vec4) into vec2
|
||||
const packedColor = [
|
||||
packUint8ToFloat(color[0] * 255, color[1] * 255),
|
||||
packUint8ToFloat(color[2] * 255, color[3] * 255)
|
||||
];
|
||||
|
||||
// construct point coords
|
||||
[
|
||||
[ -1, -1 ],
|
||||
[ 1, -1 ],
|
||||
[ 1, 1 ],
|
||||
[ -1, 1 ]
|
||||
].forEach(extrude => {
|
||||
// vec4(color, color, (4-bit extrude, 4-bit shape, 16-bit size), id)
|
||||
aPackedData.push(
|
||||
...packedColor,
|
||||
(extrude[0] + 1) * LEFT_SHIFT23 + (extrude[1] + 1) * LEFT_SHIFT21
|
||||
+ shapeIndex * LEFT_SHIFT17
|
||||
+ size,
|
||||
id
|
||||
);
|
||||
});
|
||||
|
||||
// TODO:如果使用相对瓦片坐标,还可以进一步压缩
|
||||
aPosition.push(...coordinates, ...coordinates, ...coordinates, ...coordinates);
|
||||
index.push(...[ 0, 1, 2, 0, 2, 3 ].map(n => n + i * 4));
|
||||
});
|
||||
return {
|
||||
aPosition,
|
||||
index,
|
||||
aPackedData
|
||||
};
|
||||
}
|
|
@ -29,6 +29,12 @@ export default function fillBuffer(layerData) {
|
|||
throw new Error('Invalid shape type: ' + shape);
|
||||
}
|
||||
toPointShapeAttributes(polygon, coordinates, { size, shape, color, id }, attribute);
|
||||
// toPointShapeAttributes(polygon, null, {}, attribute);
|
||||
// instanced attributes
|
||||
// attribute.vertices.push(...coordinates);
|
||||
// attribute.a_size.push(...size);
|
||||
// attribute.colors.push(...color);
|
||||
// attribute.pickingIds.push(id);
|
||||
|
||||
});
|
||||
return attribute;
|
||||
|
@ -78,5 +84,8 @@ function toPointShapeAttributes(polygon, geo, style, attribute) {
|
|||
attribute.colors.push(...color, ...color, ...color);
|
||||
attribute.pickingIds.push(id, id, id);
|
||||
|
||||
// attribute.shapePositions.push(ax, ay, az, bx, by, bz, cx, cy, cz);
|
||||
// attribute.normals.push(nx, ny, nz, nx, ny, nz, nx, ny, nz);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,3 +2,4 @@ export { default as FillBuffer } from './fillBuffer';
|
|||
export { default as StrokeBuffer } from './strokeBuffer';
|
||||
export { default as ImageBuffer } from './imageBuffer';
|
||||
export { default as NormalBuffer } from './normalBuffer';
|
||||
export { default as CircleBuffer } from './circleBuffer';
|
||||
|
|
|
@ -13,7 +13,5 @@ export default function NormalBuffer(layerData) {
|
|||
attributes.sizes.push(size);
|
||||
|
||||
});
|
||||
|
||||
|
||||
return attributes;
|
||||
}
|
||||
|
|
|
@ -1,246 +0,0 @@
|
|||
// const SDFCommonWordsKey = '_AMap_sdf_com_words';
|
||||
|
||||
// /**
|
||||
// * SDF 常用字获取/存储/check
|
||||
// *
|
||||
// */
|
||||
// const SDFCommonWords = {
|
||||
|
||||
// store() {
|
||||
|
||||
// },
|
||||
|
||||
// /**
|
||||
// * 检查一个字符是否在常用字中
|
||||
// * @param {*} charcode 汉字
|
||||
// */
|
||||
// check(charcode) {
|
||||
// const range = this.range || [];
|
||||
// const info = this.info || {};
|
||||
|
||||
// if (typeof charcode !== 'number') {
|
||||
|
||||
// charcode = charcode.substr(0).charCodeAt(0);
|
||||
// }
|
||||
|
||||
// for (let i = 0; i < range.length; i++) {
|
||||
// const curRange = range[i];
|
||||
// const [ rangeStart, rangeEnd ] = curRange.split('-');
|
||||
|
||||
// if (charcode >= rangeStart && charcode <= rangeEnd) {
|
||||
|
||||
// const curInfo = info[curRange] && info[curRange].info || {};
|
||||
|
||||
// if (curInfo[charcode]) {
|
||||
|
||||
// return true;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// return false;
|
||||
// },
|
||||
|
||||
// /**
|
||||
// * 获取纹理和位置信息
|
||||
// * @param list
|
||||
// * @param cb
|
||||
// */
|
||||
// getImagesAndInfo(list, cb) {
|
||||
// const range = this.range;
|
||||
|
||||
|
||||
// },
|
||||
|
||||
// loadCanvas(url, range, done) {
|
||||
|
||||
// try {
|
||||
// const xhr = new XMLHttpRequest();
|
||||
// xhr.open('GET', url);
|
||||
|
||||
// // 直接用 blob 格式 load 图片文件,方便直接转换成 base64
|
||||
// // 转成 base64 便于存储
|
||||
// // 使用 canvas 转换 base64 容易有损
|
||||
// xhr.responseType = 'blob';
|
||||
// xhr.onerror = function() {
|
||||
// done({ code: 0 });
|
||||
// };
|
||||
|
||||
// xhr.onload = function() {
|
||||
|
||||
// if (xhr.status === 200) {
|
||||
// const reader = new FileReader();
|
||||
|
||||
// reader.onload = () => {
|
||||
|
||||
// done(reader.result, range);
|
||||
// };
|
||||
|
||||
// reader.readAsDataURL(xhr.response);
|
||||
// } else {
|
||||
// done({ code: 0 });
|
||||
// }
|
||||
// };
|
||||
|
||||
// xhr.send();
|
||||
// } catch (err) {
|
||||
|
||||
// done({ code: 0 });
|
||||
// }
|
||||
// },
|
||||
|
||||
// loadImages(urls = []) {
|
||||
// const deferred = $.Deferred();
|
||||
// const totalNumbers = urls.length;
|
||||
// const localInfo = this.info;
|
||||
// let loadPicNum = 0;
|
||||
|
||||
// for (let i = 0; i < urls.length; i++) {
|
||||
// const { url, range } = urls[i];
|
||||
|
||||
// this.loadCanvas(url, range, (base64, range) => {
|
||||
|
||||
// // image to base64
|
||||
// loadPicNum++;
|
||||
|
||||
// !localInfo[range] && (localInfo[range] = {});
|
||||
|
||||
// localInfo[range].pic = base64;
|
||||
|
||||
// this.info = localInfo;
|
||||
|
||||
// // todo: temp 暂时用 localstorage 存储,因为数据比较大,最好使用 indexDB
|
||||
// localStorage.setItem(SDFCommonWordsKey, JSON.stringify(localInfo));
|
||||
|
||||
// if (loadPicNum === totalNumbers) {
|
||||
|
||||
// deferred.resolve();
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
|
||||
// return deferred;
|
||||
// },
|
||||
|
||||
// loadInfo(urls) {
|
||||
// const deferred = $.Deferred();
|
||||
// const totalNumbers = urls.length;
|
||||
// const localInfo = this.info;
|
||||
// let loadInfoNum = 0;
|
||||
|
||||
// for (let i = 0; i < urls.length; i++) {
|
||||
// const { url, range } = urls[i];
|
||||
|
||||
// $.ajax({
|
||||
// url,
|
||||
// dataType: 'json',
|
||||
// success: data => {
|
||||
// loadInfoNum++;
|
||||
|
||||
// !localInfo[range] && (localInfo[range] = {});
|
||||
|
||||
// localInfo[range].info = data;
|
||||
|
||||
// this.info = localInfo;
|
||||
|
||||
// localStorage.setItem(SDFCommonWordsKey, JSON.stringify(localInfo));
|
||||
|
||||
// if (loadInfoNum === totalNumbers) {
|
||||
|
||||
// deferred.resolve();
|
||||
// }
|
||||
// },
|
||||
// error: () => {
|
||||
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
|
||||
// return deferred;
|
||||
|
||||
// },
|
||||
|
||||
// getTotalAssets(info, cb) {
|
||||
// const { range = [], urlPrefix } = info;
|
||||
// const picUrls = [];
|
||||
// const infoUrls = [];
|
||||
|
||||
// this.range = range;
|
||||
|
||||
// for (let i = 0; i < range.length; i++) {
|
||||
// const curRange = range[i];
|
||||
// const baseUrl = urlPrefix + curRange;
|
||||
// const picUrl = baseUrl + '.png';
|
||||
// const infoUrl = baseUrl + '.json';
|
||||
|
||||
// picUrls.push({ range: curRange, url: picUrl });
|
||||
// infoUrls.push({ range: curRange, url: infoUrl });
|
||||
// }
|
||||
|
||||
// const imageDeferred = this.loadImages(picUrls);
|
||||
// const infoDeferred = this.loadInfo(infoUrls);
|
||||
|
||||
// $.when(imageDeferred, infoDeferred)
|
||||
// .then(() => {
|
||||
|
||||
// // all info load complete
|
||||
// // console.log("all info load complete", " -- ", 1);
|
||||
// cb && cb(this.info);
|
||||
// }, () => {
|
||||
|
||||
// // fail
|
||||
// });
|
||||
// },
|
||||
// // 获取数据
|
||||
// getData(cb) {
|
||||
|
||||
// if (!_.isEmpty(this.info)) {
|
||||
|
||||
// cb && cb(this.info);
|
||||
// } else {
|
||||
|
||||
// this.getRemoteData(cb);
|
||||
// }
|
||||
// },
|
||||
|
||||
// /**
|
||||
// * 从服务获取数据,什么时候强制去取一回数据?过期?
|
||||
// * @param cb
|
||||
// */
|
||||
// getRemoteData(cb) {
|
||||
// const self = this;
|
||||
|
||||
// $.ajax({
|
||||
// url: '/getcommonwords',
|
||||
// dataType: 'json',
|
||||
// success: data => {
|
||||
|
||||
// if (data.code == 1) {
|
||||
|
||||
// const info = data.data;
|
||||
|
||||
// self.getTotalAssets(info, cb);
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
// },
|
||||
|
||||
// destroy() {
|
||||
|
||||
// },
|
||||
|
||||
// init() {
|
||||
// let info = localStorage.getItem(SDFCommonWordsKey);
|
||||
// this.range = [];
|
||||
// this.info = {};
|
||||
|
||||
// if (info) {
|
||||
// info = JSON.parse(info);
|
||||
// this.range = Object.keys(info);
|
||||
// this.info = info;
|
||||
// }
|
||||
|
||||
// this.info = info || {};
|
||||
// }
|
||||
// };
|
||||
// export default SDFCommonWords;
|
|
@ -0,0 +1,130 @@
|
|||
export default function TextBuffer(layerData, fontAtlasManager) {
|
||||
const characterSet = [];
|
||||
layerData.forEach(element => {
|
||||
let text = element.shape || '';
|
||||
text = text.toString();
|
||||
for (let j = 0; j < text.length; j++) {
|
||||
if (characterSet.indexOf(text[j]) === -1) {
|
||||
characterSet.push(text[j]);
|
||||
}
|
||||
}
|
||||
});
|
||||
fontAtlasManager.setProps({
|
||||
characterSet
|
||||
});
|
||||
const attr = drawGlyph(layerData, fontAtlasManager);
|
||||
return attr;
|
||||
}
|
||||
function drawGlyph(layerData, fontAtlasManager) {
|
||||
const attributes = {
|
||||
originPoints: [],
|
||||
textSizes: [],
|
||||
textOffsets: [],
|
||||
colors: [],
|
||||
textureElements: [],
|
||||
pickingIds: []
|
||||
};
|
||||
const { texture, fontAtlas, mapping, scale } = fontAtlasManager;
|
||||
layerData.forEach(function(element) {
|
||||
const size = element.size;
|
||||
const pos = element.coordinates;
|
||||
let text = element.shape || '';
|
||||
text = text.toString();
|
||||
const pen = {
|
||||
x: (-text.length * size) / 2,
|
||||
y: 0
|
||||
};
|
||||
for (let i = 0; i < text.length; i++) {
|
||||
const metric = mapping[text[i]];
|
||||
const { x, y, width, height } = metric;
|
||||
const color = element.color;
|
||||
const offsetX = pen.x;
|
||||
const offsetY = pen.y;
|
||||
attributes.pickingIds.push(
|
||||
element.id,
|
||||
element.id,
|
||||
element.id,
|
||||
element.id,
|
||||
element.id,
|
||||
element.id
|
||||
);
|
||||
attributes.textOffsets.push(
|
||||
// 文字在词语的偏移量
|
||||
offsetX,
|
||||
offsetY,
|
||||
offsetX,
|
||||
offsetY,
|
||||
offsetX,
|
||||
offsetY,
|
||||
offsetX,
|
||||
offsetY,
|
||||
offsetX,
|
||||
offsetY,
|
||||
offsetX,
|
||||
offsetY
|
||||
);
|
||||
attributes.originPoints.push(
|
||||
// 词语的经纬度坐标
|
||||
pos[0],
|
||||
pos[1],
|
||||
0,
|
||||
pos[0],
|
||||
pos[1],
|
||||
0,
|
||||
pos[0],
|
||||
pos[1],
|
||||
0,
|
||||
pos[0],
|
||||
pos[1],
|
||||
0,
|
||||
pos[0],
|
||||
pos[1],
|
||||
0,
|
||||
pos[0],
|
||||
pos[1],
|
||||
0
|
||||
);
|
||||
attributes.textSizes.push(
|
||||
size,
|
||||
size * scale,
|
||||
0,
|
||||
size * scale,
|
||||
0,
|
||||
0,
|
||||
size,
|
||||
size * scale,
|
||||
0,
|
||||
0,
|
||||
size,
|
||||
0
|
||||
);
|
||||
attributes.colors.push(
|
||||
...color,
|
||||
...color,
|
||||
...color,
|
||||
...color,
|
||||
...color,
|
||||
...color
|
||||
);
|
||||
attributes.textureElements.push(
|
||||
// 文字纹理坐标
|
||||
x + width,
|
||||
y,
|
||||
x,
|
||||
y,
|
||||
x,
|
||||
y + height,
|
||||
x + width,
|
||||
y,
|
||||
x,
|
||||
y + height,
|
||||
x + width,
|
||||
y + height
|
||||
);
|
||||
pen.x = pen.x + size;
|
||||
}
|
||||
});
|
||||
attributes.texture = texture;
|
||||
attributes.fontAtlas = fontAtlas;
|
||||
return attributes;
|
||||
}
|
|
@ -0,0 +1,227 @@
|
|||
import TinySDF from '@mapbox/tiny-sdf';
|
||||
import { buildMapping } from '../../../../util/font-util';
|
||||
import * as THREE from '../../../../core/three';
|
||||
import LRUCache from './lru-cache';
|
||||
export const DEFAULT_CHAR_SET = getDefaultCharacterSet();
|
||||
export const DEFAULT_FONT_FAMILY = 'sans-serif';
|
||||
export const DEFAULT_FONT_WEIGHT = 'normal';
|
||||
export const DEFAULT_FONT_SIZE = 24;
|
||||
export const DEFAULT_BUFFER = 3;
|
||||
export const DEFAULT_CUTOFF = 0.25;
|
||||
export const DEFAULT_RADIUS = 8;
|
||||
const MAX_CANVAS_WIDTH = 1024;
|
||||
const BASELINE_SCALE = 0.9;
|
||||
const HEIGHT_SCALE = 1.2;
|
||||
const CACHE_LIMIT = 3;
|
||||
const cache = new LRUCache(CACHE_LIMIT);
|
||||
|
||||
const VALID_PROPS = [
|
||||
'fontFamily',
|
||||
'fontWeight',
|
||||
'characterSet',
|
||||
'fontSize',
|
||||
'sdf',
|
||||
'buffer',
|
||||
'cutoff',
|
||||
'radius'
|
||||
];
|
||||
|
||||
function getDefaultCharacterSet() {
|
||||
const charSet = [];
|
||||
for (let i = 32; i < 128; i++) {
|
||||
charSet.push(String.fromCharCode(i));
|
||||
}
|
||||
return charSet;
|
||||
}
|
||||
|
||||
function setTextStyle(ctx, fontFamily, fontSize, fontWeight) {
|
||||
ctx.font = `${fontWeight} ${fontSize}px ${fontFamily}`;
|
||||
ctx.fillStyle = '#000';
|
||||
ctx.textBaseline = 'baseline';
|
||||
ctx.textAlign = 'left';
|
||||
}
|
||||
function getNewChars(key, characterSet) {
|
||||
const cachedFontAtlas = cache.get(key);
|
||||
if (!cachedFontAtlas) {
|
||||
return characterSet;
|
||||
}
|
||||
|
||||
const newChars = [];
|
||||
const cachedMapping = cachedFontAtlas.mapping;
|
||||
let cachedCharSet = Object.keys(cachedMapping);
|
||||
cachedCharSet = new Set(cachedCharSet);
|
||||
|
||||
let charSet = characterSet;
|
||||
if (charSet instanceof Array) {
|
||||
charSet = new Set(charSet);
|
||||
}
|
||||
|
||||
charSet.forEach(char => {
|
||||
if (!cachedCharSet.has(char)) {
|
||||
newChars.push(char);
|
||||
}
|
||||
});
|
||||
|
||||
return newChars;
|
||||
}
|
||||
|
||||
function populateAlphaChannel(alphaChannel, imageData) {
|
||||
// populate distance value from tinySDF to image alpha channel
|
||||
for (let i = 0; i < alphaChannel.length; i++) {
|
||||
imageData.data[4 * i + 3] = alphaChannel[i];
|
||||
}
|
||||
}
|
||||
|
||||
export default class FontAtlasManager {
|
||||
constructor() {
|
||||
|
||||
// font settings
|
||||
this.props = {
|
||||
fontFamily: DEFAULT_FONT_FAMILY,
|
||||
fontWeight: DEFAULT_FONT_WEIGHT,
|
||||
characterSet: DEFAULT_CHAR_SET,
|
||||
fontSize: DEFAULT_FONT_SIZE,
|
||||
buffer: DEFAULT_BUFFER,
|
||||
// sdf only props
|
||||
// https://github.com/mapbox/tiny-sdf
|
||||
sdf: true,
|
||||
cutoff: DEFAULT_CUTOFF,
|
||||
radius: DEFAULT_RADIUS
|
||||
};
|
||||
|
||||
// key is used for caching generated fontAtlas
|
||||
this._key = null;
|
||||
this._texture = new THREE.Texture();
|
||||
}
|
||||
|
||||
get texture() {
|
||||
return this._texture;
|
||||
}
|
||||
|
||||
get mapping() {
|
||||
const data = cache.get(this._key);
|
||||
return data && data.mapping;
|
||||
}
|
||||
|
||||
get scale() {
|
||||
return HEIGHT_SCALE;
|
||||
}
|
||||
|
||||
get fontAtlas() {
|
||||
return this._fontAtlas;
|
||||
}
|
||||
|
||||
setProps(props = {}) {
|
||||
VALID_PROPS.forEach(prop => {
|
||||
if (prop in props) {
|
||||
this.props[prop] = props[prop];
|
||||
}
|
||||
});
|
||||
|
||||
// update cache key
|
||||
const oldKey = this._key;
|
||||
this._key = this._getKey();
|
||||
|
||||
const charSet = getNewChars(this._key, this.props.characterSet);
|
||||
const cachedFontAtlas = cache.get(this._key);
|
||||
|
||||
// if a fontAtlas associated with the new settings is cached and
|
||||
// there are no new chars
|
||||
if (cachedFontAtlas && charSet.length === 0) {
|
||||
// update texture with cached fontAtlas
|
||||
if (this._key !== oldKey) {
|
||||
this._updateTexture(cachedFontAtlas);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// update fontAtlas with new settings
|
||||
const fontAtlas = this._generateFontAtlas(this._key, charSet, cachedFontAtlas);
|
||||
this._fontAtlas = fontAtlas;
|
||||
this._updateTexture(fontAtlas);
|
||||
|
||||
// update cache
|
||||
cache.set(this._key, fontAtlas);
|
||||
}
|
||||
|
||||
_updateTexture({ data: canvas }) {
|
||||
this._texture = new THREE.CanvasTexture(canvas);
|
||||
this._texture.wrapS = THREE.ClampToEdgeWrapping;
|
||||
this._texture.wrapT = THREE.ClampToEdgeWrapping;
|
||||
this._texture.minFilter = THREE.LinearFilter;
|
||||
this._texture.flipY = false;
|
||||
this._texture.needUpdate = true;
|
||||
}
|
||||
|
||||
_generateFontAtlas(key, characterSet, cachedFontAtlas) {
|
||||
const { fontFamily, fontWeight, fontSize, buffer, sdf, radius, cutoff } = this.props;
|
||||
let canvas = cachedFontAtlas && cachedFontAtlas.data;
|
||||
if (!canvas) {
|
||||
canvas = document.createElement('canvas');
|
||||
canvas.width = MAX_CANVAS_WIDTH;
|
||||
}
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
setTextStyle(ctx, fontFamily, fontSize, fontWeight);
|
||||
|
||||
// 1. build mapping
|
||||
const { mapping, canvasHeight, xOffset, yOffset } = buildMapping(
|
||||
Object.assign(
|
||||
{
|
||||
getFontWidth: char => ctx.measureText(char).width,
|
||||
fontHeight: fontSize * HEIGHT_SCALE,
|
||||
buffer,
|
||||
characterSet,
|
||||
maxCanvasWidth: MAX_CANVAS_WIDTH
|
||||
},
|
||||
cachedFontAtlas && {
|
||||
mapping: cachedFontAtlas.mapping,
|
||||
xOffset: cachedFontAtlas.xOffset,
|
||||
yOffset: cachedFontAtlas.yOffset
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
// 2. update canvas
|
||||
// copy old canvas data to new canvas only when height changed
|
||||
if (canvas.height !== canvasHeight) {
|
||||
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
||||
canvas.height = canvasHeight;
|
||||
ctx.putImageData(imageData, 0, 0);
|
||||
}
|
||||
setTextStyle(ctx, fontFamily, fontSize, fontWeight);
|
||||
|
||||
// 3. layout characters
|
||||
if (sdf) {
|
||||
const tinySDF = new TinySDF(fontSize, buffer, radius, cutoff, fontFamily, fontWeight);
|
||||
// used to store distance values from tinySDF
|
||||
// tinySDF.size equals `fontSize + buffer * 2`
|
||||
const imageData = ctx.getImageData(0, 0, tinySDF.size, tinySDF.size);
|
||||
|
||||
for (const char of characterSet) {
|
||||
populateAlphaChannel(tinySDF.draw(char), imageData);
|
||||
ctx.putImageData(imageData, mapping[char].x - buffer, mapping[char].y - buffer);
|
||||
}
|
||||
} else {
|
||||
for (const char of characterSet) {
|
||||
ctx.fillText(char, mapping[char].x, mapping[char].y + fontSize * BASELINE_SCALE);
|
||||
}
|
||||
}
|
||||
return {
|
||||
xOffset,
|
||||
yOffset,
|
||||
mapping,
|
||||
data: canvas,
|
||||
width: canvas.width,
|
||||
height: canvas.height
|
||||
};
|
||||
}
|
||||
|
||||
_getKey() {
|
||||
const { fontFamily, fontWeight, fontSize, buffer, sdf, radius, cutoff } = this.props;
|
||||
if (sdf) {
|
||||
return `${fontFamily} ${fontWeight} ${fontSize} ${buffer} ${radius} ${cutoff}`;
|
||||
}
|
||||
return `${fontFamily} ${fontWeight} ${fontSize} ${buffer}`;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
/**
|
||||
* LRU Cache class with limit
|
||||
*
|
||||
* Update order for each get/set operation
|
||||
* Delete oldest when reach given limit
|
||||
*/
|
||||
|
||||
export default class LRUCache {
|
||||
constructor(limit = 5) {
|
||||
this.limit = limit;
|
||||
|
||||
this.clear();
|
||||
}
|
||||
|
||||
clear() {
|
||||
this._cache = {};
|
||||
// access/update order, first item is oldest, last item is newest
|
||||
this._order = [];
|
||||
}
|
||||
|
||||
get(key) {
|
||||
const value = this._cache[key];
|
||||
if (value) {
|
||||
// update order
|
||||
this._deleteOrder(key);
|
||||
this._appendOrder(key);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
set(key, value) {
|
||||
if (!this._cache[key]) {
|
||||
// if reach limit, delete the oldest
|
||||
if (Object.keys(this._cache).length === this.limit) {
|
||||
this.delete(this._order[0]);
|
||||
}
|
||||
|
||||
this._cache[key] = value;
|
||||
this._appendOrder(key);
|
||||
} else {
|
||||
// if found in cache, delete the old one, insert new one to the first of list
|
||||
this.delete(key);
|
||||
|
||||
this._cache[key] = value;
|
||||
this._appendOrder(key);
|
||||
}
|
||||
}
|
||||
|
||||
delete(key) {
|
||||
const value = this._cache[key];
|
||||
if (value) {
|
||||
this._deleteCache(key);
|
||||
this._deleteOrder(key);
|
||||
}
|
||||
}
|
||||
|
||||
_deleteCache(key) {
|
||||
delete this._cache[key];
|
||||
}
|
||||
|
||||
_deleteOrder(key) {
|
||||
const index = this._order.findIndex(o => o === key);
|
||||
if (index >= 0) {
|
||||
this._order.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
_appendOrder(key) {
|
||||
this._order.push(key);
|
||||
}
|
||||
}
|
|
@ -1,181 +0,0 @@
|
|||
|
||||
|
||||
import { getJSON } from '../../../util/ajax';
|
||||
import EventEmitter from 'wolfy87-eventemitter';
|
||||
import Global from '../../../global';
|
||||
// const Space = 1;
|
||||
const metrics = {
|
||||
buffer: 3,
|
||||
family: 'ios9',
|
||||
size: 24
|
||||
};
|
||||
export default function TextBuffer(layerData, style) {
|
||||
EventEmitter.call(this);
|
||||
const attributes = {
|
||||
originPoints: [],
|
||||
textSizes: [],
|
||||
textOffsets: [],
|
||||
colors: [],
|
||||
textureElements: []
|
||||
};
|
||||
const { textOffset = [ 0, 0 ] } = style;
|
||||
const chars = [];
|
||||
const textChars = {};
|
||||
layerData.forEach(element => {
|
||||
let text = element.shape || '';
|
||||
text = text.toString();
|
||||
for (let j = 0; j < text.length; j++) {
|
||||
const code = text.charCodeAt(j);
|
||||
textChars[text] = 0;
|
||||
if (chars.indexOf(code) === -1) {
|
||||
chars.push(text.charCodeAt(j));
|
||||
}
|
||||
}
|
||||
});
|
||||
loadTextInfo(chars, (chars, texture) => {
|
||||
layerData.forEach(element => {
|
||||
const size = element.size;
|
||||
const pos = layerData.coordinates;
|
||||
const pen = { x: textOffset[0], y: textOffset[1] };
|
||||
let text = element.shape || '';
|
||||
text = text.toString();
|
||||
for (let i = 0; i < text.length; i++) {
|
||||
const color = element.color;
|
||||
drawGlyph(chars, pos, text[i], pen, size, attributes.colors, attributes.textureElements, attributes.originPoints, attributes.textSizes, attributes.textOffsets, color);
|
||||
}
|
||||
this.emit('completed', { attributes, texture });
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function loadTextInfo(chars, done) {
|
||||
getJSON({
|
||||
url: `${Global.sdfHomeUrl}/getsdfdata?chars=${chars.join('|')}`
|
||||
}, (e, info) => {
|
||||
loadTextTexture(info.url, texture => {
|
||||
done(info.info, texture);
|
||||
});
|
||||
});
|
||||
}
|
||||
function loadTextTexture(url, cb) {
|
||||
|
||||
|
||||
const img = new Image();
|
||||
img.crossOrigin = 'anonymous';
|
||||
|
||||
img.onload = () => {
|
||||
const textTexture = this._creatTexture(img);
|
||||
cb(textTexture);
|
||||
};
|
||||
img.src = url;
|
||||
|
||||
}
|
||||
/**
|
||||
* 计算每个标注词语的位置
|
||||
* @param {*} chars 文本信息
|
||||
* @param {*} pos 文字三维空间坐标
|
||||
* @param {*} text 字符
|
||||
* @param {*} pen 字符在词语的偏移量
|
||||
* @param {*} size 字体大小
|
||||
* @param {*} colors 颜色
|
||||
* @param {*} textureElements 纹理坐标
|
||||
* @param {*} originPoints 初始位置数据
|
||||
* @param {*} textSizes 文字大小数组
|
||||
* @param {*} textOffsets 字体偏移量数据
|
||||
* @param {*} color 文字颜色
|
||||
*/
|
||||
function drawGlyph(chars, pos, text, pen, size, colors, textureElements, originPoints, textSizes, textOffsets, color) {
|
||||
const chr = text.charCodeAt(0);
|
||||
const metric = chars[chr];
|
||||
if (!metric) return;
|
||||
const scale = size / metrics.size;
|
||||
|
||||
let width = metric[0];
|
||||
let height = metric[1];
|
||||
const posX = metric[5];
|
||||
const posY = metric[6];
|
||||
const buffer = metrics.buffer;
|
||||
if (width > 0 && height > 0) {
|
||||
width += buffer * 2;
|
||||
height += buffer * 2;
|
||||
const originX = 0;
|
||||
const originY = 0;
|
||||
const offsetX = pen.x;
|
||||
const offsetY = pen.y;
|
||||
originPoints.push(
|
||||
pos[0] + originX, pos[1] + originY, 0,
|
||||
pos[0] + originX, pos[1] + originY, 0,
|
||||
pos[0] + originX, pos[1] + originY, 0,
|
||||
pos[0] + originX, pos[1] + originY, 0,
|
||||
pos[0] + originX, pos[1] + originY, 0,
|
||||
pos[0] + originX, pos[1] + originY, 0,
|
||||
);
|
||||
const bx = 0;
|
||||
const by = metrics.size / 2 + buffer;
|
||||
textSizes.push(
|
||||
((bx - buffer + width) * scale), (height - by) * scale,
|
||||
((bx - buffer) * scale), (height - by) * scale,
|
||||
((bx - buffer) * scale), -by * scale,
|
||||
|
||||
((bx - buffer + width) * scale), (height - by) * scale,
|
||||
((bx - buffer) * scale), -by * scale,
|
||||
((bx - buffer + width) * scale), -by * scale,
|
||||
);
|
||||
|
||||
|
||||
textOffsets.push(
|
||||
offsetX, offsetY,
|
||||
offsetX, offsetY,
|
||||
offsetX, offsetY,
|
||||
offsetX, offsetY,
|
||||
offsetX, offsetY,
|
||||
offsetX, offsetY,
|
||||
);
|
||||
|
||||
colors.push(
|
||||
...color,
|
||||
...color,
|
||||
...color,
|
||||
...color,
|
||||
...color,
|
||||
...color,
|
||||
);
|
||||
textureElements.push(
|
||||
|
||||
posX + width, posY,
|
||||
posX, posY,
|
||||
posX, posY + height,
|
||||
|
||||
posX + width, posY,
|
||||
posX, posY + height,
|
||||
posX + width, posY + height
|
||||
);
|
||||
}
|
||||
pen.x = pen.x + size * 1.8;
|
||||
|
||||
}
|
||||
|
||||
|
||||
// function measureText(text, size) {
|
||||
// const dimensions = {
|
||||
// advance: 0
|
||||
// };
|
||||
// const metrics = this.metrics;
|
||||
// const scale = size / metrics.size;
|
||||
// for (let i = 0; i < text.length; i++) {
|
||||
// const code = text.charCodeAt(i);
|
||||
// const horiAdvance = metrics.chars[code][4];
|
||||
|
||||
// dimensions.advance += (horiAdvance + Space) * scale;
|
||||
// }
|
||||
|
||||
// return dimensions;
|
||||
// }
|
||||
// function creatTexture(image) {
|
||||
// this.bufferStruct.textSize = [ image.width, image.height ];
|
||||
// const texture = new THREE.Texture(image);
|
||||
// texture.minFilter = THREE.LinearFilter;
|
||||
// texture.magFilter = THREE.ClampToEdgeWrapping;
|
||||
// texture.needsUpdate = true;
|
||||
// return texture;
|
||||
// }
|
|
@ -15,17 +15,16 @@ export class RasterBuffer extends BufferBase {
|
|||
];
|
||||
const imgPosUv = [ 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0 ];
|
||||
const size = this.get('size');
|
||||
|
||||
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');
|
||||
const colorImageData = this.getColorRamp(colors);
|
||||
const colorTexture = this._getTexture(colorImageData);
|
||||
const colorTexture = this._getTexture(colorImageData); // 颜色纹理
|
||||
this.bufferStruct.position = positions;
|
||||
this.bufferStruct.uv = imgPosUv;
|
||||
this.bufferStruct.u_raster = texture;//
|
||||
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(width, height, size, this.bufferStruct.u_extent);
|
||||
const attributes = {
|
||||
|
@ -68,16 +67,23 @@ export class RasterBuffer extends BufferBase {
|
|||
return new ImageData(data, 16, 16);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 颜色纹理
|
||||
* @param {*} image 颜色图片
|
||||
* @return {texture} texture
|
||||
*/
|
||||
_getTexture(image) {
|
||||
const texture1 = new THREE.Texture(image);
|
||||
texture1.magFilter = THREE.LinearFilter;
|
||||
texture1.minFilter = THREE.LinearFilter;
|
||||
texture1.format = THREE.RGBAFormat;
|
||||
texture1.type = THREE.UnsignedByteType;
|
||||
texture1.generateMipmaps = true;
|
||||
texture1.needsUpdate = true;
|
||||
return texture1;
|
||||
}
|
||||
_buildTriangles(width, height, size = 1, extent) {
|
||||
_buildTriangles(width, height, size = 2, extent) {
|
||||
// const extent = [ 73.482190241, 3.82501784112, 135.106618732, 57.6300459963 ]
|
||||
const indices = [];
|
||||
const vertices = [];
|
||||
|
|
|
@ -1,276 +0,0 @@
|
|||
import BufferBase from './bufferBase';
|
||||
import { getJSON } from '../../util/ajax';
|
||||
import * as THREE from '../../core/three';
|
||||
import TinySDF from '@mapbox/tiny-sdf';
|
||||
|
||||
import Global from '../../global';
|
||||
const Space = 1;
|
||||
export default class TextBuffer extends BufferBase {
|
||||
|
||||
geometryBuffer() {
|
||||
this.metrics = {
|
||||
buffer: 3,
|
||||
family: 'ios9',
|
||||
size: 24
|
||||
};
|
||||
const layerData = this.get('layerData');
|
||||
const { textOffset = [ 0, 0 ] } = this.get('style');
|
||||
const chars = [];
|
||||
const textChars = {};
|
||||
layerData.forEach(element => {
|
||||
let text = element.shape || '';
|
||||
text = text.toString();
|
||||
for (let j = 0; j < text.length; j++) {
|
||||
const code = text.charCodeAt(j);
|
||||
textChars[text] = 0;
|
||||
if (chars.indexOf(code) === -1) {
|
||||
chars.push(text.charCodeAt(j));
|
||||
}
|
||||
}
|
||||
});
|
||||
const sdfTexture = this._updateSdf(Object.keys(textChars).join(''));
|
||||
this.sdfTexture = sdfTexture;
|
||||
|
||||
this._loadTextInfo(chars);
|
||||
this.on('SourceLoaded', () => {
|
||||
const textureElements = [];
|
||||
const colors = [];
|
||||
const originPoints = [];
|
||||
const textSizes = [];
|
||||
const textOffsets = [];
|
||||
layerData.forEach(element => {
|
||||
const size = element.size;
|
||||
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 || '';
|
||||
text = text.toString();
|
||||
for (let i = 0; i < text.length; i++) {
|
||||
|
||||
|
||||
const color = element.color;
|
||||
this._drawGlyph(pos, text[i], pen, size, colors, textureElements, originPoints, textSizes, textOffsets, color);
|
||||
}
|
||||
});
|
||||
this.bufferStruct.style = layerData;
|
||||
this.attributes = {
|
||||
originPoints,
|
||||
textSizes,
|
||||
textOffsets,
|
||||
colors,
|
||||
textureElements
|
||||
};
|
||||
this.emit('completed');
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
_loadTextInfo(chars) {
|
||||
getJSON({
|
||||
url: `${Global.sdfHomeUrl}/getsdfdata?chars=${chars.join('|')}`
|
||||
}, (e, info) => {
|
||||
this.metrics.chars = info.info;
|
||||
|
||||
this._loadTextTexture(info.url);
|
||||
});
|
||||
}
|
||||
_loadTextTexture(url) {
|
||||
|
||||
|
||||
const img = new Image();
|
||||
img.crossOrigin = 'anonymous';
|
||||
|
||||
|
||||
img.onload = () => {
|
||||
this.bufferStruct.textTexture = this._creatTexture(this.sdfTexture.texure);
|
||||
this.emit('SourceLoaded');
|
||||
};
|
||||
img.src = url;
|
||||
|
||||
}
|
||||
/**
|
||||
* 计算每个标注词语的位置
|
||||
* @param {*} pos 文字三维空间坐标
|
||||
* @param {*} text 字符
|
||||
* @param {*} pen 字符在词语的偏移量
|
||||
* @param {*} size 字体大小
|
||||
* @param {*} colors 颜色
|
||||
* @param {*} textureElements 纹理坐标
|
||||
* @param {*} originPoints 初始位置数据
|
||||
* @param {*} textSizes 文字大小数组
|
||||
* @param {*} textOffsets 字体偏移量数据
|
||||
* @param {*} color 文字颜色
|
||||
*/
|
||||
_drawGlyph(pos, text, pen, size, colors, textureElements, originPoints, textSizes, textOffsets, color) {
|
||||
const metrics = this.metrics;
|
||||
const chr = text.charCodeAt(0);
|
||||
const metric = metrics.chars[chr];
|
||||
if (!metric) return;
|
||||
const info = this.sdfTexture.info;
|
||||
const { x, y } = info[text];
|
||||
const scale = size / metrics.size;
|
||||
|
||||
let width = 24; // metric[0];
|
||||
let height = 24;// metric[1];
|
||||
|
||||
// const horiBearingX = metric[2];
|
||||
// const horiBearingY = metric[3];
|
||||
|
||||
// const horiAdvance = metric[4];
|
||||
// const posX = metric[5];
|
||||
// const posY = metric[6];
|
||||
const posX = x;
|
||||
const posY = y;
|
||||
|
||||
const buffer = metrics.buffer;
|
||||
|
||||
if (width > 0 && height > 0) {
|
||||
width += buffer * 2;
|
||||
height += buffer * 2;
|
||||
|
||||
// Add a quad (= two triangles) per glyph.
|
||||
// const originX = (horiBearingX - buffer + width / 2) * scale;
|
||||
// const originY = -(height - horiBearingY) * scale;
|
||||
const originX = 0;
|
||||
const originY = 0;
|
||||
|
||||
// const offsetWidth = width / 2 * scale / (1.0 - horiBearingX * 1.5 / horiAdvance);
|
||||
// const offsetHeight = (horiAdvance / 2) * scale;
|
||||
|
||||
// const offsetWidth = width/2 * scale;
|
||||
// const offsetHeight = height / 2 * scale;
|
||||
// const offsetHeight = height * scale;
|
||||
|
||||
const offsetX = pen.x;
|
||||
const offsetY = pen.y;
|
||||
originPoints.push(
|
||||
pos[0] + originX, pos[1] + originY, 0,
|
||||
pos[0] + originX, pos[1] + originY, 0,
|
||||
pos[0] + originX, pos[1] + originY, 0,
|
||||
pos[0] + originX, pos[1] + originY, 0,
|
||||
pos[0] + originX, pos[1] + originY, 0,
|
||||
pos[0] + originX, pos[1] + originY, 0,
|
||||
);
|
||||
|
||||
// textSizes.push(
|
||||
// offsetWidth, offsetHeight,
|
||||
// -offsetWidth, offsetHeight,
|
||||
// -offsetWidth, -offsetHeight,
|
||||
// offsetWidth, offsetHeight,
|
||||
// -offsetWidth, -offsetHeight,
|
||||
// offsetWidth, -offsetHeight,
|
||||
// );
|
||||
const bx = 0;
|
||||
const by = metrics.size / 2 + buffer;
|
||||
textSizes.push(
|
||||
|
||||
|
||||
((bx - buffer + width) * scale), (height - by) * scale,
|
||||
((bx - buffer) * scale), (height - by) * scale,
|
||||
((bx - buffer) * scale), -by * scale,
|
||||
|
||||
((bx - buffer + width) * scale), (height - by) * scale,
|
||||
((bx - buffer) * scale), -by * scale,
|
||||
((bx - buffer + width) * scale), -by * scale,
|
||||
|
||||
|
||||
);
|
||||
|
||||
|
||||
textOffsets.push(
|
||||
offsetX, offsetY,
|
||||
offsetX, offsetY,
|
||||
offsetX, offsetY,
|
||||
offsetX, offsetY,
|
||||
offsetX, offsetY,
|
||||
offsetX, offsetY,
|
||||
);
|
||||
|
||||
colors.push(
|
||||
...color,
|
||||
...color,
|
||||
...color,
|
||||
...color,
|
||||
...color,
|
||||
...color,
|
||||
);
|
||||
textureElements.push(
|
||||
|
||||
posX + width, posY,
|
||||
posX, posY,
|
||||
posX, posY + height,
|
||||
|
||||
posX + width, posY,
|
||||
posX, posY + height,
|
||||
posX + width, posY + height
|
||||
);
|
||||
}
|
||||
|
||||
// pen.x = pen.x + (horiAdvance + Space) * scale;
|
||||
pen.x = pen.x + size * 1.8;
|
||||
|
||||
}
|
||||
|
||||
|
||||
_measureText(text, size) {
|
||||
const dimensions = {
|
||||
advance: 0
|
||||
};
|
||||
const metrics = this.metrics;
|
||||
const scale = size / metrics.size;
|
||||
for (let i = 0; i < text.length; i++) {
|
||||
const code = text.charCodeAt(i);
|
||||
const horiAdvance = metrics.chars[code][4];
|
||||
|
||||
dimensions.advance += (horiAdvance + Space) * scale;
|
||||
}
|
||||
|
||||
return dimensions;
|
||||
}
|
||||
_creatTexture(image) {
|
||||
this.bufferStruct.textSize = [ image.width, image.height ];
|
||||
const texture = new THREE.Texture(image);
|
||||
texture.minFilter = THREE.LinearFilter;
|
||||
texture.magFilter = THREE.ClampToEdgeWrapping;
|
||||
texture.needsUpdate = true;
|
||||
return texture;
|
||||
}
|
||||
_updateSdf(chars) {
|
||||
const canvas = document.createElement('canvas');
|
||||
const ctx = canvas.getContext('2d');
|
||||
const sdfs = {};
|
||||
|
||||
|
||||
const fontSize = 24;
|
||||
const fontWeight = 100;
|
||||
const buffer = fontSize / 8;
|
||||
const radius = fontSize / 3;
|
||||
const canvasSize = Math.floor(Math.pow(chars.length, 0.5)) * (fontSize + buffer + radius);
|
||||
canvas.width = canvasSize;
|
||||
canvas.height = canvasSize;
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
const sdf = new TinySDF(fontSize, buffer, radius, null, null, fontWeight);
|
||||
for (let y = 0, i = 0; y + sdf.size <= canvas.height && i < chars.length; y += sdf.size) {
|
||||
for (let x = 0; x + sdf.size <= canvas.width && i < chars.length; x += sdf.size) {
|
||||
ctx.putImageData(this._makeRGBAImageData(ctx, sdf.draw(chars[i]), sdf.size), x, y);
|
||||
sdfs[chars[i]] = { x, y };
|
||||
i++;
|
||||
}
|
||||
}
|
||||
return {
|
||||
info: sdfs,
|
||||
texure: canvas
|
||||
};
|
||||
}
|
||||
_makeRGBAImageData(ctx, alphaChannel, size) {
|
||||
const imageData = ctx.createImageData(size, size);
|
||||
const data = imageData.data;
|
||||
for (let i = 0; i < alphaChannel.length; i++) {
|
||||
data[4 * i + 0] = alphaChannel[i];
|
||||
data[4 * i + 1] = alphaChannel[i];
|
||||
data[4 * i + 2] = alphaChannel[i];
|
||||
data[4 * i + 3] = 255;
|
||||
}
|
||||
return imageData;
|
||||
}
|
||||
}
|
|
@ -19,18 +19,18 @@ export default function extrudePolygon(points, extrude) {
|
|||
const flattengeo = earcut.flatten(points);
|
||||
const positions = [];
|
||||
let cells = [];
|
||||
|
||||
const { dimensions } = flattengeo;
|
||||
const triangles = earcut(flattengeo.vertices, flattengeo.holes, flattengeo.dimensions);
|
||||
cells = triangles;
|
||||
|
||||
const pointCount = flattengeo.vertices.length / 3;
|
||||
const pointCount = flattengeo.vertices.length / dimensions;
|
||||
const { vertices } = flattengeo;
|
||||
extrude ? full() : flat();
|
||||
|
||||
|
||||
function flat() {
|
||||
for (let i = 0; i < pointCount; i++) {
|
||||
positions.push([ vertices[ i * 3 ], vertices[i * 3 + 1 ], 0 ]);
|
||||
positions.push([ vertices[ i * dimensions ], vertices[i * dimensions + 1 ], 0 ]);
|
||||
}
|
||||
}
|
||||
function full() {
|
||||
|
@ -41,10 +41,10 @@ export default function extrudePolygon(points, extrude) {
|
|||
// 顶部坐标
|
||||
|
||||
for (let i = 0; i < pointCount; i++) {
|
||||
positions.push([ vertices[ i * 3 ], vertices[i * 3 + 1 ], 1 ]);
|
||||
positions.push([ vertices[ i * dimensions ], vertices[i * dimensions + 1 ], 1 ]);
|
||||
}
|
||||
for (let i = 0; i < pointCount; i++) {
|
||||
positions.push([ vertices[ i * 3 ], vertices[i * 3 + 1 ], 0 ]);
|
||||
positions.push([ vertices[ i * dimensions ], vertices[i * dimensions + 1 ], 0 ]);
|
||||
}
|
||||
for (let i = 0; i < n; i++) {
|
||||
if (i === (n - 1)) {
|
||||
|
|
|
@ -1,62 +0,0 @@
|
|||
const geom = {
|
||||
point: {
|
||||
symbol: [ 'circle', 'hexagon', 'triangle', 'diamond' ],
|
||||
native: {
|
||||
buffer: '',
|
||||
geometry: 'PointGeometry',
|
||||
material: 'PointMaterial'
|
||||
},
|
||||
line: {
|
||||
buffer: 'PointBuffer',
|
||||
geometry: 'PolygonLine',
|
||||
material: 'MeshlineMaterial'
|
||||
},
|
||||
fill: {
|
||||
buffer: 'PointBuffer',
|
||||
geometry: 'PolygonGeometry',
|
||||
material: 'PolygonMaterial'
|
||||
},
|
||||
extrude: {
|
||||
buffer: 'PointBuffer',
|
||||
geometry: 'PolygonGeometry',
|
||||
material: 'PolygonMaterial'
|
||||
},
|
||||
extrudeline: {
|
||||
buffer: 'PointBuffer',
|
||||
geometry: 'PolygonLine',
|
||||
material: 'MeshlineMaterial'
|
||||
},
|
||||
pointGrid: {
|
||||
buffer: 'pointGrid',
|
||||
geometry: 'PolygonLine',
|
||||
material: 'MeshlineMaterial'
|
||||
|
||||
}
|
||||
},
|
||||
line: {
|
||||
shape: [ 'native' ]
|
||||
},
|
||||
polygon: {
|
||||
line: {
|
||||
buffer: 'polygonLineBuffer',
|
||||
geometry: 'PolygonLine',
|
||||
material: 'MeshlineMaterial'
|
||||
},
|
||||
fill: {
|
||||
buffer: 'PolygonBuffer',
|
||||
geometry: 'PolygonGeometry',
|
||||
material: 'PolygonMaterial'
|
||||
},
|
||||
extrude: {
|
||||
buffer: 'PolygonBuffer',
|
||||
geometry: 'PolygonGeometry',
|
||||
material: 'PolygonMaterial'
|
||||
},
|
||||
extrudeline: {
|
||||
buffer: 'polygonLineBuffer',
|
||||
geometry: 'PolygonLine',
|
||||
material: 'MeshlineMaterial'
|
||||
}
|
||||
}
|
||||
};
|
||||
export default geom;
|
|
@ -1,110 +0,0 @@
|
|||
import { UniformSemantic, DataType, RenderState, BlendFunc } from '@ali/r3-base';
|
||||
import { Material, RenderTechnique } from '@ali/r3-material';
|
||||
export function ArclineMaterial() {
|
||||
const tech = requireGeometryTechnique();
|
||||
const newMtl = new Material('MeshlineTech_mtl');
|
||||
newMtl.technique = tech;
|
||||
newMtl.setValue('segmentNumber', 50.0);
|
||||
return newMtl;
|
||||
}
|
||||
|
||||
function requireGeometryTechnique() {
|
||||
// 顶点着色器
|
||||
const VERT_SHADER = `
|
||||
precision mediump float;
|
||||
attribute vec3 a_position;
|
||||
attribute vec3 a_color;
|
||||
attribute vec4 a_instance;
|
||||
uniform mat4 matModelViewProjection;
|
||||
uniform float segmentNumber;
|
||||
varying vec3 v_color;
|
||||
|
||||
float getSegmentRatio(float index) {
|
||||
return smoothstep(0.0, 1.0, index / (segmentNumber - 1.0));
|
||||
}
|
||||
|
||||
float paraboloid(vec2 source, vec2 target, float ratio) {
|
||||
vec2 x = mix(source, target, ratio);
|
||||
vec2 center = mix(source, target, 0.5);
|
||||
float dSourceCenter = distance(source, center);
|
||||
float dXCenter = distance(x, center);
|
||||
return (dSourceCenter + dXCenter) * (dSourceCenter - dXCenter)*0.6;
|
||||
}
|
||||
|
||||
vec3 getPos(vec2 source, vec2 target, float height, float segmentRatio) {
|
||||
return vec3(
|
||||
mix(source, target, segmentRatio),
|
||||
sqrt(max(0.0, height))
|
||||
);
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec2 source = a_instance.rg;
|
||||
vec2 target = a_instance.ba;
|
||||
float segmentIndex = a_position.x;
|
||||
float segmentRatio = getSegmentRatio(segmentIndex);
|
||||
vec3 c1 = vec3(0.929,0.972,0.917);
|
||||
vec3 c2 = vec3(0.062,0.325,0.603);
|
||||
v_color = mix(c1, c2, segmentRatio);
|
||||
float height = paraboloid(source, target, segmentRatio);
|
||||
vec3 position = getPos(source,target,height,segmentRatio);
|
||||
gl_Position = matModelViewProjection * vec4(position, 1.0);
|
||||
}
|
||||
`;
|
||||
|
||||
// 片元着色器
|
||||
const FRAG_SHADER = `
|
||||
precision mediump float;
|
||||
varying vec3 v_color;
|
||||
|
||||
void main() {
|
||||
gl_FragColor = vec4(v_color,1.0);
|
||||
}
|
||||
`;
|
||||
|
||||
// Technique 配置信息
|
||||
const cfg = {
|
||||
attributes: {
|
||||
a_position: {
|
||||
name: 'a_position',
|
||||
semantic: 'POSITION',
|
||||
type: DataType.FLOAT_VEC3
|
||||
},
|
||||
a_color: {
|
||||
name: 'a_color',
|
||||
semantic: 'COLOR',
|
||||
type: DataType.FLOAT_VEC3
|
||||
},
|
||||
a_instance: {
|
||||
name: 'a_instance',
|
||||
semantic: 'INSPOS',
|
||||
type: DataType.FLOAT_VEC4
|
||||
}
|
||||
},
|
||||
uniforms: {
|
||||
matModelViewProjection: {
|
||||
name: 'matModelViewProjection',
|
||||
semantic: UniformSemantic.MODELVIEWPROJECTION,
|
||||
type: DataType.FLOAT_MAT4
|
||||
},
|
||||
segmentNumber: {
|
||||
name: 'segmentNumber',
|
||||
type: DataType.FLOAT
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 创建 Technique
|
||||
const tech = new RenderTechnique('MeshlineTech');
|
||||
tech.states = {
|
||||
enable: [ RenderState.BLEND ],
|
||||
functions: { blendFunc: [ BlendFunc.SRC_ALPHA, BlendFunc.ONE_MINUS_SRC_ALPHA ] }//
|
||||
};
|
||||
tech.isValid = true;
|
||||
tech.uniforms = cfg.uniforms;
|
||||
tech.attributes = cfg.attributes;
|
||||
tech.vertexShader = VERT_SHADER;
|
||||
tech.fragmentShader = FRAG_SHADER;
|
||||
|
||||
return tech;
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
import Material from './material';
|
||||
import { getModule, wrapUniforms } from '../../util/shaderModule';
|
||||
import merge from '@antv/util/lib/deep-mix';
|
||||
|
||||
export default class CircleMaterial extends Material {
|
||||
constructor(_uniforms, _defines, parameters) {
|
||||
super(parameters);
|
||||
const { vs, fs, uniforms } = getModule('circle');
|
||||
this.uniforms = wrapUniforms(merge(uniforms, _uniforms));
|
||||
this.defines = _defines;
|
||||
this.type = 'CircleMaterial';
|
||||
this.vertexShader = vs;
|
||||
this.fragmentShader = fs;
|
||||
this.transparent = true;
|
||||
}
|
||||
}
|
|
@ -1,8 +1,5 @@
|
|||
import grid_frag from '../shader/grid_frag.glsl';
|
||||
import grid_vert from '../shader/grid_vert.glsl';
|
||||
import Material from './material';
|
||||
|
||||
|
||||
import { getModule } from '../../util/shaderModule';
|
||||
export default class GridMaterial extends Material {
|
||||
getDefaultParameters() {
|
||||
return {
|
||||
|
@ -11,7 +8,9 @@ export default class GridMaterial extends Material {
|
|||
u_time: { value: 0 },
|
||||
u_xOffset: { value: 0.01 },
|
||||
u_yOffset: { value: 0.01 },
|
||||
u_coverage: { value: 0.8 }
|
||||
u_coverage: { value: 0.8 },
|
||||
u_activeId: { value: 0 },
|
||||
u_activeColor: { value: [ 1.0, 0, 0, 1.0 ] }
|
||||
},
|
||||
defines: {
|
||||
|
||||
|
@ -21,11 +20,12 @@ export default class GridMaterial extends Material {
|
|||
constructor(_uniforms, _defines, parameters) {
|
||||
super(parameters);
|
||||
const { uniforms, defines } = this.getDefaultParameters();
|
||||
const { vs, fs } = getModule('grid');
|
||||
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.vertexShader = vs;
|
||||
this.fragmentShader = fs;
|
||||
this.transparent = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
import * as THREE from '../../core/three';
|
||||
import Material from './material';
|
||||
import { getModule } from '../../util/shaderModule';
|
||||
export class HeatmapColorizeMaterial extends Material {
|
||||
getDefaultParameters() {
|
||||
return {
|
||||
uniforms: {
|
||||
u_intensity: { value: 1.0 },
|
||||
u_texture: { value: null },
|
||||
u_rampColors: { value: 0 },
|
||||
u_opacity: { value: 1 }
|
||||
},
|
||||
defines: {
|
||||
|
||||
}
|
||||
};
|
||||
}
|
||||
constructor(_uniforms, _defines = {}, parameters) {
|
||||
super(parameters);
|
||||
const { uniforms, defines } = this.getDefaultParameters();
|
||||
const { vs, fs } = getModule('heatmap_color');
|
||||
this.uniforms = Object.assign(uniforms, this.setUniform(_uniforms));
|
||||
this.type = 'HeatmapColorizeMaterial';
|
||||
this.defines = Object.assign(defines, _defines);
|
||||
this.vertexShader = vs;
|
||||
this.fragmentShader = fs;
|
||||
this.transparent = true;
|
||||
}
|
||||
}
|
||||
|
||||
export class HeatmapIntensityMaterial extends Material {
|
||||
getDefaultParameters() {
|
||||
return {
|
||||
uniforms: {
|
||||
u_intensity: { value: 10.0 },
|
||||
u_zoom: { value: 4 },
|
||||
u_radius: { value: 10 }
|
||||
},
|
||||
defines: {
|
||||
|
||||
}
|
||||
};
|
||||
}
|
||||
constructor(_uniforms, _defines = {}, parameters) {
|
||||
super(parameters);
|
||||
const { uniforms, defines } = this.getDefaultParameters();
|
||||
const { vs, fs } = getModule('heatmap_intensity');
|
||||
this.uniforms = Object.assign(uniforms, this.setUniform(_uniforms));
|
||||
this.type = 'heatmap_intensity';
|
||||
this.defines = Object.assign(defines, _defines);
|
||||
this.vertexShader = vs;
|
||||
this.blending = THREE.AdditiveBlending;
|
||||
this.fragmentShader = fs;
|
||||
this.depthTest = false;
|
||||
this.transparent = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,5 @@
|
|||
import grid_frag from '../shader/hexagon_frag.glsl';
|
||||
import grid_vert from '../shader/hexagon_vert.glsl';
|
||||
import Material from './material';
|
||||
|
||||
|
||||
import { getModule } from '../../util/shaderModule';
|
||||
export default class hexagonMaterial extends Material {
|
||||
getDefaultParameters() {
|
||||
return {
|
||||
|
@ -11,7 +8,9 @@ export default class hexagonMaterial extends Material {
|
|||
u_time: { value: 0 },
|
||||
u_radius: { value: 0.01 },
|
||||
u_angle: { value: 0.01 },
|
||||
u_coverage: { value: 0.8 }
|
||||
u_coverage: { value: 0.8 },
|
||||
u_activeId: { value: 0 },
|
||||
u_activeColor: { value: [ 1.0, 0, 0, 1.0 ] }
|
||||
},
|
||||
defines: {
|
||||
|
||||
|
@ -21,11 +20,12 @@ export default class hexagonMaterial extends Material {
|
|||
constructor(_uniforms, _defines, parameters) {
|
||||
super(parameters);
|
||||
const { uniforms, defines } = this.getDefaultParameters();
|
||||
const { vs, fs } = getModule('hexagon');
|
||||
this.uniforms = Object.assign(uniforms, this.setUniform(_uniforms));
|
||||
this.type = 'hexagonMaterial';
|
||||
this.defines = Object.assign(defines, _defines);
|
||||
this.vertexShader = grid_vert;
|
||||
this.fragmentShader = grid_frag;
|
||||
this.vertexShader = vs;
|
||||
this.fragmentShader = fs;
|
||||
this.transparent = true;
|
||||
}
|
||||
}
|
||||
|
|