使用开源地图组件leafletwx实现可无限放大的地图
发布于 2 年前 作者 zengyong 1656 次浏览 来自 分享

leafletwx是基于leaflet,使用微信原生组件开发的一套开源地图组件,目标是替换小程序的原生map组件,项目开源地址:leatletwx。>

本示例开源地址在leafletwx的qqmap_infinite页面。>
本示例使用的其他组件:> 1.本示例的主要功能来自于leaflet插件Leaflet.TileLayer.Fallback

背景及原理:

国内地图瓦片服务商提供的放大级数一般在0-18级,例如QQ地图、百度地图、高德地图、天地图等等,建筑物较小而marker较多时,会导致marker过于拥挤,而无法通过继续放大地图进行分散。

leaflet插件Leaflet.TileLayer.Fallback的主要思想是当地图放到到瓦片服务器的最大级别时,采用放大最大一级的图片大小来实现继续放大地图的效果。例如:QQ地图,原始瓦片图片的大小为256*256像素,最大18级。当放大到第19级时,将第18级图片放大到512*512;当放大到第20级时,将18级图片放大到1024*1024。

具体实现:

qqlayers.infinite.js

var L = require('../../components/zhgeo/leafletwx')

L.TileLayer.TXMapTileLayer = L.TileLayer.extend({
  options: {
    minNativeZoom0
  },

  initializefunction (urlTemplate, options{
    L.TileLayer.prototype.initialize.call(this, urlTemplate, options);
  },

  createTilefunction (coords, done{
    var tile = L.TileLayer.prototype.createTile.call(this, coords, done);
    tile._originalCoords = coords;
    tile._originalSrc = tile.src;
    return tile;
  },

  _clampZoomfunction (zoom{
    var options = this.options;
    if (undefined !== options.minNativeZoom && zoom < options.minNativeZoom) {
      return options.minNativeZoom;
    }  
    return zoom;
  },

  _addTilefunction (coords{
    var tile = L.TileLayer.prototype._addTile.call(this, coords);
    var options = this.options;
    var zoom = coords.z;
    if (undefined !== options.maxNativeZoom && options.maxNativeZoom < zoom) {
      return this._tileOutError(tile);
    }
    return tile;
  },

  _createCurrentCoordsfunction (originalCoords{
    var currentCoords = this._wrapCoords(originalCoords);
    currentCoords.fallback = true;
    return currentCoords;
  },

  _tileOutErrorfunction (tile{
    var options = this.options;
    var layer = this// `this` is bound to the Tile Layer in L.TileLayer.prototype.createTile.
        originalCoords = tile._originalCoords,
        currentCoords = tile._currentCoords = tile._currentCoords || layer._createCurrentCoords(originalCoords),
        fallbackZoom = options.maxNativeZoom,
        scale = Math.pow(2, (originalCoords.z - options.maxNativeZoom)),
        tileSize = layer.getTileSize(),
        newUrl, top, left;

    // Modify tilePoint for replacement img.
    currentCoords.z = fallbackZoom;
    currentCoords.x = Math.floor(currentCoords.x / scale);
    currentCoords.y = Math.floor(currentCoords.y / scale);

    // Generate new src path.
    newUrl = layer.getTileUrl(currentCoords);

    // Zoom replacement img.
    tile.width = (tileSize.x * scale) + 'px';
    tile.height = (tileSize.y * scale) + 'px';

    // Compute margins to adjust position.
    top = (originalCoords.y - currentCoords.y * scale) * tileSize.y;
    tile.top = (tile.top - top);
    left = (originalCoords.x - currentCoords.x * scale) * tileSize.x;
    tile.left = (tile.left - left);

    // Crop (clip) image.
    // `clip` is deprecated, but browsers support for `clip-path: inset()` is far behind.
    // http://caniuse.com/#feat=css-clip-path
    // tile.clip = 'rect(' + top + 'px ' + (left + tileSize.x) + 'px ' + (top + tileSize.y) + 'px ' + left + 'px)';
    tile.src = newUrl;
    return tile;
  },

  getTileUrlfunction (tilePoint{
      var urlArgs,
          getUrlArgs = this.options.getUrlArgs;

      if (getUrlArgs) {
          var urlArgs = getUrlArgs(tilePoint);
      } else {
          urlArgs = {
              z: tilePoint.z,
              x: tilePoint.x,
              y: tilePoint.y,
              sthis._getSubdomain(tilePoint)
          };
      }
      if (this._map && !this._map.options.crs.infinite) {
        var invertedY = this._globalTileRange.max.y - tilePoint.y;
        if (this.options.tms) {
          urlArgs['y'] = invertedY;
        }
        urlArgs['-y'] = invertedY;
      }
      return L.Util.template(this._url, L.extend(urlArgs, this.options));
  }
});

L.tileLayer.txMapTileLayer = function (type, options{
  var getUrlArgs, url, subdomain = '012';
  if (type == 'Normal') { //地图带路线轮廓 有轮廓/有路线
      url = 'http://rt1.map.gtimg.com/realtimerender/?z={z}&x={x}&y={y}&type=vector&style=1&v=1.1.1'//ok //普通地图
      getUrlArgs = function (tilePoint{
          return {
              //地图
              z: tilePoint.z,
              x: tilePoint.x,
              yMath.pow(2, tilePoint.z) - 1 - tilePoint.y
          };
      }
  } else if (type == 'Satellite') { //卫星图,无轮廓/无路线
      url = 'http://p3.map.gtimg.com/sateTiles/{z}/{x}/{y}/{G}_{H}.jpg?version=229';
      getUrlArgs = function (tilePoint{
          return {
              //卫星图
              z: tilePoint.z,
              xMath.floor(tilePoint.x / 16),
              yMath.floor((Math.pow(2, tilePoint.z) - 1 - tilePoint.y) / 16),
              G: tilePoint.x,
              HMath.pow(2, tilePoint.z) - 1 - tilePoint.y
          };
      }
  }  else if (type=='Landform') {//地形图,有轮廓/有路线
      url = 'http://rt1.map.gtimg.com/tile?z={z}&x={x}&y={y}&type=vector&styleid=3&version=263' //ok //地形地图
      getUrlArgs = function (tilePoint{
          return {
              //地形图
              z: tilePoint.z,
              x: tilePoint.x,
              yMath.pow(2, tilePoint.z) - 1 - tilePoint.y
          };
      }
  }
  options = L.extend(options, {
      subdomain:'012',
      getUrlArgs:getUrlArgs,
  })
  return new L.TileLayer.TXMapTileLayer(url, options);
};

qqmap_infinite.js

// pages/qqmap/qqmap.js
var L = require('../../components/zhgeo/leafletwx')
require('./qqlayers.infinite')
import {createMap} from '../../components/zhgeo/base.map'
import {DefaultIcons} from '../../components/zhgeo/config.js';
const defaultIcons = new DefaultIcons();

Page({
  onLoad(options) {
    const container = this.selectComponent('#qq-leafletwx')
    let min_zoom = 17
    let max_zoom = 20
    createMap(container,  {
      }, function(map{
      console.log(map)
      var Normal = L.tileLayer.txMapTileLayer("Normal", {        
        minNativeZoom3,   // 瓦片服务器最小级别
        maxNativeZoom18,  // 瓦片服务器最大级别
        minZoom: min_zoom, // 用户地图最小级别
        maxZoom: max_zoom, // 用户地图最大级别
      }); //调用 腾讯地图
      Normal.addTo(map)
      map.setView([31.294516,120.625814], 18);
      // 限定地图范围
      var corner1 = L.latLng(31.29571,120.623941),
      corner2 = L.latLng(31.289586,120.63455),
      bounds = L.latLngBounds(corner1, corner2);
      map.setMaxBounds(bounds);

      // 添加marker
      let m = L.marker([31.2948516,120.625814], {
        src: defaultIcons.locationNow,
        width32,
        height32,
        showInCenterfalse,
      }).addTo(map);
      // 删除marer
      // map.removeLayer( m );

      // popup      
      m.bindPopup("这是个弹框", {
        width60,
      });

      // 添加路线      
      let l = L.polyline([[31.293516,120.625814], [31.294516,120.625214], [31.294516,120.626114]], {color'#ff0000'weight3}).addTo(map);
      // 删除线路
      // map.removeLayer( l );

      // 添加区域
      let y = L.polygon([[31.2930516,120.625814], [31.2940516,120.625214], [31.2940516,120.626114]], {color'#ff0000'weight4}).addTo(map);
      // 删除区域
      // map.removeLayer( y );
      
      m = L.marker([31.2944516,120.625814], {
        src: defaultIcons.location,
        width32,
        height32,
        title'沧浪亭',
        showFootertrue,
        footerWidth60,
        footerPosition1,  // 0:左 1:右 2:上 3:下
        left_badge'左'//  为空时不显示
        right_badge'右',  // 为空时不显示
        showInCentertrue// ture: 图标中心在POI中心 false:图标下边缘中心位于POI中心
      }).addTo(map);
      // 删除线路
      // map.removeLayer( l );
    });
  },
})

效果展示(QQ地图第19级):

回到顶部