Google Maps API多边形中的绕线路径,带有rotate/grow/shrink/order选项

Dri*_*ium 13 html javascript google-maps-api-3

我不确定我想要实现的目标是可能的,但我知道这里有一些富有创造力的人,不可否认,我需要一些手握这个.

这是我要添加的内容的模型: 例

自动生成/填充多边形内的形状.为了我的目的,我不需要盒子,我需要一个缠绕路径来填充每个用户创建的多边形.我需要那条蜿蜒的路径来拥抱多边形的边界,如上图所示. 在多边形内绘制小方框

这是我试图将此功能应用于的代码:

//debugger;

/////////////////////////////////////////////////////////////
//Map Specifications

function initialize() {
  var map = new google.maps.Map(document.getElementById('map'), {
    zoom: 18,
    center: new google.maps.LatLng(33.27144940863937, -117.2983479390361),
    mapTypeId: google.maps.MapTypeId.SATELLITE,
    mapTypeId: google.maps.MapTypeId.HYBRID,
    tilt: 0,
    disableDefaultUI: true,
    zoomControl: true,
    mapTypeControl: false,
    scaleControl: true,
    streetViewControl: true,
    rotateControl: true,
    fullscreenControl: false
  });

  // Creates a drawing manager attached to the map that allows the user to draw
  // markers, lines, and shapes.
  drawingManager = new google.maps.drawing.DrawingManager({
    drawingControlOptions: {
      position: google.maps.ControlPosition.TOP_CENTER,
      drawingModes: [
        google.maps.drawing.OverlayType.POLYLINE,
        google.maps.drawing.OverlayType.POLYGON
      ]
    },
    markerOptions: {
      draggable: false
    },
    //https://developers.google.com/maps/documentation/javascript/reference#PolygonOptions
    polygonOptions: {
      clickable: true,
      draggable: false,
      editable: true,
      fillColor: '#00FF00',
      fillOpacity: 0.45,
      geodesic: false,
      strokeColor: '#000000',
      strokeOpacity: 08,
      //strokePosition: CENTER,
      strokeWeight: 3,
      visible: true,
      zIndex: 0
    },
    //https://developers.google.com/maps/documentation/javascript/reference#PolylineOptions
    polylineOptions: {
      clickable: true,
      draggable: false,
      editable: true,
      geodesic: false,
      //icons: ,
      strokeColor: '#FF00FF',
      strokeOpacity: 0.8,
      strokeWeight: 3,
      visible: true,
      zIndex: 0
    }
  });

  ////////////////////////////////////////////////////////////////////////////////
  var drawingManager;
  var deleteSelectedShape;
  var selectedShape;

  function clearSelection() {
    if (selectedShape) {
      if (selectedShape.type !== 'marker') {
        selectedShape.setEditable(false);
      }

      selectedShape = null;
    }
  }

  function setSelection(shape) {
    if (shape.type !== 'marker') {
      clearSelection();
      shape.setEditable(true);
    }

    selectedShape = shape;
  }
  DeleteShape = function deleteSelectedShape() {
    if (selectedShape) {
      selectedShape.setMap(null);
    }
    if (selectedShape.type == 'polygon') {
      document.getElementById("action_gon").value = 'adds, moves, deletions'
    } else if (selectedShape.type == 'polyline') {
      document.getElementById("action_line").value = 'adds, moves, deletions'
    }
  };

  /////////////////////////////////////////////////////////////
  //Populate textboxes with geo data when new polygon and polyline shape added

  drawingManager.setMap(map);

  google.maps.event.addDomListener(drawingManager, 'markercomplete', function(marker) {
    document.getElementById("action").value += "#marker\n";
    document.getElementById("action").value += marker.getPosition() + "\n";
  });

  google.maps.event.addDomListener(drawingManager, 'polylinecomplete', function(line) {
    path = line.getPath();
    //document.getElementById("action_line").value = ''
    document.getElementById("action_line").value = "#polyline shape added\n";
    for (var i = 0; i < path.length; i++) {
      document.getElementById("action_line").value += path.getAt(i) + "\n";
    }
  });

  google.maps.event.addDomListener(drawingManager, 'polygoncomplete', function(polygon) {
    var markerCnt = 0;
    path = polygon.getPath();
    //document.getElementById("action_gon").value = ''
    document.getElementById("action_gon").value = "#polygon shape added\n";
    for (var i = 0; i < path.length; i++) {
      document.getElementById("action_gon").value += path.getAt(i) + '\n';
    }
  });

  //////////////////////////////////////////////////////////////////////

  google.maps.event.addListener(drawingManager, 'overlaycomplete', function(e) {

    var newShape = e.overlay;
    newShape.type = e.type;

    if (e.type !== google.maps.drawing.OverlayType.MARKER) {
      // Switch back to non-drawing mode after drawing a shape.
      drawingManager.setDrawingMode(null);

      if (e.type == google.maps.drawing.OverlayType.POLYGON) {
        var coordinatesArray = e.overlay.getPath().getArray();
        document.getElementById("count_gon").value += "#\n";
        document.getElementById("count_gon").value += coordinatesArray + "\n";
      }

      //Catch vertex modifications (moves)
      function processVertices(e) {
        var ele;
        if (newShape.type == "polygon") {
          ele = document.getElementById("action_gon");
          //ele.value = "Modified vertex: "+e+"\n"+this.getAt(e)+"\nPolygon coords :\n";
          ele.value = "#polygon vertex " + e + " moved\n" + this.getAt(e) + "\n";
        } else if (newShape.type == "polyline") {
          ele = document.getElementById("action_line");
          //ele.value = "Modified vertex: "+e+"\n"+this.getAt(e)+"\nPolyline coords :\n";
          ele.value = "#polyline vertex " + e + " moved\n" + this.getAt(e) + "\n";
        } else return;
        for (var i = 0; i < newShape.getPath().getLength(); i++) {
          ele.value += newShape.getPath().getAt(i) + '\n';
        };
      };

      google.maps.event.addListener(newShape.getPath(), 'set_at', processVertices);
      google.maps.event.addListener(newShape.getPath(), 'insert_at', processVertices);

      /////////////////////////////////////////////////////////////
      // Add an event listener that selects the newly-drawn shape when the user clicks it.
      google.maps.event.addListener(newShape, 'click', function(e) {
        if (e.vertex !== undefined) {
          if (newShape.type === google.maps.drawing.OverlayType.POLYGON) {
            var path = newShape.getPaths().getAt(e.path);
            path.removeAt(e.vertex);

            /////////////////////////////////////////////////////////////
            //Update textboxes with geo data when polygon vertex deleted
            document.getElementById("action_gon").value = "#polygon vertex deleted\n";
            for (var i = 0; i < path.length; i++) {
              document.getElementById("action_gon").value += path.getAt(i) + '\n';
            }

            if (path.length < 3) {
              newShape.setMap(null);
              document.getElementById("action_gon").value = 'This box shows updated coords for POLYGONS based on user interactions (adds, moves, deletions).'
            }
          }

          if (newShape.type === google.maps.drawing.OverlayType.POLYLINE) {
            var path = newShape.getPath();
            path.removeAt(e.vertex);
            /////////////////////////////////////////////////////////////
            //Update textboxes with geo data when polyline vertex deleted
            document.getElementById("action_line").value = "#polyline vertex deleted\n";
            for (var i = 0; i < path.length; i++) {
              document.getElementById("action_line").value += path.getAt(i) + '\n';
            }

            if (path.length < 2) {
              newShape.setMap(null);
              document.getElementById("action_line").value = 'This box shows updated coords for POLYLINES based on user interactions (adds, moves, deletions).'
            }
          }
        }

        setSelection(newShape);
      });
      setSelection(newShape);
    } else {
      google.maps.event.addListener(newShape, 'click', function(e) {
        setSelection(newShape);
      });
      setSelection(newShape);
    }
  });

  // Link delete button to the UI element.
  var delbtn = /** @type {HTMLInputElement} */ (
    document.getElementById('delete-button'));
  map.controls[google.maps.ControlPosition.TOP_RIGHT].push(delbtn);

  // Clear the current selection when the drawing mode is changed, or when the
  // map is clicked.
  google.maps.event.addListener(drawingManager, 'drawingmode_changed', clearSelection);
  google.maps.event.addListener(map, 'click', clearSelection);

  // Listen for delete button click.
  google.maps.event.addDomListener(document.getElementById('delete-button'), 'click', deleteSelectedShape);

  /////////////////////////////////////////////////////////////////////

  //Places Search Box Setup
  var markers = [];
  var input = /** @type {HTMLInputElement} */ (
    document.getElementById('pac-input'));
  map.controls[google.maps.ControlPosition.TOP_LEFT].push(input);

  var searchBox = new google.maps.places.SearchBox(
    /** @type {HTMLInputElement} */
    (input));

  // [START region_getplaces]
  // Listen for the event fired when the user selects an item from the
  // pick list. Retrieve the matching places for that item.
  google.maps.event.addListener(searchBox, 'places_changed', function() {
    var places = searchBox.getPlaces();

    if (places.length == 0) {
      return;
    }
    for (var i = 0, marker; marker = markers[i]; i++) {
      marker.setMap(null);
    }

    // For each place, get the icon, place name, and location.
    markers = [];
    var bounds = new google.maps.LatLngBounds();
    for (var i = 0, place; place = places[i]; i++) {
      var image = {
        url: place.icon,
        size: new google.maps.Size(71, 71),
        origin: new google.maps.Point(0, 0),
        anchor: new google.maps.Point(17, 34),
        scaledSize: new google.maps.Size(25, 25)
      };

      bounds.extend(place.geometry.location);
    }

    map.fitBounds(bounds);
  });
}

