Leaflet【六】绘制交互图形、测量、经纬度展示

本文主要探讨了如何利用leaflet-draw插件在地图上绘制图形,以及通过leaflet-measure测量距离和面积,并将经纬度绘制到地图上。首先,我们使用leaflet-draw插件,该插件提供了一种简单而直观的方式来绘制各种形状(如点、线、多边形等)到地图上。然后,我们利用leaflet-measure插件,该插件可以测量地图上任意两点之间的距离,以及任意多边形的面积。最后,我们将经纬度数据绘制到地图上,以便于进行地理位置分析和可视化。这种方法为地理信息的收集、分析和可视化提供了一种有效的工具。

绘制图形

npm i leaflet-draw

简单使用

import '@luomus/leaflet-draw/dist/leaflet.draw';
import '@luomus/leaflet-draw/dist/leaflet.draw.css';

const drawnItems = new L.FeatureGroup();
map.addLayer(drawnItems);
const drawControl = new L.Control.Draw({});
map.addControl(drawControl);

配置项说明

在L.Control.Draw当中可以添加一下配置项

配置项说明
position所处位置,取值:
draw绘制时配置
edit编辑时配置

其中在draw当中可以对点线面矩形圆灯进行对应的配置,部分配置如下:

其中对于这些配置在源码当中 L.Draw.Polyline 定义了对应的这些对象,所有的配置在option当中,下面只是稍微列举了一部分,源码位置在:node_modules/@luomus/leaflet-draw/dist/leaflet.draw-src.js

const drawControl = new L.Control.Draw({
  // 位置
  position: 'topright',
  // 绘制时候的配置
  draw: {
    polyline: {
      shapeOptions: {
        stroke: true,
        color: '#3388ff',
        weight: 4,
        opacity: 0.5,
        fill: false,
        clickable: true
      }
    },
    polygon: {
      allowIntersection: false,
      drawError: {
        color: '#f40',
        message: '请点击别的位置的点'
      },
      shapeOptions: {
        stroke: true,
        color: '#3388ff',
        weight: 4,
        opacity: 0.5,
        fill: false,
        fillColor: null,
        fillOpacity: 0.2,
        clickable: true
      }
    },
    circle: {},
    rectangle: {
      shapeOptions: {
        clickable: false
      }
    },
    marker: {
      // icon: new MyCustomMarker()
    }
  },
  // 编辑
  edit: {
    featureGroup: drawnItems
  }
});

同样的在插件当中还提供了一个修改绘制配置的方法setDrawingOptions,传递的是一个对象,和draw当中配置的对象是一样的。

drawControl.setDrawingOptions({
  rectangle: {
    shapeOptions: {
      color: '#0000FF'
    }
  }
});

中英文转换

在上面初始化了一个绘制对象之后,会发现展示的内容都是英文的,这对我们国内使用不是很方便,所以将其改为中文。这里直接把所有的都给替换掉了,将这段插入代码内即可。是因为在插件源码当中也是定义了一个L.drawLocal = {} 然后他里面都是英文,我们在外面重新定义的会直接取进行一个覆盖。

