Skip to content
JavaScript CSS HTML
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
app update Jun 2, 2017
output
test
.gitignore 项目初始化 Mar 30, 2017
LICENSE Initial commit Mar 29, 2017
README.md update May 22, 2017
package.json update Jun 16, 2017
webpack.config.js

README.md

leaflet-viz

Leaflet可视化平台

leaflet是一个开源的前端地图交互类库,比较轻量级,支持移动端。而且有丰富的插件资源可供我们使用。

Echarts是百度开源的前端可视化类库,提供丰富的前端可视化图表,平台中重要的一部分是我们要将leaflet和Echarts结合在一起。

该平台是基于leaflet及其插件搭建的一个方便可用的可视化平台。详细参见[示例Demo]。(https://zrysmt.github.io/demo/leaflet-demo/)

1. 安装与编译

  • 安装
npm install
  • 编译

debug模式

npm run dev

输出

npm run build

2.示例简介

示例地址:https://zrysmt.github.io/demo/leaflet-demo/ 示例包含最基本的GIS功能和可视化Demo

3.基础的GIS功能

  • 拖放、全图、定位、打印出图片、打印出PDF功能。
  • 地图缩放、比例尺显示功能
  • 测量面积和距离功能
  • 图层切换,提供丰富的图层切换
  • 地图搜索功能
  • 基础绘图功能

4.可视化示例

  • 热力图

引入:

import '../common/leaflet-plugin/HeatLayer.js';

使用:

var heat = L.heatLayer([
    [50.5, 30.5, 0.2], // lat, lng, intensity
    [50.6, 30.4, 0.5],
...
], {radius: 25}).addTo(map)

效果图

  • 结合Echarts
let overlay = new L.echartsLayer3(map, echarts);
let chartsContainer = overlay.getEchartsContainer();
let myChart = overlay.initECharts(chartsContainer);
window.onresize = myChart.onresize;
console.log("chartsContainer:", chartsContainer);
if (type == "qianxi") {
    overlay.setOption(ecOption);
} else if (type == "scatter") {
    overlay.setOption(scatterOption);
}

迁徙图(选择了高德卫星底图) 散点图(选择了Geoq午夜蓝底图)

  • DivIcon结合Echarts

这个方案不太适合大数据量的渲染。 我写了两个简单的功能函数,一个用来渲染可视化图(echartsIcon),一个专门用来渲染图例(echartsLegend)。 效果图(底图选择了为google底图)

import echartsIcon from '../common/plugin/echartsIcon.js'; //echartsLegend
import echartsLegend from '../common/plugin/echartsLegend.js'; //echartsLegend
let option = {
    tooltip: {
        trigger: 'item',
        formatter: "{a} <br/>{b} : {c} ({d}%)"
    },
    series: [{
        name: '访问来源',
        type: 'pie',
        radius: '55%',
        center: ['50%', '50%'],
        label: {
            normal: {
                show: false
            },
            emphasis: {
                show: false
            }
        },
        lableLine: {
            normal: {
                show: false
            },
            emphasis: {
                show: false
            }
        },
        itemStyle: {
            emphasis: {
                shadowBlur: 10,
                shadowOffsetX: 0,
                shadowColor: 'rgba(0, 0, 0, 0.5)'
            }
        }
    }]
};
//经纬度不能相同
let latlngs = [
    [30, 104],
    [31, 110],
    [34, 120]
];
option.datas = [
    [
        { value: 335, name: '直接访问' },
        { value: 310, name: '邮件营销' },
        { value: 234, name: '联盟广告' },
        { value: 135, name: '视频广告' },
        { value: 1548, name: '搜索引擎' }
    ],
    [
        { value: 345, name: '直接访问' },
        { value: 410, name: '邮件营销' },
        { value: 244, name: '联盟广告' },
        { value: 145, name: '视频广告' },
        { value: 548, name: '搜索引擎' }
    ],
    [
        { value: 445, name: '直接访问' },
        { value: 410, name: '邮件营销' },
        { value: 244, name: '联盟广告' },
        { value: 145, name: '视频广告' },
        { value: 148, name: '搜索引擎' }
    ],
];
echartsIcon(map, latlngs, option);
//图例
let legendOption = {
    orient: 'vertical',
    left: 'left',
    width: "90px",
    height: "140px",
    data: ['直接访问', '邮件营销', '联盟广告', '视频广告', '搜索引擎']
};
echartsLegend(map, legendOption);
  • leaflet-dvf

leaflet-dvf是一个基于leaflet的数据可视化框架,提供有比较多的可视化图形。

import "../common/leaflet-plugin/Leaflet.dvf/css/dvf.css";
import "leaflet-dvf";

地震图: 收入水平图: 需要的数据

import earthquakesData from '../data/earthquakes.json';
import countryData from '../data/countryData.js';

地震图代码:

 let lastLayer;

 let eqfeed_callback = function(data) {

     // Initialize framework linear functions for mapping earthquake data properties to Leaflet style properties
     // Color scale - green to red using the basic HSLHueFunction
     let magnitudeColorFunction = new L.HSLHueFunction(new L.Point(0, 90), new L.Point(10, 0), { outputSaturation: '100%', outputLuminosity: '25%', postProcess: null });
     let magnitudeFillColorFunction = new L.HSLHueFunction(new L.Point(0, 90), new L.Point(10, 0), { outputSaturation: '100%', outputLuminosity: '50%', postProcess: null });
     let magnitudeRadiusFunction = new L.LinearFunction(new L.Point(0, 10), new L.Point(10, 30), { postProcess: null });

     let now = Math.round((new Date()).getTime());
     let start = now - 86400000;

     // Initialize a linear function to map earthquake time to opacity
     let timeOpacityFunction = new L.LinearFunction(new L.Point(start, 0.3), new L.Point(now, 1));
     let fontSizeFunction = new L.LinearFunction(new L.Point(0, 8), new L.Point(10, 24));

     let textFunction = function(value) {
         return {
             text: value,
             style: {
                 'font-size': fontSizeFunction.evaluate(value)
             }
         };
     };

     // Setup a new data layer
     let dataLayer = new L.DataLayer(data, {
         recordsField: 'features',
         latitudeField: 'geometry.coordinates.1',
         longitudeField: 'geometry.coordinates.0',
         locationMode: L.LocationModes.LATLNG,
         displayOptions: {
             'properties.mag': {
                 displayName: '震级',
                 color: magnitudeColorFunction,
                 fillColor: magnitudeFillColorFunction,
                 radius: magnitudeRadiusFunction,
                 text: textFunction
             },
             'properties.time': {
                 displayName: '时间',
                 opacity: timeOpacityFunction,
                 fillOpacity: timeOpacityFunction,
                 displayText: function(value) {
                     return moment.unix(value / 1000).format('MM/DD/YY HH:mm');
                 }
             }
         },
         layerOptions: {
             numberOfSides: 4,
             radius: 10,
             weight: 1,
             color: '#000',
             opacity: 0.2,
             stroke: true,
             fillOpacity: 0.7,
             dropShadow: true,
             gradient: true
         },
         tooltipOptions: {
             iconSize: new L.Point(90, 90), //hover框大小
             iconAnchor: new L.Point(-4, 76)
         },
         onEachRecord: function(layer, record, location) {
             let $html = $(L.HTMLUtils.buildTable(record));

             layer.bindPopup($html.wrap('<div/>').parent().html(), {
                 minWidth: 400,
                 maxWidth: 400
             });
         }
     });

     // Add the data layer to the map
     map.addLayer(dataLayer);

     lastLayer = dataLayer;
 };

 this.mapSetting();

 if (lastLayer) {
     map.removeLayer(lastLayer);
 }

 console.log("earthquakesData:", earthquakesData);
 eqfeed_callback(earthquakesData)

收入水平图代码:

let incomeLevelTypes = ['OEC', 'NOC', 'UMC', 'MIC', 'LMC', 'LIC', 'HPC'];
let valueArray = [{ "id": "HIC", "value": "高收入(HIC)" },
    { "id": "HPC", "value": "收入严重不足(HIPC)" }, { "id": "INX", "value": "未分类(INX)" },
    { "id": "LIC", "value": "低收入(LIC)" }, { "id": "LMC", "value": "中等偏下(LMC)" },
    { "id": "LMY", "value": "中低等收入" }, { "id": "MIC", "value": "中等收入(MIC)" },
    { "id": "NOC", "value": "高收入:nonOECD(NOC)" }, { "id": "OEC", "value": "高收入: OECD(OEC)" },
    { "id": "UMC", "value": "中等偏上(UMC)" }
];
let getMap = function(valueArray) {
    let map = {};
    for (let index = 0; index < valueArray.length; ++index) {
        let value = valueArray[index];

        map[value['id']] = value['value'];
    }
    return map;
};

let valueMap = getMap(valueArray);

let incomeLevelToText = function(value) {
    return valueMap[incomeLevelTypes[value]];
};

let colorFunction1 = new L.HSLLuminosityFunction(new L.Point(0, 0.2), new L.Point(incomeLevelTypes.length - 1, 0.75), { outputHue: 0, outputLuminosity: '100%' });
let fillColorFunction1 = new L.HSLLuminosityFunction(new L.Point(0, 0.5), new L.Point(incomeLevelTypes.length - 1, 1), { outputHue: 0, outputLuminosity: '100%' });

let styles = new L.StylesBuilder(incomeLevelTypes, {
    displayName: incomeLevelToText,
    color: colorFunction1,
    fillColor: fillColorFunction1
});

let options = {
    recordsField: '1',
    locationMode: L.LocationModes.COUNTRY,
    codeField: 'id',
    displayOptions: {
        'incomeLevel.id': {
            displayName: '收入水平',
            styles: styles.getStyles()
        }
    },
    layerOptions: {
        fillOpacity: 0.7,
        opacity: 1,
        weight: 1
    },
    tooltipOptions: {
        iconSize: new L.Point(100, 65),
        iconAnchor: new L.Point(-5, 65)
    },

    onEachRecord: function(layer, record) {
        let $html = $(L.HTMLUtils.buildTable(record));

        layer.bindPopup($html.wrap('<div/>').parent().html(), {
            maxWidth: 400,
            minWidth: 400
        });
    }
};

let incomeLayer = new L.ChoroplethDataLayer(incomeLevels, options);

let legendControl = new L.Control.Legend();

legendControl.addTo(map);
map.addLayer(incomeLayer);

5.GeoJSON

假设我们需要自己做个地图,然后加载到平台中。这个时候可以使用我们提供了编辑栏,编辑后会得到坐标,把坐标转成GeoJSON数据存到数据库中,下次加载的时候解析即可。 还可以使用GIS工具,做成GeoJSON的数据,然后使用下面的方法加载,详细说明的传送门在这,当然这包括MapServer的东西,我们将在第六部分有简要说明。 示例图: 引入数据:

import { json_china } from '../data/china.js';
function pop_0(feature, layer) {//部分有省略
    let popupContent = '<table>\
                    <tr>\
                        <td colspan="2">面积:' + (feature.properties['AREA'] !== null ? Autolinker.link(String(feature.properties['AREA'])) : '') + '</td>\
                    </tr>\
                    <tr>\
                        <td colspan="2">周长:' + (feature.properties['PERIMETER'] !== null ? Autolinker.link(String(feature.properties['PERIMETER'])) : '') + '</td>\
                    </tr>\
                    <tr>\
                        <td colspan="2">名称:' + (feature.properties['NAME'] !== null ? Autolinker.link(String(feature.properties['NAME'])) : '') + '</td>\
                    </tr>\
                </table>';
    layer.bindPopup(popupContent);
}

function style_0() {
    return {
        pane: 'pane_0',
        opacity: 1,
        color: 'rgba(0,0,0,0.494117647059)',
        dashArray: '',
        lineCap: 'butt',
        lineJoin: 'miter',
        weight: 1.0,
        fillOpacity: 1,
        fillColor: 'rgba(64,98,210,0.494117647059)',
    }
}
map.createPane('pane_0');
map.getPane('pane_0').style.zIndex = 400;
map.getPane('pane_0').style['mix-blend-mode'] = 'normal';
let layer_0 = new L.geoJson(json_china, {
    attribution: '<a href=""></a>',
    pane: 'pane_0',
    onEachFeature: pop_0,
    style: style_0
});

6.MapServer

再进一步,如果我们想要自己的地图服务器,这个时候就需要使用到了MapServer,具体的说明见传送门

有个示例在test文件夹下:leaflet-mapserver.html。注意这个是需要配置好服务器的,在这里直接预览是不能成功的。 示例得到我们的世界地图的地图服务器。

其实leaflet和D3等开源库结合起来,能得到很多丰富多彩的可视化图,姑且留下点神秘,这里就不再探究两者结合的神奇魅力了。

You can’t perform that action at this time.