記事ディレクトリ
序文
前回の記事でマップトークの基本的な機能と地図の描き方は理解しましたが、探索能力のある小規模なパートナーであれば、さらに高度な機能が完成しているかもしれませんが、ここではマニュアルとして、ゆっくりと記録していきます。開発の詳細です。
お客様が求める効果は様々ですが、機能の変更は避けられません。
次の例は前の例に基づいています
プロジェクトコードアドレス(デモ)
マーク
実際のアプリケーションにおける作成と削除
実際のアプリケーションでは、ユーザーが選択したマークの種類に応じてマークの削除と作成が表示されます。
- 次のように、最初にページを定義しましょう。マップの上にツールバーを残します。
<template>
<div class="container">
<div class="map-toolbar">
<el-checkbox-group v-model="checkList" @change="handleCheckChange">
<el-checkbox :label="item.label" v-bind:key="item.value" :value="item.value" v-for="item in chooseData"></el-checkbox>
</el-checkbox-group>
</div>
<div
id="map"
class="map-container"
/>
</div>
</template>
<style scoped lang="scss">
html, body {
margin: 0px;
height: 100%;
width: 100%;
}
.container {
width: 100%;
height: 100%;
position: absolute
}
.map-toolbar {
position: relative;
z-index: 1000;
width: 500px;
height: 50px;
float: right;
top:10px;
right: 300px;
background-color: #fff;
border-radius:5px;
padding: 10px;
}
.map-container {
width: 100%;
height: 100%
}
</style>
-
定義方法:
// 定义两个常量(仅测试) const schoolCoordinate = [ { adcode: 510104, name: "小学校", center: [118.13734735854666, 24.498801064931087], parent: { adcode: 510100 }, }, { adcode: 510104, name: "大学校", center: [118.11804966596969, 24.47633923130772], parent: { adcode: 510100 }, } ] const hospitalCoordinate = [ { adcode: 510104, name: "小医院", center: [118.18303419164465, 24.51143878000599], parent: { adcode: 510100 }, },{ adcode: 510104, name: "大医院", center: [118.19412188942488, 24.53586932160701], parent: { adcode: 510100 }, } ] // data 里定义的变量: // 类型复选框 checkList: [], chooseData: [{ label: '学校', value: 'school' }, { label: '医院', value: 'hospital' }], // 保持选择的图层的名称 checkLayers: [] // methods定义的方法 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, }) mark.setZIndex(1000) result.push(mark) }) return result }, // 复选框选中 handleCheckChange(data) { console.log(data) const _t = this // 清楚图层 this.checkLayers.forEach(l => { l.clear() }) const schoolMarkLayer = this.mapEngine.getLayer('schoolMark') const hospitalMarkLayer = this.mapEngine.getLayer('hospitalMark') data.forEach(d => { if (d === '学校') { _t.drawMark(schoolCoordinate, schoolMarkLayer) _t.checkLayers.push('schoolMark') } if (d === '医院') { _t.drawMark(hospitalCoordinate, hospitalMarkLayer) _t.checkLayers.push('hospitalMark') } }) },
動画のアップロード方法がわかりません。完全な投稿がない場合は、すべてのコードを含めることも可能ですが、あまりスペースを取りたくないので、
ここでその方法のいくつかについて話しましょう
- layer.clear() を実行すると、レイヤー上のすべてをクリアします。
- mark.remove() , これを使用してマークを削除できますが、これはマーク オブジェクトによって呼び出される必要があります。フロントエンドが上記の機能を実現したい場合は、マーク リストを保存する必要があります。これはポイントが多すぎると、フロントエンドが耐えられなくなる可能性があるため、各種類のレイヤーがここにキャッシュされ、そのレイヤーが操作に使用されます。
カスタムアイコン
これは、vue ロゴを置換アイコンとして使用する例です。
顶部require引入静态资源
const logo = require('../../assets/logo.png')
// 创建时,使用symbol替换默认的样式
const mark = new maptalks.Marker(d.center, {
symbol: {
// markerType: 'square',
markerFile: logo,
markerWidth: 40,
markerHeight: 40,
markerDx: 0,
markerDy: 0,
markerOpacity: 1
},
properties: {
// 高度设置
altitude: 0
}
}).addTo(layer)
ここで、 markFile はアイコンの置き換えに使用され、 markerType、マーカーの種類、それが表すアイコンの種類、 markerFile 属性がある場合は上書きできません、 markerFile がない場合は markerType で表示できます、いくつかのタイプがあります: ellipse cross x diamond bar square triangle pin pie
、効果は自分で設定して確認できます。
効果は次のとおりです。
アニメーション効果を追加する
独自のマークを追加した後、いくつかの視覚効果が必要になる場合があります。Mark にはanimate
独自のアニメーションを設定するメソッドも用意されています。次に、マークが表示されるときのアニメーションと、vue ロゴの横方向の拡張を設定します。
上記のコードに基づいて次のコードを追加し、new maptalks.Marker
デフォルトsymbol.markerHeight
設定を 0 と 10 にsymbol.markerWidth
設定します。これは、初期の高さは 0、初期の幅は 10 であり、高さ 40、幅 10 に変換されます。 40.
mark.animate({
symbol: {
markerWidth: 40,
markerHeight: 40
},
properties: {
altitude: 800
}
}, {
duration: 150,
}, function (frame) {
if (frame.state.playState === 'finished') {
console.log('animation finished');
}
});
学校または病院のオプションをクリックすると、これらのスプリンクルが自動的に展開され、監視を追加したり、アニメーションの各段階で何らかの処理を実行したりすることもできます。
完全なコード:
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: {
markerType: 'square',
markerFile: logo,
markerWidth: 10,
markerHeight: 0,
markerDx: 0,
markerDy: 0,
markerOpacity: 1
},
properties: {
// 高度设置
altitude: 0
}
}).addTo(layer)
mark.setInfoWindow({
title: d.name,
content: '<div>' + d.adcode + '</div>',
// autoPan: true,
width: d.info.width,
minHeight: d.info.minHeight,
})
mark.animate({
symbol: {
markerWidth: 40,
markerHeight: 40
},
properties: {
altitude: 800
}
}, {
duration: 150,
}, function (frame) {
if (frame.state.playState === 'finished') {
console.log('animation finished');
}
});
mark.setZIndex(1000)
result.push(mark)
})
return result
},
最初にマーク オブジェクトを作成するとき、設定された幅はアニメーションの開始幅である 10 だけであることがわかります。width
その後、animate メソッドで設定されたプロパティが変更する必要があるプロパティであり、duration
アニメーション時間の実行を制御することで、最終的なプロパティも設定します。
道具
ここでは基本的にシンボルなので、ここでもう一度言及する必要があります。つまり、symbol
この属性についてはドキュメントを確認できます。これはシステムであり、統一されており、完全であるためです。
次の関連する属性コメントの最初のコメントは比較的完全で、後者はほぼ同じであるため、すべてがマークされるわけではありません。
距離測定ツール
API:クラス: DistanceTool (maptalks.org)
距離を計算し、公式のケースをコピーし、わずかな修正を加え、終了イベントを追加し、
/**
* 测距工具
* @returns {*}
*/
addDistanceTool () {
return new maptalks.DistanceTool({
symbol: {
lineColor: '#34495e',
lineWidth: 2
},
// 请看 symbol属性说明文档:https://github.com/maptalks/maptalks.js/wiki/Symbol-Reference
// 绘制的线的样式
vertexSymbol: {
// 绘制的marker类型,也就是简单形状,支持:ellipse cross x diamond bar square triangle pin pie
markerType: 'square',
// 绘制的marker的填充色
markerFill: '#1bbc9b',
// 绘制的线的颜色
markerLineColor: '#000',
// 绘制的线的宽度
markerLineWidth: 1,
// 绘制的marker大小
markerWidth: 10,
markerHeight: 10
},
// 文本标签属性
labelOptions: {
textSymbol: {
// 文本呈现的样式,默认是: monospace, 当你设置 textFont时会覆盖这个属性
textFaceName: 'monospace',
// 文本填充色(字体颜色)
textFill: '#fff',
// 行距
textLineSpacing: 1,
// 对齐方式
textHorizontalAlignment: 'right',
// 文本标签与marker的距离,也就是与打点的位置的距离
textDx: 20,
// 标签的线的颜色
markerLineColor: '#b4b3b3',
// 标签的填充色
markerFill: '#000'
},
boxStyle: {
// 标签的padding, 第一个值是左右的padding,第二个是上下的padding
padding: [6, 5],
symbol: {
// 绘制的marker类型,也就是简单形状,支持:ellipse cross x diamond bar square triangle pin pie
markerType: 'square',
markerFill: '#000',
markerFillOpacity: 0.9,
markerLineColor: '#b4b3b3'
}
}
},
// 清楚按钮的symbol
clearButtonSymbol: [{
markerType: 'square',
markerFill: '#000',
markerLineColor: '#b4b3b3',
markerLineWidth: 2,
markerWidth: 15,
markerHeight: 15,
markerDx: 20
}, {
markerType: 'x',
markerWidth: 10,
markerHeight: 10,
markerLineColor: '#fff',
markerDx: 20
}],
language: 'zh-CN',
once: true
}).addTo(this.mapEngine)
.on('drawend', p => {
console.log('地图坐标:' + p.coordinate)
console.log('界面坐标:' + p.containerPoint)
console.log('测量距离:' + p.drawTool.getLastMeasure() + '米')
})
},
ツールボタンを追加します。
ここでは[距離測定]ボタンを追加しました。クリックすると旧正月や祝日の測量とマッピングが有効になり、再度クリックすると測量とマッピングが終了します。
/**
* 创建工具栏
*/
addToolbar () {
const _t = this
const map = this.mapEngine
new maptalks.control.Toolbar({
items: [
{
item: '测距',
click: () => {
if (_t.distanceTool.isEnabled()) {
_t.distanceTool.disable()
} else {
_t.distanceTool.enable()
}
}
},
{
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)
},
効果:
上記の機能は[Range]をクリックした後に距離測定モードを開始する機能ですが、ボタンを1回クリックして1回測定する場合もあります。これもここで実現できます。maptalksはそのようなAPIを提供しています。オプションに「」を追加するだけです。 onece" 属性を選択し、true に設定します。
/**
* 测距工具
* @returns {*}
*/
addDistanceTool () {
return new maptalks.DistanceTool({
// ... 省略
language: 'zh-CN',
// 只测绘一次
once: true
}).addTo(this.mapEngine)
.on('drawend', p => {
console.log('地图坐标:' + p.coordinate)
console.log('界面坐标:' + p.containerPoint)
console.log('测量距离:' + p.drawTool.getLastMeasure() + '米')
})
}
上記のように構成されている場合addToolbar
、このメソッドでは変更する必要はありません。調査後にこの onece の属性も無効になるため、メソッドをaddToolbar
拡張することができます。
描画ツール
同様に、公式デモを直接取得して変更することもできます。
/**
* 绘制工具
*/
addDrawTool (layer) {
const drawTool = new maptalks.DrawTool({
mode: 'Point'
}).addTo(this.mapEngine)
// 默认是禁用的,当点击按钮后才能使用
.disable()
drawTool.on('drawend', function (param) {
layer.addGeometry(param.geometry)
})
this.drawTool = drawTool
return drawTool
}
/**
*增加绘制工具栏
*/
addDrawToolbar (layer) {
const _t = this
const items = [{
code: 'Point', name: '点'},
{
code: 'LineString', name: '线'},
{
code: 'Polygon', name: '几何面'},
{
code: 'Circle', name: '圆'},
{
code: 'Ellipse', name: '椭圆'},
{
code: 'Rectangle', name: '矩形'},
{
code: 'FreeHandLineString', name: '自由绘制'},
{
code: 'FreeHandPolygon', name: '任意几何面'}]
.map(function (value) {
return {
item: value.name,
click: function () {
_t.drawTool.setMode(value.code).enable()
}
}
})
new maptalks.control.Toolbar({
position: {
top: 100,
right: 50
},
items: [
{
item: '绘制工具',
children: items
},
{
item: '禁用',
click: function () {
_t.drawTool.disable()
}
},
{
item: '清除',
click: function () {
layer.clear()
}
}
]
}).addTo(this.mapEngine)
},
効果:
表面測定ツール
もう 1 つのツールは、より面目を保つツールでもあります。
/**
* 测面工具
*/
addAreaTool () {
const areaTool = new maptalks.AreaTool({
once: true,
// 请看 symbol属性说明文档:https://github.com/maptalks/maptalks.js/wiki/Symbol-Reference
symbol: {
lineColor: '#1bbc9b',
lineWidth: 2,
polygonFill: '#fff',
polygonOpacity: 0.3
},
vertexSymbol: {
// 绘制的marker类型,也就是简单形状,支持:ellipse cross x diamond bar square triangle pin pie
markerType: 'ellipse',
// 绘制的marker的填充色
markerFill: '#34495e',
// 绘制的线的颜色
markerLineColor: '#1bbc9b',
// 绘制的线的宽度
markerLineWidth: 3,
// 绘制的marker大小
markerWidth: 10,
markerHeight: 10
},
// 文本标签属性
labelOptions: {
textSymbol: {
// 文本呈现的样式,默认是: monospace, 当你设置 textFont时会覆盖这个属性
textFaceName: 'monospace',
// 文本填充色(字体颜色)
textFill: '#fff',
// 行距
textLineSpacing: 1,
// 对齐方式
textHorizontalAlignment: 'right',
// 文本标签与marker的距离,也就是与打点的位置的距离
textDx: 15
},
boxStyle: {
// 标签的padding, 第一个值是左右的padding,第二个是上下的padding
padding: [6, 2],
symbol: {
markerType: 'square',
markerFill: '#000',
markerFillOpacity: 0.9,
markerLineColor: '#b4b3b3'
}
}
},
clearButtonSymbol: [{
markerType: 'square',
markerFill: '#000',
markerLineColor: '#b4b3b3',
markerLineWidth: 2,
markerWidth: 15,
markerHeight: 15,
markerDx: 22
}, {
markerType: 'x',
markerWidth: 10,
markerHeight: 10,
markerLineColor: '#fff',
markerDx: 22
}],
language: 'zh-CN'
}).addTo(this.mapEngine)
// 默认关闭
areaTool.disable()
this.areaTool = areaTool
return areaTool
}
描画ツールバーにこのメニューを追加します。
{
item: '测面工具',
click: function () {
if (_t.areaTool.isEnabled()) {
_t.areaTool.disable()
} else {
_t.areaTool.enable()
}
}
},
また、描画ツールバーにメニューを追加すると、次のような効果が得られます。
地図アニメーション
ページを開いた瞬間にカメラが上から下にゆっくりと回転し、360度回転して指定された場所を見つけてアイコンが飛び出すような演出があれば、ほとんどのハートを掴めるはずです。
API は Maptalks でも提供されており、単にそれを呼び出すだけです。
/**
* 地图动画
*/
mapAnimate () {
const _t = this
_t.mapEngine.animateTo({
center: [118.13245430046891, 24.495713873147764],
zoom: 13,
pitch: 0,
bearing: 20
}, {
duration: 5000
})
setTimeout(function () {
_t.mapEngine.animateTo({
center: [118.087828, 24.462059],
zoom: 14,
pitch: 65,
bearing: 360
}, {
duration: 7000
})
}, 5000)
}
私たちのソフトウェアはアニメーションをカットすることはできませんが、このためのコードを実行するだけです。
通常、マーカーは持ち込まれ、マーカーもアニメーション化され、この 2 つの組み合わせは完璧です。
そこで、上で作成した学校と病院の機能を分解して組み合わせてみます。
/**
* 地图动画
*/
mapAnimate () {
const _t = this
_t.mapEngine.animateTo({
center: [118.13245430046891, 24.495713873147764],
zoom: 13,
pitch: 0,
bearing: 20
}, {
duration: 5000
})
setTimeout(function () {
_t.mapEngine.animateTo({
center: [118.087828, 24.462059],
zoom: 14,
pitch: 65,
bearing: 360
}, {
duration: 7000
})
}, 5000)
// 在地图动画块结束时,撒点动画加入,并设置默认值
setTimeout(function () {
_t.checkList = []
_t.checkList.push('学校')
_t.handleCheckChange(_t.checkList)
}, 9000)
}
繰り返し再生できる再生ボタンを追加しました。ソフトウェアもGIFもありません。
重合
集約の機能についても言及しなければなりません。ズームするすべてのマップ デザインにはこの機能があります。
Maptalks だけに依存するのは少し難しいですが、maptalks を使用する利点は、多くのプラグインがあることです。また、このすべての集計機能を実現するためにプラグインも使用します。
文档:GitHub - maptalks/maptalks.markercluster: クラスター マーカーへの Maptalks のレイヤー。
プラグインをインポートします。
npm インストール maptalks.markercluster
次のように使用します。
import * as maptalks from 'maptalks'
import {
ClusterLayer} from 'maptalks.markercluster'
前に書いたマーカー ページを変更してみましょう。
今回作成したレイヤーはマーカークラスターで少し変わっていますが、レイヤーの作成方法を変更しただけで、それに対応するコメントを追加しています。コードを見るとわかりやすいと思います。
ここにすべての記号を書いたわけではありませんが、公式サイトを直接コピーする方法と、怠けて使用する方法がありますが、他の記号と同じでコピーできます。
// 先创建图层
// 创建学校和医院的mark图层
// new maptalks.VectorLayer('schoolMark').addTo(_t.mapEngine)
// new maptalks.VectorLayer('hospitalMark').addTo(_t.mapEngine)
const symbol = {
// 聚合最大半径
maxClusterRadius: 160,
// 聚合的最大缩放比例,也就是当缩放到某一个层级时,进行聚合
maxClusterZoom: 19,
// 是否开启动画,默认true(开启)
animation: true,
// 动画时长
animationDuration: 30,
// textSumProperty: '',
// 这里的属性,可以看官网,都是统一的
// symbol: {},
textSymbol: {
// 文本呈现的样式,默认是: monospace, 当你设置 textFont时会覆盖这个属性
textFaceName: 'monospace',
// 字体大小
textSize: 16
}
}
new ClusterLayer('schoolMark', symbol).addTo(this.mapEngine)
new ClusterLayer('hospitalMark', symbol).addTo(this.mapEngine)
addMarker
メソッドの変更:
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: {
markerType: 'square',
markerFile: logo,
markerWidth: 10,
markerHeight: 0,
markerDx: 0,
markerDy: 0,
markerOpacity: 1
},
properties: {
// 高度设置
altitude: 0
}
})
mark.setInfoWindow({
title: d.name,
content: '<div>' + d.adcode + '</div>',
// autoPan: true,
width: d.info.width,
minHeight: d.info.minHeight
})
mark.animate({
symbol: {
markerWidth: 40,
markerHeight: 40
},
properties: {
altitude: 800
}
}, {
duration: 150
}, function (frame) {
if (frame.state.playState === 'finished') {
console.log('animation finished')
}
})
mark.setZIndex(1000)
result.push(mark)
})
layer.addMarker(result)
return result
},
効果は次のとおりです。
ベースマップのスタイル
従来のマップのデザインは良くなく、見る人を非常に疲れさせるので、スタイルを変更できます。
これは比較的単純で、属性です。cssFilter
/**
* 初始化地图
*/
initMap () {
const _t = this
return new maptalks.Map('map', {
// 默认中心点点位
center: _t.center,
// 缩放层级
zoom: _t.zoom,
// 倾斜度
pitch: _t.pitch,
// 最小缩放层级
minZoom: 1,
// 最大缩放层级
maxZoom: 18,
baseLayer: new maptalks.TileLayer('base', {
// 电子地图图层
urlTemplate: _t.urlTemplate,
subdomains: _t.subdomains,
attribution: _t.attribution,
cssFilter: 'sepia(100%) invert(90%)'
})
})
},
次の 2 つが提供されます。
効果:
ヒートマップ
heatmap.js と統合されたヒート マップ
サーマルレイヤープラグインを追加
npm インストール maptalks.heatmap
追加メソッドもそのメソッドの 1 つです。そのデータは座標の配列です。独自の変数を追加するだけです。ここではそれを実行します。
// 获取热力点
const h1 = []
for (let i = 0; i < simingAreaData.length; i++) {
const s = simingAreaData[i]
s.pointList.forEach(ss => {
ss.forEach(sss => {
sss.push(i + '100')
h1.push(sss)
})
})
}
// 创建图层
new HeatLayer('heat', h1, {
// 热力比例(我的理解是,在0-1之间,也就是0-100%,一个热力点,的热力程度是多少
heatValueScale: 0.7,
// 旋转时强制刷新
forceRenderOnRotating: true,
// 移动时强制刷新
forceRenderOnMoving: true,
// 模糊因子, 值越大,越平滑,默认值是15
// blur: 20,
// 每个数据点的半径(默认值25,也建议25,我建议不能小于20)
// radius: 25,
// 最小不透明读,越小越透明
// minOpacity: 0.8,
// 热力梯度,是热力点外围的颜色值,从外围到里,值是递增的,最大值就是中心的位置
// gradient: {
// 0.4: 'blue',
// 0.6: 'cyan',
// 0.7: 'lime',
// 0.8: 'yellow',
// 1.0: 'red'
// }
}).addTo(this.mapEngine)
まず第一に、熱がどのように与えられるかを理解する必要があります
- ポイントの中心から外側へのグラデーション。
- 複数の点を重ね合わせたり、複数の点を集めて表面を表現したりできます。
- 各ポイントの表示は同じであり、温度勾配があり、これは外側から内側へ (明るいところから暗いところへ) 色が変化します。
したがって、上記のジオメトリとは異なります。ジオメトリには 3 つ以上のポイントが必要ですが、ヒート マップには少なくとも 1 つのポイントだけが必要です。
上記のコードは、熱特性もマークします。
- heatValueScale: この値の役割はよく理解されていません。
- ForceRenderOnRotating: 回転時に強制的に更新します
- ForceRenderOnMoving: 移動時に強制的に更新します
- ブラー: ブラー係数、値が大きいほど滑らかになります、デフォルト値は 15 です
- radius: 各データ ポイントの半径 (デフォルト値は 25 ですが、25 も推奨されます。20 以上をお勧めします)
- minOpacity: 最小の不透明読み取り値。小さいほど透明度が高くなります。
- gradient: サーマルポイントの周囲のカラー値であるサーマルグラデーション、周囲から内側に向かって値が増加しており、最大値は中心の位置です
上記の値にはデフォルト構成があり、直接使用することもカスタマイズすることもできます。
ヒートマップの相互作用
友人からの質問に答えて、文字数制限のため記事に追加しました
ヒート マップ自体は点描画によってレンダリングされ、オープンなインタラクティブ インターフェイスがない (私はまだ見つけていません) ため、代替方法を見つけるしかありません
。
// 方法一
// canvas绘制point,和热力图重合,并添加监听事件,也可以绘制mark,不过它们之间的性能需要分析下
// 这个方法可以单独对点做弹窗
// 对应的point文档:https://maptalks.org/maptalks.js/api/1.x/MultiPoint.html#setSymbol
const h1 = []
for (let i = 0; i < simingAreaData.simingAreaData.length; i++) {
simingAreaData.simingAreaData[i].pointList[0].forEach(d => {
h1.push(d)
})
}
var mPoint = new maptalks.MultiPoint(h1, {
visible: true,
// 可交互
interactive: true,
cursor: 'pointer',
// 可拖拽
draggable: false,
zIndex: 10,
symbol: {
// 不透明度,这里不设置0是因为,值为0交互会失效
markerOpacity: 0.1,
markerFillOpacity: 0.1,
markerLineOpacity: 0,
// 填充色,使用热力图相近的颜色
markerFill: 'red',
markerType: 'ellipse',
// 这里保持和热力图(对应heatMap的radiu,而且要大一点,这里我是比热力图的大10)
markerWidth: 35,
markerHeight: 35
}
})
mPoint.on('mousemove', function (e) {
e.target.openInfoWindow(e.coordinate)
}).on('mouseout', function (e) {
e.target.closeInfoWindow()
}).setInfoWindow({
content: 'hello world',
title: 'message',
animationDuration: 0,
autoOpenOn: false
})
new maptalks.VectorLayer('pointLayer', mPoint).addTo(this.mapEngine)
// 方法二
// 用其他可交互图形替代,使用几何图形,这个要求是对真个热力图都是一样的弹窗,将整个热力图作为一个整体
// 这里我用的几何图形,有太多限制,不太符合要求,但可以作为一个思路参考,
const geometry = maptalks.GeoJSON.toGeometry(simingAreaData.geoData)
if (geometry) {
geometry.forEach(g => {
g.setSymbol({
// 线色
lineColor: '#f298bc',
// 线宽
// 这里保持和热力图(对应heatMap的radiu,而且要大一点)
lineWidth: 30,
// 不透明度
polygonOpacity: 0,
// 圆角设置
lineJoin: 'round'
})
g.on('mousemove', function (e) {
e.target.openInfoWindow(e.coordinate)
}).on('mouseout', function (e) {
e.target.closeInfoWindow()
}).setInfoWindow({
content: 'hello world',
title: 'message',
animationDuration: 0,
autoOpenOn: false
})
})
}
const options = {
enableAltitude: true,
zIndex: 10
}
new maptalks.VectorLayer('lineLayer', options).addGeometry(geometry).addTo(this.mapEngine)
最初の方法の効果:
2 番目の方法の効果の実証:
3D - three.js
上記のechart以外にも3次元フロントエンド開発フレームワークthree.jsがあり、地図の操作と同期しており、よりシンプルに感じられます。
Maptalks が 3 つを統合した後のドキュメント:マスターの maptalks.three/API.ZH-CN.md · maptalks/maptalks.three · GitHub
npm インストール maptalks.three
// ここでは 128 バージョンをインストールするだけです。他のバージョンではエラーが報告されます
npm インストール [email protected]
ボードを作成する
maptalks はthree.js を統合しており、一部は maptalks 用にカスタマイズされていますが、基本的には 3 と同じです。maptalks.three のドキュメントを見ているだけでは何も進みません。特に強力な効果を作成する必要がある場合は、ぜひ読んでみてください。 3 つの公式ドキュメントです。
drawArea() {
const _t = this
const layer = new ThreeLayer('three')
// 图层三维设置
layer.setOptions({
// 地图移动、缩放、旋转时强制渲染
forceRenderOnMoving: true,
forceRenderOnZooming: true,
forceRenderOnRotating: true,
zIndex: 10
})
layer.prepareToDraw = function(gl, scene) {
_t.create3DPlan2(scene, layer)
}
_t.mapEngine.addLayer(layer)
},
/**
* 创建3D板块
* @param scene
* @param layer
* @returns {ExtrudePolygons}
*/
create3DPlan(scene, layer) {
// 创建直线光
const light = new THREE.DirectionalLight(0xffffff)
light.position.set(0, -10, 10).normalize()
scene.add(light)
// 创建环境光
scene.add(new THREE.AmbientLight(0xffffff, 0.2))
// 创建材质
var material = new THREE.MeshLambertMaterial({
color: cc.defaultPolygonFillColor, opacity: 0.7 })
const list = []
const polygons = maptalks.GeoJSON.toGeometry(geoJson)
polygons.forEach(p => {
// 该方法是旧版本方法,官方推荐使用toExtrudePolygon
var mesh = layer.toExtrudeMesh(p, 400, material)
if (Array.isArray(mesh)) {
scene.add.apply(scene, mesh)
} else {
scene.add(mesh.object3d)
}
// infowindow test
mesh.setInfoWindow({
content: '<br style="color:#f00">中心点:' + p.properties.center + ' </br>行政区划:' + p.properties.adcode + ' </br>父级行政区划:' + p.properties.parent.adcode + '</div>',
title: 'message',
animationDuration: 0,
autoOpenOn: false
})
list.push(mesh)
})
// 批量添加到图层
layer.addMesh(list)
},
create3DPlan2(scene, layer) {
// 创建直线光
const light = new THREE.DirectionalLight(0xffffff)
light.position.set(0, -10, 10).normalize()
scene.add(light)
// 创建环境光
scene.add(new THREE.AmbientLight(0xffffff, 0.2))
// 创建材质
var material = new THREE.MeshLambertMaterial({
color: cc.defaultPolygonFillColor, opacity: 0.7 })
const options = {
altitude: 1,
height: 400,
bottomHeight: 0,
topColor: null,
bottomColor: cc.defaultPolygonFillColor,
interactive: true
}
const list = []
const polygons = maptalks.GeoJSON.toGeometry(geoJson)
polygons.forEach(p => {
// 这里源码中,可以找到toExtrudePolygons,但是创建的得到的MultiExtrudePolygons无法正常添加到scence中,所以这里还是用它推荐的方法
var mesh = layer.toExtrudePolygon(p, options, material)
if (Array.isArray(mesh)) {
scene.add.apply(scene, mesh)
} else {
scene.add(mesh)
}
mesh.setInfoWindow({
title: p.properties.name,
content: '<br style="color:#f00">中心点:' + p.properties.center + ' </br>行政区划:' + p.properties.adcode + ' </br>父级行政区划:' + p.properties.parent.adcode + '</div>',
animationDuration: 0,
autoOpenOn: false
})
list.push(mesh)
})
// 批量添加到图层
layer.addMesh(list)
}
オブジェクトを押し出すには上記の 2 つの方法があります: toExtrudeMesh
,toExtrudePolygon
実際には別の方法があります。ソース コードでは、toExtrudePolygons
これは配列で渡すことができますが、実際の使用ではオブジェクトを取得し、実行するとエラーが報告されます scene.add(mesh)
。結果は 3D オブジェクトではなく、サーフェスになります。
効果は次のとおりです。
これでマップ ブロックが生成されましたが、領域を描画してブロックに分割し、マウス イベントを追加するのと同じように、まだ少し見苦しいです。
ブロック
生成されたオブジェクトに分割線を追加します。先ほどの方法でレイヤーを追加してサーフェスを生成します。サーフェスの透明度を0、ワイヤーフレーム、幅1、マッチョなファンに設定すればOK
drawLine() {
const geometry = maptalks.GeoJSON.toGeometry(geoJson)
if (geometry) {
geometry.forEach(g => {
g.setSymbol({
// 线色
lineColor: '#f298bc',
// 线宽
lineWidth: 1,
// 不透明度
polygonOpacity: 0
})
})
}
new maptalks.VectorLayer("line").addGeometry(geometry).addTo(this.mapEngine)
}
効果を見てみましょう
これを地図に貼り付けます。少し見苦しいので、別の標高を設定しましょう
drawLine() {
const geometry = maptalks.GeoJSON.toGeometry(geoJson)
if (geometry) {
geometry.forEach(g => {
g.setSymbol({
// 线色
lineColor: '#f298bc',
// 线宽
lineWidth: 1,
// 不透明度
polygonOpacity: 0
})
// 添加高程
g.setProperties({
// 高度设置(对应挤压的高度height)
altitude: 400
})
})
}
const options = {
enableAltitude: true,
zIndex: 9
}
new maptalks.VectorLayer("line", options).addGeometry(geometry).addTo(this.mapEngine)
}
効果:
イベント
3 人によって作成されたオブジェクト ドキュメント:マスター Maptalks/maptalks.three GitHub の maptalks.three/API.ZH-CN.md
適応性が非常に優れていることがわかります。
設定方法はエリアプレーンと同じですが、問題があり、先にコードを見てみましょう、先ほどの方法ではon
モニタリングも追加されています。
create3DPlan2(scene, layer) {
// 创建直线光
const light = new THREE.DirectionalLight(0xffffff)
light.position.set(0, -10, 10).normalize()
scene.add(light)
// 创建环境光
scene.add(new THREE.AmbientLight(0xffffff, 0.2))
// 创建材质
var material = new THREE.MeshLambertMaterial({
color: cc.defaultPolygonFillColor, opacity: 0.7 })
const options = {
altitude: 1,
height: 400,
bottomHeight: 0,
topColor: null,
bottomColor: cc.defaultPolygonFillColor,
interactive: true
}
// 定义高亮材质
const highlightmaterial = new THREE.MeshBasicMaterial({
color: '#ff87a4', transparent: true, opacity: 0.8 })
const list = []
const polygons = maptalks.GeoJSON.toGeometry(geoJson)
polygons.forEach(p => {
// 这里源码中,可以找到toExtrudePolygons,但是创建的得到的MultiExtrudePolygons无法正常添加到scence中,所以这里还是用它推荐的方法
var mesh = layer.toExtrudePolygon(p, options, material)
if (Array.isArray(mesh)) {
scene.add.apply(scene, mesh)
} else {
scene.add(mesh)
}
mesh.setInfoWindow({
title: p.properties.name,
content: '<br style="color:#f00">中心点:' + p.properties.center + ' </br>行政区划:' + p.properties.adcode + ' </br>父级行政区划:' + p.properties.parent.adcode + '</div>',
animationDuration: 0,
autoOpenOn: false
})
// 添加鼠标事件
mesh.on('mouseover', function(e) {
e.target.setSymbol(highlightmaterial)
layer.redraw()
}).on('mouseout', function(e) {
e.target.setSymbol(material)
layer.redraw()
})
list.push(mesh)
})
// 批量添加到图层
layer.addMesh(list)
},
ご覧のとおり、イベントに追加しましたがlayer.redraw()
、なぜですか?
公式ドキュメントやサンプルにはこれに関する言及がありません。ベクター グラフィックスのように自動的にレンダリングされると常に思っていましたが、そうではありませんでした。しかも、常に不可解なレンダリングが行われました。後から知ったのですが、オブジェクトを動かすたびに、マップ、またはそのグラフィックスは回転したときにのみ変化します。これで、ベクター レイヤーには移動および回転時にレンダリングを強制する設定があることを思い出しました。これは現在の状況と非常によく似ており、ドキュメントをもう一度見てください。レンダリング メソッドについては言及されていませんが、人々を喜ばせるのは、3 つのレイヤーが から継承され、そのCanvasLayer
すべてのメソッドを備えていることです。この時点で、問題は解決されており、redraw()
3 つのレイヤーを再描画する呼び出しを行うだけです。
1 つの違いは、maptalks.three では
setSymbol
パラメータが渡されたマテリアル オブジェクトであることです。
レンダリングを見てください。
他のプラグインの助けを借りずに 3D を描画します
three.js や echarts を使用せずに、独自のサーフェスを通じて 3D 構造を構築することもできます。3D モデルを使用したことがある場合は、より明確になるかもしれません。3D ソフトウェアでは、サーフェスを押し出すことができるため、多面体を取得できます最も伝統的な方法は、3 次元空間で顔を結合することであり、これはマップトークでも実現できます。
前のコードを少し変更します。
/**
* 再图层上画面
*/
drawArea() {
const options = this.getOptions()
const lowSymbol = {
}
const topSymbol = {
}
Object.assign(lowSymbol, options.symbol)
Object.assign(topSymbol, options.symbol)
const lowOptions = {
zIndex: 9,
symbol: lowSymbol,
properties: {
altitude: 0
}
}
const topOptions = {
zIndex: 11,
symbol: topSymbol,
properties: {
altitude: 400
}
}
const _t = this
const layer = new maptalks.VectorLayer('vector')
// 创建底面
_t.drawAreaPlan(_t.geoJson, layer, lowOptions)
// 高面
const centerCoordinate = _t.drawAreaPlan(_t.geoJson, layer, topOptions, true)
const lineOptions = {
zIndex: 10,
symbol: options.symbol,
properties: {
altitude: 400
}
}
const lineCoordinate = []
if (geoJson.type === 'FeatureCollection') {
geoJson.features.forEach(c => {
lineCoordinate.push(c.geometry.coordinates[0])
})
}
lineCoordinate.forEach(l => {
const line = new maptalks.LineString(l[0], lineOptions).addTo(layer)
})
_t.drawMark(centerCoordinate, layer)
layer.setOptions({
// 启用高度绘制
enableAltitude: true,
altitudeProperty: 'altitude',
// 绘制高度线
drawAltitude: {
// lineWidth: 1,
// lineColor: '#000',
// 高度面的填充色
polygonFill: options.symbol.polygonFill,
// 高度面的透明度
polygonOpacity: options.symbol.polygonOpacity
}
})
layer.addTo(_t.mapEngine)
},
/**
* 根据geojson画区域面
* @param geoJson geoJson数据
* @param layer 需要话的图层
*/
drawAreaPlan(geoJson, layer, options, isActions) {
const geometry = maptalks.GeoJSON.toGeometry(geoJson)
const centerCoordinates = []
if (geometry) {
geometry.forEach(g => {
g.setSymbol(options.symbol)
const gProperties = g.properties
// 避免信息覆盖
gProperties.altitude = options.properties.altitude
g.setProperties(gProperties)
// 设置图形层级
g.setZIndex(options.zIndex)
// 设置信息框
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: '#ffaeb0'
})
}).on('mouseout', function(e) {
e.target.updateSymbol({
polygonFill: 'rgb(135,196,240)'
})
})
if (isActions) {
g.on('click', function(e) {
e.target.openInfoWindow(e.coordinate)
});
}
// 获取中心坐标
centerCoordinates.push(g.properties)
})
}
layer.addGeometry(geometry)
return centerCoordinates
},
// mark
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: 400
}
}).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
},
getOptions() {
return {
zIndex: 10,
symbol: {
// 线色
lineColor: '#ff87a4',
// 线宽
lineWidth: 1,
// 填充色
polygonFill: 'rgb(135,196,240)',
// 不透明度
polygonOpacity: 0.9,
},
properties: {
// 高度设置(不生效,需要设置图层的高度polygonOpacity属性)
altitude: 400
}
}
}
効果は次のとおりです。
ここではアイデアを提供しているだけで、はっきり言ってこの方法の導入効果は 3 つに比べて劣ります。