Работа с картами. GMAP C#

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

На написание данной статьи меня подтолкнуло не столь большое наличие текстовой информации по работе с GMap на просторах интернета, как хотелось бы.

Стояла задача визуализировать на карте положение объектов по известным координатам для дальнейшего анализа и выявления каких-либо закономерностей расположения этих объектов.

Первое что могу порекомендовать из своего опыта – начать работать сразу с WinForm, а не с WPF т.к. проще и больше информации можно добыть именно по ней.

Краткий словарь терминов, которые встретятся в моей статье:

  • Тайловая, плиточная или знакоместная графика (от англ. tile — плитка) — метод создания больших изображений (как правило, уровней в компьютерных играх) из маленьких фрагментов одинаковых размеров

  • Тайл — элемент разбиения

  • Слой можно сравнить с прозрачной пленкой, наложенной на изображение, на который наносятся пользовательские элементы

  • Тепловая карта — графическое представление данных, где индивидуальные значения в таблице отображаются при помощи цвета. Термин «heatmap» изначально был придуман и официально зарегистрирован как товарный знак разработчиком программного обеспечения Кормаком Кинни в 1991 году.

  • Полигон — многоугольник, минимальная поверхность для визуализации в трёхмерной графике.

  • Маркер — отметка на карте

Переходим к работе с библиотекой.

Нам понадобятся GMap.NET.Core.dll и GMap.NET.WindowsForms.dll Для установки можно использовать Nuget.

или скачать dll из интернета и вставить ссылки

Щелкните правой кнопкой мыши панель инструментов и нажмите «Выбрать элементы…». В появившемся окне с помощью кнопки «Обзор» найдите файл GMap.NET.WindowsForms.dll:

После закрытия окна новый элемент управления появится в наборе инструментов. Перетаскиваем его в форму и настраиваем положение и размеры.

Создаем эвент (событие) загрузки контрала.

Вставляем код:

GMap.NET.GMaps.Instance.Mode = GMap.NET.AccessMode.ServerAndCache; //выбор подгрузки карты – онлайн или из ресурсов
            gMapControl1.MapProvider = GMap.NET.MapProviders.GoogleMapProvider.Instance; //какой провайдер карт используется (в нашем случае гугл) 
            gMapControl1.MinZoom = 2; //минимальный зум
            gMapControl1.MaxZoom = 16; //максимальный зум
           gMapControl1.Zoom = 4; // какой используется зум при открытии
            gMapControl1.Position = new GMap.NET.PointLatLng(66.4169575018027, 94.25025752215694);// точка в центре карты при открытии (центр России)
            gMapControl1.MouseWheelZoomType = GMap.NET.MouseWheelZoomType.MousePositionAndCenter; // как приближает (просто в центр карты или по положению мыши)
            gMapControl1.CanDragMap = true; // перетаскивание карты мышью
            gMapControl1.DragButton = MouseButtons.Left; // какой кнопкой осуществляется перетаскивание
            gMapControl1.ShowCenter = false; //показывать или скрывать красный крестик в центре
            gMapControl1.ShowTileGridLines = false; //показывать или скрывать тайлы

Результат выполнения кода. Карта делится на квадраты, называемые тайлами. Тайлы и крест по центру включены.

Работа с библиотекой. Маркеры

Чтобы отобразить объект на карте мы создаем маркер с координатами объекта и выводим его на карту. Теперь создадим функцию, возвращающую тот самый маркер. В коде ниже функция принимает параметры: классы Us откуда я беру название и координаты и необязательный параметр gMarkerGoogleType – тип маркера.

private GMarkerGoogle GetMarker(Us us, GMarkerGoogleType gMarkerGoogleType= GMarkerGoogleType.red)
        {
            GMarkerGoogle mapMarker = new GMarkerGoogle(new GMap.NET.PointLatLng(us.сoordinates[0].lat, us.сoordinates[0].lon), gMarkerGoogleType);//широта, долгота, тип маркера
            mapMarker.ToolTip = new GMap.NET.WindowsForms.ToolTips.GMapRoundedToolTip(mapMarker);//всплывающее окно с инфой к маркеру
            mapMarker.ToolTipText = us.id; // текст внутри всплывающего окна
            mapMarker.ToolTipMode = MarkerTooltipMode.OnMouseOver; //отображение всплывающего окна (при наведении)
            return mapMarker;
        }