L.drawLocal = {
  draw: {
    toolbar: {
      // #TODO: this should be reorganized where actions are nested in actions
      // ex: actions.undo  or actions.cancel
      actions: {
        title: '取消绘图',//'Cancel drawing',
        text: '取消'//'Cancel'
      },
      finish: {
        title: '完成绘图',//'Finish drawing',
        text: '完成'
      },
      undo: {
        title: '删除最后绘制的点',//'Delete last point drawn',
        text: '撤销'//'Delete last point'
      },
      buttons: {
        polyline: '绘制一个多段线',//'Draw a polyline',
        polygon: '绘制一个多边形',//'Draw a polygon',
        rectangle: '绘制一个矩形',//'Draw a rectangle',
        circle: '绘制一个圆',//'Draw a circle',
        marker: '绘制一个标记',//'Draw a marker',
        circlemarker: '绘制一个圆形标记'//'Draw a circlemarker'
      }
    },
    handlers: {
      circle: {
        tooltip: {
          start: '单击并拖动以绘制圆'//'Click and drag to draw circle.'
        },
        radius: 'Radius'
      },
      circlemarker: {
        tooltip: {
          start: '单击“地图”以放置圆标记'//'Click map to place circle marker.'
        }
      },
      marker: {
        tooltip: {
          start: '单击“地图”以放置标记'//'Click map to place marker.'
        }
      },
      polygon: {
        tooltip: {
          start: '单击开始绘制形状',//'Click to start drawing shape.',
          cont: '单击继续绘制形状',//'Click to continue drawing shape.',
          end: '单击第一个点关闭此形状'//'Click first point to close this shape.'
        }
      },
      polyline: {
        error: '<strong>错误:</strong>形状边缘不能交叉!',//'<strong>Error:</strong> shape edges cannot cross!',
        tooltip: {
          start: '单击开始绘制线',//'Click to start drawing line.',
          cont: '单击以继续绘制线',//'Click to continue drawing line.',
          end: '单击“最后一点”以结束线'//'Click last point to finish line.'
        }
      },
      rectangle: {
        tooltip: {
          start: '单击并拖动以绘制矩形'//'Click and drag to draw rectangle.'
        }
      },
      simpleshape: {
        tooltip: {
          end: '释放鼠标完成绘图'//'Release mouse to finish drawing.'
        }
      }
    }
  },
  edit: {
    toolbar: {
      actions: {
        save: {
          title: '保存更改',//'Save changes',
          text: '保存'//'Save'
        },
        cancel: {
          title: '取消编辑,放弃所有更改',//'Cancel editing, discards all changes',
          text: '取消'//'Cancel'
        },
        clearAll: {
          title: '清除所有图层',//'Clear all layers',
          text: '清除所有'//'Clear All'
        }
      },
      buttons: {
        edit: '编辑图层',//'Edit layers',
        editDisabled: '无可编辑的图层',//'No layers to edit',
        remove: '删除图层',//'Delete layers',
        removeDisabled: '无可删除的图层'//'No layers to delete'
      }
    },
    handlers: {
      edit: {
        tooltip: {
          text: '拖动控制柄或标记以编辑要素',//'Drag handles or markers to edit features.',
          subtext: '单击“取消”撤消更改'//'Click cancel to undo changes.'
        }
      },
      remove: {
        tooltip: {
          text: '单击要删除的要素'//'Click on a feature to remove.'
        }
      }
    }
  }
};

事件

leaflet-draw插件的事件都是注册到map对象上的,到这里,可以了解一下Evented 事件

// map.fire 也就是 触发指定类型的事件。您可以选择提供一个数据对象——侦听器函数的第一个参数将包含其属性,事件可以选择性地传播到事件父级。
this._map.fire(L.Draw.Event.CREATED, {layer: layer, layerType: this.type});

也就是相当于自定义事件了,在他插件源码中相当于定义了一个map的L.Draw.Event.CREATED事件,然后在引入插件之后就可以通过map去使用这个事件了。

  • 由于前面通过fire将事件传播过来,并且传递过来了一个对象,这里通过一个event去接收
  • 同时也就是为什么会有event.layerType和event.layer这两个值,对于绘制不同面可以去取不同的值,拿到值之后还可以将这个layer图层给添加到前面定义好的一个drawnItems,之后的修改也是修改这里面的layer。
map.on(L.Draw.Event.CREATED, function (event) {
  console.log(' =====', event);
  // 作为永久存储 取这几个值给后端存着,后面再拿出来进行渲染
  console.log('类型 =====', event.layerType);
  console.log('坐标 =====', event.layer._latlngs || event.layer._latlng);
  console.log('配置 =====', event.layer.options); // 圆的半径在配置当中
  const layer = event.layer;
  drawnItems.addLayer(layer);
});

事件说明

事件名说明反参
L.Draw.Event.CREATED创建完成layer、layerType
L.Draw.Event.EDITED编辑完成layers
L.Draw.Event.DELETED删除layers
L.Draw.Event.DRAWSTART开始绘制layerType
L.Draw.Event.DRAWSTOP停止绘制layerType
L.Draw.Event.DRAWVERTEX绘制顶点layers
L.Draw.Event.EDITSTART开始编辑handler
L.Draw.Event.EDITMOVE编辑移动layer
L.Draw.Event.EDITRESIZE编辑缩放layer
L.Draw.Event.EDITVERTEX编辑顶点layers、poly
L.Draw.Event.EDITSTOP完成编辑handler
L.Draw.Event.DELETESTART开始删除handler
L.Draw.Event.DELETESTOP完成删除handler
L.Draw.Event.TOOLBAROPENED点击了图标,触发绘制
L.Draw.Event.TOOLBARCLOSED取消绘制触发
L.Draw.Event.MARKERCONTEXT右键单击标记marker,layer,poly

测量

用来测量某两个或多个点之间的距离以及所围成的面积。

// 先执行npm按照插件
npm i leaflet-measure

import 'leaflet-measure/dist/leaflet-measure';
import 'leaflet-measure/dist/leaflet-measure.css';

const measureControl = new L.Control.Measure({
  position: 'topright',
  primaryLengthUnit: 'feet',
  secondaryLengthUnit: 'miles',
  activeColor: '#F40',
  completedColor: '#F40',
  popupOptions: {className: 'leaflet-measure-resultpopup', autoPanPadding: [10, 10]}
});
measureControl.addTo(map);

插件的配置说明

属性说明
position插件按钮所处位置,左上、右下等等
primaryLengthUnit主要单位
secondaryLengthUnit次要单位
activeColor主动执行测量时渲染的地图要素的基色
completedColor测量完成之后渲染的颜色
popupOptions弹出框配置,可以额外设置class类名进行自定义样式

经纬度标识

可以使用插件,插件可以在github,插件:leaflet.latlng-graticule 上进行下载获取,之后将插件引入,然后通过L.latlngGraticule进行实例化插件

// 引入刚才下载的插件js
import './plugin/leaflet.latlng-graticule';

L.latlngGraticule({
  weight: '2.0',
  color: '#f40',
  fontColor: '#fff',
  opacity: 1,
  showLabel: true,
  dashArray: [5, 5],
  sides: ['北', '南', '东', '西'],
  zoomInterval: [
    {start: 2, end: 3, interval: 30},
    {start: 4, end: 4, interval: 10},
    {start: 5, end: 7, interval: 5},
    {start: 8, end: 18, interval: 1}
  ]
}).addTo(map);

插件的配置

属性说明
weightnumber 表示
color颜色值 表示经纬度线的颜色
fontColor颜色值 表示经纬度文字的颜色
opacitynumber 表示透明度
showLabelboolean 是否显示经纬度文字
dashArrayArray[number] 表示经纬度线的分隔程度
sidesArray[string] 表示四个方向展示的内容
zoomInterval展示层级 地图在第start到第end层级时,每interval度画一条经纬线

在这里插入图片描述

插件源码解析

首先可以参考一下Leaflet【三】图层组 & geoJson & 热力图 这篇文章,里面有对leaflet-heat插件的源码分析,这里同时简单看一下这个绘制经纬度的源码。

初始化对象,他是从layer基类当中继承而来。这里面有两个属性,一个是includes这个下面说一下,另外一个是options,也就是对这个插件的配置对象。

includes是Class基类当中的一个属性,是一个特殊的类属性,它将所有指定的对象合并到类中(这样的对象被称为mixins。)理解成vue2当中的混入就好了。那么这里就是通过继承L.Evented基类或L.Mixin.Events,该类获得了事件处理功能

