Skip to content

lby0102/leaflet-viz

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

42 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

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等开源库结合起来,能得到很多丰富多彩的可视化图,姑且留下点神秘,这里就不再探究两者结合的神奇魅力了。

Releases

No releases published

Packages

No packages published

Languages

  • JavaScript 98.8%
  • CSS 1.1%
  • HTML 0.1%