Как перевести широту и долготу в пиксели?

Есть массив координат на местности: широта и долгота:
coords = [
	{'lat':55.992405, 'lon':37.263676},
	{'lat':55.992622, 'lon':37.263103},
	{'lat':55.992840, 'lon':37.262547},
]

Нужно отобразить эти точки на канве. Дан размер канвы в пкс и разрешение в км на пиксель. Первая точка должна быть нарисована в центре канвы. Тип проекции особой роли не играет (любая картографическая). Подскажите формулу по которой можно перевести координаты в пространстве в пиксели на канве?
  • Вопрос задан
  • 1900 просмотров
Решения вопроса 1
fedorez
@fedorez
Хатуль мадан
в общем да.
а если точность нужна то всё посложнее будет.
земля не круглая, она геоид, на практике юзают эллипсоид. от поставщика данных зависит какой.
географические координаты переводят в плоские по довольно громоздким формулам согласно ГОСТ Р 51794-2008

ну а потом согласуете эти плоские координаты со своими пикселями в зависимости от разрешения и окна просмотра

я когда-то плотно сидел на этой теме и написал библиотеку на C# которая расчёт по этим формулам обеспечивала.
вот вам оттуда кусок
простите за столь неряшливое оформление, валяюсь болею, не имею доступа к нормальному коду, выдрал из старого архива, пишу через утюг.
но думаю разберётесь

