На написание данной статьи меня подтолкнуло не столь большое наличие текстовой информации по работе с 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: работать со слоями, тайлами, полигонами и добавлять маркеры. Надеюсь, идеи и код, изложенные в статье, будут полезны при решении ваших задач.
Также предлагаю заглянуть:
Комментарии (10)
Skykharkov
27.10.2021 09:22+2Круто, спасибо.
Много лет занимаюсь с картами. OSM + Leaflet + Chromium пока что лучшая связка которую можно вменяемо использовать. Гугл мапс платный же с некоторого времени. Для OSM нужен свой сервер конечно-же (для некоторых сценариев), но и возможностей больше в разы.
Kamigakuro
28.10.2021 09:28Тоже пытался баловаться с этой библиотекой. В итоге только пару карт работало (вроде гугл и бинг), а нужны были карты Яндекс, автор вроде как их заложил, но всё равно у меня они не завелись. Если получится их оживить, расскажите как)
Sith_Lord
29.10.2021 13:39Яндекс у меня так же не завёлся, только снимками со спутника. сам использовал гугловые. так же вроде интересные для использования в роли провайдера CzechMap, OpenCycleMap и Wikimapia
mazik7512
29.10.2021 10:20+1Здравствуйте, какое-то время назад тоже работал с данными картами, и тогда возникла проблема сохранения хотя бы небольшой части карты(для использования оффлайн), получалось сохранять только то что грузилось в кэш автоматически, но выкачать бд так и не получилось. Тоже находил серию видео, что вы оставили в ссылках, но там сохранение карт, у автора самого не получилось, хотелось бы узнать пробовали вы это и получилось ли?
NewTechAudit Автор
29.10.2021 13:44Так же какое-то время искал возможность сохранить карты, но времени было мало на поиски, поэтому быстро сделал на онлайн. Если кто-нибудь знает секрет и поделится, буду признателен)
Denis_Andreevich
Вы рассматривали возможность использования официальных карт Майкрософт?
NewTechAudit Автор
Имеете в виду, через GMap так же или что-то еще?