L.LatLngGraticule = L.Layer.extend({
  includes: (L.Evented.prototype || L.Mixin.Events),
  options:{}  // 配置,这里省略了
});

initialize初始化,这里面没什么,就是将传递过来的options配置重新设置一下,以及将配置组装给到对应的样式。

initialize: function (options) {
  L.setOptions(this, options);
  // 。。。。样式组装,省略
}

onAdd方法,添加图层到map上会执行,这个方法基本上都大同小异,先初始化canvas,将canvas加到html当中,然后把需要监听的事件都监听上,最后绘制。这个内容有点多,简单概括一下。

  • 首先拿到canvas的宽高以及地图的当前层级,然后根据zoomInterval当中的start和end和地图层级比较拿到对应的interval
  • 已当前地图层级时6为例,这个时候拿到的interval是5,那么绘制的时候会从canvas左上开始绘制,取对应的经纬度,然后往右往下进行绘制,每过了5度就绘制下一条破折线
  • 并且绘制的时候将对应的文本一起绘制上去,这样也就完成了绘制
  • 之后的地图移动事件会重新执行_reset方法,那么也就会重新__redraw。
onAdd: function (map) {
  this._map = map;

  if (!this._canvas) {
    this._initCanvas();
  }

  map._panes.overlayPane.appendChild(this._canvas);

  map.on('viewreset', this._reset, this);
  map.on('move', this._reset, this);
  map.on('moveend', this._reset, this);

  if (map.options.zoomAnimation && L.Browser.any3d) {
    map.on('zoomanim', this._animateZoom, this);
  }

  this._reset();
},

直接看他绘制的方法,这里的initcanvas初始化一个canvas对象,然后_reset会将canvas大小设置一下,然后执行__draw绘制方法。