spoiler
/// <summary>
		/// Возвращает плоскую координату X для пары угловых координат (зона проекции определена константой)
		/// </summary>
		/// <param name="Bd">Широта, град</param>
		/// <param name="Ld">Долгота, град</param>
		/// <returns></returns>
		static public double flat_x(double Bd, double Ld)
		{
            int N = 6;
            //int N = GetProjectionZone(Ld);

			return flat_x(Bd, Ld, N);
		}

		/// <summary>
		/// Возвращает плоскую координату X для пары угловых координат в заданной зоне проекции
		/// </summary>
		/// <param name="Bd">Широта, град</param>
		/// <param name="Ld">Долгота, град</param>
        /// <param name="N">Зона проекции</param>
		/// <returns></returns>
		static public double flat_x(double Bd, double Ld, int N)
		{
			double b, L, l_rad;
			b = Bd * Math.PI / 180;
			L = Ld * Math.PI / 180;
			l_rad = (Ld - (3 + 6 * (N - 1))) / 57.29577951;
			double Result, R1, R2, R3, R4, M1, M2;
			R1 = 1594561.25 + 5336.535 * Math.Pow((Math.Sin(b)), 2) + 26.79 * Math.Pow((Math.Sin(b)), 4) + 0.149 * Math.Pow((Math.Sin(b)), 6);
			R2 = 672483.4 - 811219.9 * Math.Pow((Math.Sin(b)), 2) + 5420.0 * Math.Pow((Math.Sin(b)), 4) - 10.6 * Math.Pow((Math.Sin(b)), 6);
			R3 = 278194 - 830174 * Math.Pow((Math.Sin(b)), 2) + 572434 * Math.Pow((Math.Sin(b)), 4) - 16010 * Math.Pow((Math.Sin(b)), 6);
			R4 = 109500 - 574700 * Math.Pow((Math.Sin(b)), 2) + 863700 * Math.Pow((Math.Sin(b)), 4) - 398600 * Math.Pow((Math.Sin(b)), 6);
			M2 = (R2 + (Math.Pow(l_rad, 2)) * (R3 + (Math.Pow(l_rad, 2)) * (R4)));
			M1 = (R1 + (Math.Pow(l_rad, 2)) * M2);
			Result = 6367558.4698 * b - Math.Sin(2 * b) * (16002.89 + 66.9607 * Math.Pow((Math.Sin(b)), 2) + 0.3515 * Math.Pow((Math.Sin(b)), 4) - Math.Pow(l_rad, 2) * M1);
			return Result;
		}

		/// <summary>
        /// Возвращает плоскую координату Y для пары угловых координат (зона проекции определена константой)
        /// </summary>
		/// <param name="Bd">Широта, град</param>
		/// <param name="Ld">Долгота, град</param>		
		/// <returns></returns>
		static public double flat_y(double Bd, double Ld)
		{
            int N = 6;
            //int N = GetProjectionZone(Ld);
			return flat_y(Bd, Ld, N);
		}

		/// <summary>
        /// Возвращает плоскую координату Y для пары угловых координат в заданной зоне проекции
        /// </summary>
		/// <param name="Bd">Широта, град</param>
		/// <param name="Ld">Долгота, град</param>
		/// <param name="N">Зона проекции</param>
		/// <returns></returns>
		static public double flat_y(double Bd, double Ld, int N)
		{
			double b, L, l_rad;
			b = Bd * Math.PI / 180;
			L = Ld * Math.PI / 180;
			l_rad = (Ld - (3 + 6 * (N - 1))) / 57.29577951;
			double Result, R1, R2, R3, R4, M1;
			R1 = 6378245 + 21346.1415 * Math.Pow((Math.Sin(b)), 2) + 107.159 * Math.Pow((Math.Sin(b)), 4) + 0.5977 * Math.Pow((Math.Sin(b)), 6);
			R2 = 1070204.16 - 2136826.66 * Math.Pow((Math.Sin(b)), 2) + 17.98 * Math.Pow((Math.Sin(b)), 4) - 11.99 * Math.Pow((Math.Sin(b)), 6);
			R3 = 270806 - 1523417 * Math.Pow((Math.Sin(b)), 2) + 1327645 * Math.Pow((Math.Sin(b)), 4) - 21701 * Math.Pow((Math.Sin(b)), 6);
			R4 = 79690 - 866190 * Math.Pow((Math.Sin(b)), 2) + 1730360 * Math.Pow((Math.Sin(b)), 4) - 945460 * Math.Pow((Math.Sin(b)), 6);

			M1 = Math.Pow(l_rad, 2) * (R2 + Math.Pow(l_rad, 2) * (R3 + Math.Pow(l_rad, 2) * (R4)));
			Result = (5 + 10 * N) * Math.Pow(10, 5) + l_rad * Math.Cos(b) * (R1 + M1);
			return Result;
		}

        /// <summary>
        /// Возвращает значение зоны проекции для заданного значения долготы
        /// </summary>
        /// <param name="Ld">Долгота, град</param>
        /// <returns>Значение зоны</returns>
        static public int GetProjectionZone(double Ld)
        {
            return Convert.ToInt32(Microsoft.VisualBasic.Conversion.Fix((6 + Ld) / 6));
        }

        /// <summary>
        /// Возвращает значение зоны проекции, пригодное для проецирования большинства из массива долгот
        /// </summary>
        /// <param name="longitudes">Массив долгот, град</param>
        /// <returns></returns>
        static public int GetProjectionZone(double[] longitudes)
        {
            // Значения зон
            List<int> lst_values = new List<int>();
            // Количество вхождений
            List<int> lst_valueCounts = new List<int>();

            foreach (double longitude in longitudes)
            {
                // Зона для текущей долготы
                int currentN = GetProjectionZone(longitude);

                // Если значение новое, то занесем его в список и создадим нулевой счетчик этого значения
                if (!lst_values.Exists(p => p == currentN))
                {
                    lst_values.Add(currentN);
                    lst_valueCounts.Add(0);
                }

                int index = lst_values.IndexOf(currentN);   // Иднекс этого значения
                lst_valueCounts[index] += 1;                // Увеличим счетчик этого значения
            }

            // Индекс значения с максимальным вхождением
            int maxIndex = lst_valueCounts.IndexOf(lst_valueCounts.Max());

            return lst_values[maxIndex];
        }

		static public double DegreesMinutesToDouble(int degrees, double minutes)
		{
			return DegreesMinutesSecondsToDouble((double)degrees, minutes, 0);
		}

		static public double DegreesMinutesSecondsToDouble(int degrees, int minutes, double seconds)
		{
            return DegreesMinutesSecondsToDouble((double)degrees, minutes, seconds);
		}

        static public double DegreesMinutesSecondsToDouble(double degrees, double minutes, double seconds)
        {
            minutes += seconds / 60;

            degrees += minutes / 60;

            return degrees;
        }
}

Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 3
longclaps
@longclaps
При изменении широты ты движешься по дуге большого круга - по меридиану. Соответственно 1град==1/57радиана<~>1/57радиуса_Земли
При изменении долготы ты движешься по параллели - кругу с радиусом, пропорциональным косинусу широты.
Вот и вся формула.
Ответ написан
@koanvl
Может наглядный пример вам как-то поможет: тыц
Ответ написан
Комментировать
@maxfox
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы