画一个圆圈谷歌静态地图

时间:2011-09-06 08:28:10

标签: google-maps geometry google-static-maps

我在v3 api上绘制了一个Google Maps Circle。当用户绘制圆(或多边形,如果他们选择),他们可以将数据保存到服务器。如果用户选择了径向搜索,则中心坐标和以英尺为单位的半径将存储到数据库中。这意味着当用户重新加载他的搜索时,它可以再次通过圆圈(如下所示)。

Example of load

然而,我遇到了一个问题,即当用户选择他们想要使用的搜索时。如果它们绘制了一个多边形,它会加载多边形,如果它是一个圆形,它会拉过中心的标记。然而我需要的是静态地图中的一个函数来绘制一个圆。

7 个答案:

答案 0 :(得分:26)

游戏中有点晚了,但我发现没有解决我的问题(只有服务器端的php,没有javascript)。 我最终到达那里并详细说明了我的方法:http://jomacinc.com/map-radius/,简短版本如下。

这个PHP函数将在指定点周围和指定半径的圆周内返回一个lat / lng点的编码折线字符串。该函数需要Gabriel Svennerberg的PHP折线编码类(http://www.svennerberg.com/examples/polylines/PolylineEncoder.php.txt)。

function GMapCircle($Lat,$Lng,$Rad,$Detail=8){
 $R    = 6371;

 $pi   = pi();

 $Lat  = ($Lat * $pi) / 180;
 $Lng  = ($Lng * $pi) / 180;
 $d    = $Rad / $R;

 $points = array();
 $i = 0;

 for($i = 0; $i <= 360; $i+=$Detail):
   $brng = $i * $pi / 180;

   $pLat = asin(sin($Lat)*cos($d) + cos($Lat)*sin($d)*cos($brng));
   $pLng = (($Lng + atan2(sin($brng)*sin($d)*cos($Lat), cos($d)-sin($Lat)*sin($pLat))) * 180) / $pi;
   $pLat = ($pLat * 180) /$pi;

   $points[] = array($pLat,$pLng);
 endfor;

 require_once('PolylineEncoder.php');
 $PolyEnc   = new PolylineEncoder($points);
 $EncString = $PolyEnc->dpEncode();

 return $EncString['Points'];
}

您现在可以使用上述功能创建静态地图。

/* set some options */
$MapLat    = '-42.88188'; // latitude for map and circle center
$MapLng    = '147.32427'; // longitude as above
$MapRadius = 100;         // the radius of our circle (in Kilometres)
$MapFill   = 'E85F0E';    // fill colour of our circle
$MapBorder = '91A93A';    // border colour of our circle
$MapWidth  = 640;         // map image width (max 640px)
$MapHeight = 480;         // map image height (max 640px)

/* create our encoded polyline string */
$EncString = GMapCircle($MapLat,$MapLng, $MapRadius);

/* put together the static map URL */
$MapAPI = 'http://maps.google.com.au/maps/api/staticmap?';
$MapURL = $MapAPI.'center='.$MapLat.','.$MapLng.'&size='.$MapWidth.'x'.$MapHeight.'&maptype=roadmap&path=fillcolor:0x'.$MapFill.'33%7Ccolor:0x'.$MapBorder.'00%7Cenc:'.$EncString.'&sensor=false';

/* output an image tag with our map as the source */
echo '<img src="'.$MapURL.'" />'

答案 1 :(得分:15)

function GMapCircle(lat,lng,rad,detail=8){

var uri = 'https://maps.googleapis.com/maps/api/staticmap?';
var staticMapSrc = 'center=' + lat + ',' + lng;
staticMapSrc += '&size=100x100';
staticMapSrc += '&path=color:0xff0000ff:weight:1';

var r    = 6371;

var pi   = Math.PI;

var _lat  = (lat * pi) / 180;
var _lng  = (lng * pi) / 180;
var d    = (rad/1000) / r;

var i = 0;

for(i = 0; i <= 360; i+=detail) {
    var brng = i * pi / 180;

    var pLat = Math.asin(Math.sin(_lat) * Math.cos(d) + Math.cos(_lat) * Math.sin(d) * Math.cos(brng));
    var pLng = ((_lng + Math.atan2(Math.sin(brng) * Math.sin(d) * Math.cos(_lat), Math.cos(d) - Math.sin(_lat) * Math.sin(pLat))) * 180) / pi;
    pLat = (pLat * 180) / pi;

   staticMapSrc += "|" + pLat + "," + pLng;
}

return uri + encodeURI(staticMapSrc);}

Javascript版

答案 2 :(得分:2)

我认为在静态Google地图上绘制圆圈是不可能的。您需要通过折线近似圆(最好在encoded format)。 Stackoverflow中已经提到过这一点,例如, Free Map Tools

答案 3 :(得分:2)

基于answer from Jomac,这是相同代码的Java / Android版本。

它使用PolyUtil中的Google Maps Android API Utility Library类对路径进行编码。

import android.location.Location;

import com.google.android.gms.maps.model.LatLng;
import com.google.maps.android.PolyUtil;

import java.util.ArrayList;

public class GoogleStaticMapsAPIServices
{
    private static final double EARTH_RADIUS_KM = 6371;

    private static String GOOGLE_STATIC_MAPS_API_KEY = "XXXXXXXXXXXXX";

    public static String getStaticMapURL(Location location, int radiusMeters)
    {
        String pathString = "";
        if (radiusMeters > 0)
        {
            // Add radius path
            ArrayList<LatLng> circlePoints = getCircleAsPolyline(location, radiusMeters);

            if (circlePoints.size() > 0)
            {
                String encodedPathLocations = PolyUtil.encode(circlePoints);
                pathString = "&path=color:0x0000ffff%7Cweight:1%7Cfillcolor:0x0000ff80%7Cenc:" + encodedPathLocations;
            }
        }

        String staticMapURL = "https://maps.googleapis.com/maps/api/staticmap?size=640x320&markers=color:red%7C" +
                location.getLatitude() + "," + location.getLongitude() +
                pathString +
                "&key=" + GOOGLE_STATIC_MAPS_API_KEY;

        return staticMapURL;
    }

    private static ArrayList<LatLng> getCircleAsPolyline(Location center, int radiusMeters)
    {
        ArrayList<LatLng> path = new ArrayList<>();

        double latitudeRadians = center.getLatitude() * Math.PI / 180.0;
        double longitudeRadians = center.getLongitude() * Math.PI / 180.0;
        double radiusRadians = radiusMeters / 1000.0 / EARTH_RADIUS_KM;

        double calcLatPrefix = Math.sin(latitudeRadians) * Math.cos(radiusRadians);
        double calcLatSuffix = Math.cos(latitudeRadians) * Math.sin(radiusRadians);

        for (int angle = 0; angle < 361; angle += 10)
        {
            double angleRadians = angle * Math.PI / 180.0;

            double latitude = Math.asin(calcLatPrefix + calcLatSuffix * Math.cos(angleRadians));
            double longitude = ((longitudeRadians + Math.atan2(Math.sin(angleRadians) * Math.sin(radiusRadians) * Math.cos(latitudeRadians), Math.cos(radiusRadians) - Math.sin(latitudeRadians) * Math.sin(latitude))) * 180) / Math.PI;
            latitude = latitude * 180.0 / Math.PI;

            path.add(new LatLng(latitude, longitude));
        }

        return path;
    }
}

答案 4 :(得分:0)

分享我的C#版本

private string GMapCircle(double lat, double lng, double rad, int detail = 8)
{
    const string uri = "https://maps.googleapis.com/maps/api/staticmap?";
    var staticMapSrc = "center=" + lat + "," + lng;
    staticMapSrc += "&zoom=16";
    staticMapSrc += "&maptype=satellite";
    staticMapSrc += "&key=[YOURKEYHERE]";
    staticMapSrc += "&size=640x426";
    staticMapSrc += "&path=color:0xff0000ff:weight:1";

    const int r = 6371;
    const double pi = Math.PI;

    var latAux = (lat * pi) / 180;
    var longAux = (lng * pi) / 180;
    var d = (rad / 1000) / r;

    var i = 0;

    if (rad > 0)
    {
        for (i = 0; i <= 360; i += detail)
        {
            var brng = i * pi / 180;

            var pLat = Math.Asin(Math.Sin(latAux) * Math.Cos(d) + Math.Cos(latAux) * Math.Sin(d) * Math.Cos(brng));
            var pLng = ((longAux + Math.Atan2(Math.Sin(brng) * Math.Sin(d) * Math.Cos(latAux), Math.Cos(d) - Math.Sin(latAux) * Math.Sin(pLat))) * 180) / pi;
            pLat = (pLat * 180) / pi;

            staticMapSrc += "|" + pLat + "," + pLng;
        }
    }
    else
    {
        //TODO - Add marker
    }

    return uri + staticMapSrc;
}

答案 5 :(得分:0)

此解决方案使用功能更强大的Canvas API绘制地图图像所有代码都在 Typescript 中,因此,如果您使用的是 Javascript ,只需删除类型声明即可。

使用画布的优势

  • 绘制形状更容易。
  • 也可以修改这些形状,而无需重新向Google请求地图图像。
  • 绘图“层”可以独立于基础地图图像进行序列化。

用法

// DEFINE BASIC VARIABLES
const latitude: -34.3566871,
const longitude: 18.4967666
const mapZoom = 12;
const imageWidth: 100;
const imageHeight: 100;

// INVOKE UTILITY FUNCTION
savePlaceImage({

  // SET BASIC PROPS
  latitude,
  longitude,
  mapZoom,
  imageWidth,
  imageHeight,
  fileName: 'Cape Point',

  // DRAW IMAGE USING CANVAS API
  draw: ctx => {

    // draw location as dot
    ctx.fillStyle = '#FF3366';
    ctx.beginPath();
    ctx.arc(imageWidth / 2, imageHeight / 2, 10, 0, 2 * Math.PI);
    ctx.fill();

    // draw circle around location with 1 kilometer radius
    ctx.strokeStyle = '#0000FF';
    ctx.beginPath();
    ctx.arc(imageWidth / 2, imageHeight / 2, pixelsPerMeter(latitude) * 1000, 0, 2 * Math.PI);
    ctx.stroke();
  }
})

实用工具

function savePlaceImage(
  config: {
    latitude: number,
    longitude: number,
    mapZoom: number,
    imageWidth: number,
    imageHeight: number,
    fileName: string,
    draw: (ctx: CanvasRenderingContext2D) => void,
  },
) {

  // DOWNLOAD MAP IMAGE FROM GOOGLE'S STATIC MAPS API AS A BLOB
  return from(axios.get<Blob>(`https://maps.googleapis.com/maps/api/staticmap`, {
    params: {
      key: GOOGLE_MAPS_API_KEY,
      size: `${config.imageWidth}x${config.imageHeight}`,
      zoom: `${config.mapZoom}`,
      center: `${config.latitude},${config.longitude}`,
      style: 'feature:all|element:labels|visibility:off',
    },
    responseType: 'blob',

  // CONVERT BLOB TO BASE64 ENCODED STRING
  }).then(response => {
    const reader = new FileReader();
    reader.readAsDataURL(response.data);
    return new Promise<string>(resolve => reader.onloadend = () => resolve(reader.result as string));

  // CREATE HTML IMG ELEMENT, SET IT'S SRC TO MAP IMAGE, AND WAIT FOR IT TO LOAD
  }).then(response => {
    const image = document.createElement('img');
    image.src = response;
    return new Promise<HTMLImageElement>(resolve => image.onload = () => resolve(image));

  // CREATE HTML CANVAS ELEMENT, THEN DRAW ON TOP OF CANVAS USING CANVAS API, THEN CONVERT TO BLOB
  }).then(image => {
    const canvas = document.createElement('canvas');
    canvas.width = config.imageWidth;
    canvas.height = config.imageHeight;
    const ctx = canvas.getContext('2d');
    ctx.drawImage(image, 0, 0);
    config.draw(ctx);
    return new Promise<Blob>(resolve => canvas.toBlob(blob => resolve(blob)));

  // ATTACH BLOB TO HTML FORM WHICH CONVERTS IT TO A FILE TO BE POSTED, THEN SEND FILE TO SERVER
  }).then(blob => {
    const form = new FormData();
    form.append('blob', blob, `${config.fileName}.png`);
    const file = form.get('blob') as File;
    return axios.post<{ file }>('https://www.my-api.com/save-image', form);
  }));
}

function pixelsPerMeter(latitude: number) {
  const radiusOfEarthInKilometers = 6371;
  return Math.cos(latitude * Math.PI / 180) * 2 * Math.PI * radiusOfEarthInKilometers / (256 * Math.pow(2, 12));
}

答案 6 :(得分:0)

python版本,用于对多边形进行编码的折线库

  {array.map((item, index) => {
    if (index === array.length - 1) {
      return item;
    }
    return [item, "+"];
  })}