__draw: function (label) {
  function _parse_px_to_int(txt) {
    if (txt.length > 2) {
      if (txt.charAt(txt.length - 2) == 'p') {
        txt = txt.substr(0, txt.length - 2);
      }
    }
    try {
      return parseInt(txt, 10);
    } catch (e) {
    }
    return 0;
  };

  var self = this,
    canvas = this._canvas,
    map = this._map,
    curvedLon = this.options.lngLineCurved,
    curvedLat = this.options.latLineCurved;

  if (L.Browser.canvas && map) {

    var latInterval = this._currLatInterval,
      lngInterval = this._currLngInterval;


    // 获取canvas对象,设置对应的宽高样式等
    var ctx = canvas.getContext('2d');
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    ctx.lineWidth = this.options.weight;
    ctx.strokeStyle = this.options.color;
    ctx.fillStyle = this.options.fontColor;
    ctx.setLineDash(this.options.dashArray);

    if (this.options.font) {
      ctx.font = this.options.font;
    }
    var txtWidth = ctx.measureText('0').width;
    var txtHeight = 12;
    try {
      var _font_size = ctx.font.trim().split(' ')[0];
      txtHeight = _parse_px_to_int(_font_size);
    } catch (e) {
    }

    var ww = canvas.width,
      hh = canvas.height;

    var lt = map.containerPointToLatLng(L.point(0, 0));
    var rt = map.containerPointToLatLng(L.point(ww, 0));
    var rb = map.containerPointToLatLng(L.point(ww, hh));

    var _lat_b = rb.lat,
      _lat_t = lt.lat;
    var _lon_l = lt.lng,
      _lon_r = rt.lng;

    var _point_per_lat = (_lat_t - _lat_b) / (hh * 0.2);

    _point_per_lat = _point_per_lat < 1 ? 1 : _point_per_lat;

    _lat_b = _lat_b < -90 ? -90 : parseInt(_lat_b - _point_per_lat, 10);

    _lat_t = _lat_t > 90 ? 90 : parseInt(_lat_t - _point_per_lat, 10);

    var _point_per_lon = (_lon_r - _lon_l) / (ww * 0.2);

    _point_per_lon = _point_per_lon < 1 ? 1 : _point_per_lon;

    if (_lon_l > 0 && _lon_r < 0) {
      _lon_r += 360;
    }
    _lon_r = parseInt(_lon_r + _point_per_lon, 10);
    _lon_l = parseInt(_lon_l - _point_per_lon, 10);

    var ll, latstr, lngstr, _lon_delta = 0.5;

    function __draw_lat_line(self, lat_tick) {
      ll = self._latLngToCanvasPoint(L.latLng(lat_tick, _lon_l));
      latstr = self.__format_lat(lat_tick);
      txtWidth = ctx.measureText(latstr).width;
      var spacer = self.options.showLabel && label ? txtWidth + 10 : 0;

      if (curvedLat) {
        if (typeof (curvedLat) == 'number') {
          _lon_delta = curvedLat;
        }

        var __lon_left = _lon_l, __lon_right = _lon_r;
        if (ll.x > 0) {
          var __lon_left = map.containerPointToLatLng(L.point(0, ll.y));
          __lon_left = __lon_left.lng - _point_per_lon;
          ll.x = 0;
        }
        var rr = self._latLngToCanvasPoint(L.latLng(lat_tick, __lon_right));
        if (rr.x < ww) {
          __lon_right = map.containerPointToLatLng(L.point(ww, rr.y));
          __lon_right = __lon_right.lng + _point_per_lon;
          if (__lon_left > 0 && __lon_right < 0) {
            __lon_right += 360;
          }
        }

        ctx.beginPath();
        ctx.moveTo(ll.x + spacer, ll.y);
        var _prev_p = null;
        for (var j = __lon_left; j <= __lon_right; j += _lon_delta) {
          rr = self._latLngToCanvasPoint(L.latLng(lat_tick, j));
          ctx.lineTo(rr.x - spacer, rr.y);

          if (self.options.showLabel && label && _prev_p != null) {
            if (_prev_p.x < 0 && rr.x >= 0) {
              var _s = (rr.x - 0) / (rr.x - _prev_p.x);
              var _y = rr.y - ((rr.y - _prev_p.y) * _s);
              ctx.fillText(latstr, 0, _y + (txtHeight / 2));
            } else if (_prev_p.x <= (ww - txtWidth) && rr.x > (ww - txtWidth)) {
              var _s = (rr.x - ww) / (rr.x - _prev_p.x);
              var _y = rr.y - ((rr.y - _prev_p.y) * _s);
              ctx.fillText(latstr, ww - txtWidth, _y + (txtHeight / 2) - 2);
            }
          }

          _prev_p = {x: rr.x, y: rr.y, lon: j, lat: i};
        }
        ctx.stroke();
      } else {
        var __lon_right = _lon_r;
        var rr = self._latLngToCanvasPoint(L.latLng(lat_tick, __lon_right));
        if (curvedLon) {
          __lon_right = map.containerPointToLatLng(L.point(0, rr.y));
          __lon_right = __lon_right.lng;
          rr = self._latLngToCanvasPoint(L.latLng(lat_tick, __lon_right));

          var __lon_left = map.containerPointToLatLng(L.point(ww, rr.y));
          __lon_left = __lon_left.lng;
          ll = self._latLngToCanvasPoint(L.latLng(lat_tick, __lon_left));
        }

        ctx.beginPath();
        ctx.moveTo(1 + spacer, ll.y);
        ctx.lineTo(rr.x - 1 - spacer, rr.y);
        ctx.stroke();
        if (self.options.showLabel && label) {
          var _yy = ll.y + (txtHeight / 2) - 2;
          ctx.fillText(latstr, 0, _yy);
          ctx.fillText(latstr, ww - txtWidth, _yy);
        }
      }
    };

    if (latInterval > 0) {
      for (var i = latInterval; i <= _lat_t; i += latInterval) {
        if (i >= _lat_b) {
          __draw_lat_line(this, i);
        }
      }
      for (var i = 0; i >= _lat_b; i -= latInterval) {
        if (i <= _lat_t) {
          __draw_lat_line(this, i);
        }
      }
    }

    function __draw_lon_line(self, lon_tick) {
      lngstr = self.__format_lng(lon_tick);
      txtWidth = ctx.measureText(lngstr).width;
      var bb = self._latLngToCanvasPoint(L.latLng(_lat_b, lon_tick));
      var spacer = self.options.showLabel && label ? txtHeight + 5 : 0;

      if (curvedLon) {
        if (typeof (curvedLon) == 'number') {
          _lat_delta = curvedLon;
        }

        ctx.beginPath();
        ctx.moveTo(bb.x, 5 + spacer);
        var _prev_p = null;
        for (var j = _lat_b; j < _lat_t; j += _lat_delta) {
          var tt = self._latLngToCanvasPoint(L.latLng(j, lon_tick));
          ctx.lineTo(tt.x, tt.y - spacer);

          if (self.options.showLabel && label && _prev_p != null) {
            if (_prev_p.y > 8 && tt.y <= 8) {
              ctx.fillText(lngstr, tt.x - (txtWidth / 2), txtHeight + 5);
            } else if (_prev_p.y >= hh && tt.y < hh) {
              ctx.fillText(lngstr, tt.x - (txtWidth / 2), hh - 2);
            }
          }

          _prev_p = {x: tt.x, y: tt.y, lon: lon_tick, lat: j};
        }
        ctx.stroke();
      } else {
        var __lat_top = _lat_t;
        var tt = self._latLngToCanvasPoint(L.latLng(__lat_top, lon_tick));
        if (curvedLat) {
          __lat_top = map.containerPointToLatLng(L.point(tt.x, 0));
          __lat_top = __lat_top.lat;
          if (__lat_top > 90) {
            __lat_top = 90;
          }
          tt = self._latLngToCanvasPoint(L.latLng(__lat_top, lon_tick));

          var __lat_bottom = map.containerPointToLatLng(L.point(bb.x, hh));
          __lat_bottom = __lat_bottom.lat;
          if (__lat_bottom < -90) {
            __lat_bottom = -90;
          }
          bb = self._latLngToCanvasPoint(L.latLng(__lat_bottom, lon_tick));
        }

        ctx.beginPath();
        ctx.moveTo(tt.x, 5 + spacer);
        ctx.lineTo(bb.x, hh - 1 - spacer);
        ctx.stroke();

        if (self.options.showLabel && label) {
          ctx.fillText(lngstr, tt.x - (txtWidth / 2), txtHeight + 5);
          ctx.fillText(lngstr, bb.x - (txtWidth / 2), hh - 3);
        }
      }
    };

    if (lngInterval > 0) {
      for (var i = lngInterval; i <= _lon_r; i += lngInterval) {
        if (i >= _lon_l) {
          __draw_lon_line(this, i);
        }
      }
      for (var i = 0; i >= _lon_l; i -= lngInterval) {
        if (i <= _lon_r) {
          __draw_lon_line(this, i);
        }
      }
    }
  }
},

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/773867.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

