How to display curved polyline on one side of a st

2019-01-29 01:23发布

问题:

This question is related to this SO question and answered by @geocodezip.

A problem I have is drawing a line from either north-to-south or south-to-north. The curved line is now bent like number 8 or a S. How do I control the curved line to display on one side of the straight line. Sometimes the curved line is expanded beyond the starting point and contracted at the ending point. Thanks.

Here are my codes. I have two sample polylines north-to-south.

var map;

function init() {
var Map = google.maps.Map,
    LatLng = google.maps.LatLng,
    LatLngBounds = google.maps.LatLngBounds,
    Marker = google.maps.Marker,
    Point = google.maps.Point;

var pos1 = new LatLng(29.703642, -95.152274);
var pos2 = new LatLng(29.702452, -95.152296);

var pos3 = new LatLng(29.703514, -95.151405);
var pos4 = new LatLng(29.702359, -95.152078);

var bounds = new LatLngBounds();
bounds.extend(pos1);
bounds.extend(pos2);
var mapOptions = {
        disableDefaultUI: true,
        mapTypeId: google.maps.MapTypeId.SATELLITE,
        draggableCursor: 'auto',
        panControl: true,
        scaleControl: true,
        smallMapControl: true,
        tilt: 0,
        zoom: 19,           
        zoomControl: true,
        rotateControl:true,
        zoomControlOptions: { style: google.maps.ZoomControlStyle.SMALL }
    };
map = new Map(document.getElementById('map-canvas'), mapOptions);
map.fitBounds(bounds);

var markerP1 = new Marker({
    position: pos1,
    draggable: true,
    map: map
});
var markerP2 = new Marker({
    position: pos2,
    draggable: true,
    map: map
});

var markerP2 = new Marker({
    position: pos3,
    draggable: true,
    map: map
});
var markerP3 = new Marker({
    position: pos4,
    draggable: true,
    map: map
});

var curvedLine = new GmapsCubicBezier(pos1, pos2, 0.01, map);
var curvedLine = new GmapsCubicBezier(pos3, pos4, 0.01, map);

var line = new google.maps.Polyline({
  path: [pos1, pos2],
  strokeOpacity: 0,
  icons: [{
    icon: {
      path: 'M 0,-1 0,1',
      strokeOpacity: 1,
      scale: 4
    },
    offset: '0',
    repeat: '20px'
  }],
  map: map
});

var line = new google.maps.Polyline({
    path: [pos3, pos4],
    strokeOpacity: 0,
    icons: [{
      icon: {
        path: 'M 0,-1 0,1',
        strokeOpacity: 1,
        scale: 4
      },
      offset: '0',
      repeat: '20px'
    }],
    map: map
  });
}

google.maps.event.addDomListener(window, 'load', init);

var GmapsCubicBezier = function (latlong1, latlong4, resolution, map) {

var lineLength = google.maps.geometry.spherical.computeDistanceBetween(latlong1, latlong4);
var lineHeading = google.maps.geometry.spherical.computeHeading(latlong1, latlong4);

var positionA = google.maps.geometry.spherical.computeOffset(latlong1, lineLength / 3, lineHeading - 60);
var positionB = google.maps.geometry.spherical.computeOffset(latlong4, lineLength / 3, -lineHeading + 120);

var lat1 = latlong1.lat();
var long1 = latlong1.lng();

var lat2 = positionA.lat();
var long2 = positionA.lng();

var lat3 = positionB.lat();
var long3 = positionB.lng();

var lat4 = latlong4.lat();
var long4 = latlong4.lng();

var points = [];

for (it = 0; it <= 1; it += resolution) {
    points.push(this.getBezier({
        x: lat1,
        y: long1
    }, {
        x: lat2,
        y: long2
    }, {
        x: lat3,
        y: long3
    }, {
        x: lat4,
        y: long4
    }, it));
}
var path = [];
for (var i = 0; i < points.length - 1; i++) {
    path.push(new google.maps.LatLng(points[i].x, points[i].y));
    path.push(new google.maps.LatLng(points[i + 1].x, points[i + 1].y, false));
              }

    var Line = new google.maps.Polyline({
        path: path,
        geodesic: true,
        strokeOpacity: 0.0,
                    icons: [{
                        icon: {
                            path: 'M 0,-1 0,1',
                            strokeOpacity: 1,
                            scale: 4
                        },
                        offset: '0',
                        repeat: '20px'
                    }],
         strokeColor: 'grey'
     });

    Line.setMap(map);

return Line;
};


GmapsCubicBezier.prototype = {

B1: function (t) {
    return t * t * t;
},
B2: function (t) {
    return 3 * t * t * (1 - t);
},
B3: function (t) {
    return 3 * t * (1 - t) * (1 - t);
},
B4: function (t) {
    return (1 - t) * (1 - t) * (1 - t);
},
getBezier: function (C1, C2, C3, C4, percent) {
    var pos = {};
    pos.x = C1.x * this.B1(percent) + C2.x * this.B2(percent) + C3.x * this.B3(percent) + C4.x * this.B4(percent);
    pos.y = C1.y * this.B1(percent) + C2.y * this.B2(percent) + C3.y * this.B3(percent) + C4.y * this.B4(percent);
    return pos;
}
};

回答1:

A general routine to draw the Bezier curve you are looking for would be:

