pandas 在卫星Map上绘制数据

hxzsmxv2  于 2023-04-28  发布在  其他
关注(0)|答案(3)|浏览(171)

如何在python(笔记本)中使用高分辨率的卫星背景图像在Map上绘制(lat, lon, value)数据?
Folium不提供卫星瓦片。SimpleKMLgoogleearthplot似乎只对巨大的低分辨率地球数据有用。EarthPy可以接受图像瓦片,但它们到NASA网站的链接只提供〉0.1 °的低分辨率图像。Cartopy是matplotlib用户的新希望,但我找不到任何卫星图像的例子。
这种挫折感特别大,因为使用R,使用RGoogleMaps包,这项工作非常容易,例如:

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

如何在Python中做到这一点?

kpbwa7wx

kpbwa7wx1#

另一种选择是使用gmplot。它基本上是围绕Google Maps javascript API的Python Package 器,允许您生成.html文件,在后台渲染您的Map。
在这里,我使用它来绘制一个以卫星图像为背景的随机漫步(默认情况下不支持这种Map类型,但它非常简单地使其工作):

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 Maps一样进行交互。不幸的是,这意味着您不会获得漂亮的matplotlib图形窗口或任何东西,因此为了生成图像文件,您需要自己截图或破解一些东西来为您呈现HTML。
另一件要记住的事情是,你可能需要一个Google Maps API密钥,否则你最终会像我一样得到一个丑陋的暗水印Map:

另外,由于你想用颜色来描述值,你需要手工将它们转换成颜色字符串,并使用gmap.scatter()方法。如果你对这种方法感兴趣,请告诉我,这样我就可以尝试编写一些代码来完成这一任务。

更新

下面是一个版本,它支持将值编码为卫星图像上散点图中的颜色。为了达到这个效果,我使用了matplotlib的色彩Map表。如果你愿意,你可以更改色彩Map表,请参阅here选项列表。我还包含了一些代码,用于从apikey.txt文件中读取API密钥,这允许每个研究人员使用他们自己的单独密钥而不改变代码(如果没有找到这样的文件,则默认为没有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颜色Map中从蓝色阴影Map到红色:

hc8w905p

hc8w905p2#

通过注册Mapbox(mapbox.com)并使用他们提供的API密钥,您可以获得folium以使用自定义平铺集(他们的API_key=tile='Mapbox'参数似乎不适合我)。
例如,这对我来说是有效的(然而,公共Map的分辨率会根据位置而有所不同):

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,但看起来你甚至可以创建自己的Tileset,如果你碰巧有你想使用的图像。
NB:我在我的笔记本上运行了这个安装folium第一:

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

针对评论意见:

  • Mapbox是一家提供位置和Map服务的公司(正如我提到的,我从来没有使用过它们,我想你可以在https://www.mapbox.com上找到更多)
  • Mapbox需要令牌,因为它不是一个无限的免费服务......即他们给予你一个令牌来跟踪请求......如果你使用超过包括在免费分配,我猜他们节流你的帐户
  • “v4”只是Mapbox的API路由的一部分。我猜他们还有v1,v2等。
  • 有更新版本的贴图吗?我不确定,我想你可以看看Mapbox的文档。看起来你也可以上传你自己的Map到Mapbox,他们会存储它们并提供给你。
  • 如何将x/y轴添加到输出中?我不是很确定。但folium是LeafletJS的 Package 器,这是一个流行的库,有很多plugins。编写一个类来 Package 任何LeafetJS插件看起来并不太棘手(参见开箱即用的示例here),所以也许你可以找到一个适合你的问题并自己 Package 它?
crcmnpdw

crcmnpdw3#

使用散景,这可能是最简单的方式,根据我使用的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选项

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

添加一些工具以获得交互式Map

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

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

创建Map并对其进行自定义

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)

相关问题