配置基于不同IP地址的虚拟主机

定义配置文件vhost.conf <directory /www> allowoverride none require all granted </directory> <virtualhost 192.168.209.136:80> documentroot /www servername 192.168.209.136 </virtualhost><virtualhost 192.168.209.138:80> document…

Restore Equipment

Restore Equipment 魔兽世界 - 盗号申请 - 恢复装备流程 魔兽和网易真的不行啊 1&#xff09;这个装备本来就是兑换的竟然可以卖NPC 2&#xff09;针对这个情况竟然无法挽回 3&#xff09;设计理念真的不得不吐槽一下 4&#xff09;策划真的不咋样&#xff0c;要是有机会我要自…

STM32F1+HAL库+FreeTOTS学习5——内核中断管理及中断控制函数

STM32F1HAL库FreeTOTS学习5——中断管理和临界段代码保护 中断简介中断优先级寄存器拓展FreeRTOS中PendSV和Systick中断优先级配置三个中断屏蔽寄存器FreeRTOS中断管理函数代码验证 上一期我们学习了FreeRTOS中任务挂起与恢复&#xff0c;在中断服务程序中恢复任务过程中&#…

Fish Speech: 开源文本转语音技术(TTS)的新里程碑

简介 Fish Speech 是一个全新的文本转语音(TTS)解决方案&#xff0c;该项目由fishaudio开发。当前模型使用约十五万小时三语数据训练&#xff0c;对中文支持非常的完美。 能够熟练处理和生成中文、日语和英语的语音&#xff0c;语言处理能力接近人类水平&#xff0c;并且声音…

