Google 地图多边形:为相交的圆设置透明

Google map Polygon: Set transparent for circles intersecting

我遵循postChange map opacity outside circle of Google Maps JavaScript API v3,但我有多个圈子,有时它们是相交的。在相交处,它们是不透明的。如何将它们设置为透明?

Code On jsfiddle.net

谢谢。

// This example uses the Google Maps JavaScript API's Data layer
// to create a rectangular polygon with 2 holes in it.
const places = [
  {
    center: { "lat": 1.3512492664554796, "lng": 103.85072488875811 },
    radius: 2000
  },
  {
    center: { "lat": 1.3225896205754812, "lng": 103.86600275130694 },
    radius: 2000
  },
  {
    center:{ "lat": 1.3136655910729036, "lng": 103.88866205306475 },
    radius: 2000
  },
  {
    center: { "lat": 1.305427997082587, "lng": 103.83613367171709 },
    radius: 2000
  }
];

let map;

const mapDefault = {
    
};

function initMap() {
    map = new google.maps.Map(document.getElementById('map'), mapDefault);
  const bounds = new google.maps.LatLngBounds();
  
  let pathsCircle = [];
  
  places.forEach(place => {
    bounds.extend(place.center);
    pathsCircle.push(drawCircle(place.center, place.radius, 1));
  });

  map.fitBounds(bounds);
  
  const worldCoords = [
    new google.maps.LatLng(-85.1054596961173, -180),
    new google.maps.LatLng(85.1054596961173, -180),
    new google.maps.LatLng(85.1054596961173, 180),
    new google.maps.LatLng(-85.1054596961173, 180),
    new google.maps.LatLng(-85.1054596961173, 0)
  ];
  
  new google.maps.Polygon({
    paths: [worldCoords, ...pathsCircle],
    strokeColor: '#fff',
    strokeOpacity: 0,
    strokeWeight: 0,
    fillColor: '#000',
    fillOpacity: 0.3,
    map,
  });
}

function drawCircle(point, radius, dir) {
}

这是我的代码。

为了避免 non-transparent 相交,需要将圆组合成一个多边形。

一种选择是使用 JSTS library 计算圆的并集。

var geometryFactory = new jsts.geom.GeometryFactory();

var poly1,poly2,polyunion;
var polys = [];
for (var i=0; i<pathsCircle.length; i++) {
   if (i==0) {
     poly1 = geometryFactory.createPolygon(geometryFactory.createLinearRing(array2JSTS(pathsCircle[i])));
     poly1.normalize();
     polys[i] = poly1;

   } else {
     poly2 = geometryFactory.createPolygon(geometryFactory.createLinearRing(array2JSTS(pathsCircle[i])));
     poly2.normalize();
     polys[i] = polys[i-1].union(poly2);
     polys[i].normalize();
   }
}

  polys[i-1].normalize();
  var boundary = polys[i-1].getBoundary();
  var outputPath = jsts2googleMaps(boundary);

var jsts2googleMaps = function (geometry) {
  var coordArray = geometry.getCoordinates();
  GMcoords = [];
  for (var i = 0; i < coordArray.length; i++) {
    GMcoords.push(new google.maps.LatLng(coordArray[i].x, coordArray[i].y));
  }
  return GMcoords;
}
var array2JSTS = function(boundaries) {
  var coordinates = [];
  for (var i = 0; i < boundaries.length; i++) {
    coordinates.push(new jsts.geom.Coordinate(
        boundaries[i].lat, boundaries[i].lng));
  }
  return coordinates;
};

proof of concept fiddle

代码片段:

const places = [
  {
    center: { "lat": 1.3512492664554796, "lng": 103.85072488875811 },
    radius: 2000
  },
  {
    center: { "lat": 1.3225896205754812, "lng": 103.86600275130694 },
    radius: 2000
  },
  {
    center:{ "lat": 1.3136655910729036, "lng": 103.88866205306475 },
    radius: 2000
  },
  {
    center: { "lat": 1.305427997082587, "lng": 103.83613367171709 },
    radius: 2000
  }
];

let map;

const mapDefault = {
    center: places[0].center,
    zoom: 10,
    minZoom: 1,
    maxZoom: 19,
    clickableIcons: false
};

