├── .gitignore ├── LICENSE ├── Readme.md ├── image ├── 1.png ├── 10.png ├── 2.png ├── 3.png ├── 4.png ├── 5.png ├── 6.png ├── 7.png ├── 8.png ├── 9.png └── output_6_0.png ├── setup.py └── src ├── .DS_Store ├── plot_map.egg-info ├── PKG-INFO ├── SOURCES.txt ├── dependency_links.txt ├── requires.txt └── top_level.txt └── plot_map ├── __init__.py └── plot_map.py /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.toptal.com/developers/gitignore/api/macos,python 3 | # Edit at https://www.toptal.com/developers/gitignore?templates=macos,python 4 | 5 | ### macOS ### 6 | # General 7 | .DS_Store 8 | .AppleDouble 9 | .LSOverride 10 | 11 | # Icon must end with two \r 12 | Icon 13 | 14 | 15 | # Thumbnails 16 | ._* 17 | 18 | # Files that might appear in the root of a volume 19 | .DocumentRevisions-V100 20 | .fseventsd 21 | .Spotlight-V100 22 | .TemporaryItems 23 | .Trashes 24 | .VolumeIcon.icns 25 | .com.apple.timemachine.donotpresent 26 | 27 | # Directories potentially created on remote AFP share 28 | .AppleDB 29 | .AppleDesktop 30 | Network Trash Folder 31 | Temporary Items 32 | .apdisk 33 | 34 | ### Python ### 35 | # Byte-compiled / optimized / DLL files 36 | __pycache__/ 37 | *.py[cod] 38 | *$py.class 39 | 40 | # C extensions 41 | *.so 42 | 43 | # Distribution / packaging 44 | .Python 45 | build/ 46 | develop-eggs/ 47 | dist/ 48 | downloads/ 49 | eggs/ 50 | .eggs/ 51 | lib/ 52 | lib64/ 53 | parts/ 54 | sdist/ 55 | var/ 56 | wheels/ 57 | share/python-wheels/ 58 | *.egg-info/ 59 | .installed.cfg 60 | *.egg 61 | MANIFEST 62 | 63 | # PyInstaller 64 | # Usually these files are written by a python script from a template 65 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 66 | *.manifest 67 | *.spec 68 | 69 | # Installer logs 70 | pip-log.txt 71 | pip-delete-this-directory.txt 72 | 73 | # Unit test / coverage reports 74 | htmlcov/ 75 | .tox/ 76 | .nox/ 77 | .coverage 78 | .coverage.* 79 | .cache 80 | nosetests.xml 81 | coverage.xml 82 | *.cover 83 | *.py,cover 84 | .hypothesis/ 85 | .pytest_cache/ 86 | cover/ 87 | 88 | # Translations 89 | *.mo 90 | *.pot 91 | 92 | # Django stuff: 93 | *.log 94 | local_settings.py 95 | db.sqlite3 96 | db.sqlite3-journal 97 | 98 | # Flask stuff: 99 | instance/ 100 | .webassets-cache 101 | 102 | # Scrapy stuff: 103 | .scrapy 104 | 105 | # Sphinx documentation 106 | docs/_build/ 107 | 108 | # PyBuilder 109 | .pybuilder/ 110 | target/ 111 | 112 | # Jupyter Notebook 113 | .ipynb_checkpoints 114 | 115 | # IPython 116 | profile_default/ 117 | ipython_config.py 118 | 119 | # pyenv 120 | # For a library or package, you might want to ignore these files since the code is 121 | # intended to run in multiple environments; otherwise, check them in: 122 | # .python-version 123 | 124 | # pipenv 125 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 126 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 127 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 128 | # install all needed dependencies. 129 | #Pipfile.lock 130 | 131 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 132 | __pypackages__/ 133 | 134 | # Celery stuff 135 | celerybeat-schedule 136 | celerybeat.pid 137 | 138 | # SageMath parsed files 139 | *.sage.py 140 | 141 | # Environments 142 | .env 143 | .venv 144 | env/ 145 | venv/ 146 | ENV/ 147 | env.bak/ 148 | venv.bak/ 149 | 150 | # Spyder project settings 151 | .spyderproject 152 | .spyproject 153 | 154 | # Rope project settings 155 | .ropeproject 156 | 157 | # mkdocs documentation 158 | /site 159 | 160 | # mypy 161 | .mypy_cache/ 162 | .dmypy.json 163 | dmypy.json 164 | 165 | # Pyre type checker 166 | .pyre/ 167 | 168 | # pytype static type analyzer 169 | .pytype/ 170 | 171 | # Cython debug symbols 172 | cython_debug/ 173 | 174 | # End of https://www.toptal.com/developers/gitignore/api/macos,python 175 | 176 | 177 | .vscode -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020 Qing. Y 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * Neither the name of Sean C. Gillies nor the names of 13 | its contributors may be used to endorse or promote products derived from 14 | this software without specific prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 20 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | POSSIBILITY OF SUCH DAMAGE. 27 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # plot_map 2 | 3 | [![Downloads](https://pepy.tech/badge/plot-map)](https://pepy.tech/project/plot-map) [![Conda Downloads](https://img.shields.io/conda/dn/conda-forge/plot_map.svg)](https://anaconda.org/conda-forge/plot_map) 4 | 5 | plot_map包提供了在matplotlib上绘制地图底图的功能. 目前,plot_map的功能已经并入[TransBigData](https://transbigdata.readthedocs.io/zh_CN/latest/)包中,你可以直接使用plot_map,也可以使用TransBigData(带有与plot_map相同的功能) 6 | 7 | ## 安装 8 | 9 | ### 用pypi安装 [![PyPI version](https://badge.fury.io/py/plot-map.svg)](https://badge.fury.io/py/plot-map) 10 | 11 | 12 | plot_map包依赖于geopandas和matplotlib,如果你已经安装了这些依赖,则直接在命令提示符中运行下面代码即可安装: 13 | 14 | pip install -U plot-map 15 | 16 | ### 用conda-forge安装 [![Conda Version](https://img.shields.io/conda/vn/conda-forge/plot_map.svg)](https://anaconda.org/conda-forge/plot_map) 17 | 18 | 你也可以用conda-forge安装`plot_map`,这种方式会自动解决环境依赖,不过国内可能需要更换conda源。运行下面代码即可安装: 19 | 20 | conda install -c conda-forge plot_map 21 | 22 | ## 底图加载功能 23 | 24 | ### 底图配置 25 | 26 | 地图底图由mapbox提供,坐标系为WGS84。如果你要使用该功能,首先需要点击[这个链接](https://account.mapbox.com/auth/signin/?route-to=%22https://account.mapbox.com/%22)注册一个mapbox的账号,mapbox上注册成为开发者,并获取到一个mapbox token。 [这个链接](https://docs.mapbox.com/help/getting-started/access-tokens/#how-access-tokens-work)介绍了mapbox token的作用。 27 | 28 | 如果你已经得到了mapbox token,可以用以下代码为TransBigData设置mapbox token(只需要设置一次,后面重新打开python也不需要再重新设置了): 29 | 30 | ```python 31 | import plot_map 32 | #用下面代码设置你的mapboxtoken 33 | plot_map.set_mapboxtoken('pk.eyxxxxxxxxxx.xxxxxxxxx')#必须在里面设置你申请的token,直接复制此行代码无效! 34 | ``` 35 | 36 | 另外还需要设置一个地图底图的存储位置,下一次显示同一个位置时,地图会从本地读取加载。 37 | 38 | ```python 39 | #设置你的地图底图存储路径 40 | #如果你是linux或者mac系统,路径是这么写,注意最后有一个反斜杠 41 | plot_map.set_imgsavepath(r'/Users/xxxx/xxxx/') 42 | #如果是windows系统,路径这么写,最后注意要两个斜杠以防转义 43 | plot_map.set_imgsavepath(r'E:\pythonscript\xxx\\') 44 | ``` 45 | 46 | 设置好后,下次绘制底图时,会在你设置的路径下创建一个tileimg文件夹,底图都放在里面 47 | 尝试一下下面的代码,看看能否成功绘制底图 48 | 49 | ```python 50 | #定义显示范围范围 51 | bounds = [113.6,22.4,114.8,22.9] 52 | #创建图框 53 | import matplotlib.pyplot as plt 54 | fig =plt.figure(1,(8,8),dpi=250) 55 | ax =plt.subplot(111) 56 | plt.sca(ax) 57 | #添加地图底图 58 | plot_map.plot_map(plt,bounds,zoom = 11,style = 4) 59 | #添加比例尺和指北针 60 | plot_map.plotscale(ax,bounds = bounds,textsize = 10,compasssize = 1,accuracy = 2000,rect = [0.06,0.03],zorder = 10) 61 | plt.axis('off') 62 | plt.xlim(bounds[0],bounds[2]) 63 | plt.ylim(bounds[1],bounds[3]) 64 | plt.show() 65 | ``` 66 | 67 | ![plot_map绘图结果](image/output_6_0.png) 68 | 69 | ### 地图底图加载 70 | 71 | ```python 72 | plot_map.plot_map(plt,bounds,zoom='auto',style=4,printlog = False,styleid = 'dark') 73 | ``` 74 | 75 | 添加地图底图 76 | 77 | **输入** 78 | 79 | bounds : List 80 | 底图的绘图边界,[lon1,lat1,lon2,lat2] (WGS84坐标系) 其中,lon1,lat1是左下角坐标,lon2,lat2是右上角坐标 81 | zoom : number 82 | 底图的放大等级,默认为auto自动选取。越大越精细,加载的时间也就越久,一般单个城市大小的范围,这个参数选取12到16之间 83 | printlog : bool 84 | 是否显示日志 85 | style : number 86 | 地图底图的样式,可选1-10,对应分别如下(需要plot_map包在0.3.3版本以上) 87 | 88 | 底图样式1:streets 89 | ---------------------------------------- 90 | 91 | ![plot_map绘图结果](image/1.png) 92 | 93 | 94 | 底图样式2:outdoors 95 | ---------------------------------------- 96 | 97 | ![plot_map绘图结果](image/2.png) 98 | 99 | 100 | 底图样式3:satellite 101 | ---------------------------------------- 102 | 103 | ![plot_map绘图结果](image/3.png) 104 | 105 | 106 | 底图样式4:light 107 | ---------------------------------------- 108 | 109 | ![plot_map绘图结果](image/4.png) 110 | 111 | 112 | 底图样式5:dark 113 | ---------------------------------------- 114 | 115 | ![plot_map绘图结果](image/5.png) 116 | 117 | 118 | 底图样式6:light-ch(中文) 119 | ---------------------------------------- 120 | 121 | ![plot_map绘图结果](image/6.png) 122 | 123 | 124 | 底图样式7:ice creem 125 | ---------------------------------------- 126 | 127 | ![plot_map绘图结果](image/7.png) 128 | 129 | 130 | 底图样式8:night 131 | ---------------------------------------- 132 | 133 | ![plot_map绘图结果](image/8.png) 134 | 135 | 136 | 底图样式9:terrain 137 | ---------------------------------------- 138 | 139 | ![plot_map绘图结果](image/9.png) 140 | 141 | 142 | 底图样式10:basic blue 143 | ---------------------------------------- 144 | 145 | ![plot_map绘图结果](image/10.png) 146 | 147 | 用法 148 | ---------------------------------------- 149 | 150 | #设定显示范围 151 | bounds = [lon1,lat1,lon2,lat2] 152 | tbd.plot_map(plt,bounds,zoom = 12,style = 4) 153 | 154 | 指北针和比例尺 155 | ============================= 156 | 157 | plot_map.plotscale(ax,bounds,textcolor = 'k',textsize = 8,compasssize = 1,accuracy = 'auto',rect=[0.1,0.1],unit = "KM",style = 1,**kwargs) 158 | 159 | 为底图添加指北针和比例尺 160 | 161 | **输入** 162 | 163 | bounds : List 164 | 底图的绘图边界,[lon1,lat1,lon2,lat2] (WGS84坐标系) 其中,lon1,lat1是左下角坐标,lon2,lat2是右上角坐标 165 | textsize : number 166 | 标注文字大小 167 | compasssize : number 168 | 标注的指北针大小 169 | accuracy : number 170 | 标注比例尺的长度(米) 171 | unit : str 172 | 'KM','km','M','m' 比例尺的单位 173 | style : number 174 | 1或2,比例尺样式 175 | rect : List 176 | 比例尺在图中的大致位置,如[0.9,0.9]则在右上角 177 | 178 | 使用方法: 179 | 180 | ```python 181 | plot_map.plotscale(ax,bounds = bounds,textsize = 10,compasssize = 1,accuracy = 2000,rect = [0.06,0.03]) 182 | ``` -------------------------------------------------------------------------------- /image/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ni1o1/plot_map/703f9612f404dfd3173451ce9cd12d1bea5e4622/image/1.png -------------------------------------------------------------------------------- /image/10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ni1o1/plot_map/703f9612f404dfd3173451ce9cd12d1bea5e4622/image/10.png -------------------------------------------------------------------------------- /image/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ni1o1/plot_map/703f9612f404dfd3173451ce9cd12d1bea5e4622/image/2.png -------------------------------------------------------------------------------- /image/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ni1o1/plot_map/703f9612f404dfd3173451ce9cd12d1bea5e4622/image/3.png -------------------------------------------------------------------------------- /image/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ni1o1/plot_map/703f9612f404dfd3173451ce9cd12d1bea5e4622/image/4.png -------------------------------------------------------------------------------- /image/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ni1o1/plot_map/703f9612f404dfd3173451ce9cd12d1bea5e4622/image/5.png -------------------------------------------------------------------------------- /image/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ni1o1/plot_map/703f9612f404dfd3173451ce9cd12d1bea5e4622/image/6.png -------------------------------------------------------------------------------- /image/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ni1o1/plot_map/703f9612f404dfd3173451ce9cd12d1bea5e4622/image/7.png -------------------------------------------------------------------------------- /image/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ni1o1/plot_map/703f9612f404dfd3173451ce9cd12d1bea5e4622/image/8.png -------------------------------------------------------------------------------- /image/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ni1o1/plot_map/703f9612f404dfd3173451ce9cd12d1bea5e4622/image/9.png -------------------------------------------------------------------------------- /image/output_6_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ni1o1/plot_map/703f9612f404dfd3173451ce9cd12d1bea5e4622/image/output_6_0.png -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import setuptools 2 | 3 | with open("README.md", "r", encoding="utf-8") as fh: 4 | long_description = fh.read() 5 | 6 | setuptools.setup( 7 | name="plot_map", 8 | version="0.3.7", 9 | author="Qing Yu", 10 | author_email="qingyu0815@foxmail.com", 11 | description="A tool to add basemap in matplotlib", 12 | long_description=long_description, 13 | long_description_content_type="text/markdown", 14 | license="MIT", 15 | url="https://github.com/ni1o1/plot_map", 16 | project_urls={ 17 | "Bug Tracker": "https://github.com/ni1o1/plot_map/issues", 18 | }, 19 | install_requires=[ 20 | "geopandas","matplotlib" 21 | ], 22 | classifiers=[ 23 | "Environment :: Web Environment", 24 | "Intended Audience :: Developers", 25 | "Operating System :: OS Independent", 26 | "Framework :: Matplotlib", 27 | "Topic :: Text Processing :: Indexing", 28 | "Topic :: Utilities", 29 | "Topic :: Internet", 30 | "Topic :: Software Development :: Libraries :: Python Modules", 31 | "Programming Language :: Python", 32 | "Programming Language :: Python :: 3.8", 33 | ], 34 | package_dir={'plot_map': 'src/plot_map'}, 35 | packages=['plot_map'], 36 | python_requires=">=3.6", 37 | ) 38 | -------------------------------------------------------------------------------- /src/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ni1o1/plot_map/703f9612f404dfd3173451ce9cd12d1bea5e4622/src/.DS_Store -------------------------------------------------------------------------------- /src/plot_map.egg-info/PKG-INFO: -------------------------------------------------------------------------------- 1 | Metadata-Version: 2.1 2 | Name: plot-map 3 | Version: 0.3.6 4 | Summary: A tool to add basemap in matplotlib 5 | Home-page: https://github.com/ni1o1/plot_map 6 | Author: Qing Yu 7 | Author-email: qingyu0815@foxmail.com 8 | License: MIT 9 | Project-URL: Bug Tracker, https://github.com/ni1o1/plot_map/issues 10 | Platform: UNKNOWN 11 | Classifier: Environment :: Web Environment 12 | Classifier: Intended Audience :: Developers 13 | Classifier: Operating System :: OS Independent 14 | Classifier: Framework :: Matplotlib 15 | Classifier: Topic :: Text Processing :: Indexing 16 | Classifier: Topic :: Utilities 17 | Classifier: Topic :: Internet 18 | Classifier: Topic :: Software Development :: Libraries :: Python Modules 19 | Classifier: Programming Language :: Python 20 | Classifier: Programming Language :: Python :: 3.8 21 | Requires-Python: >=3.6 22 | Description-Content-Type: text/markdown 23 | License-File: LICENSE 24 | 25 | # plot_map 26 | 27 | ## 安装 28 | 29 | pip install -U plot-map 30 | 31 | ## 底图加载功能 32 | 33 | ### 地图底图加载 34 | 35 | 只需要用以下代码: 36 | 37 | import plot_map 38 | #设定显示范围 39 | bounds = [lon1,lat1,lon2,lat2] 40 | plot_map.plot_map(plt,bounds,zoom = 12,style = 4) 41 | 42 | 参数 43 | 44 | | 参数 | 描述 | 45 | | ----------- | ------------------------------------------------------------ | 46 | | bounds | 底图的绘图边界,[lon1,lat1,lon2,lat2] (WGS84坐标系) 其中,lon1,lat1是左下角坐标,lon2,lat2是右上角坐标 | 47 | | zoom | 底图的放大等级,越大越精细,加载的时间也就越久,一般单个城市大小的范围,这个参数选取12到16之间 | 48 | | style | 地图底图的样式,可选1-5,或'light','dark',style为4时为light,style为5时为dark | 49 | | imgsavepath | 瓦片地图储存路径,设置路径后,会把地图下载到本地的文件夹下,使用时也会优先搜索是否有已经下载的瓦片,默认的存放路径是C:\\ | 50 | | printlog | 是否显示日志 | 51 | 52 | ### 绘制指北针和比例尺的功能plotscale 53 | 54 | 为底图添加指北针和比例尺 55 | 56 | plot_map.plotscale(ax,bounds = bounds,textsize = 10,compasssize = 1,accuracy = 2000,rect = [0.06,0.03]) 57 | 58 | 参数 59 | 60 | | 参数 | 描述 | 61 | | ----------- | ------------------------------------------------------------ | 62 | | bounds | 底图的绘图边界,[lon1,lat1,lon2,lat2] (WGS84坐标系) 其中,lon1,lat1是左下角坐标,lon2,lat2是右上角坐标 | 63 | | textsize | 标注文字大小 | 64 | | compasssize | 标注的指北针大小 | 65 | | accuracy | 标注比例尺的长度 | 66 | | unit | 'KM','km','M','m' 比例尺的单位 | 67 | | style | 1或2,比例尺样式 | 68 | | rect | 比例尺在图中的大致位置,如[0.9,0.9]则在右上角 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /src/plot_map.egg-info/SOURCES.txt: -------------------------------------------------------------------------------- 1 | LICENSE 2 | Readme.md 3 | setup.py 4 | .ipynb_checkpoints/Tutorial_of_plot_map-checkpoint.ipynb 5 | .ipynb_checkpoints/plot_map包修理-checkpoint.ipynb 6 | dist/.DS_Store 7 | src/.DS_Store 8 | src/plot_map/__init__.py 9 | src/plot_map/plot_map.py 10 | src/plot_map.egg-info/PKG-INFO 11 | src/plot_map.egg-info/SOURCES.txt 12 | src/plot_map.egg-info/dependency_links.txt 13 | src/plot_map.egg-info/requires.txt 14 | src/plot_map.egg-info/top_level.txt -------------------------------------------------------------------------------- /src/plot_map.egg-info/dependency_links.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/plot_map.egg-info/requires.txt: -------------------------------------------------------------------------------- 1 | geopandas 2 | matplotlib 3 | -------------------------------------------------------------------------------- /src/plot_map.egg-info/top_level.txt: -------------------------------------------------------------------------------- 1 | plot_map 2 | -------------------------------------------------------------------------------- /src/plot_map/__init__.py: -------------------------------------------------------------------------------- 1 | from .plot_map import plot_map,plotscale,set_mapboxtoken,set_imgsavepath 2 | __version__ = '0.3.7' 3 | -------------------------------------------------------------------------------- /src/plot_map/plot_map.py: -------------------------------------------------------------------------------- 1 | 2 | import pandas as pd 3 | import numpy as np 4 | import math 5 | import urllib 6 | import io 7 | from PIL import Image 8 | import os 9 | import sys 10 | 11 | #检索文件 12 | def searchfile(filename): 13 | fileroot = '' 14 | for i in sys.path: 15 | try: 16 | if filename in os.listdir(i): 17 | fileroot = i 18 | except: 19 | pass 20 | if fileroot == '': 21 | fileroot = sys.path[-1] 22 | return fileroot+'/'+filename 23 | 24 | #写入 25 | def set_mapboxtoken(mapboxtoken): 26 | filepath = searchfile('mapboxtoken.txt') 27 | f = open(filepath,mode = 'w') 28 | f.write(mapboxtoken) 29 | f.close() 30 | print('Success') 31 | 32 | #读取 33 | def read_mapboxtoken(): 34 | filepath = searchfile('mapboxtoken.txt') 35 | try: 36 | f = open(filepath,mode = 'r') 37 | mapboxtoken = f.readline() 38 | f.close() 39 | except: 40 | raise Exception('Mapboxtoken not found, please use tbd.set_mapboxtoken() to set it first, see: https://transbigdata.readthedocs.io/en/latest/plot_map.html') 41 | return mapboxtoken 42 | 43 | #写入 44 | def set_imgsavepath(imgsavepath): 45 | filepath = searchfile('imgsavepath.txt') 46 | f = open(filepath,mode = 'w') 47 | f.write(imgsavepath) 48 | f.close() 49 | print('Success') 50 | #读取 51 | def read_imgsavepath(): 52 | filepath = searchfile('imgsavepath.txt') 53 | try: 54 | f = open(filepath,mode = 'r') 55 | imgsavepath = f.readline() 56 | f.close() 57 | except: 58 | raise Exception('Map base map storage path not found, please use tbd.set_imgsavepath() to set it first, see: https://transbigdata.readthedocs.io/en/latest/plot_map.html') 59 | return imgsavepath 60 | 61 | def deg2num(lat_deg, lon_deg, zoom): 62 | lat_rad = math.radians(lat_deg) 63 | n = 2.0 ** zoom 64 | xtile = int((lon_deg + 180.0) / 360.0 * n) 65 | ytile = int((1.0 - math.log(math.tan(lat_rad) + (1 / math.cos(lat_rad))) / math.pi) / 2.0 * n) 66 | return (xtile, ytile) 67 | 68 | def num2deg(xtile, ytile, zoom): 69 | n = 2.0 ** zoom 70 | lon_deg = xtile / n * 360.0 - 180.0 71 | lat_rad = math.atan(math.sinh(math.pi * (1 - 2 * ytile / n))) 72 | lat_deg = math.degrees(lat_rad) 73 | return (lat_deg, lon_deg) 74 | 75 | def getImageCluster( lon_deg,lat_deg, delta_long, delta_lat,zoom,printlog,imgsavepath,style = 4,access_token = ''): 76 | ''' 77 | access_token - mapbox token 78 | ''' 79 | if (style == 1 )|(style == 'streets' ): 80 | styleid = 'ckwinzgw581od14mpyfhka6nk' 81 | smurl = r'https://api.mapbox.com/styles/v1/ni1o1/'+styleid+r'/tiles/256/{0}/{1}/{2}?&access_token='+access_token 82 | if (style == 2 )|(style == 'outdoors' ): 83 | styleid = 'ckwinx7aj4y4a15p7ftfwq9dn' 84 | smurl = r'https://api.mapbox.com/styles/v1/ni1o1/'+styleid+r'/tiles/256/{0}/{1}/{2}?&access_token='+access_token 85 | if (style == 3 )|(style == 'satellite' ): 86 | styleid = 'cjv36cj9u4h1q1ftemjed4f2y' 87 | smurl = r'https://api.mapbox.com/styles/v1/ni1o1/'+styleid+r'/tiles/256/{0}/{1}/{2}?&access_token='+access_token 88 | if (style == 4 )|(style == 'light' ): 89 | styleid = 'ckwfx658z4dpb14ocnz6tky9d' 90 | smurl = r'https://api.mapbox.com/styles/v1/ni1o1/'+styleid+r'/tiles/256/{0}/{1}/{2}?&access_token='+access_token 91 | if (style == 5 )|(style == 'dark' ): 92 | styleid = 'cjetnd20i1vbi2qqxbh0by7p8' 93 | smurl = r'https://api.mapbox.com/styles/v1/ni1o1/'+styleid+r'/tiles/256/{0}/{1}/{2}?&access_token='+access_token 94 | if (style == 6 )|(style == 'light-ch' ): 95 | styleid = 'ckj9bhq7s9mvj19mq3e3fye35' 96 | smurl = r'https://api.mapbox.com/styles/v1/ni1o1/'+styleid+r'/tiles/256/{0}/{1}/{2}?&access_token='+access_token 97 | if (style == 7 )|(style == 'ice creem' ): 98 | styleid = 'cjv36iiz9243t1fo8mweb4z6r' 99 | smurl = r'https://api.mapbox.com/styles/v1/ni1o1/'+styleid+r'/tiles/256/{0}/{1}/{2}?&access_token='+access_token 100 | if (style == 8 )|(style == 'night' ): 101 | styleid = 'ck2o3fyvy0dch1cp6j2pkz2dv' 102 | smurl = r'https://api.mapbox.com/styles/v1/ni1o1/'+styleid+r'/tiles/256/{0}/{1}/{2}?&access_token='+access_token 103 | if (style == 9 )|(style == 'terrain' ): 104 | styleid = 'cjv36gyklf43q1fnuwibiuetl' 105 | smurl = r'https://api.mapbox.com/styles/v1/ni1o1/'+styleid+r'/tiles/256/{0}/{1}/{2}?&access_token='+access_token 106 | if (style == 10 )|(style == 'basic blue' ): 107 | styleid = 'ckwio2ze12fgk15p2alr5a4xj' 108 | smurl = r'https://api.mapbox.com/styles/v1/ni1o1/'+styleid+r'/tiles/256/{0}/{1}/{2}?&access_token='+access_token 109 | 110 | else: 111 | styleid = '' 112 | xmin, ymax =deg2num(lat_deg, lon_deg, zoom) 113 | xmax, ymin =deg2num(lat_deg + delta_lat, lon_deg + delta_long, zoom) 114 | 115 | def get_img(smurl,zoom, xtile, ytile,imgsize,imgsavepath): 116 | import os 117 | 118 | filename = str(style)+str(styleid)+'-'+str(zoom)+'-'+str(xtile)+'-'+str(ytile)+'-'+str(imgsize)+'.png' 119 | def savefig(filename,tile): 120 | try: 121 | if 'tileimg' in os.listdir(imgsavepath): 122 | if filename in os.listdir(imgsavepath+'tileimg'): 123 | pass 124 | else: 125 | tile.save(imgsavepath+'tileimg/'+filename) 126 | if printlog: 127 | print('figsaved:'+imgsavepath+'tileimg/'+filename) 128 | else: 129 | os.mkdir(imgsavepath+'tileimg') 130 | except: 131 | pass 132 | def loadfig(filename): 133 | try: 134 | if 'tileimg' in os.listdir(imgsavepath): 135 | if filename in os.listdir(imgsavepath+'tileimg'): 136 | tile = Image.open(imgsavepath+'tileimg\\'+filename) 137 | return tile 138 | else: 139 | return None 140 | else: 141 | os.mkdir(imgsavepath+'tileimg') 142 | return None 143 | except: 144 | return None 145 | tile = loadfig(filename) 146 | if tile is None: 147 | try: 148 | t = 0 149 | while t<10: 150 | try: 151 | imgurl=smurl.format(zoom, xtile, ytile) 152 | #print("Opening: " + imgurl) 153 | imgstr = urllib.request.urlopen(imgurl,timeout = 6).read() 154 | tile = Image.open(io.BytesIO(imgstr)) 155 | savefig(filename,tile) 156 | Cluster.paste(tile, box=((xtile-xmin)*imgsize , (ytile-ymin)*imgsize)) 157 | t = 10 158 | except: 159 | if printlog: 160 | print('Get map tile failed, retry ',t) 161 | t += 1 162 | except: 163 | print("Couldn't download image") 164 | tile = None 165 | else: 166 | Cluster.paste(tile, box=((xtile-xmin)*imgsize , (ytile-ymin)*imgsize)) 167 | 168 | imgsize = 256 169 | import threading 170 | threads = [] 171 | Cluster = Image.new('RGB',((xmax-xmin+1)*imgsize-1,(ymax-ymin+1)*imgsize-1)) 172 | for xtile in range(xmin, xmax+1): 173 | for ytile in range(ymin, ymax+1): 174 | threads.append(threading.Thread(target=get_img,args = (smurl,zoom, xtile, ytile,imgsize,imgsavepath))) 175 | for t in threads: 176 | t.setDaemon(True) 177 | t.start() 178 | for t in threads: 179 | t.join() 180 | threads.clear() 181 | 182 | return Cluster 183 | 184 | 185 | def plot_map(plt,bounds,zoom = 'auto',style=4,printlog = False): 186 | ''' 187 | Plot the basemap 188 | 189 | Parameters 190 | ------- 191 | plt : matplotlib.pyplot 192 | Where to plot 193 | bounds : List 194 | The drawing boundary of the base map, [lon1,lat1,lon2,lat2] (WGS84 coordinate system), where lon1 and lat1 are the coordinates of the lower left corner and lon2 and lat2 are the coordinates of the upper right corner 195 | zoom : number 196 | The larger the magnification level of the base map, the longer the loading time. Generally, the range for a single city is between 12 and 16 197 | printlog : bool 198 | Show log 199 | style : number 200 | The style of map basemap can be 1-10, as follows 201 | ''' 202 | access_token = read_mapboxtoken() 203 | imgsavepath = read_imgsavepath() 204 | try: 205 | import os 206 | os.listdir(imgsavepath) 207 | except: 208 | print('imgsavepath do not exist, your tile map will not save') 209 | lon1 = bounds[0] 210 | lat1 = bounds[1] 211 | lon2 = bounds[2] 212 | lat2 = bounds[3] 213 | if zoom == 'auto': 214 | zoom = 11-np.log(lon2-lon1)/np.log(2) 215 | zoom = min(18,int(zoom+0.5)) 216 | a = getImageCluster(lon1, lat1, lon2-lon1, lat2-lat1, zoom,style = style,printlog = printlog,imgsavepath = imgsavepath,access_token = access_token) 217 | x1, y1 =deg2num(lat1, lon1, zoom) 218 | x2, y2 =deg2num(lat2, lon2, zoom) 219 | x1,y1 = num2deg(x1, y1+1, zoom) 220 | x2,y2 = num2deg(x2+1, y2, zoom) 221 | plt.imshow(np.asarray(a),extent = (y1,y2,x1+0.00,x2+0.00)) 222 | 223 | 224 | def plotscale(ax,bounds,textcolor = 'k',textsize = 8,compasssize = 1,accuracy = 'auto',rect=[0.1,0.1],unit = "KM",style = 1,**kwargs): 225 | ''' 226 | Add compass and scale for a map 227 | 228 | Parameters 229 | ------- 230 | bounds : List 231 | The drawing boundary of the base map, [lon1,lat1,lon2,lat2] (WGS84 coordinate system), where lon1 and lat1 are the coordinates of the lower left corner and lon2 and lat2 are the coordinates of the upper right corner 232 | textsize : number 233 | size of the text 234 | compasssize : number 235 | size of the compass 236 | accuracy : number 237 | Length of scale bar (m) 238 | unit : str 239 | ‘KM’,’km’,’M’,’m’, the scale units 240 | style : number 241 | 1 or 2, the style of the scale 242 | rect : List 243 | The approximate position of the scale bar in the figure, such as [0.9,0.9], is in the upper right corner 244 | ''' 245 | #栅格化代码 246 | import math 247 | 248 | #划定栅格划分范围 249 | lon1 = bounds[0] 250 | lat1 = bounds[1] 251 | lon2 = bounds[2] 252 | lat2 = bounds[3] 253 | latStart = min(lat1, lat2); 254 | lonStart = min(lon1, lon2); 255 | if accuracy == 'auto': 256 | accuracy = (int((lon2-lon1)/0.0003/1000+0.5)*1000) 257 | a,c=rect 258 | b = 1-a 259 | d = 1-c 260 | alon,alat = (b*lon1+a*lon2)/(a+b),(d*lat1+c*lat2)/(c+d) 261 | 262 | #计算栅格的经纬度增加量大小▲Lon和▲Lat 263 | deltaLon = accuracy * 360 / (2 * math.pi * 6371004 * math.cos((lat1 + lat2) * math.pi / 360)); 264 | 265 | #加比例尺 266 | 267 | from shapely.geometry import Polygon 268 | import geopandas as gpd 269 | if style == 1: 270 | scale = gpd.GeoDataFrame({'color':[(0,0,0),(1,1,1),(0,0,0),(1,1,1)],'geometry': 271 | [Polygon([(alon,alat),(alon+deltaLon,alat),(alon+deltaLon,alat+deltaLon*0.4),(alon,alat+deltaLon*0.4)]), 272 | Polygon([(alon+deltaLon,alat),(alon+2*deltaLon,alat),(alon+2*deltaLon,alat+deltaLon*0.4),(alon+deltaLon,alat+deltaLon*0.4)]), 273 | Polygon([(alon+2*deltaLon,alat),(alon+4*deltaLon,alat),(alon+4*deltaLon,alat+deltaLon*0.4),(alon+2*deltaLon,alat+deltaLon*0.4)]), 274 | Polygon([(alon+4*deltaLon,alat),(alon+8*deltaLon,alat),(alon+8*deltaLon,alat+deltaLon*0.4),(alon+4*deltaLon,alat+deltaLon*0.4)]) 275 | ]}) 276 | scale.plot(ax = ax,edgecolor= textcolor,facecolor = scale['color'],lw = 0.6,**kwargs) 277 | 278 | if (unit == 'KM')|(unit == 'km'): 279 | ax.text(alon+1*deltaLon,alat+deltaLon*0.5,str(int(1*accuracy/1000)),color = textcolor,fontsize = textsize,ha = 'center',va = 'bottom') 280 | ax.text(alon+2*deltaLon,alat+deltaLon*0.5,str(int(2*accuracy/1000)),color = textcolor,fontsize = textsize,ha = 'center',va = 'bottom') 281 | ax.text(alon+4*deltaLon,alat+deltaLon*0.5,str(int(4*accuracy/1000)),color = textcolor,fontsize = textsize,ha = 'center',va = 'bottom') 282 | ax.text(alon+8*deltaLon,alat+deltaLon*0.5,str(int(8*accuracy/1000)),color = textcolor,fontsize = textsize,ha = 'center',va = 'bottom') 283 | ax.text(alon+8.5*deltaLon,alat+deltaLon*0.5,unit,color = textcolor,fontsize = textsize,ha = 'left',va = 'top') 284 | if (unit == 'M')|(unit == 'm'): 285 | ax.text(alon+1*deltaLon,alat+deltaLon*0.5,str(int(1*accuracy)),color = textcolor,fontsize = textsize,ha = 'center',va = 'bottom') 286 | ax.text(alon+2*deltaLon,alat+deltaLon*0.5,str(int(2*accuracy)),color = textcolor,fontsize = textsize,ha = 'center',va = 'bottom') 287 | ax.text(alon+4*deltaLon,alat+deltaLon*0.5,str(int(4*accuracy)),color = textcolor,fontsize = textsize,ha = 'center',va = 'bottom') 288 | ax.text(alon+8*deltaLon,alat+deltaLon*0.5,str(int(8*accuracy)),color = textcolor,fontsize = textsize,ha = 'center',va = 'bottom') 289 | ax.text(alon+8.5*deltaLon,alat+deltaLon*0.5,unit,color = textcolor,fontsize = textsize,ha = 'left',va = 'top') 290 | if style == 2: 291 | scale = gpd.GeoDataFrame({'color':[(0,0,0),(1,1,1)],'geometry': 292 | [Polygon([(alon+deltaLon,alat),(alon+4*deltaLon,alat),(alon+4*deltaLon,alat+deltaLon*0.4),(alon+deltaLon,alat+deltaLon*0.4)]), 293 | Polygon([(alon+4*deltaLon,alat),(alon+8*deltaLon,alat),(alon+8*deltaLon,alat+deltaLon*0.4),(alon+4*deltaLon,alat+deltaLon*0.4)]) 294 | ]}) 295 | scale.plot(ax = ax,edgecolor= textcolor,facecolor = scale['color'],lw = 0.6,**kwargs) 296 | 297 | if (unit == 'KM')|(unit == 'km'): 298 | ax.text(alon+4*deltaLon,alat+deltaLon*0.5,str(int(4*accuracy/1000)),color = textcolor,fontsize = textsize,ha = 'center',va = 'bottom') 299 | ax.text(alon+8*deltaLon,alat+deltaLon*0.5,str(int(8*accuracy/1000)),color = textcolor,fontsize = textsize,ha = 'center',va = 'bottom') 300 | ax.text(alon+8.5*deltaLon,alat+deltaLon*0.5,unit,color = textcolor,fontsize = textsize,ha = 'left',va = 'top') 301 | if (unit == 'M')|(unit == 'm'): 302 | ax.text(alon+4*deltaLon,alat+deltaLon*0.5,str(int(4*accuracy)),color = textcolor,fontsize = textsize,ha = 'center',va = 'bottom') 303 | ax.text(alon+8*deltaLon,alat+deltaLon*0.5,str(int(8*accuracy)),color = textcolor,fontsize = textsize,ha = 'center',va = 'bottom') 304 | ax.text(alon+8.5*deltaLon,alat+deltaLon*0.5,unit,color = textcolor,fontsize = textsize,ha = 'left',va = 'top') 305 | 306 | 307 | #加指北针 308 | deltaLon = compasssize*deltaLon 309 | alon = alon-deltaLon 310 | compass = gpd.GeoDataFrame({'color':[(0,0,0),(1,1,1)],'geometry': 311 | [Polygon([[alon,alat],[alon,alat+deltaLon],[alon+1/2*deltaLon,alat-1/2*deltaLon]]), 312 | Polygon([[alon,alat],[alon,alat+deltaLon],[alon-1/2*deltaLon,alat-1/2*deltaLon]])]}) 313 | compass.plot(ax= ax, edgecolor= textcolor,facecolor = compass['color'],lw = 0.6,**kwargs) 314 | ax.text(alon,alat+deltaLon,'N',color = textcolor,fontsize = textsize,ha = 'center',va = 'bottom') 315 | 316 | --------------------------------------------------------------------------------