Необходимо сказать о GMapOverlay – это слой, похожий на слой в Photoshop, который можно описать как прозрачный холст размером с карту на который мы размещаем все маркеры.

Теперь функция возвращающая слой с маркерами. Входные параметры: список классов с именами и координатами, название слоя и все тот же необязательный параметр gMarkerGoogleType.

private GMapOverlay GetOverlayMarkers(List<Us> uss, string name, GMarkerGoogleType gMarkerGoogleType= GMarkerGoogleType.red)
        {
            GMapOverlay gMapMarkers = new GMapOverlay(name);// создание именованного слоя 
            foreach (Us us in uss)
            {
                gMapMarkers.Markers.Add(GetMarker(us, gMarkerGoogleType));// добавление маркеров на слой
            }
            return gMapMarkers;
        }

Добавление слоя. Вызываем функцию создающую слой и добавляем его к слоям контрола карты:

gMapControl1.Overlays.Add(GetOverlayMarkers(groups, "GroupsMarkers"));

то же самое, но с указанием типов маркеров:

gMapControl1.Overlays.Add(GetOverlayMarkers(LUs, " GroupsMarkers", GMarkerGoogleType.blue));

Можно отображать / скрывать слои

gMapControl1.Overlays[0].IsVisibile = false;

Слой добавлен, теперь просто обновляем контрол

gMapControl1.Update();// обновить контрол

На выходе получаем карту с нанесенными на нее маркерами

Тепловая карта

Изначально хотелось сделать тепловую карту по количеству маркеров в районах разных городов, но для этого придется искать и добывать координаты границ этих районов. А это больше труда и времени, а дедлайн, как всегда, уже позавчера, поэтому было придумано составлять карту по концентрациям в тайлах. Чтобы это хорошо смотрелось, тайлы лучше брать меньшего размера, чем для нынешнего зума. И из таких соображений я делаю набор слоев для каждого зума, и при изменении зума меняю слой.

Код создания слоев с цветными полигонами:

public void GetHeatMap(List<Us> LUs, string name, bool AllCoord = true)
        {
            MercatorProjection mercatorProjection = new MercatorProjection(); // класс с функциями https://github.com/radioman/greatmaps/blob/master/GMap.NET.Core/GMap.NET.Projections/MercatorProjection.cs#L73
            for (int myZoom = gMapControl1.MinZoom + 3; myZoom <= gMapControl1.MaxZoom + 3; myZoom++)
            {
                GMapOverlay gMapOverlay = new GMapOverlay(name + myZoom); // создание слоя с именем включающий зум
                int tileSizePx = (int)mercatorProjection.TileSize.Height; //размер тайла
                List<GPoint> gPoints = new List<GPoint>();//список точек 
                foreach (Us us in LUs)
                {
                    if (AllCoord)
                    {
                        foreach (Coordinat coordinat in us.сoordinates)
                        {
                            GPoint pixelCoord = mercatorProjection.FromLatLngToPixel(coordinat.lat, coordinat.lon, myZoom);
                            gPoints.Add(new GPoint(pixelCoord.X / tileSizePx * tileSizePx, pixelCoord.Y / tileSizePx * tileSizePx));
                        }
                    }
                    else
                    {
                        GPoint pixelCoord = mercatorProjection.FromLatLngToPixel(us.сoordinates[0].lat, us.сoordinates[0].lon, myZoom);//из широты долготы в пиксели
                        gPoints.Add(new GPoint(pixelCoord.X / tileSizePx * tileSizePx, pixelCoord.Y / tileSizePx * tileSizePx));
                    }
                }
                var group = gPoints.GroupBy(r => r).ToList();
                int TileMaxCount = group.Max(r => r.Count());// максимальное содержание маркеров на тайле
                foreach (var item in group)
                {
                    int minus = Convert.ToInt32(155 * item.Count() / TileMaxCount);//сколько вычитать из РГБ
                    if (minus == 0) { minus++; }
                    Color color = Color.FromArgb(200, 255, 155 - minus, 155 - minus);// задаем цвет для использования
                    List<PointLatLng> pointLatLngs = new List<PointLatLng>()
                {
                    mercatorProjection.FromPixelToLatLng(item.Key, myZoom),
                    mercatorProjection.FromPixelToLatLng(item.Key.X+tileSizePx, item.Key.Y, myZoom),
                    mercatorProjection.FromPixelToLatLng(item.Key.X+tileSizePx, item.Key.Y+tileSizePx, myZoom),
                    mercatorProjection.FromPixelToLatLng(item.Key.X, item.Key.Y+tileSizePx, myZoom)
                }; //создаем список точек для полигона
                    GMapPolygon gMapPolygon = new GMapPolygon(pointLatLngs, item.Key.ToString()); //создаем полигон из списка точек
                    gMapPolygon.Fill = new SolidBrush(color);// заливка
                    gMapPolygon.Stroke = new Pen(color, -1); // рамка
                    gMapPolygon.IsVisible = true; // видимость 
                    gMapOverlay.Polygons.Add(gMapPolygon); // добавляем полигон
                }
                gMapControl1.Overlays.Add(gMapOverlay);// добавляем слой
                GC.Collect();// сбор мусора
            }
        }

