使用leaflet插件绘制手绘地图h5

手绘地图简介:

手绘地图顾名思义就是手工绘制的地图,比普通的地图更有观赏性和生动性,通过把特定的地点绘制出来,兼具实用和纪念性,同时更加具有可看性。

前期开发准备:

手绘地图绘制。

需要一个专门做手绘地图的团队。

比如图片如下:

地图瓦片的切割

使用切片工具对图片进行切片之后会对每个瓦片进行编号,一般会有三个变量XYZ,以腾讯地图为例左上角为原点,x表示列号,y表示行号,z表示缩放等级。

注意:手绘地图的图片一般都很大,会有十几M,需要对手绘地图进行瓦片切割,加载图片会很慢,页面比较卡,影响性能。

地图瓦片的切割工具

可以使用ps软件。(网站找了很多种办法,有的要安装软件等,最简单推荐使用ps自带的zoomify瓦片切割)。

ps软件切割瓦片教程:
  • 用法:文件-导出-Zoomify

  • 地图瓦片尺寸:切割选择配置浏览器尺寸256*256。

  • 自动生成文件包:

leaflet组件下载。

  1. 去官网下载组件文件包:

  • leaflet.js- 这是缩小后的 Leaflet JavaScript 代码。

  • leaflet-src.js- 这是可读的、未压缩的 Leaflet JavaScript,有时有助于调试。

  • leaflet.css- 这是 Leaflet 的样式表。

  • images- 这是一个文件夹,其中包含 引用的图像leaflet.css。它必须与leaflet.css.

  1. 手绘地图需要用到瓦片Zoomify,还需下载L.TileLayer.Zoomify.js组件,官网插件有相关参考demo:https://cmulders.github.io/Leaflet.Zoomify/examples/hubble-image.html

  1. 如果需要全屏缩放组件,还需下载:

Leaflet.fullscreen.min.js

leaflet.fullscreen.css

参考demo: https://leaflet.github.io/Leaflet.fullscreen/

开发代码

容器id:

<div class="mapContainer" id="map"></div>

组件相关引入:

<link rel="stylesheet" href="map/leaflet.css"/>
<link href="map/leaflet.fullscreen.css" rel="stylesheet"/>
 <script src="map/leaflet.js"></script>
<script src="map/L.TileLayer.Zoomify.js"></script>
<script src="map/Leaflet.fullscreen.min.js"></script>

组件初始化:

var map = L.map('map', {
    maxZoom: 7,/**这边7为最大放大到原图大小,可以自己设置修改*/
    minZoom: 1,
    zoomSnap: 0,/**强制地图的缩放级别始终是该缩放级别的倍数*/
    zoomDelta: 1,/**地图缩放级别的变化量 */
    zoomControl: false,/*缩放控件是否显示*/
    doubleClickZoom: false, /* 禁用双击放大*/
    attributionControl: false, /* 移除右下角leaflet标识*/
    crs: L.CRS.Simple,/*一个简单的 CRS,可以将经纬度直接映射到 x 和 y。可用于平面地图(如手绘地图)*/
    closePopupOnClick:false, /*弹窗提示一直显示*/
    tap: false /* https://github.com/Leaflet/Leaflet/issues/7255*/
  });
  /***宽高按图片宽高 */
  var layer = L.tileLayer.zoomify('./map/mapbg/{g}/{z}-{x}-{y}.jpg', {
    width: 19095, /**手绘地图原始图片的宽度*/
    height: 6890/**手绘地图原始图片的高度*/
  }).addTo(map);
  map.setMaxBounds(layer.getBounds());/*将地图视图限制在给定范围内*/
  map.fitBounds(layer.getBounds());/*设置包含给定地理边界的地图视图,并具有可能的最大缩放级别*/

一个简单的手绘地图就形成了。

自定义多边形遮罩层绘制

使用svg在地图上绘制遮罩层svgOverlay。

