计算Google地图的日/夜叠加层

时间:2011-08-17 23:24:18

标签: google-maps

我正在努力寻找一种方法来为Google Maps API V3创建一个叠加层,以显示世界上阳光照射的区域。这是我要找的基本结果:

http://www.daylightmap.com/index.php

但是想要更好地控制外观(理想情况下只有10%的黑色覆盖,没有城市灯光)。我可以在画布元素中绘制形状,但无法弄清楚如何根据地球的倾斜和旋转等来计算形状。

任何帮助都将不胜感激。

编辑:Javascript

我仍然不知道在哪里实现y-offset变量。我还需要弄清楚如何调整/拉伸从这个(等距离纬度线)到墨卡托(在极点附近)的y偏移。

// Get the canvas element
var ctx = document.getElementById('canvas').getContext('2d');
ctx.clearRect( 0, 0, 800, 620 );

// Current time
var map_width = $("#canvas").width();
var map_height = $("#canvas").height();
var now = new Date();
var cur_hour = now.getHours();
var cur_min = now.getMinutes();
var cur_sec = now.getSeconds();
var cur_jul = now.julianDate() - 1;
var equinox_jul = new Date(now.getFullYear(),2,20,24,-now.getTimezoneOffset(),0,0).julianDate() - 1;

var offset_x = Math.round(((cur_hour*3600 + cur_min*60 + cur_sec)/86400) * map_width); // Resulting offset X
var offset_sin = ((365.25 - equinox_jul + cur_jul)%365.25)/365.25; // Day offset, mapped on the equinox offset
var offset_sin_factor = Math.sin(offset_sin * 2 * Math.PI); // Sine wave offset
var offset_y = offset_sin_factor * 23.44; // Map onto angle. Maximum angle is 23.44° in both directions

var degrees_per_radian = 180.0 / Math.PI;
var offset_y_mercator = Math.atan( offset_y.sinh() ) * degrees_per_radian;


// Global wave variables
var period = 1 / 6.28291;   // Original value 2Pi: 6.28291
var amplitude = (map_height/2);

// Draw vertical lines: One for each horizontal pixel on the map
for( var x = 0; x <= map_width; x++ ) {
    ctx.beginPath();

    // Start at the bottom of the map
    ctx.moveTo(x,map_height);

    // Get the y value for the x pixel on the sine wave
    var y = (map_height/2) - (Math.sin( (offset_x / map_width) / period ) * amplitude);

    offset_x++;

    // Draw the line up to the point on the sine wave
    ctx.lineTo(x,y);
    ctx.stroke();
}

2 个答案:

答案 0 :(得分:7)

如果您希望它在物理上准确,您实际上需要考虑两个偏移:垂直(取决于当前日期)和水平(取决于当前时间)。

可以通过查看地球上某个固定地理位置上的当前时间来计算水平偏移X.午夜阴影偏移为0,午夜后每秒增加1/86400。所以公式是

offsetX = (curHour*3600 + curMinute*60 + curSeconds)/86400

垂直偏移量将在6月21日至12月22日的Solstices之间变化(如果它不是闰年,其中冬至将在6月20日和12月21日)。两个方向的最大角度为23.44°。我们在每个半球有90°,在两个至日之间有365/2 = 182.5天,我们正在使用圆周运动的映射,因此必须使用sin() - 函数。正弦波的波长是2pi,因此我们需要pi为一年垂直偏移Y的一半。

请注意,我没有考虑闰秒,所以在遥远的过去/未来计算可能有点偏差。

// current time
$curHour = date("H");
$curMin  = date("i");
$curSec  = date("s");

// resulting offset X
$offsetX = ($curHour*3600 + $curMin*60 + $curSec)/86400;


echo "======== OFFSET X ==========\n";
echo "curHour:      $curHour\n";
echo "curMin:       $curMin\n";
echo "curSec:       $curSec\n";
echo "offsetX:      $offsetX\n\n";


// spring equinox date as day of year
$equinox = date("z", mktime(0, 0, 0, 3, 20));

// current day of year
    // first line is for testing purposes
//$curDay = date("z", mktime(0, 0, 0, 6, 21));
    $curDay = date("z");

// Day offset, mapped on the equinox offset
$offsetSin = ((365.25 - $equinox + $curDay)%365.25)/365.25;

// sinus wave offset
$offsetSinFactor = sin($offsetSin * 2 * pi());

// map onto angle
$offsetY = $offsetSinFactor * 23.44;

// optional: Mercator projection
$degreesPerRadian = 180.0 / pi();
$offsetYmercator = atan(sinh($offsetY)) * $degreesPerRadian;

// missing: mapping onto canvas height (it's currently
// mapped on $offsetY = 90 as the total height of the
// canvas.


echo "========= OFFSET Y =========\n";
echo "equinox day:  $equinox\n";
echo "curDay:       $curDay\n";
echo "offsetSin:    $offsetSin\n";
echo "offsetSinFac: $offsetSinFactor\n";
echo "offsetY:      $offsetY\n";
echo "offsetYmerc:  $offsetYmercator\n";

您应该能够将此计算移植到您想要的任何语言。

答案 1 :(得分:1)

您要求更多控制外观

查看Geocommons JS api,这可能比Google地图更适合您的目的。

如果你想要灵活性,GEOS比绘制特定投影的形状更可取。 I know there are php bindings,但我自己没有使用它们。

Helmer Aslaksen有一篇关于heavenly mathematics的精彩文章,可以帮助你创建一个算法来使用地理绘制阳光照射的多边形区域。您可以针对measured sunrise/sunset times测试代码的准确性。

编辑1

你说必须是Python和谷歌地图吗?