/*
* @Author: wxq
* @Date: 2017-08-02 17:50:57
* @Last Modified by: hydata
* @Last Modified time: 2018-05-10 16:34:13
* @Email: 304861063@qq.com
* @File Path: F:\work\hyMap\src\hyMapBox.js
* @File Name: hyMapBox.js
* @Descript: 入口类
*/
'use strict';
import mapboxgl from 'mapbox-gl';
import baseClass from '../core/baseClass';
import hyMapOption from '../model/mapModel';
import baseUtil from '../util/baseUtil';
import * as Layer from './layer/index';
import rotate from './animations/rotate';
import Threebox from '../plugins/threebox/threebox';
import coordtransform from '../tools/convert';
import * as Control from '../control/index';
import '../../public/lib/mapbox-gl.css';
import '../css/map.css';
const turf = require('../tools/turf');
class HyMapBox extends baseClass {
/**
* 初始化
* @param {Document} dom dom对象
* @param {Object} options 参数
* @extends baseClass
*/
constructor(dom, options) {
super(options);
this.init(dom);
this._geo = hyMapOption;
this.layers = {};
this._serverUrl = '';
this.markers = {};
this.routeLayer = null;
this.threebox = null;
this.coordtransform = coordtransform; //坐标转化工具方法
this.mapControls = {};
this.geoTool = turf;
}
/**
*初始化
* @param {dom} dom 容器
*/
init(dom) {
this.dom = dom;
this.map = new mapboxgl.Map({
container: dom, // container id
hash: false
});
this.map.on('moveend', (data) => {
if (data.mehtod === 'fly') {
this.map.easeTo({ pitch: data.current_pitch, duration: 2000 }, { method: 'fly_ease' });
if (data.callback && typeof data.callback === 'function') {
data.callback(data);
}
} else if (data.method === 'fly_ease') {
if (this.rotateObj) {
this.rotateObj.startAnimation();
}
} else if (data.method === 'panto') {
if (this.rotateObj) {
this.rotateObj.startAnimation();
}
} else if (data.method === 'zoomto') {
if (this.rotateObj) {
this.rotateObj.startAnimation();
}
}
});
this.createBaseLayer();
this.map.on('load', () => {
this.map.realReady = true;
console.info('%c map is realReady.', 'color:green');
});
// this.createQueryLayer();
}
/**
* 绘制查
* @param {requestCallback} callback 回调
*/
drawTool(callback) {
return new Control.DrawControl(this.map, callback);
}
/**
* 创建底图
* @private
* @return {Object} object
*/
createBaseLayer() {
this.baseLayer = new Layer.BaseMap({
map: this.map
});
return this.baseLayer;
}
/**
* 创建查询图层
* @private
*
*/
createQueryLayer() {
if (!this.threebox) {
this.threebox = new Threebox(this.map);
}
this.queryLayer = new Layer.queryLayer({
map: this.map,
queryLayer: this._addLayerGroupArray,
threebox: this.threebox
});
// this.on('afterClear', () => {
//
// this.clearTrackInfo();
//
// });
}
/**
* 空间查询
* @param {*} obj
*/
spatialQuery(obj) {
if (!this.queryLayer) {
this.createQueryLayer();
}
this.queryLayer.spatialQuery(obj);
}
/**
* 设置空间查询半径
* @param {Number} radius 半径
* @param {Boolean} relative 是否相对位置,默认为相对位置
*/
setSpatialRadius(radius, relative) {
this.queryLayer.setRadius(radius, relative);
}
/**
* 移除空间查询结果
* @return null
*/
removeSpatialQuery() {
this.queryLayer.clear();
}
clearQueryResult() {
}
/**
* 设置地图参数
* @param {Object} options 参数
* @example
*
* mapObj = map.init(document.getElementById('map'))
let options = {
serverUrl: 'http://192.168.1.50:8080/geoserver',
show: true, //地图的显示状态 true为显示 false 为不显示
roam: 'true', //地图是否开启缩放、平移功能
center: [118.62778784888256, 36.58892145091036], //当前视角中心: [经度, 纬度]
// theme: {
// type: 'tile',
// url: 'http://mape.shanghai-map.net/ArcGIS/rest/services/SHMAP_DZJ/MapServer'
// url: 'http://192.168.4.35:6080/arcgis/rest/services/MZL/MapServer'
// url: 'http://localhost:8080/all'
// }, //地图风格
// center: [117.52359952545166, 49.52355306224895], //当前视角中心: [经度, 纬度]
zoom: 3, //当前地图缩放比例
scaleLimit: [2, 18], //滚轮缩放的边界
// string('dark','blue','white')|mapObjectr{mapId,key} 对应maobox中的mapid和access_token
theme: {
type: 'mapbox',
mapId: 'zhangyujie.a80cdc83',
key: 'sk.eyJ1Ijoiemhhbmd5dWppZSIsImEiOiJkTEp6WDZrIn0.nY5bsQlZegBbb2uGgJ5jEA'
// type: 'arcgis',
// type: 'satellite',
// url: 'http://192.168.1.201/MapTileDownload/googlemaps/satellite'
// url: 'http://192.168.4.35:8080/QingHai/'
// url: 'http://192.168.4.35:6080/arcgis/rest/services/TianJin/MapServer'
// url: 'http://192.168.4.35:6080/a/arcgisserver/directories/arcgiscache/TianJin/%E5%9B%BE%E5%B1%82/_alllayers'
}, //地图风格
};
*/
setOption(options) {
if (!options) {
return;
}
baseUtil.merge(this._geo, options || {}, true);
mapboxgl.accessToken = this._geo.token;
// this._geo.theme = this.analysisURL(this._geo.theme);
this.setServerUrl(this._geo.serverUrl);
this.setView(this._geo);
this.addControl(this._geo.control);
this.addLayer(this._geo.series);
this.isDebug(this._geo.debug);
}
/**
* 开启debug模式
* @param {Boolean} flag 开启|关闭
* @return {Boolean}
*/
isDebug(flag = false) {
this.map.showTileBoundaries = flag;
if (flag) {
this.map.on('mousemove', function (e) {
console.info(JSON.stringify(e.lngLat));
});
}
}
/**
* 获取option
* @return {Object} 地图参数
*/
getOption() {
return this._geo;
}
/**
* 设置可视参数
* @param {Object} geo 地图参数
*/
setView(geo) {
this.setTheme(geo.theme);
this._setView(geo);
this.setRoam(geo.roam);
}
/**
* 设置底图风格
* @param {String} theme
* setTheme('url');
*/
setTheme(theme) {
this._geo.theme = this.analysisURL(theme);
this.baseLayer.setUrl(this._serverUrl);
this.baseLayer.setTheme(this._geo.theme);
}
/**
* 设置请求服务器地址
* @param {String} url 服务器地址ip
*/
setServerUrl(url) {
this._serverUrl = url;
}
/**
* 获取服务器地址
*/
getServerUrl() {
return this._serverUrl;
}
/**
* 设置可视范围
* @private
* @param {Number} zoom 级别
* @param {Array} center 中心点
* @param {Array} scaleLimit 缩放级别
* @param {Array} maxBounds 最大范围
* @param {Number} bearing 旋转
* @param {Number} pitch 偏移
* @private
*/
_setView({
zoom = 1,
center = [0, 0],
scaleLimit = [0, 24],
maxBounds,
bearing = 0,
pitch = 0
} = {}) {
this.map.jumpTo({
zoom,
center,
bearing,
pitch
});
this.map.setMinZoom(scaleLimit[0]);
this.map.setMaxZoom(scaleLimit[1]);
this.map.setMaxBounds(maxBounds);
}
/**
* 设置地图响应模式
* @param roam true|false|drag(只拖拽)|scale|roate
*/
setRoam(roam) {
if (roam === true || roam === 'true') {
return null;
} else if (roam === 'drag') {
this.map.scrollZoom.disable();
this.map.dragRotate.disable();
this.map.doubleClickZoom.disable();
} else if (roam === 'scale') {
this.map.dragPan.disable();
this.map.dragRotate.disable();
} else if (roam === 'roate') {
this.map.scrollZoom.disable();
this.map.dragPan.disable();
this.map.doubleClickZoom.disable();
} else if (roam === false || roam === 'false') {
this.map.scrollZoom.disable();
this.map.dragPan.disable();
this.map.dragRotate.disable();
this.map.doubleClickZoom.disable();
}
}
/**
* 设置中心点
* @param {Array} center 中心点
*/
setCenter(center = [0, 0]) {
this.map.setCenter(center);
}
/**
* 设置场景旋转
* @param {Number} value 旋转速度
* @param {Boolean} status 是否旋转
*/
setSceneBearing(value, status = true) {
let self = this;
function draw(now) {
self.map.setBearing(now / value);
requestAnimationFrame(draw);
}
if (status === true) {
requestAnimationFrame(draw);
}
}
/**
* 设置控件
* @private
* @param {Array} control 控件
*/
addControl(control = []) {
control.map(value => {
let type = value.type || value; //控件名称或者对象
let position = value.position || 'top-right'; //控件位置
if (typeof type === 'object') { //开发者自定义控件
this.map.addControl(type, position);
} else {
if (type === 'StyleToggleControl') { //hymap拓展控件
this.mapControls[type] = new Control[type](this._geo.theme);
} else if (type === 'Pitch2dControl') {
this.mapControls[type] = new Control[type]();
} else if (type === 'LayerControl') {
value.position = position;
this.mapControls[type] = new Control[type](value);
} else if (type === 'DrawControl') {
let callback = value.callback || null;
this.mapControls[type] = new Control[type](this.map, callback, );
} else if (type === 'MapMeasureControl') {
this.mapControls[type] = new Control[type]();
} else { //mapbox-gl原生控件
this.mapControls[type] = new mapboxgl[type]();
}
this.map.addControl(this.mapControls[type], position);
}
});
}
/**
*@移除控件
*@author lc
* @param {Array} controls 控件数组
*@date 2018-03-28 18:07:59
*/
removeControl(controls) {
controls.map(value => {
if (typeof value === 'object') { //开发者自定义控件
this.map.removeControl(value);
} else { //hymap以及mapbox-gl原生控件 (GeolocateControl移除不了?)
this.map.removeControl(this.mapControls[value]);
}
});
}
/**
* 增加标记
* @param {String} id id
* @param {Array} coord 坐标
* @param {Array} offset 偏移量
* @return {marker} marker
*/
addMarker({
id = baseUtil.guid(),
coord = [0, 0],
offset = [0, 0]
} = {}) {
let coords = (typeof coord === 'string') ? JSON.parse(coord) : coord;
let el = document.createElement('div');
el.className = 'marker';
const marker = new mapboxgl.Marker(el, {
offset
}).setLngLat(coords).addTo(this.map);
marker.id = id;
this.markers[id] = marker;
this.map.fire('addMarker', {
marker
});
return marker;
}
/**
* 移除标记
* @param {String} id id
*/
removeMarker(id) {
let marker = this.markers[id];
marker && this.map.fire('removeMarker', { marker }) && marker.remove();
delete this.markers[id];
}
/**
* 显示标记
* @param {String} id id
*/
showMarker(id) {
let marker = this.markers[id];
if (marker) {
marker.getElement().style.display = '';
}
}
/**
* 隐藏标记
* @param {String} id id
*/
hideMarker(id) {
let marker = this.markers[id];
if (marker) {
marker.getElement().style.display = 'none';
}
}
/**
* 增加图层
* @param {Array} arrays 参数
* @example
* //样式遵守maobox规则
* const series1 = [{
id: 1,
data: values,
type: 'circle',
symbolStyle: {
normal: {
'circle-radius': 3, //半径
'circle-opacity': 1,//透明度
'circle-blur': 0.2,//模糊
'circle-color': {//颜色
'property': 'NUM',//属性字段 区分大小写
'type': 'categorical',
'default': 'green',//如果该属性不存在
'stops': [
['A', 'green'],//值=1
['B', 'yellow'],//值=2
['C', 'red'],
['D', 'orange']
]
}
},
emphasis: {
'circle-radius': 10 //半径
}
}
}];
let layer1 = this.mapObj.addLayer(series1);
const series1 = [{
id: 2,
data: values,
type: 'symbol',
visible: true,
symbolStyle: {
normal: {
'visibility': 'visible',//'none'
'icon-image': 'icon:example/images/kakou.png',
'icon-size': 0.7
},
emphasis: {
'icon-image': 'icon:example/data/hk/icon/luggage1.png'
}
},
showPopup: false //显示气泡框
}];
let layer1 = this.mapObj.addLayer(series1);
// this.mapObj.on('click', e => {
//
// console.log(e);
//
// });
this.mapObj.on('click', 2, (data) => {
console.log(data)
})
*/
addLayer(arrays) {
//todo 重构循环
let layers = [];
if (!baseUtil.isEmpty(arrays)) {
if (Array.isArray(arrays)) {
arrays.forEach(serie => {
const layer = this._addLayer(serie);
this.layers[layer.id] = layer;
layers.push(layer);
});
} else {
const layer = this._addLayer(arrays);
this.layers[layer.id] = layer;
layers.push(layer);
}
return layers;
}
}
/**
* 更新图层
* @param {Object} arrays serie对象
*/
updateLayer(arrays) {
if (Array.isArray(arrays)) {
arrays.forEach(serie => {
if (this.layers.hasOwnProperty(serie.id)) {
this.layers[serie.id].update(serie);
} else {
this._addLayer(serie);
}
});
} else {
if (this.layers.hasOwnProperty(arrays.id)) {
this.layers[arrays.id].update(arrays);
} else {
this._addLayer(arrays);
}
}
}
/**
* 增加图层
* @param {Object} serie 数据对象
* @private
*/
_addLayer(serie) {
let layer;
switch (serie.type) {
case 'track':
layer = this.initTrackData(serie);
break;
case 'gps':
layer = new Layer.GpsLayer({
map: this.map,
serie: serie
});
break;
case 'heatmap':
layer = new Layer.HeatmapLayer({
map: this.map,
serie
});
break;
case '3d':
layer = new Layer.FillExtrusionLayer({
map: this.map,
serie
});
break;
case 'line':
layer = new Layer.LineLayer({
map: this.map,
serie
});
break;
case 'symbol':
layer = new Layer.SymbolLayer({
map: this.map,
serie
});
break;
case 'radar':
layer = new Layer.RadarLayer({
map: this.map,
serie: serie
});
break;
/* case 'building':
layer = new Layer.BuildingLayer({
map: this.map,
serie: serie
});
break;*/
case 'fill':
layer = new Layer.FillLayer({
map: this.map,
serie: serie
});
break;
case 'symbol3D':
layer = new Layer.SymbolLayer3D({
map: this.map,
serie: serie
});
break;
case 'clusters':
layer = new Layer.ClustersLayer({
map: this.map,
serie: serie
});
break;
case 'video':
layer = new Layer.VideoLayer({
map: this.map,
serie: serie
});
break;
case 'echarts':
layer = new Layer.EchartsLayer({
map: this.map,
serie: serie
});
break;
default:
layer = new Layer.CircleLayer({
map: this.map,
serie: serie
});
}
return layer;
}
/**
* 判断图层是否存在
* @param id
* @returns {boolean}
*/
hasLayer(id) {
let flag = false;
let layer = this.layers[id];
if (layer) {
flag = true;
}
return flag;
}
/**
* 显示图层
* @param {String} id id
*/
showLayer(id) {
let layer = this.layers[id];
layer && layer.show();
}
/**
* 隐藏图层
* @param {String} id id
*/
hideLayer(id) {
let layer = this.layers[id];
layer && layer.hide();
}
/**
* 移除指定id的图层
* @param {String} id id
*/
removeLayer(id) {
let layer = this.layers[id];
if (layer) {
layer.dispose();
delete this.layers[id];
}
}
/**
* 移除图层
* @param {String} id id不填写则移除所有图层
*/
removeLayers(id) {
if (id) {
this.removeLayer(id);
} else {
for (let index in this.layers) {
this.removeLayer(index);
}
}
}
/**
* 获取图层配置信息
* @param {String} layerId 图层id
* @return {Object} 图层信息
*/
getSerie(layerId) {
let layer = this.layers[layerId];
return layer.getSerie();
}
/**
*
* @returns {*}
*/
getSeries() {
return this.map.getStyle();
}
/**
* 根据id获取数据
* @param layerId 图层id
* @param id 数据id
* @return {Array} features features数组
*/
getFeatureById(id, layerId) {
let result = [];
if (layerId) {
const layer = this.layers[layerId];
if (layer) {
result = layer.getFeatureById(id);
}
} else {
for (let index in this.layers) {
let feature = this.layers[index].getFeatureById(id);
feature && result.push(feature);
}
}
return result;
}
/**
* 根据属性获取features
* @param {String} layerId 图层id
* @param {String} key 属性
* @param {String} value 值
* @return {Array} features features数组
*/
getFeaturesByProperty(layerId, key, value) {
let layer = this.layers[layerId];
if (layer) {
return layer.getFeaturesByProperty(key, value);
} else {
return [];
}
}
/**
* 缩放到指定级别
* @param {Number} num 级别
* @param {Boolean} relative 是否相对级别
*/
zoomTo(num = 1, relative = false) {
if (this.rotateObj) {
this.rotateObj.stopAnimation();
}
let zoom = this.map.getZoom();
if (relative) {
zoom += num;
} else {
zoom = num;
const minZoom = this.map.getMinZoom();
if (num < minZoom) {
zoom = minZoom;
}
}
this.map.zoomTo(zoom, {
duration: 2000,
animate: true
}, {
method: 'zoomto'
});
}
/**
* 飞到
* @param {Array} center 坐标数组
* @param {Number} zoom 地图级别
* @param {Function} callback 回调方法
* @param {Number} pitch
*/
flyTo({ center, zoom, callback = undefined, pitch = 0 } = {}) {
if (this.rotateObj) {
this.rotateObj.stopAnimation();
}
const current_pitch = this.map.getPitch();
this.map.flyTo({
center, zoom,
pitch
}, {
mehtod: 'fly',
current_pitch,
callback
});
}
/**
* 平移
* @param {Number} meter 距离 单位 米
* @param {String} direction 朝向:up,down,left,right上下左右
*/
panTo(meter = 0, direction = 'right') {
if (this.rotateObj) {
this.rotateObj.stopAnimation();
}
let degree = 90;
switch (direction) {
case 'up':
degree = 0;
break;
case 'down':
degree = 180;
break;
case 'left':
degree = 270;
break;
case 'right':
degree = 90;
break;
}
let center = this.map.getCenter();
let point = {
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [center.lng, center.lat]
},
properties: {}
};
let translatedPoly = turf.transformTranslate(point, meter / 1000, degree);
this.map.panTo(translatedPoly.geometry.coordinates, { method: 'panto' });
}
/**
* 旋转动画效果
* @param {Number} speed 旋转速度
* @param {Number} time 旋转时长(小于0代表一直旋转下去)
* @param {Boolean} mouseControl 是否响应鼠标事件(鼠标移动则动画停止)
* @returns {rotate}
*/
rotate({
speed = 0.2,
time = -1,
mouseControl = false
} = {}) {
this.rotateObj = new rotate(this.map, {
speed: speed,
time: time,
mouseControl: mouseControl
});
return this.rotateObj;
}
/**
* 移除旋转动画
*/
removeRotate() {
this.rotateObj && this.rotateObj.destory();
this.rotateObj = null;
}
/**
* 注册事件
* @param {String} type 类型
* @param {Function|String} id id为layerid时注册为图层事件,否则为map事件
* @param {Function} listener 回调
*/
on(type, id, listener) {
if (typeof id === 'function') {
this.map.on(type, id);
} else {
let layer = this.layers[id];
if (layer) {
layer.on(type, listener);
} else {
console.info('not found layer');
}
}
}
/**
* 移除事件
* @param {String} type 类型
* @param {Function|String} id id为layerid时移除为图层事件,否则为map事件
* @param {Function} listener 回调
*/
off(type, id, listener) {
if (typeof id === 'function') {
this.map.off(type, id);
} else {
let layer = this.layers[id];
if (layer) {
layer.off(type, listener);
}
}
}
/**
* 重绘地图
*/
resize() {
this.map.resize();
}
/**
* 移除所有信息
*/
remove() {
this.removeLayers();
this.threebox && this.threebox.removeThreeBoxScene(); //移除three场景
this.map.remove();
}
/**
* 绘制轨迹(依赖路网数据,目前仅提供济宁市的测试数据。)
* @param {Array} start 起始点
* @param {Array} end 结束点
* @param {Function} callback 结果回调
* @param {Function} tooltipFun 弹出气泡框回调
* @param {Boolean} isCustom 是否通用气泡框
* @param {String} tbName 表名
*/
drawTrack(start,
end, {
dataUrl,
callback = undefined,
isCustom = false,
tbName = this._geo.routeName
} = {}) {
if (!this.routeLayer) {
this.routeLayer = new Layer.routeLayer({
map: this.map,
serverUrl: this._serverUrl
});
// this.trackOverlayArray = [];
}
this.routeLayer.drawTrack(start, end, {
dataUrl,
callback,
// tooltipFun: tooltip,
isCustom,
tbName
});
}
/**
* 清空路径
*/
clearTrack() {
this.routeLayer && this.routeLayer.clear();
}
/**
* 添加three三维模型
* @param group three三维模型组
* @param coord 经纬度
* @param preScale 缩放比例
* @returns {*}
*/
add3DModel(group = {}, coord = [0, 0, 0], preScale = 1) {
if (!this.threebox) {
this.threebox = new Threebox(this.map);
}
return this.threebox.addAtCoordinate(group, coord, { preScale: preScale });
}
/**
* 移除三维模型
* @param {Object} model
*/
remove3DModel(model) {
if (model.length) {
model.forEach(obj => {
this.threebox.remove(obj);
});
} else {
this.threebox.remove(model);
}
}
/**
*@desc
*@author lc
*@param {Object} group three模型对象
*@param {Array} coord 模型放置坐标
*@param {Number} preScale 模型缩放比例
*@date 2018-04-27 13:58:43
*/
moveModelToCoordinate(group = {}, coord = [0, 0, 0], preScale = 1) {
if (!this.threebox) {
this.threebox = new Threebox(this.map);
}
return this.threebox.moveToCoordinate(group, coord, { preScale: preScale });
}
/**
*@更新three场景
*@author lc
*@date 2018-04-27 14:00:55
*/
updateThreeBoxScene() {
this.threebox.update();
}
/**
*@移除three场景
*@author lc
*@date 2018-04-27 14:01:14
*/
removeThreeBoxScene() {
this.threebox.removeThreeBoxScene();
}
/**
*@添加一个雷达图
*@author lc
*@param {Array} center 中心点坐标
*@param {Number} radius 半径
*@param {String} color 颜色
*@date 2018-04-13 09:45:50
*/
addRadar({ center = [0, 0], radius = 10, color = '#ff0000' } = {}) {
if (!this.threebox) {
this.threebox = new Threebox(this.map);
}
return new Layer.RadarLayer({
threebox: this.threebox,
map: this.map,
center: center,
radius: radius,
color: color
});
}
/**
*@分析url
*@author lc
*@param {String} url 链接地址
*@date 2018-04-04 11:40:12
*/
analysisURL = (url) => {
url = url.replace(/\s+/g, '');
console.log(url, 9898);
let temArr = url.split('?');
let host = temArr[0];
let params = temArr[1].split('&');
let types = params.map(value => {
let typesString = /(?:types)/.exec(value).input;
return typesString.split('=')[1];
}).toString();
if (types) {
types = types.split(',');
}
return { host, types };
};
/**
*@经纬度转屏幕像素坐标
*@author lc
*@param {x} Number 经度
*@param {y} Number 纬度
*@date 2018-04-27 13:43:33
*/
project({ x = 0, y = 0 } = {}) {
return this.map.transform.locationPoint({ lng: x, lat: y });
}
/**
*@屏幕像素坐标转经纬度
*@author lc
*@param {x} Number 屏幕像素x
*@param {y} Number 屏幕像素y
*@date 2018-04-27 13:43:38
*/
unproject({ x = 0, y = 0 } = {}) {
let temResult = this.map.transform.pointLocation({ x: x, y: y });
return { x: temResult.lng, y: temResult.lat };
}
}
export default HyMapBox;