Код при изменении зума:

private void gMapControl1_OnMapZoomChanged()
        {
            if (AllPolygonsToolStripMenuItem.CheckState == CheckState.Checked)
            {
                CheckHeatMap("AllPolygons");
                gMapControl1.Update();
            }
        }

Код функции:

private void CheckHeatMap(string name)
        {
            foreach (GMapOverlay gMapOverlay in gMapControl1.Overlays.Where(r => r.Id.Contains("Polygons")))
            {
                gMapOverlay.IsVisibile = false;
            }// все слои содержащие слово полигон невидимы
            if (name != null)
            {
                name += gMapControl1.Zoom + 3;
                GMapOverlay gMapOverlaySearch = gMapControl1.Overlays.Where(r => r.Id == name).FirstOrDefault();
                gMapControl1.Overlays.Where(r => r.Id == name).FirstOrDefault().IsVisibile = true;//видимость слоя с заданным именем
            }
        }

При приближении размер тайлов меняется:

Заключение

В статье показано как начать знакомство с GMap: работать со слоями, тайлами, полигонами и добавлять маркеры. Надеюсь, идеи и код, изложенные в статье, будут полезны при решении ваших задач.

Также предлагаю заглянуть:

Gmap.Net Beginners Tutorial

OpenStreetMap в Visual Studio 2017. GMap.Net

Источник: https://habr.com/ru/post/585734/


Интересные статьи

Интересные статьи

Мы продолжаем цикл публикаций о системе хранения Dell EMC PowerStore. Сегодня расскажем о том, как эффективно организовать работу с различными версиями продуктивных данны...
Многие новички пропускают настройку модулей Terraform, чтобы облегчить процесс настройки. По крайней мере, они так думают, что облегчили себе задачу. Рассмотрим что такое модули Terraform...
В современной экономике интеллектуальная собственность самых известных брендов стоит намного дороже материальных активов. Поэтому любой уважающий себя стартап должен знат...
За последние полтора месяца (с начала августа 2020) уже довольно много изданий/платформ и ресурсов говорили/писали про Алгоритм Fawkes: https://sandlab.cs.uchicago.edu/fawkes/#pre...
Однажды, в понедельник, мне пришла в голову мысль — "а покопаюсь ка я в новом ядре" (новым относительно, но об этом позже). Мысль не появилась на ровном месте, а предпосылками для нее стали: ...