绘制圆形 Google 静态地图

19

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

Example of load

但是我有一个问题,就是当用户选择要使用哪种搜索时,它可以很好地加载多边形,如果他们绘制了多边形,并且如果它是圆形,则会拉出中心点的标记。然而,我需要在静态地图中编写一个画圆的函数。


可能是在Google静态地图上画圆的方法的重复问题。 - Jasper de Vries
7个回答

29
有点晚了,但我找到的解决方法都没有解决我的问题(仅限服务器端的PHP,不使用JavaScript)。最终我成功了,并在此详细说明了我的方法:http://jomacinc.com/map-radius/,简短版本如下。
这个PHP函数将返回一个编码的折线字符串,其中包含指定点周围以及指定半径内的纬度/经度坐标点。这个函数需要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
欢迎来到Stack Overflow!虽然这个回答在理论上是正确的,但更好的做法是在这里包含回答的基本部分,并提供参考链接。 - Shawn Chin
@jomac 必须承认,这是一个很好的替代方案,请遵循Shawn Chin的建议,在你的答案中发布PHP代码和解释以供将来参考! - rickyduck
1
谢谢大家,我已经扩展了我的回答。 - Jomac
@Jomac,你的代码非常有用,我已经实现了它,但是我还想知道是否有其他关于多边形的示例与你上面的代码相同? - jalpa

23
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版本


非常有用! - iulial
1
在这个版本中,“rad”以米为单位。 - Azmisov
1
能否填充圆形? - Made in Moon
2
填充:只需添加 |fillcolor:${circleHexColor}(注意使用 | 分隔符而不是 :)。 - Made in Moon
1
@user1274820,你需要将string连接到staticMapSrc字符串后面,如下所示:staticMapSrc += string - Made in Moon
显示剩余3条评论

6

根据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;
    }
}

这个答案应该得到更多的赞,因为它为所有变量使用了清晰的名称,并且在理解那些初始的 rdbrng 变量实际代表方面非常有帮助。 - Ciprian Teiosanu

2

我认为在静态的谷歌地图上画一个圆是不可能的。你需要用折线来近似表示这个圆(最好使用编码格式)。这已经在Stackoverflow中提到过,并且例如Free Map Tools已经演示了这一点。


谢谢提供链接,我本来打算用多边形来画圆的,但想想可能有点过头了。不过第二个链接是针对API2的,有些遗憾。再次感谢。 - rickyduck
1
"Google Static Maps API v2" 不是 "Google Maps API v2/v3" 的一部分,它是一个独立的API。因此,它也可以与Google Maps API v3一起使用。在Free Map Tools中,您可以生成带有近似圆形的静态地图。您可以从Google Maps API v3中生成相同格式的地图。 - Jiri Kriz
啊,抱歉我的错,我会看看我能做什么并在完成后发布代码。 - rickyduck

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;
}

0

此解决方案使用更加灵活多变的Canvas API来在地图图像上进行绘制所有代码均用Typescript编写,因此如果您使用Javascript,只需删除类型声明即可

CANVAS的优点:

  • 更容易绘制形状。
  • 这些形状也可以在无需重新请求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));
}

0

Python版本,使用Polyline库对多边形进行编码

import math, polyline

def g_map_circle(lat,lng,radius,detail=8):
  points = []
  r = 6371
  pi = math.pi

  _lat = (lat * pi) /180
  _lng = (lng * pi) /180
  d = radius / r

  i = 0
  while i <= 360:
    i = i + detail
    brng = i * pi /180

    p_lat = math.asin(math.sin(_lat) * math.cos(d) + math.cos(_lat) * math.sin(d) * math.cos(brng));
    p_lng = (_lng + math.atan2(math.sin(brng) * math.sin(d) * math.cos(_lat), math.cos(d) - math.sin(_lat) * math.sin(p_lat))) * 180 / pi
    p_lat = (p_lat * 180) /pi
    points.append((p_lat,p_lng))

  return polyline.encode(points)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接