【问题标题】:Drawing a circle Google Static Maps画一个圆圈谷歌静态地图
【发布时间】:2011-11-11 03:08:39
【问题描述】:

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

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

【问题讨论】:

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


【解决方案1】:

游戏有点晚了,但我没有找到解决我的问题(仅服务器端 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.'" />'

【讨论】:

  • 欢迎来到 Stack Overflow!虽然这在理论上可以回答这个问题,it would be preferable 在此处包含答案的基本部分,并提供链接以供参考。
  • @jomac 必须承认,一个很好的选择,请按照肖恩·钦的建议,并在您的答案上发布 PHP 代码和解释以供将来参考!
  • 谢谢大家,我已经扩展了我的答案。
  • @Jomac 您的代码非常有用,我实现了它,但我也实现了。 Polygon 是否还有其他与上述相同的示例?
【解决方案2】:
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 版本

【讨论】:

  • 非常有用!
  • 在此版本中,rad 以米为单位。
  • 可以填满圈子吗?
  • 填充:只需添加|fillcolor:${circleHexColor}(注意|分隔符而不是:
  • @user1274820 你必须连接到staticMapSrc 字符串,例如:staticMapSrc += string
【解决方案3】:

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

它使用来自Google Maps Android API Utility LibraryPolyUtil 类对路径进行编码。

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 变量实际代表什么。
【解决方案4】:

我认为不可能在静态 Google 地图上画一个圆圈。您需要用折线近似圆(最好在encoded format 中)。这已经在Stackoverflow 中提到过,并且已经在例如Free Map Tools.

【讨论】:

  • 感谢您的链接,我正要沿着用多边形画一个圆的路线走下去,但想想,也许这有点矫枉过正。不幸的是,第二个链接适用于 API2。再次感谢
  • “Google Static Maps API v2”不是“Google Maps API v2/v3”的一部分,它是一个单独的 API。因此它也与 Google Maps API v3 一起使用。在免费地图工具中,您可以生成带有近似圆的静态地图。您将从 Google Maps API v3 生成相同的格式。
  • 对不起,我很抱歉,我会看看我能做什么,并在完成后发布代码。
【解决方案5】:

分享我的 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;
}

【讨论】:

    【解决方案6】:

    此解决方案使用功能更丰富的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));
    }
    

    【讨论】:

      【解决方案7】:

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

      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)
      

      【讨论】:

        猜你喜欢
        • 2016-07-30
        • 1970-01-01
        • 2011-04-15
        • 1970-01-01
        • 2013-02-12
        • 1970-01-01
        • 2018-11-17
        • 1970-01-01
        • 2015-08-22
        相关资源
        最近更新 更多