function drawDashedCurve(P1, P2, map) {
  var lineLength = google.maps.geometry.spherical.computeDistanceBetween(P1, P2);
  var lineHeading = google.maps.geometry.spherical.computeHeading(P1, P2);
  if (lineHeading < 0) {
    var lineHeading1 = lineHeading + 45;
    var lineHeading2 = lineHeading + 135;
  } else {
    var lineHeading1 = lineHeading + -45;
    var lineHeading2 = lineHeading + -135;
  }
  var pA = google.maps.geometry.spherical.computeOffset(P1, lineLength / 2.2, lineHeading1);
  var pB = google.maps.geometry.spherical.computeOffset(P2, lineLength / 2.2, lineHeading2);

  var curvedLine = new GmapsCubicBezier(P1, pA, pB, P2, 0.01, map);
}

Changing the angles and distance to compute the control points (pA, pB) will change the amount of curvature.

test fiddle

code snippet:

// draw a dashed curve anchored at P1, P2
function drawDashedCurve(P1, P2, map) {
  var lineLength = google.maps.geometry.spherical.computeDistanceBetween(P1, P2);
  var lineHeading = google.maps.geometry.spherical.computeHeading(P1, P2);
  if (lineHeading < 0) {
    var lineHeading1 = lineHeading + 45;
    var lineHeading2 = lineHeading + 135;
  } else {
    var lineHeading1 = lineHeading + -45;
    var lineHeading2 = lineHeading + -135;
  }
  var pA = google.maps.geometry.spherical.computeOffset(P1, lineLength / 2.2, lineHeading1);
  var pB = google.maps.geometry.spherical.computeOffset(P2, lineLength / 2.2, lineHeading2);

  var curvedLine = new GmapsCubicBezier(P1, pA, pB, P2, 0.01, map);
}

function initialize() {
  var map = new google.maps.Map(
    document.getElementById("map_canvas"), {
      center: new google.maps.LatLng(37.4419, -122.1419),
      zoom: 13,
      mapTypeId: google.maps.MapTypeId.ROADMAP
    });
  // generate test points every 10 degrees
  for (var angle = 0; angle < 360; angle += 10) {
    var p1 = google.maps.geometry.spherical.computeOffset(map.getCenter(), 1000, angle);
    var p2 = google.maps.geometry.spherical.computeOffset(map.getCenter(), 2000, angle);
    drawDashedCurve(p1, p2, map);
    var straightPoly = new google.maps.Polyline({
      map: map,
      path: [p1, p2],
      strokeOpacity: 0.2,
      strokeColor: "blue"
    });
    var markerP1 = new google.maps.Marker({
      position: p1,
      map: map,
      icon: {
        path: google.maps.SymbolPath.CIRCLE,
        scale: 4,
        fillColor: "black",
        fillOpacity: 1.0
      }
    });
    var markerP2 = new google.maps.Marker({
      position: p2,
      map: map,
      icon: {
        path: google.maps.SymbolPath.CIRCLE,
        scale: 4,
        fillColor: "black",
        fillOpacity: 1.0
      }
    });
  }
}
google.maps.event.addDomListener(window, "load", initialize);

// original Belzier Curve code from nicoabie's answer to this question on StackOverflow:
// http://stackoverflow.com/questions/5347984/letting-users-draw-curved-lines-on-a-google-map
var GmapsCubicBezier = function(latlong1, latlong2, latlong3, latlong4, resolution, map) {
  var lat1 = latlong1.lat();
  var long1 = latlong1.lng();
  var lat2 = latlong2.lat();
  var long2 = latlong2.lng();
  var lat3 = latlong3.lat();
  var long3 = latlong3.lng();
  var lat4 = latlong4.lat();
  var long4 = latlong4.lng();

  var points = [];

  for (it = 0; it <= 1; it += resolution) {
    points.push(this.getBezier({
      x: lat1,
      y: long1
    }, {
      x: lat2,
      y: long2
    }, {
      x: lat3,
      y: long3
    }, {
      x: lat4,
      y: long4
    }, it));
  }
  var path = [];
  for (var i = 0; i < points.length - 1; i++) {
    path.push(new google.maps.LatLng(points[i].x, points[i].y));
    path.push(new google.maps.LatLng(points[i + 1].x, points[i + 1].y, false));
  }

  var Line = new google.maps.Polyline({
    path: path,
    geodesic: true,
    strokeOpacity: 0.0,
    icons: [{
      icon: {
        path: 'M 0,-1 0,1',
        strokeOpacity: 1,
        scale: 4
      },
      offset: '0',
      repeat: '20px'
    }],
    strokeColor: 'grey'
  });

  Line.setMap(map);

  return Line;
};


GmapsCubicBezier.prototype = {

  B1: function(t) {
    return t * t * t;
  },
  B2: function(t) {
    return 3 * t * t * (1 - t);
  },
  B3: function(t) {
    return 3 * t * (1 - t) * (1 - t);
  },
  B4: function(t) {
    return (1 - t) * (1 - t) * (1 - t);
  },
  getBezier: function(C1, C2, C3, C4, percent) {
    var pos = {};
    pos.x = C1.x * this.B1(percent) + C2.x * this.B2(percent) + C3.x * this.B3(percent) + C4.x * this.B4(percent);
    pos.y = C1.y * this.B1(percent) + C2.y * this.B2(percent) + C3.y * this.B3(percent) + C4.y * this.B4(percent);
    return pos;
  }
};
html,
body,
#map_canvas {
  height: 100%;
  width: 100%;
  margin: 0px;
  padding: 0px
}
<script src="https://maps.googleapis.com/maps/api/js?libraries=geometry"></script>
<div id="map_canvas"></div>