做过地图的小伙伴们都知道,每个地图框架产商都与自家的地图资源进行绑定,如非常受欢迎的mapBox、高德、百度、腾讯等,你必须注册他们产品,获取key,然后调用的api,才能进行地图的相关操作,虽然带来了便利, 但同时这也存在这限制。
那么有人可能要问,那这么方便,就用他们的,就不用自己开发了不是,为什么还用其他的呢?
这个问题很好,做产品,最好的做法就是由开发自己掌控,能做什么,能做到什么程度,一切都由开发把握,这才是一个完好的自主产品。说到自主,作为中国人也是有亲身体会的,作物基因专利、光刻机、软件等等,由外国掌控核心技术,作为使用者的我们只能认栽吃瘪,maptalks是我们国人开源的一个地图框架,可以自定义我们的地图资源,不用第三方支持,而且也集成了很多插件,想three、echarts、热力图,支持我们的开发需求。这不是宣传,我就我个人的一些观点。
https://maptalks.org/maptalks.js/api/0.x/Map.html
首先需要安装maptalks
npm install maptalks
和其他框架的差不多,了解就行;
哦,还有一个点,出现跨域的问题,需要设置crossOrigin: undefined
<template>
<div id="map" class="container"></div>
</template>
<script>
// 引入样式和组件
import 'maptalks/dist/maptalks.css';
import * as maptalks from 'maptalks';
export default {
data() {
return {
// 地图引擎
mapEngine: null,
// 倾斜度
pitch: 50,
// toolbar
// 旋转
whirl: null,
// 放大/缩小
zoom: 14,
// 轴承
bearing: 0,
// 屏幕坐标
containerPoint: {x: null, y: null},
// 地图坐标
coordinatePoint: {x: null, y: null}
}
},
mounted() {
const _t = this
this.$nextTick(() => {
const map = new maptalks.Map('map', {
// 默认中心点点位
center: [118.13245430046891, 24.495713873147764],
// 缩放层级
zoom: _t.zoom,
// 倾斜度
pitch: _t.pitch,
// 最小缩放层级
minZoom: 1,
// 最大缩放层级
maxZoom: 18,
// baseLayer 表示基础图层,它可以添加多个,逗号隔开
baseLayer: new maptalks.TileLayer('base', {
// 出现跨域问题,要设置这个,一定是undefined
crossOrigin: undefined,
// 电子地图图层
urlTemplate: 'https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png',
subdomains: ['a', 'b', 'c', 'd'],
attribution: '© <a href="http://osm.org">OpenStreetMap</a> contributors, © <a href="https://carto.com/">CARTO</a>'
})
});
});
}
}
</script>
<style lang="scss">
.container {
width: 100%;
height: 100%
}
</style>
这个属性是用来设置样式的,是它的一个规则,之后我们的用的功能都与这个有关系,所以这里提前的了解,下面是它的文档说明:
https://github.com/maptalks/maptalks.js/wiki/Symbol-Reference
创建图层的方式有两种:
const map = new maptalks.Map('map', {
// 默认中心点点位
center: [118.13245430046891, 24.495713873147764],
// 缩放层级
zoom: _t.zoom,
// 倾斜度
pitch: _t.pitch,
// 最小缩放层级
minZoom: 1,
// 最大缩放层级
maxZoom: 18,
// baseLayer 表示基础图层,它可以添加多个,逗号隔开
baseLayer: new maptalks.TileLayer('base', {
// 电子地图图层
urlTemplate: 'https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png',
subdomains: ['a', 'b', 'c', 'd'],
attribution: '© <a href="http://osm.org">OpenStreetMap</a> contributors, © <a href="https://carto.com/">CARTO</a>'
}),
layers: [
// 创建矢量图层 v
// new maptalks.VectorLayer('v', 几何图形列表(geometries), 可选参数配置(options))
new maptalks.VectorLayer('v')
]
});
addTo(map)
这个方法对所有组件通用,意思就是,下面我们介绍的工具、组件等添加到地图上,都是用这个方法。new maptalks.VectorLayer('v').addTo(map)
在地图初始化时,我们也可以添加一些我们的工具:
它提供了new maptalks.control.Toolbar
让我们去实例一个工具栏,格式:
// 工具栏实例化
new maptalks.control.Toolbar({
items: [
{
// 显示名称
item: '放大',
// 点击时间
click: () => {
map.setZoom(_t.zoom += 1)
}
}
]
}).addTo(map);
这里我简单了列了items
里的属性,它其实共有4个属性:position、vertical、reverseMenu、items,详细可以去看:Maptalks/docs/api/0.x/control.Toolbar.html,也可以从源码角度去看,在源码里它是有默认配置的,比如下面这个:
var options$s = {
'height': 28,
'vertical': false,
'position': 'top-right',
'reverseMenu': false,
'items': {}
};
缩放工具它有提供现成的组件:
/**
* 增加缩放工具
* @param map
*/
addZoomTool(map) {
new maptalks.control.Zoom({
// 工具位置
position: 'top-left',
// 是否是以线段条方式展示
slider: false,
// 是否显示缩放级别文本框
zoomLevel: true
}).addTo(map);
},
我们也可以自定义创建:
new maptalks.control.Toolbar({
items: [
{
item: '放大',
click: () => {
map.setZoom(_t.zoom += 1)
}
},
{
item: '缩小',
click: () => {
map.setZoom(_t.zoom -= 1)
}
},
{
item: '旋转',
click: () => {
map.setBearing(_t.bearing -= 50)
}
},
{
item: '重置',
click: () => {
_t.mapDataReset(map)
}
},
{
item: '锁定',
click: (t) => {
if (t.target.item === '锁定') {
map.setOptions({
// 可拖动
draggable: false,
// 平移
dragPan: false,
// 旋转
dragRotate: false,
// 间距
dragPitch: false,
// 滚动缩放
scrollWheelZoom: false,
// 点击 缩放
touchZoom: false,
// 双击缩放
doubleClickZoom: false
})
t.target.item = '取消锁定'
} else {
map.setOptions({
// 可拖动
draggable: true,
// 平移
dragPan: true,
// 旋转
dragRotate: true,
// 间距
dragPitch: true,
// 滚动缩放
scrollWheelZoom: true,
// 点击 缩放
touchZoom: true,
// 双击缩放
doubleClickZoom: true
})
t.target.item = '锁定'
}
}
}
]
}).addTo(map);
效果如下:
首先我们要知道它有2个概念:Geometry Polygon,polygon继承Geometry,两个可以看做一个东西;
并且,绘制面需要VectorLayer
图层上进行绘制。
它画面的原理是,两点定义一条直线,多个点连成多条线,近大远小,远看就是曲线,那么面就是连接了开始和结束的点,使之闭环,加上颜色就是一个面。
好,下载可以找一个数据来测试一下:地图选择器 (aliyun.com)
上面下载一个geojson的数据,名称随便,这里就说一下怎么用它的方法:
首先要清除怎么添加几何面,使用layer.addGeometry(geometry)
,addGeometry支持单个,也支持数组,那么就是说,我们可以传入的参数可以是:Polygon、MultiPolygon、Geometry、MultiGeometry,或者他们数组。
const geoJson = require( '@/mock/xiamen.json')
/**
* 根据geojson画区域面
* @param geoJson geoJson数据
* @param layer 需要话的图层
*/
drawAreaPolygon(geoJson, layer) {
const _t = this
const geometry = maptalks.GeoJSON.toGeometry(geoJson)
if (geometry) {
geometry.forEach(g => {
g.setSymbol({
// 线色
lineColor: '#34495e',
// 线宽
lineWidth: 1,
// 填充色
polygonFill: 'rgb(135,196,240)',
// 不透明度
polygonOpacity: 0.2
})
})
}
layer.addGeometry(geometry)
},
maptalks.GeoJSON.toGeometry(geoJson) 获取到的是一个MultiPolygon
数组对象
效果如下:
好像这个JSON的数据不是很准确,这个不重要,重要的是我们已经将面画出来了,还需要一些交互。
上面我们画出了面,但是只能看,而且没有任何交互,用户体验非常非常的,额…是没有用户体验,现在我们来加一下事件,实现鼠标移动、点击等的交互。
Geometry Polygon 提供了监听事件on
和js的一样,这个没什么好说的,下面我就以geoJson创建的面为例:
drawAreaPolygon(geoJson, layer) {
const _t = this
const geometry = maptalks.GeoJSON.toGeometry(geoJson)
if (geometry) {
geometry.forEach(g => {
g.setSymbol({
// 线色
lineColor: '#34495e',
// 线宽
lineWidth: 1,
// 填充色
polygonFill: 'rgb(135,196,240)',
// 不透明度
polygonOpacity: 0.2
})
// 设置信息框
g.setInfoWindow({
title : g.properties.name,
content : '<br style="color:#f00">中心点:' + g.properties.center + ' </br>行政区划:' + g.properties.adcode + ' </br>父级行政区划:' + g.properties.parent.adcode + '</div>'
})
// 鼠标交互事件监听
g.on('mouseenter', function (e) {
e.target.updateSymbol({
polygonFill: '#f00'
});
}).on('mouseout', function (e) {
e.target.updateSymbol({
polygonFill: 'rgb(135,196,240)'
});
}).on('click', function (e) {
e.target.openInfoWindow(e.coordinate)
})
})
}
layer.addGeometry(geometry)
},
效果如下:
它在鼠标点击位置显示了弹框
基础的操作现在有了,也比较常用,可是你觉得你还需要右键菜单,菜单啊,这个,可以,它也提供了这些东西的设置,下面再来一个简单例子:
drawAreaPolygon(geoJson, layer) {
const _t = this
const geometry = maptalks.GeoJSON.toGeometry(geoJson)
if (geometry) {
geometry.forEach(g => {
g.setSymbol({
// 线色
lineColor: '#34495e',
// 线宽
lineWidth: 1,
// 填充色
polygonFill: 'rgb(135,196,240)',
// 不透明度
polygonOpacity: 0.2
})
// 设置信息框
g.setInfoWindow({
title: g.properties.name,
content: '<br style="color:#f00">中心点:' + g.properties.center + ' </br>行政区划:' + g.properties.adcode + ' </br>父级行政区划:' + g.properties.parent.adcode + '</div>'
})
// 设置右键菜单
g.setMenu({
width: 160,
custom: false,
items: [
{ item: '菜单一', click: function() { alert('Query Clicked!'); return false } },
'-',
{ item: '菜单二', click: function() { alert('Edit Clicked!') } },
{ item: '菜单三', click: function() { alert('About Clicked!') } }
]
})
// 鼠标交互事件监听
g.on('mouseenter', function(e) {
e.target.updateSymbol({
polygonFill: '#f00'
})
}).on('mouseout', function(e) {
e.target.updateSymbol({
polygonFill: 'rgb(135,196,240)'
})
}).on('click', function(e) {
e.target.openInfoWindow(e.coordinate)
})
})
}
layer.addGeometry(geometry)
},
这里的菜单有一个返回值,如果返回false,菜单就不会关闭。
效果如下:
绘制mark没有Polygon 那么复杂,它只要一个坐标点就行,然后在指定坐标出绘制一个图标,
它有一个addTo
方法,可以添加到任何一个图层
drawMark(centerPointList, layer) {
if (!centerPointList) {
console.log('无区域中心点数据')
return
}
const info = { content: '', width: 150, minHeight: 100 }
const result = []
// 这里 d 的数据格式是数组,如:[-0.113049, 51.498568]
centerPointList.forEach(d => {
if (!d.info) {
d.info = info
}
// 设有高度、高亮的mark
const mark = new maptalks.Marker(d.center, {
// 设置了这个属性,会替换默认的图标
// symbol: {
// markerFile: 'foo.png',
// textName: d.name
// },
properties: {
// 高度设置
altitude: 50
}
}).addTo(layer)
mark.setInfoWindow({
title: d.name,
content: '<div>' + d.adcode + '</div>',
// autoPan: true,
width: d.info.width,
minHeight: d.info.minHeight,
// 'custom': false,
// 点击打开和关闭
// autoOpenOn: 'click',
// autoCloseOn: 'click'
})
// 没有高度的mark
// new maptalks.Marker(d).updateSymbol({
// markerOpacity: 0.5,
// markerFill: '#bbb'
// }).addTo(layer)
mark.setZIndex(1000)
result.push(mark)
})
return result
},
这里的centerPointList
是geoJson里的properties属性;
这里有一个关键点是,要绘制三维的mark,需要设置图层layer启用高度绘制如下:
layer.setOptions({
// 启用高度绘制
enableAltitude: true,
altitudeProperty: 'altitude',
// 绘制高度线
drawAltitude: {
// 这里是绘制高度线的宽度,可以理解为Z,那么要透明,这里设置为0
lineWidth: 1,
lineColor: '#000'
}
})
当启用锁定后,我们所观看到的视图,只会是我们设定好的区域,这块区域默认是地图初始化时设定的center
lockView() {
const extent = this.map.getExtent()
this.map.setMaxExtent(extent)
},