Сюжет данных на спутниковых картах - PullRequest
0 голосов
/ 20 декабря 2018

Как построить (lat, lon, value) данные на карте, используя спутниковые фоновые изображения с высоким разрешением в python (записные книжки)?

Я ползал весь интернет, но не смог найти ничего полезного. Фолиум не предоставляет спутниковые плитки. SimpleKML и googleearthplot кажутся полезными только для огромных земных данных низкого разрешения. EarthPy может принимать плитки изображений, но их ссылка на веб-сайт НАСА предоставляет только изображения с низким разрешением> 0,1 градуса. Cartopy - это новая надежда для пользователей matplotlib, но я не могу найти ни одного примера для плиток спутниковых изображений.

Разочарование особенно велико, так как эта работа очень проста с R, используя RGoogleMaps пакет, например:

plotmap(lat, lon, col=palette(value), data=mydataframe, zoom = 17, maptype="satellite")

Как мы можем сделать это в Python?plot on satellite maps

Ответы [ 3 ]

0 голосов
/ 13 января 2019

Альтернативным вариантом является использование gmplot.По сути, это оболочка Python для API JavaScript Карт Google, которая позволяет генерировать .html файлы, которые отображают ваши графики на фоне карты.

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

from gmplot import GoogleMapPlotter
from random import random

# We subclass this just to change the map type
class CustomGoogleMapPlotter(GoogleMapPlotter):
    def __init__(self, center_lat, center_lng, zoom, apikey='',
                 map_type='satellite'):
        super().__init__(center_lat, center_lng, zoom, apikey)

        self.map_type = map_type
        assert(self.map_type in ['roadmap', 'satellite', 'hybrid', 'terrain'])

    def write_map(self,  f):
        f.write('\t\tvar centerlatlng = new google.maps.LatLng(%f, %f);\n' %
                (self.center[0], self.center[1]))
        f.write('\t\tvar myOptions = {\n')
        f.write('\t\t\tzoom: %d,\n' % (self.zoom))
        f.write('\t\t\tcenter: centerlatlng,\n')

        # This is the only line we change
        f.write('\t\t\tmapTypeId: \'{}\'\n'.format(self.map_type))


        f.write('\t\t};\n')
        f.write(
            '\t\tvar map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);\n')
        f.write('\n')

initial_zoom = 16
num_pts = 40

lats = [37.428]
lons = [-122.145]
for pt in range(num_pts):
    lats.append(lats[-1] + (random() - 0.5)/100)
    lons.append(lons[-1] + random()/100)
gmap = CustomGoogleMapPlotter(lats[0], lons[0], initial_zoom,
                              map_type='satellite')
gmap.plot(lats, lons, 'cornflowerblue', edge_width=10)

gmap.draw("mymap.html")

Вы можете открыть получившийся файл .html в браузере и взаимодействовать так же, как с GoogleКарты.К сожалению, это означает, что вы не получите красивое графическое окно matplotlib или что-либо еще, поэтому для создания файла изображения вам нужно либо сделать снимок экрана самостоятельно, либо взломать что-то, чтобы отобразить HTML для вас.

Еще одна вещь, которую нужно иметь в виду, это то, что вам, вероятно, понадобится ключ API Карт Google , в противном случае вы получите уродливую затемненную карту с водяным знаком, как я:

Random walk map

Кроме того, поскольку вы хотите изобразить значения в виде цветов, вам нужно будет вручную преобразовать их в цветные строки и использовать метод gmap.scatter().Если вы заинтересованы в этом подходе, дайте мне знать, чтобы я мог попытаться придумать некоторый код для этого.

Обновление

Вот версия, которая поддерживает значения кодирования в виде цветов в разбросесюжет над спутниковыми снимками.Для достижения эффекта я использую карты цветов matplotlib.Вы можете изменить цветовую карту, если хотите, см. Список опций здесь .Я также включил некоторый код для чтения ключа API из файла apikey.txt, который позволяет каждому исследователю использовать свой собственный индивидуальный ключ без изменения кода (если такой файл не найден, по умолчанию не используется ключ API, как обычно).

import matplotlib.pyplot as plt
from matplotlib.colors import Normalize
from matplotlib.cm import ScalarMappable
from gmplot import GoogleMapPlotter
from random import random