var mapAreaPathArr=[{
    "indexId":0,
    "fillColor":"#0072ff",
    "textBounds":[-40.1875,106.65625 ],
    "centerBounds":[-40.25,110.5625 ],
    "text":"A区",
    "strokeWidth":2,
    "path":'M1369.3,576.6c-0.5-0.2-0.9-0.5-1.4-0.6c-12.6-3-21.6-10.6-28-21.6c-1.8-3-2.6-6.3-2.4-9.9   c0.4-5.8-1.6-10.8-6-14.8c-2.2-2-4.3-4-6.6-5.9c-3.1-2.7-6.1-5.4-8.9-8.4c-3.2-3.4-5.9-7.1-7.8-11.3c-2.8-6.1-3.2-12.3-1.2-18.7   c1.4-4.6,3.7-8.7,6.4-12.6c3.3-4.8,7-9.1,10.9-13.4c7.6-8.2,15.4-16.1,23.5-23.7c5-4.7,10.4-9.1,16.4-12.7   c4.8-2.9,9.9-5.3,15.5-6.4c6.4-1.2,12.5-0.2,18.1,3.2c2.9,1.7,5.4,3.9,7.6,6.4c1.5,1.6,3,3.2,4.8,4.4c4.7,3.3,9.6,3.9,14.9,1.7   c2.1-0.9,4.1-2.1,5.9-3.5c2-1.6,4-3.2,5.8-5c2.9-2.9,5.8-5.8,8.5-8.8c7-7.7,9.9-16.8,9.5-27.1c-0.3-7.4-2-14.4-4.6-21.3   c-1.9-5.3-4.2-10.5-6.2-15.7c-1.1-2.7-2-5.5-3-8.3c-0.6-1.6-0.7-3.3-0.5-4.9c0.2-2.5,1.4-4.3,3.6-5.5c1.6-0.8,3.3-1.2,5.2-1.3   c3.7-0.2,7.4,0.1,11,1.1c34.2,9,68,18.9,100.9,31.9c13.8,5.4,27.3,11.5,40.6,18.1c4.9,2.4,9.9,4.9,14.8,7.4c2,1,4,2.2,6,3.5   c1.6,1,3.1,2.3,4.3,3.8c4.5,5.1,4.9,11,1.1,16.5c-2.3,3.4-4.6,6.8-6.7,10.3c-11.2,18.9-19.8,39-26.3,59.9c-0.7,2.2-1.4,4.4-2.1,6.6   c-1,2.9-2.5,5.6-4.4,8.1c-4,5.3-9.4,8.2-15.9,8.9c-6.7,0.7-13.3-0.3-19.7-2c-6.6-1.7-12.9-4-19.4-6.1c-4.2-1.4-8.5-2.8-12.9-3.7   c-5.1-1.1-10.1-0.9-15,1.2c-7.2,3-14.1,6.7-20.8,10.6c-17.6,10.2-34.5,21.5-51.2,33.2c-13.1,9.2-26,18.6-38.5,28.7   c-4.4,3.6-9.3,6.2-15,7.4c-0.3,0.1-0.6,0.4-0.9,0.6C1376,576.6,1372.6,576.6,1369.3,576.6z',
    "arealnglat":[[-32.1875,107.9375 ],[-36.9375,123.5625],[-45.375,120],[-45.3125,114],[-51.5625,103.625],[-43.4375,96.25],[-37.4375,103.25],[-32.1875,107.75]]
  },{},{}];
function setMapSvgHtml(map,Data){
  var svgElement = document.createElementNS("http://www.w3.org/2000/svg", "svg");
      svgElement.setAttribute('xmlns', "http://www.w3.org/2000/svg");
      svgElement.setAttribute('viewBox', "0 0 1987.5 586.5");
      svgElement.setAttribute('class', "mapAreaSvg");
      var html='';
      var len=Data.length;
      for(var i=0;i<len;i++){
        html+='<path d="'+Data[i].path+'" style="fill:'+Data[i].fillColor+'; stroke:#fed07f;stroke-width:'+Data[i].strokeWidth+'"/>';
        var polyonOwn= L.polygon(Data[i].arealnglat, {color: 'transparent',"indexId":i}).addTo(map);
        polygonLayerArr.push(polyonOwn);
      }
      svgElement.innerHTML=html;
      polygonSvgLayer=L.svgOverlay(svgElement, [[-2,-2],[-55.75,150.25]],{opacity:0.7}).addTo(map);
}

遮罩层区域文字标注

