Вычисление наложения дня/ночи для Карт Google

Я пытаюсь найти способ создания наложения для Google Maps API V3, который показывает освещенные солнцем области мира. Это основной результат, который я ищу:

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

Но нужно больше контролировать внешний вид (в идеале только 10% черного наложения без огней города). Я могу нарисовать фигуру в элементе canvas, но не могу понять, как рассчитать форму, основанную на наклоне и повороте земли и т.д.

Любая помощь будет оценена.

EDIT: Javascript

Я все еще не знаю, где реализовать переменную y-offset ниже. Мне также нужно выяснить, как отрегулировать/растянуть у-смещение от этого (равные дальние широтные линии) до меркатора (ближе к полюсам).

// 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();
}

Ответ 1

Если вы хотите, чтобы он был физически точным, вам необходимо рассмотреть два смещения: вертикальный (в зависимости от текущей даты) и горизонтальный (в зависимости от текущего времени).

Горизонтальное смещение X можно рассчитать, просмотрев текущее время в каком-либо фиксированном географическом местоположении на земле. Смещение тени будет 0 в полночь и будет увеличиваться на 1/86400 за каждую секунду после полуночи. Итак, формальный

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

Сдвиг по вертикали будет меняться между Solstices от 21 июня и 22 декабря (если это не високосный год, где Solstices находятся в июне 20 и 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 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";

Вы можете переносить это вычисление на любой желаемый язык.

Ответ 2

Вы попросили больше контролировать внешний вид

Просмотрите Geocommons JS api, которые могут быть более подходящими для вашей цели, чем Google Maps.

Если вы планируете гибкость, GEOS было бы предпочтительнее рисовать фигуру для конкретной проекции. Я знаю, что есть привязки php, но я не использовал их сам.

У Хелмера Аслаксена есть отличная запись о небесной математике, которая должна помочь вам создать алоризм, чтобы нарисовать многоугольник с солнечной областью, используя геосферы. Вы можете проверить свой код на измеренное время восхода/захода солнца для точности.

Изменить 1

Должны быть Python и Google Maps, вы говорите?