class CustomGoogleMapPlotter(GoogleMapPlotter):
    def __init__(self, center_lat, center_lng, zoom, apikey='',
                 map_type='satellite'):
        if apikey == '':
            try:
                with open('apikey.txt', 'r') as apifile:
                    apikey = apifile.readline()
            except FileNotFoundError:
                pass
        super().__init__(center_lat, center_lng, zoom, apikey)

        self.map_type = map_type
        assert(self.map_type in ['roadmap', 'satellite', 'hybrid', 'terrain'])

    def write_map(self,  f):
        f.write('\t\tvar centerlatlng = new google.maps.LatLng(%f, %f);\n' %
                (self.center[0], self.center[1]))
        f.write('\t\tvar myOptions = {\n')
        f.write('\t\t\tzoom: %d,\n' % (self.zoom))
        f.write('\t\t\tcenter: centerlatlng,\n')

        # Change this line to allow different map types
        f.write('\t\t\tmapTypeId: \'{}\'\n'.format(self.map_type))

        f.write('\t\t};\n')
        f.write(
            '\t\tvar map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);\n')
        f.write('\n')

    def color_scatter(self, lats, lngs, values=None, colormap='coolwarm',
                      size=None, marker=False, s=None, **kwargs):
        def rgb2hex(rgb):
            """ Convert RGBA or RGB to #RRGGBB """
            rgb = list(rgb[0:3]) # remove alpha if present
            rgb = [int(c * 255) for c in rgb]
            hexcolor = '#%02x%02x%02x' % tuple(rgb)
            return hexcolor

        if values is None:
            colors = [None for _ in lats]
        else:
            cmap = plt.get_cmap(colormap)
            norm = Normalize(vmin=min(values), vmax=max(values))
            scalar_map = ScalarMappable(norm=norm, cmap=cmap)
            colors = [rgb2hex(scalar_map.to_rgba(value)) for value in values]
        for lat, lon, c in zip(lats, lngs, colors):
            self.scatter(lats=[lat], lngs=[lon], c=c, size=size, marker=marker,
                         s=s, **kwargs)


initial_zoom = 12
num_pts = 40

lats = [37.428]
lons = [-122.145]
values = [random() * 20]
for pt in range(num_pts):
    lats.append(lats[-1] + (random() - 0.5)/100)
    lons.append(lons[-1] + random()/100)
    values.append(values[-1] + random())
gmap = CustomGoogleMapPlotter(lats[0], lons[0], initial_zoom,
                              map_type='satellite')
gmap.color_scatter(lats, lons, values, colormap='coolwarm')

gmap.draw("mymap.html")

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

New map example with colors

0 голосов
/ 05 марта 2019

Используя Bokeh, для меня это может быть самый простой способ использовать спутниковую плитку GMAP.

from bokeh.io import output_notebook, show
from bokeh.models import ColumnDataSource, GMapOptions, HoverTool
from bokeh.plotting import gmap, figure

output_notebook()

api_key = your_gmap_api_key

Параметры вашей карты

map_options = GMapOptions(lat=47.1839600, lng= 6.0014100, map_type="satellite", zoom=8, scale_control=True)

Добавление некоторых инструментов дляинтерактивная карта

hover=HoverTool(tooltips=[("(x,y)","($x,$y)")])

tools=[hover, 'lasso_select','tap']

Создание карты и настройка ее

p = gmap(api_key, map_options, title="your_title", plot_height=600, plot_width=1000, tools=tools)
p.axis.visible = False
p.legend.click_policy='hide'

Добавление ваших данных

your_source = ColumnDataSource(data=dict(lat=your_df.lat, lon=your_df.lon, size = your_df.value))

p.circle(x="lon",y="lat",size=size, fill_color="purple",legend = "your_legend", fill_alpha=0.2, line_alpha=0, source=your_source)
show(p)
0 голосов
/ 07 января 2019

Регистрируясь с помощью Mapbox ( mapbox.com ) и используя предоставляемый ими ключ API, вы можете заставить folium использовать собственный набор плиток (их параметры API_key= и tile='Mapbox' не работаютдля меня).

Например, это работает для меня (однако разрешение общедоступных карт немного варьируется в зависимости от местоположения):

import folium

mapboxAccessToken = 'your api key from mapbox'

mapboxTilesetId = 'mapbox.satellite'


m = folium.Map(
    location=[51.4826486,12.7034238],
    zoom_start=16,
    tiles='https://api.tiles.mapbox.com/v4/' + mapboxTilesetId + '/{z}/{x}/{y}.png?access_token=' + mapboxAccessToken,
    attr='mapbox.com'
)

tooltip = 'Click me!'

folium.Marker([51.482696, 12.703918], popup='<i>Marker 1</i>', tooltip=tooltip).add_to(m)
folium.Marker([51.481696, 12.703818], popup='<b>Marker 2</b>', tooltip=tooltip).add_to(m)

m

Я действительно никогда не использовал Mapbox, но этоПохоже, вы даже можете создать свой собственный набор плиток, если у вас есть изображения, которые вы хотели бы использовать.

Примечание: я запустил это в своей папке установки ноутбука:

import sys
!{sys.executable} -m pip install folium

Вответ на комментарии:

  • Mapbox - это компания, которая предоставляет услуги определения местоположения и картографии (как я уже упоминал, я никогда ими не пользовался, думаю, вы можете узнать больше на https://www.mapbox.com)
  • Mapbox требует токенов, потому что это не неограниченный бесплатный сервис ... то есть они дают вам токен для отслеживания запросов ... и если вы используете больше, чем включено в бесплатное распределение, я думаю, они блокируют ваш аккаунт
  • Существуют ли более новые версии плиток?Я не уверен, думаю, вы можете взглянуть на документацию Mapbox.Похоже, вы также можете загрузить свои собственные карты в Mapbox, и они будут хранить их и возвращать вам.
  • Как добавить ось x- / y к выходным данным?Я не совсем уверен.Но folium - это обертка вокруг LeafletJS , популярной библиотеки с множеством плагинов .Не так уж сложно написать класс, чтобы обернуть любой плагин LeafetJS (см. Готовые примеры здесь ), так что, может быть, вы можете найти тот, который подходит вашей проблеме, и обернуть его самостоятельно?
...