/**
* 地图操作工具类
* Created by Aegean on 2017/3/8 0008.
*/
;(function(global) {
//TODO:ol.source.Vector getExtent()报错,如不能解决则手动计算所有feature的extent
//TODO:使用ol.extent.boundingExtent
/**
* 命名空间
* @namespace wol
*/
global.wol = global.wol || {};
/**
* 命名空间
* @namespace wol.util
*/
global.wol.util = global.wol.util || {};
/**
* 通用样式库
* @property {ol.style.Style} DEFAULT_STYLE - 默认样式
* @property {ol.style.Style} TEXT_STYLE - 文本样式
* @property {ol.style.Style} MASK_BLACK - 黑色遮罩样式
*/
wol.util.styles = {
DEFAULT_STYLE: new ol.style.Style({
fill: new ol.style.Fill({
color : 'rgba(0, 191, 255, 0.5)'
}),
image: new ol.style.Circle({
fill: new ol.style.Fill({
color : '#00bfff'
}),
stroke: new ol.style.Stroke({
color: '#bdecff',
width: 2
}),
radius: 8
}),
stroke: new ol.style.Stroke({
color: '#00bfff',
width: 3
})
}),
TEXT_STYLE: new ol.style.Style({
text: new ol.style.Text({
text: '',
offsetY: 18,
font: '14px 微软雅黑',
textAlign : 'center',
fill: new ol.style.Fill({
color : '#2d2d2d',
})
})
}),
MASK_BLACK: new ol.style.Style({
fill : new ol.style.Fill({
color : 'rgba(0, 0, 0, 0.5)'
})
})
};
/**
* 创建矢量图层
* @param {object} option - 配置项
* @param {string|undefined} option.name - 图层名称,默认为 undefined
* @param {string} option.label - 图层说明,默认为 ''
* @param {ol.style.Style} option.style - 图层样式,默认为 wol.util.styles.DEFAULT_STYLE
* @param {Array} option.features - 图层默认要素,默认为 []
* @param {boolean} option.updateWhileAnimating - 动画执行过程是否实时渲染要素,默认为 true
* @param {boolean} option.updateWhileInteracting - 交互过程是否实时渲染要素,默认为 false
* @param {boolean} option.useSpatialIndex - 是否开启空间索引,默认为 false
* @param {number} option.opacity - 图层透明度,默认为 1
* @param {boolean} option.visible - 图层可见性,默认为 true
* @param {number|undefined} option.zIndex - 图层序号,默认为 undefined
* @param {string|undefined} option.group - 图层所属图层组,默认为 undefined 即不属于任何组
* @return {ol.layer.Vector} - 矢量图层
*/
wol.util.createVectorLayer = function(option) {
var defaults = {
name: undefined,
label: '',
style: wol.util.styles.DEFAULT_STYLE,
features: [],
updateWhileAnimating: true,
updateWhileInteracting: false,
useSpatialIndex: false,
opacity: 1,
visible: true,
zIndex: undefined,
group: undefined
};
option = wol.util.extend(defaults, option);
var layer = new ol.layer.Vector({
source: new ol.source.Vector({
features: option.features,
useSpatialIndex: option.useSpatialIndex
}),
style: option.style,
updateWhileAnimating: option.updateWhileAnimating,
updateWhileInteracting: option.updateWhileInteracting,
opacity: option.opacity,
visible: option.visible,
zIndex: option.zIndex
});
layer.set('name', option.name);
layer.set('label', option.label);
layer.set('group', option.group);
layer.set('type', 'Vector');
return layer;
}
/**
* 创建聚合图层
* @param {object} option - 配置项
* @param {string|undefined} option.name - 图层名称,默认为 undefined
* @param {string} option.label - 图层说明,默认为 ''
* @param {ol.StyleFunction} option.styleFunction - 图层样式函数, 携带feature和resolution两个参数,此函数应返回一个ol.style.Style对象或其数组,由此为单个要素及聚合要素显示不同的样式
* @param {Array} option.features - 图层默认要素,默认为 []
* @param {boolean} option.updateWhileAnimating - 动画执行过程是否实时渲染要素,默认为 true
* @param {boolean} option.updateWhileInteracting - 交互过程是否实时渲染要素,默认为 false
* @param {number} option.opacity - 图层透明度,默认为 1
* @param {boolean} option.visible - 图层可见性,默认为 true
* @param {number|undefined} option.zIndex - 图层序号,默认为 undefined
* @param {string|undefined} option.group - 图层所属图层组,默认为 undefined 即不属于任何组
* @return {ol.layer.Vector} - 矢量聚合图层
*/
wol.util.createClusterLayer = function(option) {
var styleCache = {};
var defaults = {
name: undefined,
label: '',
styleFunction: function(feature) {
var size = feature.get('features').length;
var style = styleCache[size];
if (!style) {
style = new ol.style.Style({
image: new ol.style.Circle({
radius: 10,
stroke: new ol.style.Stroke({
color: '#fff'
}),
fill: new ol.style.Fill({
color: '#3399CC'
})
}),
text: new ol.style.Text({
text: size.toString(),
fill: new ol.style.Fill({
color: '#fff'
})
})
});
styleCache[size] = style;
}
return style;
},
features: [],
updateWhileAnimating: true,
updateWhileInteracting: false,
opacity: 1,
visible: true,
zIndex: undefined,
group: undefined
};
option = wol.util.extend(defaults, option);
var layer = new ol.layer.Vector({
source: new ol.source.Cluster({
distance: 40,
source: new ol.source.Vector({
features: option.features,
//useSpatialIndex: option.useSpatialIndex //聚合图层中不能使用空间索引
})
}),
style: function(feature, resolution) {
return option.styleFunction(feature, resolution);
},
updateWhileAnimating: option.updateWhileAnimating,
updateWhileInteracting: option.updateWhileInteracting,
opacity: option.opacity,
visible: option.visible,
zIndex: option.zIndex
});
layer.set('name', option.name);
layer.set('label', option.label);
layer.set('group', option.group);
layer.set('type', 'Cluster');
return layer;
}
/**
* 创建XYZ图层
* @param {object} option - 配置项
* @param {string} option.name - 图层名称,默认为 undefined
* @param {string} option.label - 图层说明,默认为 ''
* @param {string} option.url - 切片服务地址
* @param {string|undefined} option.crossOrigin - 请求类型,'anonymous' 或 undefined,默认为 undefined;设置为'anonymous'时请求类型变为跨域请求,但切片服务器必须进行跨域设置
* @param {number|undefined} option.minZoom - 图层的最小缩放等级,默认为 undefined
* @param {number|undefined} option.maxZoom - 图层的最大缩放等级,超过该等级不再请求新的瓦片,默认为 undefined
* @param {number} option.opacity - 图层透明度,默认为 1
* @param {boolean} option.visible - 图层可见性,默认为 true
* @param {number} option.zIndex - 图层序号,默认为 undefined
* @param {string|undefined} option.group - 图层所属图层组,默认为 undefined 即不属于任何组
* @return {ol.layer.Tile} - 切片图层
*/
wol.util.createXYZLayer = function(option) {
var defaults = {
name: undefined,
label: '',
url: '',
crossOrigin: undefined,
minZoom: undefined,
maxZoom: undefined,
opacity: 1,
visible: true,
zIndex: undefined,
group: undefined
};
option = wol.util.extend(defaults, option);
var layer = new ol.layer.Tile({
source : new ol.source.XYZ({
url : option.url,
crossOrigin: option.crossOrigin,
wrapX: option.wrapX,
minZoom: option.minZoom,
maxZoom: option.maxZoom
}),
opacity: option.opacity,
visible: option.visible,
zIndex: option.zIndex
});
layer.set('name', option.name);
layer.set('label', option.label);
layer.set('group', option.group);
layer.set('type', 'XYZ');
return layer;
}
/**
* 创建网络地图图层
* @param {object} option - 配置项
* @param {string} option.name - 图层名称,默认为 undefined
* @param {string} option.label - 图层说明,默认为 ''
* @param {string} option.url - 网络地图服务地址
* @param {object} option.params - WMS必须参数,其中必须包含 LAYERS
* @param {string|undefined} option.serverType - 远程网络地图服务类型,可选值为'carmentaserver', 'geoserver', 'mapserver', 'qgis',默认为 undefined
* @param {number} option.maxZoom - 图层的最大缩放等级
* @param {number} option.opacity - 图层透明度,默认为 1
* @param {boolean} option.visible - 图层可见性,默认为 true
* @param {number} option.zIndex - 图层序号,默认为 undefined
* @param {string|undefined} option.group - 图层所属图层组,默认为 undefined 即不属于任何组
* @return {ol.layer.Tile} - WMS图层
*/
wol.util.createWMSLayer = function(option) {
var defaults = {
name: undefined,
label: '',
url: '',
params: {},
serverType: undefined,
maxZoom: undefined,
opacity: 1,
visible: true,
zIndex: undefined,
group: undefined
};
option = wol.util.extend(defaults, option);
var layer = new ol.layer.Tile({
source: new ol.source.TileWMS({
url: option.url,
params: option.params,
serverType: option.serverType,
wrapX: option.wrapX,
maxZoom: option.maxZoom
}),
opacity: option.opacity,
visible: option.visible,
zIndex: option.zIndex
});
layer.set('name', option.name);
layer.set('label', option.label);
layer.set('group', option.group);
layer.set('type', 'WMS');
return layer;
}
/**
* 创建遮罩图层
* @param {object} option - 配置项
* @param {string} option.name - 图层名称,默认为 undefined
* @param {string} option.label - 图层说明,默认为 ''
* @param {ol.style.Style} option.style - 图层样式,默认为 wol.util.styles.MASK_BLACK
* @param {boolean} option.updateWhileAnimating - 动画执行过程是否实时渲染要素,默认为 true
* @param {boolean} option.updateWhileInteracting - 交互过程是否实时渲染要素,默认为 true
* @param {number} option.opacity - 图层透明度,默认为 1
* @param {boolean} option.visible - 图层可见性,默认为 false
* @param {number} option.zIndex - 图层序号,默认为 1
* @param {string|undefined} option.group - 图层所属图层组,默认为 undefined 即不属于任何组
* @return {ol.layer.Vector} - 矢量图层
*/
wol.util.createMaskLayer = function(option) {
//遮罩要素
var maskFeature = wol.util.createFeatureFromWKT('POLYGON((8140237.764258131 7592337.145509984,8218509.281222153 313086.06785608083,15889117.94369616 313086.06785608083,15732574.909768116 7514065.628545966,8140237.764258131 7592337.145509984))');
// wol.util.transform(maskFeature, 'EPSG:4326', 'EPSG:3857');
var defaults = {
name: undefined,
label: '',
features: [maskFeature],
style: wol.util.styles.MASK_BLACK,
updateWhileAnimating: true,
updateWhileInteracting: true,
opacity: 1,
visible: false,
zIndex: 1,
group: undefined
};
option = wol.util.extend(defaults, option);
var maskLayer = wol.util.createVectorLayer(option);
maskLayer.set('name', option.name);
maskLayer.set('label', option.label);
maskLayer.set('group', option.group);
maskLayer.set('type', 'Mask');
return maskLayer;
}
/**
* 创建热力图层
* @param {object} option - 配置项
* @param {string|undefined} option.name - 图层名称,默认为 undefined
* @param {string} option.label - 图层说明,默认为 ''
* @param {Array} option.feature - 图层默认要素,默认为 []
* @param {Array} option.gradient - 热力图层的色彩渐层颜色,默认为 ['#0ff', '#00f', '#0f0', '#ff0', '#ff5d3e']
* @param {number} option.blur - 模糊值,默认为 24
* @param {number} option.radius - 非聚合状态下最小半径,默认为 10
* @param {number} option.shadow - 阴影值,默认为 300
* @param {number} option.opacity - 图层透明度,默认为 1
* @param {boolean} option.visible - 图层可见性,默认为 true
* @param {number|undefined} option.zIndex - 图层序号,默认为 undefined
* @param {string|undefined} option.group - 图层所属图层组,默认为 undefined 即不属于任何组
* @return {ol.layer.Heatmap} - 热力图层
*/
wol.util.createHeatMap = function(option) {
var defaults = {
name: undefined,
label: '',
feature: [],
gradient : ['#0ff', '#00f', '#0f0', '#ff0', '#ff5d3e'],
blur : 24,
radius : 10,
shadow : 300,
opacity: 1,
visible: true,
zIndex: undefined,
group: undefined,
};
option = wol.util.extend(defaults, option);
var layer = new ol.layer.Heatmap({
source: new ol.source.Vector({
features : option.features
}),
gradient: option.gradient,
blur: option.blur,
radius: option.radius,
shadow: option.shadow,
opacity: option.opacity,
visible: option.visible,
zIndex: option.zIndex
});
layer.set('name', option.name);
layer.set('label', option.label);
layer.set('group', option.group);
layer.set('type', 'HeatMap');
return layer;
}
/**
* 从WKT字符串中解析要素
* @param {string} wkt - 要素的wkt描述(关于wkt请参见:{@link http://www.cnblogs.com/tiandi/archive/2012/07/18/2598093.html})
* @return {ol.Feature} - 要素对象
*/
wol.util.createFeatureFromWKT = function(wkt) {
var format = new ol.format.WKT();
return format.readFeature(wkt);
}
/**
* 导出要素为WKT字符串
* @param {ol.Feature} feature - 要素对象
* @return {string} - 要素的wkt描述
*/
wol.util.createWKTFromFeature = function(feature) {
var format = new ol.format.WKT();
return format.writeFeature(feature);
}
/**
* 对要素进行坐标转化
* @param {ol.Feature} feature - 待转换要素
* @param {ol.proj.ProjectionLike} from - 转换前投影坐标系
* @param {ol.proj.ProjectionLike} to - 转换后投影坐标系
*/
wol.util.transform = function(feature, from, to) {
feature.getGeometry().transform(from, to);
}
/**
* 对象扩展(jQuery extend机制)
* @param {boolean|object} arg - 可变参数,你可以传入任意个参数将它们合并为一个对象;当不传入任何参数或者只有一个参数且类型不是对象,或者该参数为布尔值时将返回空对象;当参数个数大于等于两个时,
* 若第一个参数类型不是布尔值或为 false ,将返回后续参数的浅拷贝合并,否则返回第一个参数后续参数的深拷贝合并
* @return {object} - 扩展后的对象
*/
wol.util.extend = (function fn() {
var options, name, src, copy, copyIsArray, clone,
target = arguments[0] || {},
length = arguments.length,
i = 1, deep = false;
//是否深度拷贝
if(typeof target === "boolean") {
deep = target;
target = arguments[ i ] || {};
i++;
}
if(typeof target !== "object" && !wol.util.isFunction(target)) {
target = {};
}
if(i === length) {
target = {};
i--;
}
for(; i < length; i++) {
if((options = arguments[i]) != null) {
for(name in options) {
src = target[name];
copy = options[name];
// Prevent never-ending loop
if(target === copy) {
continue;
}
// Recurse if we're merging plain objects or arrays
if(deep && copy && (wol.util.isPlainObject(copy) || (copyIsArray = Array.isArray(copy)))) {
if(copyIsArray) {
copyIsArray = false;
clone = src && Array.isArray(src) ? src : [];
}else {
clone = src && wol.util.isPlainObject(src) ? src : {};
}
// Never move original objects, clone them
target[name] = fn(deep, clone, copy);
}else if (copy !== undefined) {
target[name] = copy;
}
}
}
}
// Return the modified object
return target;
});
wol.util.isFunction = function(obj) {
return typeof obj === "function" && typeof obj.nodeType !== "number";
};
wol.util.isPlainObject = function(obj) {
var proto, Ctor;
if(!obj || obj.toString() !== "[object Object]") {
return false;
}
proto = Object.getPrototypeOf(obj);
if(!proto) {
return true;
}
Ctor = proto.hasOwnProperty("constructor") && proto.constructor;
return typeof Ctor === "function" && Ctor.toString() === "[object Object]";
};
/**
* 获取颜色表示类型
* @param {string} colorStr - 颜色字符串表示
* @return {string} - RGB/十六进制
*/
wol.util.getColorType = function(colorStr) {
//颜色正则
var rgbColorReg = /^(rgb[(]|RGB[(]).+([)])$/;
var hexColorReg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/;
if(rgbColorReg.test(colorStr)) {
return 'RGB';
}else if(hexColorReg.test(colorStr)) {
return 'HEX';
}else {
return 'UNKNOWN';
}
}
/**
* 将rgb颜色值转换为16进制形式
* @param {string} rgbStr - 颜色RGB字符串表示
* @return {string} - 颜色16进制字符串表示
*/
wol.util.toHexColor = function(rgbStr) {
var colorType = wol.util.getColorType(rgbStr);
if(colorType === 'RGB') {
var aColor = rgbStr.replace(/(?:\(|\)|rgb|RGB)*/g, '').split(',');
var hexStr = '#';
for(var i = 0; i<aColor.length; i++) {
var hex = Number(aColor[i]).toString(16);
if(hex === '0'){
hex += hex;
}
hexStr += hex;
}
if(hexStr.length !== 7){
hexStr = '#000000';
}
return hexStr;
}else if(colorType === 'HEX') {
var aNum = rgbStr.replace(/#/, '').split('');
if(aNum.length === 6) {
return rgbStr;
}else if(aNum.length === 3) {
var numHex = '#';
for(var i=0; i<aNum.length; i+=1){
numHex += (aNum[i]+aNum[i]);
}
return numHex;
}
}else {
return '#000000';
}
}
/**
* 将16进制颜色值转换为rgb形式
* @param {string} hexStr - 颜色16进制字符串表示
* @return {string} - 颜色RGB字符串表示
*/
wol.util.toRGBColor = function(hexStr) {
var sColor = hexStr.toLowerCase();
var colorType = wol.util.getColorType(sColor);
if(sColor && colorType === 'HEX') {
if(sColor.length === 4){
var sColorNew = '#';
for(var i = 1; i < 4; i+=1) {
sColorNew += sColor.slice(i,i+1).concat(sColor.slice(i,i+1));
}
sColor = sColorNew;
}
//处理六位的颜色值
var sColorChange = [];
for(var i = 1; i < 7; i+=2) {
sColorChange.push(parseInt('0x' + sColor.slice(i,i+2)));
}
return 'RGB(' + sColorChange.join(',') + ')';
}else{
return 'RGB(0, 0, 0)';
}
}
/**
* 获取颜色值数组
* @param {string} color - 颜色字符串表示
* @return {Array} - 颜色RGB数组
*/
wol.util.getColorArray = function(color) {
var type = wol.util.getColorType(color);
if(type === 'HEX') {
color = wol.util.toRGBColor(color);
}else if(type !== 'RGB') {
color = 'RGB(0, 0, 0)';
}
var temp = color.replace(/(?:\(|\)|rgb|RGB)*/g, '').split(',');
for(var i = 0; i < temp.length; i++) {
temp[i] = parseInt(temp[i]);
}
return temp;
}
/**
* 拆分坐标点数组,在两两坐标点之间按均匀步长生成密集的坐标点
* @param {Array<ol.Coordinate>} sourceCoords - 原始坐标点数组
* @param {number} step - 步长(沿线方向)
* @return {Array<ol.Coordinate>}
*/
wol.util.splitCoordinates = function(sourceCoords, step) {
var len = sourceCoords.length;
if(len >= 2) {
var i = 0;
var newCoords = [], temp;
for(; i < len - 1; i++) {
temp = _getInterpolation(sourceCoords[i], sourceCoords[i + 1], step);
newCoords = newCoords.concat(temp);
}
return newCoords;
}else {
throw new Error('至少包含两个点');
}
}
/**
* 根据两个坐标点获取插值数组
* @private
* @param {ol.Coordinate} point1
* @param {ol.Coordinate} point2
* @param {number} step - 步长(沿线方向)
* @return {Array<Array>}
*/
function _getInterpolation(point1, point2, step) {
//参数设置
var x1 = point1[0], y1 = point1[1],
x2 = point2[0], y2 = point2[1];
var targetArray = [point1];
var tempX, tempY;
var dirX = x1 < x2 ? 1 : -1, dirY = y1 < y2 ? 1 : -1;
var stepX, stepY;
if(y1 === y2) {
stepX = dirX * step;
tempX = x1 + stepX;
tempY = y1;
while(dirX > 0 ? tempX < x2 : tempX > x2) {
targetArray.push([tempX, tempY]);
tempX += stepX;
}
}else if(x1 === x2) {
stepY = dirY * step;
tempX = x1;
tempY = y1 + stepY;
while(dirX > 0 ? tempY < y2 : tempY > y2) {
targetArray.push([tempX, tempY]);
tempY += stepY;
}
}else {
//斜率
var slope = Math.abs((y2 - y1) / (x2 - x1));
//根据步长计算x和y方向增量
var tmpStepX = step / Math.pow((1 + slope * slope), 0.5);
var tmpStepY = tmpStepX * slope;
stepX = dirX * tmpStepX;
stepY = dirY * tmpStepY;
tempX = x1 + stepX;
tempY = y1 + stepY;
while(dirX > 0 ? tempX < x2 : tempX > x2) {
targetArray.push([tempX, tempY]);
tempX += stepX;
tempY += stepY;
}
}
targetArray.push(point2);
return targetArray;
}
wol.util.createShadowFeature = function(map, lineFeature) {
var deltaS = 1;
var coords = lineFeature.getGeometry().getCoordinates();
var p1 = coords[0], p2 = coords[1];
var k = (p2[1] - p1[1]) / (p2[0] - p1[0]);
var tmpSqr1 = Math.sqrt(Math.pow(k, 2) - 1);
var newP1 = [p1[0] + (deltaS * k / tmpSqr1), p1[1] - (deltaS / tmpSqr1)],
newP2 = [p2[0] + (deltaS * k / tmpSqr1), p2[1] - (deltaS / tmpSqr1)];
console.info(newP1, newP2);
var shadowFea = new ol.Feature({
geometry: new ol.geom.LineString([newP1, newP2])
});
var listenerKey;
var shadowStyle = new ol.style.Style({
stroke: new ol.style.Stroke({
color: 'white',
width: 5
})
});
function animate(event) {
var vectorContext = event.vectorContext;
var flashGeom = shadowFea.getGeometry();
vectorContext.setStyle(shadowStyle);
vectorContext.drawGeometry(flashGeom);
}
listenerKey = map.on('postcompose', animate);
}
})(window);