狂赚三个亿,百亿医用耗材上市公司重金押注老人轮椅

布局海外市场&#xff0c;轮椅销量翻两番 作者 | 艾米莉 排版 | 张思琪 抛砖引玉 1.年销售60万台轮椅&#xff0c;英科医疗如何做到&#xff1f; 2.老年人轮椅是出海&#xff0c;还是深耕国内市场&#xff1f; 3.2022年全球轮椅市场规模为48亿美元&#xff0c;谁在喝汤&…

Android仿天眼查人物关系图

效果图预览 绘制思路 这里使用了中学解析几何知识 XPoint OPointX OPointXcosθ&#xff1b; YPoint OPointY OPointYsinθ&#xff1b; canvas.drawText(lists.get(i).getName(), XPoint (float) Math.cos(pere * i 5) * radius[i % radius.length] - 30, YPoint (fl…

【笔试记录】腾讯音乐 | 20230903 | cpp (更新ing)

1 完美数 1.1 题目描述 小红定义一个数为“完美数”&#xff0c;当且仅当该数仅有一个非零数字。例如 5000, 4, 1, 10, 200 都是完美数。 小红拿到了一个大小为 n&#xff08;2 < n < 2000&#xff09;的数组 a&#xff0c;她希望选择数组中的两个元素&#xff08;1 …

CVE-2023-30212(xss漏洞)

简介 OURPHP版本<7.2.0存在XSS漏洞&#xff0c;攻击路径为/client/manage/ourphp_out.php。 过程 打开靶场 访问攻击路径/client/manage/ourphp_out.php 得到flag{354c7c41-cc23-4de5-be73-79cbbf384aba}

上海计算机考研炸了,这所学校慎报!上海大学计算机考研考情分析!

上海大学&#xff08;Shanghai University&#xff09;&#xff0c;简称“上大”&#xff0c;是上海市属、国家“211工程”重点建设的综合性大学&#xff0c;教育部与上海市人民政府共建高校&#xff0c;国防科技工业局与上海市人民政府共建高校&#xff0c;国家“双一流”世界…

leetcode--二叉搜索子树的最大键值和

leetcode地址&#xff1a;二叉搜索子树的最大键值和 给你一棵以 root 为根的 二叉树 &#xff0c;请你返回 任意 二叉搜索子树的最大键值和。 二叉搜索树的定义如下&#xff1a; 任意节点的左子树中的键值都 小于 此节点的键值。 任意节点的右子树中的键值都 大于 此节点的键值…

【matlab 路径规划】基于改进遗传粒子群算法的药店配送路径优化

一 背景介绍 本文分享的是一个基于订单合并的订单分配和路径规划联合优化&#xff0c;主要背景是骑手根据客户需求&#xff0c;从药店取药之后进行配送&#xff0c;配送的过程中考虑路径的长度、客户的服务时间窗、车辆的固定成本等要素&#xff0c;经过建模和优化得到最优的配…