function setMapAreaTextHtml(map,el){
  var myIcon = L.divIcon({
      html: el.text,
      className: 'mapAreaText',
      iconSize:0
  });
  var areaTextlayer=L.marker(el.textBounds, { icon: myIcon,indexId:el.indexId}).addTo(map);
  mapAreaTextArr.push(areaTextlayer);
};

全屏组件初始化:

注意:

全屏控制----如果地图有用到svg遮罩层,得要放在最后一步执行完成后初始化,不然苹果13会影响遮罩层显示,或者事件点击

var fsControl = L.control.fullscreen({
    position: 'topright'
  });
  map.addControl(fsControl);

附上地图常用的函数判断

判断点是否多边形内

/**
 * 判断点是否多边形内
 * @param {Point} point 点对象
 * @param {Polyline} polygon 多边形对象
 * @returns {Boolean} 点在多边形内返回true,否则返回false
 */
function isPointInArea(point, pts) {

  // 下述代码来源:http:// paulbourke.net/geometry/insidepoly/,进行了部分修改
  // 基本思想是利用射线法,计算射线与多边形各边的交点,如果是偶数,则点在多边形外,否则
  // 在多边形内。还会考虑一些特殊情况,如点在多边形顶点上,点在多边形边上等特殊情况。
  const N = pts.length
  const boundOrVertex = true // 如果点位于多边形的顶点或边上,也算做点在多边形内,直接返回true
  let intersectCount = 0// cross points count of x
  const precision = 2e-10 // 浮点类型计算时候与0比较时候的容差
  let p1, p2// neighbour bound vertices
  const p = point // 测试点

  p1 = pts[0]// left vertex
  // console.log(pts,N)
  for (let i = 1; i <= N; ++i) {
    // check all rays
    if (p.equals(p1)) {
      return boundOrVertex// p is an vertex
    }

    p2 = pts[i % N]
    // right vertex
    if (p.lat < Math.min(p1.lat, p2.lat) || p.lat > Math.max(p1.lat, p2.lat)) {
      // ray is outside of our interests
      p1 = p2
      continue
      // next ray left point
    }

    if (p.lat > Math.min(p1.lat, p2.lat) && p.lat < Math.max(p1.lat, p2.lat)) {
      // ray is crossing over by the algorithm (common part of)
      if (p.lng <= Math.max(p1.lng, p2.lng)) {
        // x is before of ray
        if (p1.lat === p2.lat && p.lng >= Math.min(p1.lng, p2.lng)) {
          // overlies on a horizontal ray
          return boundOrVertex
        }

        if (p1.lng === p2.lng) {
          // ray is vertical
          if (p1.lng === p.lng) {
            // overlies on a vertical ray
            return boundOrVertex
          } else {
            // before ray
            ++intersectCount
          }
        } else {
          // cross point on the left side
          const xinters = (p.lat - p1.lat) * (p2.lng - p1.lng) / (p2.lat - p1.lat) + p1.lng
          // cross point of lng
          if (Math.abs(p.lng - xinters) < precision) {
            // overlies on a ray
            return boundOrVertex
          }

          if (p.lng < xinters) {
            // before ray
            ++intersectCount
          }
        }
      }
    } else {
      // special case when ray is crossing through the vertex
      if (p.lat === p2.lat && p.lng <= p2.lng) {
        // p crossing over p2
        const p3 = pts[(i + 1) % N]
        //  next vertex
        if (p.lat >= Math.min(p1.lat, p3.lat) && p.lat <= Math.max(p1.lat, p3.lat)) {
          // p.lat lies between p1.lat & p3.lat
          ++intersectCount
        } else {
          intersectCount += 2
        }
      }
    }
    p1 = p2
    //  next ray left point
  }

  if (intersectCount % 2 === 0) {
    //  偶数在多边形外
    return false
  } else {
    //  奇数在多边形内
    return true
  }
}

判断点是否在矩形内

/**
 * 判断点是否在矩形内
 * @param {Point} point 点对象
 * @param {Bounds} bounds 矩形边界对象
 * @returns {Boolean} 点在矩形内返回true,否则返回false
 */
function isPointInRect(point, bounds) {
  const sw = bounds.getSouthWest()
  // 西南脚点
  const ne = bounds.getNorthEast()
  // 东北脚点
  return (point.lng >= sw.lng && point.lng <= ne.lng && point.lat >= sw.lat && point.lat <= ne.lat)
}

  • 2
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值