function initMap() {
    map = new google.maps.Map(document.getElementById('map'), mapDefault);
  const bounds = new google.maps.LatLngBounds();
  
  let pathsCircle = [];
  
  places.forEach(place => {
    bounds.extend(place.center);
    pathsCircle.push(drawCircle(place.center, place.radius, 1));
  });

  map.fitBounds(bounds);
  
  const worldCoords = [
    new google.maps.LatLng(-85.1054596961173, -180),
    new google.maps.LatLng(85.1054596961173, -180),
    new google.maps.LatLng(85.1054596961173, 180),
    new google.maps.LatLng(-85.1054596961173, 180),
    new google.maps.LatLng(-85.1054596961173, 0)
  ];
var geometryFactory = new jsts.geom.GeometryFactory();

var poly1,poly2,polyunion;
var polys = [];
for (var i=0; i<pathsCircle.length; i++) {
   if (i==0) {
     poly1 = geometryFactory.createPolygon(geometryFactory.createLinearRing(array2JSTS(pathsCircle[i])));
     poly1.normalize();
     polys[i] = poly1;

   } else {
     poly2 = geometryFactory.createPolygon(geometryFactory.createLinearRing(array2JSTS(pathsCircle[i])));
     poly2.normalize();
     polys[i] = polys[i-1].union(poly2);
     polys[i].normalize();
   }
}

  polys[i-1].normalize();
  var boundary = polys[i-1].getBoundary();
  var outputPath = jsts2googleMaps(boundary);

new google.maps.Polygon({
    paths: [worldCoords, outputPath],
    strokeColor: '#fff',
    strokeOpacity: 0,
    strokeWeight: 0,
    fillColor: '#000',
    fillOpacity: 0.3,
    map,
  });
}

function drawCircle(point, radius, dir) {
  let d2r = Math.PI / 180;   // degrees to radians 
  let r2d = 180 / Math.PI;   // radians to degrees 
  let earthsradius = 6378137; // 6378137 is the radius of the earth in metters
  let points = 128; // number of point to draw Circle

  // find the raidus in lat/lon 
  let rlat = (radius / earthsradius) * r2d;
  let rlng = rlat / Math.cos(point.lat * d2r);

  let extp = [];

  let start, end;
  if (dir == 1) {  // one extra here makes sure we connect the
    start = 0; end = points + 1
  } else {
    start = points + 1; end = 0
  }
  for (let i = start; (dir == 1 ? i < end : i > end); i = i + dir) {
    let theta = Math.PI * (i / (points / 2));
    const ey = point.lng + (rlng * Math.cos(theta)); // center a + radius x * cos(theta) 
    const ex = point.lat + (rlat * Math.sin(theta)); // center b + radius y * sin(theta) 
    extp.push({
      lat: ex,
      lng: ey,
    });
  }
  return extp;
}
var jsts2googleMaps = function (geometry) {
  var coordArray = geometry.getCoordinates();
  GMcoords = [];
  for (var i = 0; i < coordArray.length; i++) {
    GMcoords.push(new google.maps.LatLng(coordArray[i].x, coordArray[i].y));
  }
  return GMcoords;
}
var array2JSTS = function(boundaries) {
  var coordinates = [];
  for (var i = 0; i < boundaries.length; i++) {
    coordinates.push(new jsts.geom.Coordinate(
        boundaries[i].lat, boundaries[i].lng));
  }
  return coordinates;
};
/* Always set the map height explicitly to define the size of the div
       * element that contains the map. */
#map {
  height: 100%;
}

/* Optional: Makes the sample page fill the window. */
html,
body {
  height: 100%;
  margin: 0;
  padding: 0;
}
<!DOCTYPE html>
<html>
  <head>
    <title>Data Layer: Polygon</title>
    <script src="https://polyfill.io/v3/polyfill.min.js?features=default"></script>
    <script src="https://cdn.jsdelivr.net/npm/jsts@2.8.1/dist/jsts.js"></script>
    <!-- jsFiddle will insert css and js -->
  </head>
  <body>
    <div id="map"></div>

    <!-- Async script executes immediately and must be after any DOM elements used in callback. -->
    <script
      src="https://maps.googleapis.com/maps/api/js?key=AIzaSyCkUOdZ5y7hMm0yrcCQoCvLwzdM6M8s5qk&callback=initMap&v=weekly&channel=2"
      async
    ></script>
  </body>
</html>