什么是声明式编程?发展趋势怎么样的?

一、什么是声明式编程&#xff1f; 声明式编程&#xff08;Declarative programming&#xff09;是一种编程范式&#xff0c;与命令式编程相对立。它主要描述目标的性质&#xff0c;让计算机明白目标&#xff0c;而非具体的执行流程。在声明式编程中&#xff0c;开发者只需声明…

彻底搞懂Kafka生产消费流程,这篇文章就够了!

Hey, 小伙伴们!今天小米给大家带来一篇关于Kafka生产消费基本流程的揭秘,内容超干货!让我们一起揭开Kafka神秘的面纱,探索它的工作原理吧! Producer创建及其内部结构 当我们创建一个Kafka Producer时,Kafka会为我们创建一个叫做Sender的线程,并将其设置为守护线程(Da…

论文解读StyleGAN系列——StyleGANv3

论文&#xff1a;Alias-Free Generative Adversarial Networks&#xff08;2021.06&#xff09; 作者&#xff1a;Tero Karras, Miika Aittala, Samuli Laine, Erik Hrknen, Janne Hellsten, Jaakko Lehtinen, Timo Aila 链接&#xff1a;https://arxiv.org/abs/2106.12423 代码…

计算两个经纬度之间的球面距离(基于Mysql和PHP实现)

计算两个经纬度之间的球面距离 1、MySQL实现方式 - 基于空间函数(ST_Distance_Sphere)实现 前置条件&#xff1a;确保您使用的是 MySQL 8.0 或更高版本&#xff0c;因为较早的版本对地理空间的支持有限。 1.1 创建表和索引 说明&#xff1a;设置 location 为 point 类型 #…

驭码CodeRider将亮相世界人工智能大会,AI 产品、重磅分享,真的很City!

GitLab 是一个全球知名的一体化 DevOps 平台&#xff0c;很多人都通过私有化部署 GitLab 来进行源代码托管。极狐GitLab &#xff1a;https://gitlab.cn/install?channelcontent&utm_sourcecsdn 是 GitLab 在中国的发行版&#xff0c;专门为中国程序员服务。可以一键式部署…

Redis 中 Set 和 Zset 类型

目录 1.Set类型 1.1 Set集合 1.2 普通命令 1.3 集合操作 1.4 内部编码 1.5 使用场景 2.Zset类型 2.1 Zset有序集合 2.2 普通命令 2.3 集合间操作 2.4 内部编码 2.5 使用场景 1.Set类型 1.1 Set集合 集合类型也是保存多个字符串类型的元素&#xff0c;但是和列表类型不同的是&…

LVS+Keepalived 实现高可用负载均衡

前言 在业务量达到一定量的时候&#xff0c;往往单机的服务是会出现瓶颈的。此时最常见的方式就是通过负载均衡来进行横向扩展。其中我们最常用的软件就是 Nginx。通过其反向代理的能力能够轻松实现负载均衡&#xff0c;当有服务出现异常&#xff0c;也能够自动剔除。但是负载…

基于Redisson实现分布式锁

基于redisson实现分布式锁 之前背过分布式锁几种实现方案的八股文&#xff0c;但是并没有真正自己实操过。现在对AOP有了更深一点的理解&#xff0c;就自己来实现一遍。 1、分布式锁的基础知识 分布式锁是相对于普通的锁的。普通的锁在具体的方法层面去锁&#xff0c;单体应…

搜维尔科技:详谈ART的工具追踪技术

您的生产流程中是否已经受益于刀具跟踪系统&#xff1f;您是否意识到它们的价值&#xff1f;因为它们可以优化您的装配顺序&#xff0c;从而节省您的时间和金钱。 目前我们提供两种工具跟踪解决方案&#xff1a; 1.ART与 VERPOSE的解决方案——易于使用的图像识别 安装在工…