google.maps.event.addDomListener(window, 'load', initialize);
Run Code Online (Sandbox Code Playgroud)
#map,
html,
body {
  padding: 0;
  margin: 0;
  width: 100%;
  height: 100%;
}
#geoinfoboxes {
  display: none;
}
#delete-button {
  background: #0084ff;
  background-image: -webkit-linear-gradient(top, #0084ff, #000000);
  background-image: -moz-linear-gradient(top, #0084ff, #000000);
  background-image: -o-linear-gradient(top, #0084ff, #000000);
  background-image: linear-gradient(to bottom, #0084ff, #000000);
  border-radius: 30px;
  text-shadow: 0px 1px 3px #cfcdcf;
  -webkit-box-shadow: 0px 1px 3px #666666;
  -moz-box-shadow: 0px 1px 3px #666666;
  box-shadow: 0px 1px 3px #666666;
  font-family: Arial;
  margin-top: 5px;
  right: 0.5%;
  color: #ffffff;
  font-size: 15px;
  padding: 8px 10px 8px 10px;
  border: solid #a8a8a8 2px;
  text-decoration: none;
}
#delete-button:hover {
  background: #09ff00;
  background-image: -webkit-linear-gradient(top, #09ff00, #000000);
  background-image: -moz-linear-gradient(top, #09ff00, #000000);
  background-image: -o-linear-gradient(top, #09ff00, #000000);
  background-image: linear-gradient(to bottom, #09ff00, #000000);
  text-decoration: none;
}
.controls {
  border: 1px solid transparent;
  border-radius: 30px 30px 30px 30px;
  box-sizing: border-box;
  -moz-box-sizing: border-box;
  height: 32px;
  outline: none;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
  margin-top: 5px;
}
#pac-input {
  background-color: #fff;
  font-family: Roboto;
  font-size: 15px;
  font-weight: 300;
  margin-left: 12px;
  padding: 0 11px 0 13px;
  text-overflow: ellipsis;
  width: 400px;
}
#pac-input:focus {
  border-color: #4d90fe;
}
.pac-container {
  font-family: Roboto;
}
Run Code Online (Sandbox Code Playgroud)
<!DOCTYPE html>
<!-- saved from url=(0014)about:internet -->
<html>

<head>
  <meta http-equiv="X-UA-Compatible" content="IE=Edge" />
  <meta name="viewport" content="initial-scale=1.0, user-scalable=no">
  <meta charset="UTF-8">
  <title>Test</title>
  <script type="text/javascript" src="http://maps.google.com/maps/api/js?key=AIzaSyBgmfaITmUDhXxk-0V33IPmNPd43mMd4ZU&libraries=drawing,places"></script>
</head>
<!-- -->

<body>
  <input id="pac-input" class="controls" type="text" placeholder="Search...">
  <input id="delete-button" onclick="DeleteShape();" type=button value="Delete Selected Shape">
  <div id="geoinfoboxes">
    <textarea id="action_line" rows="8" cols="46"></textarea>
    <textarea id="action_gon" rows="8" cols="46"></textarea>
    <textarea id="count_gon" rows="8" cols="46"></textarea>
  </div>
  <div id="map"></div>
</body>

</html>
Run Code Online (Sandbox Code Playgroud)

编辑:看起来我的图像中的网格填充多边形已经使用Android App,Tower中的Google API完成.以下是我想要创建的网格类型的确切示例(跳至2:10):https://www.youtube.com/watch?v = u-k8ax2JGC4 .看起来他们在这里提供源代码:https://github.com/DroidPlanner/Tower,但我不知道如何提取我需要的东西并转换为javascript ...

以下是生成折线如何缠绕的更多示例: 在此输入图像描述

这将使用多边形的曲线在间隙周围来回继续,但这不是非常有效.
例

在多边形像这样凹入的情况下,这种方式是理想的.折线路径可以在一侧向下运行,而在另一侧向后运行.
例

例

我愿意以最有效的方式实现这一目标(最短距离旅行).

shu*_*van 5

如何建造蜿蜒的小路

您可以按照以下方式构建蜿蜒路径,适用于没有复杂腔体的区域。

  1. 查找包含用户折线的矩形 -getBounds示例。
  2. 将给定的方向视为一条线,从角点d(必须小于缠绕尺寸)移动,找到第一对交点I1I2使用折线。所以我们需要一条路径的第一段。让它成为点P[0]P[1]
  3. 将交线移动D更远的距离并找到下一对交点I3I4P[1]制作一条从到 的短路径I4。如果路径包含N折线路径点,我们将N+1点添加到路径:P[2]...P[N+2]
  4. 然后I4作为蜿蜒路径的下一个点。
  5. 当下一个路口存在时,多次执行 和3.4.如果没有,请完成路径创建。
  6. 手动更改方向以找到最小路径和最大覆盖范围。

第一次尝试

首先,我们对一个覆盖折线的不可见矩形执行此操作。绕一圈for并从西北角向下移动到东西角。导出交点后,将两个点添加到 a 中windingPath示例代码在这里

在创建示例时,我意识到,与矩形不同边框的一组条件相比,以更通用的方式找到移动线与矩形或多段线的交点会更容易。

在此输入图像描述

查找移动线与折线的交点

人们可以使用几何公式和for用户折线的每一段的循环来找到交点。如果移动线的一段与用户的任何线段相交,我们将其保留为蜿蜒路径的点。

工作代码在这里。使用 找到移动线和用户形状之间的交点,该函数使用在此处非常有用的 SO 帖子中var getLinePolylineCollisions = function(firstPoint, secondPoint, shape)找到的修改解决方案。我只是改变了and到and方法。xylat()lng()

在此输入图像描述

还有什么?

您可以注意到,蜿蜒路径的小段是直线,当它们遇到凹面时,它们可能会位于形状之外。下一步是使该线段完全重复用户的折线线段。

PS您可以避免使用第一个矩形来使每条移动线具有恒定的长度L = width / Math.cos(angleRadians)