├── .gitignore ├── LICENSE ├── README.md ├── doc ├── api_download.md ├── api_draw.md ├── api_geoarray.md ├── api_indicate.md ├── api_io.md ├── api_match.md ├── api_pretreat.md ├── api_util.md ├── dem_draw.md ├── dem_forest_statistic.md ├── dem_geoarray.md ├── dem_io.md ├── dem_match.md ├── dem_pretreat.md └── index.md ├── geonumpy ├── __init__.py ├── base │ ├── __init__.py │ └── geonumpy.py ├── download │ ├── __init__.py │ ├── landsat_download.py │ ├── landsat_search.py │ ├── modis_download.py │ └── tile_online_download.py ├── draw │ ├── __init__.py │ └── draw.py ├── indicate │ └── geo_indicate.py ├── io │ ├── __init__.py │ └── geoio.py ├── match │ ├── __init__.py │ └── match.py ├── pretreat │ ├── __init__.py │ └── gaprepair.py ├── project │ └── geo_prj.py ├── test │ ├── download_test.py │ ├── draw_test.py │ ├── forest_statistic.py │ ├── geoarray_test.py │ ├── geoio_test.py │ ├── match_test.py │ └── pretreat_test.py └── util │ ├── __init__.py │ └── util.py ├── install.sh ├── requirements.txt └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | geonumpy/data/ 23 | wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .coverage 43 | .coverage.* 44 | .cache 45 | nosetests.xml 46 | coverage.xml 47 | *.cover 48 | .hypothesis/ 49 | .pytest_cache/ 50 | geonumpy/data/ 51 | doc/imgs/ 52 | 53 | # Translations 54 | *.mo 55 | *.pot 56 | 57 | # Django stuff: 58 | *.log 59 | local_settings.py 60 | db.sqlite3 61 | 62 | # Flask stuff: 63 | instance/ 64 | .webassets-cache 65 | 66 | # Scrapy stuff: 67 | .scrapy 68 | 69 | # Sphinx documentation 70 | docs/_build/ 71 | 72 | # PyBuilder 73 | target/ 74 | 75 | # Jupyter Notebook 76 | .ipynb_checkpoints 77 | 78 | # pyenv 79 | .python-version 80 | 81 | # celery beat schedule file 82 | celerybeat-schedule 83 | 84 | # SageMath parsed files 85 | *.sage.py 86 | 87 | # Environments 88 | .env 89 | .venv 90 | env/ 91 | venv/ 92 | ENV/ 93 | env.bak/ 94 | venv.bak/ 95 | 96 | # Spyder project settings 97 | .spyderproject 98 | .spyproject 99 | 100 | # Rope project settings 101 | .ropeproject 102 | 103 | # mkdocs documentation 104 | /site 105 | 106 | # mypy 107 | .mypy_cache/ 108 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2019, ImagePy 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # geonumpy 2 | geonumpy 是一个 GIS,遥感影像处理库,实现了矢量,遥感影像读取,存储,预处理,拼接,重采样,常规指标计算,地图绘制等功能。 3 | 4 | 5 | 6 | ### 安装 7 | 8 | gdal, fiona, shapely, geopandas这几个依赖库不但是由python实现,所以比较难以安装 9 | 10 | geonumpy 暂时没有上传pypi, 请下载后使用 pip install -e 命令加入PythonPath 11 | 12 | - Windows系统 13 | - clone或者下载源码 14 | - `pip install --upgrade setuptools` 15 | - `python -m pip install --upgrade pip` 16 | - 分别下载对应本机版本的包[gdal](https://www.lfd.uci.edu/~gohlke/pythonlibs/#GDAL)、[fiona](https://www.lfd.uci.edu/~gohlke/pythonlibs/#fiona)、[shapely](https://www.lfd.uci.edu/~gohlke/pythonlibs/#shapely),然后分别执行`pip install package_name.whl`安装 17 | - geopandas通过`pip install geopandas`或者去查看[官方文档](http://geopandas.org/) 18 | - 最后执行`pip install -e geonumpy`安装完毕 19 | - like-unix系统(ubuntu / centos / mac) 20 | - `git clone https://github.com/Image-Py/geonumpy; ./geonumpy/install.sh` 21 | 22 | 23 | ### 文档 24 | [**geonumpy 文档**]() 25 | 26 | 1. [geonumpy api 文档](doc/index.md#geonumpy-API-文档) 27 | 2. [geonumpy 用户手册](doc/index.md#geonumpy-用户手册) 28 | 29 | 30 | 31 | ## 功能简介 32 | 33 | 这里简单介绍 geonumpy 的部分功能,可以让读者快速有一个认识,更多,具体功能,请参阅文档。 34 | 35 | 36 | 37 | ### 影像读取 38 | 39 | ```python 40 | path = '../data/landsat/LC08_L1TP_122033_20190506_20190506_01_RT_B5.TIF' 41 | landsat = gio.read_tif(path) 42 | 43 | # landsat is a GeoArray object which subclass the numpy.ndarray 44 | >>> landsat.shape 45 | (7821, 7691) 46 | 47 | # but with crs 48 | >>> landsat.crs 49 | 4326 50 | 51 | # and transform matrix 52 | >>> landsat.mat 53 | array([[ 4.556850e+05, 3.000000e+01, 0.000000e+00], 54 | [ 4.423215e+06, 0.000000e+00, -3.000000e+01]]) 55 | 56 | plt.imshow(landsat, cmap='gray') 57 | plt.show() 58 | ``` 59 | 60 | ![landsat](http://idoc.imagepy.org/gis/02.png) 61 | 62 | 63 | 64 | ### 去条带 65 | 66 | ```python 67 | path = '../data/landsat-gap/LE07_L1TP_123037_20180721_20180816_01_T1_sr_ndvi.tif' 68 | img = gio.read_tif(path) 69 | # 去条带, img==-999 是掩膜,-999为无效值 70 | degapimg = gpt.degap(img.copy(), img==-9999, 10) 71 | 72 | # plot two images ... 73 | ``` 74 | ![](http://idoc.imagepy.org/gis/16.png) 75 | 76 | 77 | 78 | ### 影像拼接 79 | 80 | ![](http://idoc.imagepy.org/gis/06.png) 81 | 82 | ```python 83 | # 读取山东省 shapefile 84 | shandong = gio.read_shp('../data/shape/shandong.shp') 85 | # 转为 web 墨卡托 投影 86 | shandong = shandong.to_crs(3857) 87 | # 缩放到 2048*1536大小,边距0.05,计算相关空间信息 88 | box = gutil.shp2box(shandong, (2048,1536), 0.05, 1) 89 | # 用空间信息实例化 GeoArray 对象 90 | paper = gnp.frombox(*box, dtype=np.int16) 91 | # 读取所有影像的 0 通道 92 | fs = glob('../data/modis/*.hdf') 93 | rasters = [gio.read_hdf(i, 0) for i in fs] 94 | # 将 rasters 投影到 paper 95 | gmt.match_multi(rasters, paper, out='in') 96 | 97 | plt.imshow(paper) 98 | plt.show() 99 | ``` 100 | 101 | ![](http://idoc.imagepy.org/gis/04.png) 102 | 103 | 这里我们使用一个 shapefile 转换到 web 墨卡托坐标系,并用矢量图形确定了图像空间信息,然后将图像块投影到目标图像上,从而实现拼接。 104 | 105 | 106 | 107 | ### 矢量图绘制 108 | 109 | ```python 110 | import geonumpy.io as gio 111 | import geonumpy.draw as gdraw 112 | 113 | # 读取山东省矢量图 114 | shandong = gio.read_shp('../data/shape/shandong.shp') 115 | # 投影成 web 墨卡托 116 | shandong = shandong.to_crs(3857) 117 | # 从矢量图计算图像空间信息,尺寸3600*2400,边距十分之一 118 | box = gutil.shp2box(shandong, (3600, 2400), 0.1, 1) 119 | # 从空间信息实例化 GeoArray 对象 120 | paper = gnp.frombox(*box, dtype=np.uint8) 121 | # 底图赋值为白色 122 | paper[:] = 255 123 | # 绘制多边形,颜色为0,线条宽度为2 124 | gdraw.draw_polygon(paper, shandong, 0, 2) 125 | # 绘制刻度,左右80,上下50,单位间隔1,坐标系4326,使用times字体,32好,颜色0,线条宽度2,刻度高5 126 | gdraw.draw_ruler(paper, 80, 50, -80, -50, 1, 4326, ('times', 32), 0, 2, 5) 127 | # 绘制文字标签,用name字段,颜色0,黑体,32好,中心对齐 128 | gdraw.draw_lab(paper, shandong, 'name', 0, ('simhei', 32), 'ct') 129 | # 绘制比例尺,右侧180,底部100的位置,宽度占十分之三宽度,高度30,times字体,48号,颜色0,单位km,线条宽度3, 右对齐 130 | gdraw.draw_unit(paper, -180, -100, 0.3, 30, ('times', 48), 0, 'km', 3, anc='r') 131 | # 绘制标题文字,在180, 120的位置,颜色0,楷体,128号,绘制山东省 132 | gdraw.draw_text(paper, '山东省', 180, 120, 0, ('simkai', 128)) 133 | # 在右上角240,240的位置,黑体,100号,线条宽度2,箭头中心线高度100,颜色0,绘制指北针 134 | gdraw.draw_N(paper, -240, 240, ('simhei', 100), 2, 100, 0) 135 | 136 | from PIL import Image 137 | Image.formarray(paper).show() 138 | ``` 139 | 140 | ![](http://idoc.imagepy.org/gis/08.png) 141 | 142 | geonumpy 提供了为地图定制的一套绘图函数,可以方便的绘制比例尺,指北针,图例等元素。 143 | 144 | 145 | 146 | ## 更多功能 147 | 148 | geopandas 还在开发过程中,更多功能请查阅文档,也欢迎提交 issue 或贡献代码。 149 | -------------------------------------------------------------------------------- /doc/api_download.md: -------------------------------------------------------------------------------- 1 | # geonumpy.download 2 | 3 | 遥感影像获取是一切分析的前提,好在目前有一些开放的卫星,免费向大家提供下载服务,download 模块主要功能就是实现从各大开放网站上下载遥感影像数据。 4 | 5 | 6 | 7 | ## modis_search 8 | 9 | search(product, level, areas, terms) 10 | 11 | **product:** modis 的产品类型,例如['MOD09Q1', 'MOD11A2'], level=6, areas=['h25v05', 'h27v05', 'h28v05'], terms=[(2019, (0,30))] 12 | 13 | **level:** level=6,代表影像等级 14 | 15 | **areas:** 要下载区域进行行列编号,例如 ['h25v05', 'h27v05', 'h28v05'] 16 | 17 | **terms:** 周期,[(2019, (0,30)), ...],年份跟着变数,支持多个。 18 | 19 | ```python 20 | files = search(['MOD09Q1', 'MOD11A2'], 21 | level=6, 22 | areas=['h25v05', 'h27v05', 'h28v05'], 23 | terms=[(2019, (0,30))]) 24 | 25 | >>> 26 | searching... MOD09Q1 level 6 2019 27 | 001 day ... done 28 | 009 day ... done 29 | 017 day ... done 30 | 025 day ... done 31 | 32 | searching... MOD11A2 level 6 2019 33 | 001 day ... done 34 | 009 day ... done 35 | 017 day ... done 36 | 025 day ... done 37 | 38 | 24 new files found! 39 | ``` 40 | 41 | 42 | 43 | ## modis_download 44 | 45 | modis_download(files, des): 46 | 47 | **files:** 上一步search到的结果,要下载的文件名 48 | 49 | **des:** 存储目录 50 | 51 | ```python 52 | files = search(['MOD09Q1', 'MOD11A2'], level=6, areas=['h25v05', 'h27v05', 'h28v05'], terms=[(2019, (0,30))]) 53 | 54 | download(files, '') 55 | 56 | >>> MOD09Q1.A2019001.h25v05.006.2019010205323.hdf ... 57 | ``` 58 | 59 | 60 | 61 | ## landsat_search 62 | 63 | landsat_search(product, crs, start, end, month=None, lcloud='', scloud='', page=1) 64 | 65 | **product:** 选项,7,8,45,表示不同代的产品 66 | 67 | **crs:** 要下载的行列号序列 68 | 69 | **start:** 要下载的开始时间 70 | 71 | **end:** 要下载的结束时间 72 | 73 | **month:** 下载的月份列表 74 | 75 | **lcloud:** 陆地云量小于该值 76 | 77 | **scloud:** 卫星影像云量小于该值 78 | 79 | **pare:** 下载的页数,满页后不再改变 80 | 81 | ```python 82 | records = get_record(7, blocks, '06/30/2018', '07/30/2019', None, 10, 10, 1) 83 | >>> 84 | searching 123 043, ... 85 | 1 86 | searching 119 037, ... 87 | 3 88 | searching 122 037, ... 89 | 6 90 | 91 | >>> 92 | LE07_L1TP_123043_20181126_20181222_01_T1 93 | LE07_L1TP_119037_20190509_20190604_01_T1 94 | LE07_L1TP_119037_20181029_20181124_01_T1 95 | LE07_L1TP_119037_20181013_20181108_01_T1 96 | LE07_L1TP_122037_20190701_20190727_01_T1 97 | LE07_L1TP_122037_20190311_20190406_01_T1 98 | LE07_L1TP_122037_20190122_20190217_01_T1 99 | LE07_L1TP_122037_20181018_20181113_01_T1 100 | LE07_L1TP_122037_20181002_20181030_01_T1 101 | LE07_L1TP_122037_20180714_20180809_01_T1 102 | ``` 103 | 104 | 105 | 106 | # landsat_download 107 | 108 | ... -------------------------------------------------------------------------------- /doc/api_draw.md: -------------------------------------------------------------------------------- 1 | # geonumpy.draw 2 | 3 | 此模块用于绘图,这里说明本模块凡是需要提供坐标,长度的内容,都遵循以下规则: 4 | 5 | * 正整数表示从左边算或从上方算,具体看作用的方向 6 | 7 | * 负整数表示从右边算或从底部算,具体看作用的方向 8 | 9 | * 小数表示对应维度的比例,例如x给0.5,表示宽度的一半 10 | 11 | 12 | 13 | ### draw_polygon 14 | 15 | --- 16 | 17 | draw_polygon(raster, shape, color, width) 18 | 19 | **raster:** 被绘制的 geoarray 对象 20 | 21 | **shape:** 用于绘图的GeoDataFrame对象,必须是polygon类型 22 | 23 | **color: ** 绘制用的颜色 24 | 25 | * 如果是整数,浮点,表示用这个值绘制 26 | 27 | * 如果是pandas.series对象或ndarray对象,则表示依次用这些颜色绘制每个对象 28 | 29 | * 如果是字符类型,则会到shape中查找对应列的值一次绘制每个对象 30 | 31 | * 如果是tuple类型,则认为是rgb色 32 | 33 | **width:** 线条宽度,如果给0,则表示进行多边形填充 34 | 35 | 36 | 37 | ### draw_text 38 | 39 | --- 40 | 41 | draw_text(raster, txt, x, y, color, ft, anc='lt', align='left') 42 | 43 | **raster:** 被绘制的geoarray对象 44 | 45 | **txt:** 要绘制的文字,支持多行 46 | 47 | **x, y:** 绘制的坐标,比例尺的基准点是左下角或右小角,取决于anc 48 | 49 | **w, h:** 绘制比例尺的宽度,高度(通常w会给小数,比如0.3,表示占用十分之三宽度) 50 | 51 | **color:** 绘图使用的颜色 52 | 53 | **ft:** 字体,字号的二元组 54 | 55 | **anc:** lt, rt, lb, rb, ct,表示左上角,右上角,左下角,右下角,中心 56 | 57 | **align:** left, right, center,多行文字时,左右对齐,或中心对齐 58 | 59 | 60 | 61 | ### draw_lab 62 | 63 | --- 64 | 65 | draw_lab(raster, shp, name, color, ft, anc) 66 | 67 | **raster:** 被绘制的geoarray对象 68 | 69 | **shp:** 用于绘图的GeoDataFrame对象 70 | 71 | **name:** 列名称,需要时str类型的列,用于绘制标签 72 | 73 | **color:** 绘图使用的颜色 74 | 75 | **ft:** 字体,字号的二元组 76 | 77 | **anc:** lt, rt, lb, rb, ct,表示左上角,右上角,左下角,右下角,中心 78 | 79 | 80 | 81 | ### draw_unit 82 | 83 | --- 84 | 85 | draw_unit(raster, x, y, w, h, ft, color, unit, lw, anc='l') 86 | 87 | **raster:** 被绘制的geoarray对象 88 | 89 | **x, y:** 绘制的坐标,比例尺的基准点是左下角或右小角,取决于anc 90 | 91 | **w, h:** 绘制比例尺的宽度,高度(通常w会给小数,比如0.3,表示占用十分之三宽度) 92 | 93 | **ft:** 字体,字号的二元组 94 | 95 | **color:** 绘图使用的颜色 96 | 97 | **lw:** 线条宽度 98 | 99 | **anc:** left 或 right,表示左对齐或右对齐 100 | 101 | 102 | 103 | ### draw_N 104 | 105 | --- 106 | 107 | draw_N(raster, x, y, ft, lw, h, color) 108 | 109 | **raster:** 被绘制的geoarray对象 110 | 111 | **x, y:** 绘制的坐标,基准点是箭头顶端 112 | 113 | **ft:** 字体,字号的二元组 114 | 115 | **lw:** 线条宽度 116 | 117 | **h:** 指北针中心竖线高度 118 | 119 | **color:** 绘图使用的颜色 120 | 121 | 122 | 123 | ### draw_bound 124 | 125 | --- 126 | 127 | draw_bound(raster, left, top, right, bot, color, lw, clear=None): 128 | 129 | **raster:** 被绘制的geoarray对象 130 | 131 | **left, top, right, bot:** 上下左右的边界坐标,右侧,底部通常用左侧与上边的相反数 132 | 133 | **color:** 绘图使用的颜色 134 | 135 | **lw:** 线条宽度 136 | 137 | **clear:** 线框以外的清除色,None表示不清除 138 | 139 | 140 | 141 | ### draw_ruler 142 | 143 | --- 144 | 145 | draw_ruler(raster, left, top, right, bot, step, crs, ft, color, lw, dh) 146 | 147 | **raster:** 被绘制的geoarray对象 148 | 149 | **left, top, right, bot:** 上下左右的边界坐标,右侧,底部通常用左侧与上边的相反数 150 | 151 | **step:** 刻度间隔 152 | 153 | **crs:** 刻度坐标系,很可能使用投影坐标绘图,但使用经纬度绘制刻度 154 | 155 | **ft:** 字体,字号的二元组 156 | 157 | **color:** 绘图使用的颜色 158 | 159 | **lw:** 线条宽度 160 | 161 | **dh:** 刻度高度 162 | 163 | 164 | 165 | ### draw_style 166 | 167 | --- 168 | 169 | draw_style(raster, x, y, body, mar, recsize, ft, color, box) 170 | 171 | **raster:** 被绘制的geoarray对象 172 | 173 | **x, y:** 绘制的坐标,基准点是左下角 174 | 175 | **body:** 三元组,具体用法如下: 176 | 177 | * (str, font, height) 表示用font字体,height高度,绘制str 178 | * ('rect', c, str) 用颜色c,绘制一个矩形,后面跟着文字str 179 | * ('line', c, str) 用颜色c,绘制一条线段,后面跟着文字str 180 | * ('circle', c, str) 用颜色c,绘制一个圆,后面跟着文字str 181 | * ('blank', h) 一个高度为h的空行 182 | 183 | **mar:** 二元组,图例之间的行列间距 184 | 185 | **recsize:** 三元组,矩形的尺寸,以及边框线条宽度 186 | 187 | **ft:** 字体,字号的二元组 188 | 189 | **color:** 绘图使用的颜色,将被用于文字,矩形边框,以及外框 190 | 191 | **box:** 最外侧边框宽度,0表示无边框 192 | 193 | ```python 194 | body = [('Style', 'simhei', 72), 195 | ('line', 1, 'line'), 196 | ('circle', 2, 'circle'), 197 | ('rect', 3, 'rect')] 198 | 199 | draw_style(paper,30,-30, body, mar=(20, 30), recsize=(120,60,3), 200 | font=('simsun', 60), color=4, box=5) 201 | ``` 202 | 203 | -------------------------------------------------------------------------------- /doc/api_geoarray.md: -------------------------------------------------------------------------------- 1 | # GeoArray 2 | 3 | GeoArray 是地理图像处理的基础,继承于ndarray,携带坐标系与投影矩阵。 4 | 5 | 6 | 7 | ### Class GeoArray 8 | 9 | --- 10 | 11 | GeoArray(array, crs=None, mat=np.array([[1,1,0],[1,0,1]]) 12 | 13 | >> **array:** image data, 2D-3D 14 | >> 15 | >> **crs:** egps code or wkt string 16 | >> 17 | >> **mat:** matrix from pixel coordinate to crs point 18 | 19 | > >**channels(self, n=None):** 获取指定通道,不传参返回通道数 20 | > > 21 | > >**project(self, r, c):** 行列投影到crs坐标系 22 | > > 23 | > >**invpro(self, r, c):** crs坐标系获取对应行列 24 | > > 25 | > >**lookup(self, lut):** 套用假彩色 26 | > > 27 | > >**getbox(self):** 获取 (shape, crs, mat, channels) 28 | 29 | 30 | 31 | ```python 32 | gimg = GeoArray(np.zeros(100,100, dtype=np.uint8), 4326) 33 | print(gimg.crs) 34 | print(gimg.mat) 35 | ``` 36 | 37 | 38 | 39 | ### geoarray 40 | 41 | --- 42 | 43 | geoarray(arr, crs=None, mat=np.array([[1,1,0],[1,0,1]])) 44 | 45 | **array:** image data, 2d-3d 46 | 47 | **crs:** egps code or wkt string 48 | 49 | **mat:** matrix for pixel coordinate to crs 50 | 51 | 52 | 53 | **return:** GeoArray object with the given data 54 | 55 | ```python 56 | import geonumpy as gnp 57 | gnp.geoarray(np.zeros(100,100, dtype=np.uint8), 4326) 58 | ``` 59 | 60 | 61 | 62 | ### frombox 63 | 64 | --- 65 | 66 | frombox(shp, crs=None, mat, chan=1, dtype=np.uint8): 67 | 68 | **shp:** image shape 69 | 70 | **crs:** egps code or wkt string 71 | 72 | **mat:** matrix for pixel coordinate to crs 73 | 74 | **chan:** number of channels 75 | 76 | **dtype:** data's type 77 | 78 | 79 | 80 | **return:** GeoArray object with the given data 81 | 82 | ```python 83 | import geonumpy as gnp 84 | gnp.frombox((100,100), 4326, np.array([[1,1,0],[1,0,1]]),1) 85 | ``` 86 | 87 | -------------------------------------------------------------------------------- /doc/api_indicate.md: -------------------------------------------------------------------------------- 1 | # indicate 2 | 3 | 一些经典指标 4 | 5 | 6 | 7 | ### ndvi 8 | 9 | --- 10 | 11 | ndvi(raster1, raster2) 12 | 13 | **raster1,raster2:** 图1,图2 14 | 15 | **return:** 返回ndvi 16 | 17 | -------------------------------------------------------------------------------- /doc/api_io.md: -------------------------------------------------------------------------------- 1 | # geonumpy.io 2 | 3 | 地理数据的读取,支持shapefile,tif,hdf 4 | 5 | 6 | 7 | ### read_shp 8 | 9 | ------ 10 | 11 | 此函数重命名了geopandas.read_file 12 | 13 | read_shp(path, encoding='utf-8') 14 | 15 | **path:** 文件路径 16 | 17 | **encoding:** 属性表字符集 18 | 19 | **return:** GeoDataFrame 对象 20 | 21 | 22 | 23 | ### write_shp 24 | 25 | --- 26 | 27 | 此函数重命名了geopandas.to_file 28 | 29 | write_shp(shp, path, encoding='utf-8') 30 | 31 | **shp:** GeoDataFrame 对象 32 | 33 | **path:** 存储目录 34 | 35 | **encoding:** 属性表字符集 36 | 37 | 38 | 39 | ### read_tif 40 | 41 | --- 42 | 43 | read_tif(path, chans=None) 44 | 45 | **path:** 文件路径 46 | 47 | **chans:** 读取的通道,None表示所有通道 48 | 49 | **return:** GeoArray 对象,2维或3维,取决于通道 50 | 51 | 52 | 53 | ### read_hdf 54 | 55 | ------ 56 | 57 | read_hdf(path, chans=None) 58 | 59 | 所有参数意义同 read_hdf 60 | 61 | 62 | 63 | ### read_raster 64 | 65 | ------ 66 | 67 | read_raster(path, chans=None) 68 | 69 | 通过path自动判断,然后调用 read_tif 或 read_hdf 70 | 71 | 72 | 73 | ### read_tif_info 74 | 75 | ------ 76 | 77 | read_tif_info(path,) 78 | 79 | **path:** 文件路径 80 | 81 | **return:** 图像基础信息,(shape, crs, mat, chans) 82 | 83 | 84 | 85 | ### read_hdf_info 86 | 87 | ------ 88 | 89 | read_hdf_info(path) 90 | 91 | 所有参数意义同 read_tif_info 92 | 93 | 94 | 95 | ### read_raster_info 96 | 97 | ------ 98 | 99 | read_hdf_info(path) 100 | 101 | 通过path自动判断,然后调用 read_tif_info 或 read_hdf_info 102 | 103 | 104 | 105 | ### write_tif 106 | 107 | --- 108 | 109 | write_tif(raster, path) 110 | 111 | **raster:** 要写入的 GeoArray 对象 112 | 113 | **path:** 写入地址 114 | 115 | 116 | 117 | ### write_hdf 118 | 119 | ------ 120 | 121 | write_hdf(raster, path) 122 | 123 | 所有参数意义同 write_tif 124 | 125 | 126 | 127 | ### write_raster 128 | 129 | ------ 130 | 131 | write_raster(raster, path) 132 | 133 | 通过path自动判断 write_tif 或 write_hdf -------------------------------------------------------------------------------- /doc/api_match.md: -------------------------------------------------------------------------------- 1 | # geonumpy.match 2 | 3 | 坐标系转换,拼接,重采样是遥感影像中常见的问题 4 | 5 | 6 | 7 | ### mp2pm 8 | 9 | ------ 10 | 11 | mp2pm(pts, m1, prj1, prj2, m2) 12 | 13 | **pts:** 要投影的点,ndarray n*2 14 | 15 | **mat1, crs1:** 第一张图的投影矩阵及坐标系 16 | 17 | **crs2, mat2:** 第二张图的坐标系及投影矩阵 18 | 19 | **return: ** 投射后的结果,ndarray n*2 20 | 21 | 22 | 23 | ### match_one 24 | 25 | --- 26 | 27 | match_one(raster, des, step=10, out='auto', order=1) 28 | 29 | **raster:** 被投射图像,GeoArray对象 30 | 31 | **des:** 投射目标,GeoArray对象 32 | 33 | **step:** 真实采样间距,只有这些点真实计算投射位置,剩下的用双线性插值获得,注意并非是色彩的插值,而是对投射位置的插值,所以只要不是很大,对结果影响很小,但性能会增加许多。 34 | 35 | **out:** 输出参数,具体如下 36 | 37 | * dtype: 例如 np.uint8, np.int16,指明输出类型 38 | * auto:投影成与 raster 相同的类型 39 | * in: 结果投影到 des,当des的维度或数据类型与raster不同的时候,则退化为auto 40 | 41 | **order:** 插值方式,0表示最邻近,1表示双线性 42 | 43 | **return:** 返回投影后的图像 44 | 45 | 46 | 47 | ### match_multi 48 | 49 | --- 50 | 51 | match_multi(rasters, des, step=10, out='auto', order=1) 52 | 53 | **rasters:** 被投射图像,GeoArray 的 list 54 | 55 | **des:** 投射目标,GeoArray对象 56 | 57 | **step:** 真实采样间距,只有这些点真实计算投射位置,剩下的用双线性插值获得,注意并非是色彩的插值,而是对投射位置的插值,所以只要不是很大,对结果影响很小,但性能会增加许多。 58 | 59 | **out:** 输出参数,具体如下 60 | 61 | - dtype: 例如 np.uint8, np.int16,指明输出类型 62 | - auto:投影成与 raster 相同的类型 63 | - in: 结果投影到 des,当des的维度或数据类型与raster不同的时候,则退化为auto 64 | 65 | **order:** 插值方式,0表示最邻近,1表示双线性 66 | 67 | **return:** 返回投影后的图像 68 | 69 | 70 | 71 | ### build_index 72 | 73 | --- 74 | 75 | build_index(fs) 76 | 77 | **fs:** 文件路径列表,函数为每个文件建立基础信息及空间索引关系。 78 | 79 | **return:** 根据文件路径创建一个GeoDataFrame对象,列如下:['geometry', 'shape', 'mat', 'channels', 'path'],里面存有图像边界矢量对象,图像尺寸,图像投影矩阵,通道列表,以及文件目录。 80 | 81 | 82 | 83 | ### match_idx 84 | 85 | --- 86 | 87 | match_idx(idx, des, step=10, out='auto', order=1, chan=None) 88 | 89 | 类似于 match_multi, 但文件并不事先加载,而是在通过索引确定需要的块,然后及时读取,用完及时释放。 90 | 91 | **idx:** 文件集索引,通常是build_index的结果,用于快速判断有交集的块,只在需要时候读取。 92 | 93 | **des:** 投射目标,GeoArray对象 94 | 95 | **step:** 真实采样间距,只有这些点真实计算投射位置,剩下的用双线性插值获得,注意并非是色彩的插值,而是对投射位置的插值,所以只要不是很大,对结果影响很小,但性能会增加许多。 96 | 97 | **out:** 输出参数,具体如下 98 | 99 | - dtype: 例如 np.uint8, np.int16,指明输出类型 100 | - auto:投影成与 raster 相 101 | 102 | **chan:** 要投影的通道列表,None表示所有通道 -------------------------------------------------------------------------------- /doc/api_pretreat.md: -------------------------------------------------------------------------------- 1 | # pretreat 2 | 3 | 遥感影像需要很多预处理工作,比如大气校正,去条带 4 | 5 | 6 | 7 | ### degap 8 | 9 | --- 10 | 11 | degap(img, msk, r=0) 12 | 13 | **img:** GeoArray对象,要去条带的影像 14 | 15 | **msk:** 条带掩膜 16 | 17 | **r:** 去条带影响半径,超出这个范围则不进行修复,0 表示整图修复 -------------------------------------------------------------------------------- /doc/api_util.md: -------------------------------------------------------------------------------- 1 | # geonumpy.util 2 | 3 | 一些常用函数 4 | 5 | 6 | 7 | ### shp2box 8 | 9 | ------ 10 | 11 | shp2box(shape, scale, margin=0.05, chan=1) 12 | 13 | **shape:** GeoDataFrame 对象,要计算边界的矢量数据 14 | 15 | **scale:** 比例尺,有如下情况 16 | 17 | * 浮点数或整数,使用给定比例尺生成box 18 | * 二元tuple,按照指定尺寸的box,比例尺自动计算 19 | 20 | **margin:** 边距,输入比例,0.05表示上下左右各留出5%空间 21 | 22 | **chan:** 指定 box 的通道数 23 | 24 | **return:** 图像基础信息,(shape, crs, mat, chans) 25 | 26 | 27 | 28 | ### box2shp 29 | 30 | --- 31 | 32 | box2shp(shape, crs, mat, chans) 33 | 34 | **shape:** GeoArray 对象的尺寸 35 | 36 | **crs:** 坐标系 37 | 38 | **mat:** 映射矩阵 39 | 40 | **chans:** 通道数 41 | 42 | **return:** 返回边界多边形,带着crs,构成一个 GeoSeries 对象 -------------------------------------------------------------------------------- /doc/dem_draw.md: -------------------------------------------------------------------------------- 1 | # geonumpy.draw 2 | 3 | 地图绘制是成果的重要展现形式,虽然 matplotlib 内置了丰富的绘图函数,但是要绘制地图的比例尺,指北针等特定元素,依然不是一个简单工作,而 geonumpy 的 draw 模块,提供了定制化的地图绘制函数。 4 | 5 | 6 | 7 | ## 矢量图绘制 8 | 9 | ```python 10 | import geonumpy.io as gio 11 | import geonumpy.draw as gdraw 12 | 13 | # 读取山东省矢量图 14 | shandong = gio.read_shp('../data/shape/shandong.shp') 15 | # 投影成 web 墨卡托 16 | shandong = shandong.to_crs(3857) 17 | # 从矢量图计算图像空间信息,尺寸3600*2400,边距十分之一 18 | box = gutil.shp2box(shandong, (3600, 2400), 0.1, 1) 19 | # 从空间信息实例化 GeoArray 对象 20 | paper = gnp.frombox(*box, dtype=np.uint8) 21 | # 底图赋值为白色 22 | paper[:] = 255 23 | # 绘制多边形,颜色为0,线条宽度为2 24 | gdraw.draw_polygon(paper, shandong, 0, 2) 25 | # 绘制刻度,左右80,上下50,单位间隔1,坐标系4326,使用times字体,32好,颜色0,线条宽度2,刻度高5 26 | gdraw.draw_ruler(paper, 80, 50, -80, -50, 1, 4326, ('times', 32), 0, 2, 5) 27 | # 绘制文字标签,用name字段,颜色0,黑体,32好,中心对齐 28 | gdraw.draw_lab(paper, shandong, 'name', 0, ('simhei', 32), 'ct') 29 | # 绘制比例尺,右侧180,底部100的位置,宽度占十分之三宽度,高度30,times字体,48号,颜色0,单位km,线条宽度3, 右对齐 30 | gdraw.draw_unit(paper, -180, -100, 0.3, 30, ('times', 48), 0, 'km', 3, anc='r') 31 | # 绘制标题文字,在180, 120的位置,颜色0,楷体,128号,绘制山东省 32 | gdraw.draw_text(paper, '山东省', 180, 120, 0, ('simkai', 128)) 33 | # 在右上角240,240的位置,黑体,100号,线条宽度2,箭头中心线高度100,颜色0,绘制指北针 34 | gdraw.draw_N(paper, -240, 240, ('simhei', 100), 2, 100, 0) 35 | 36 | from PIL import Image 37 | Image.formarray(paper).show() 38 | ``` 39 | 40 | ![](http://idoc.imagepy.org/gis/08.png) 41 | 42 | 43 | 44 | ## 图例的使用 45 | 46 | ```python 47 | paper = gnp.geoarray(np.zeros((480,1024), dtype=np.uint8)) 48 | 49 | body = [('Style', 'simhei', 72), 50 | ('blank',50), 51 | ('line', 1, 'this is line style'), 52 | ('circle', 2, 'this is circle style'), 53 | ('rect', 3, 'this is rect style')] 54 | # 色彩索引表 55 | lut = np.array([[255,255,255], 56 | [255,0 ,0 ], 57 | [0 ,255,0 ], 58 | [0 ,0 ,255], 59 | [0 ,0 ,0 ]], dtype=np.uint8) 60 | 61 | gdraw.draw_style(paper,128,-20, body, mar=(20, 30), 62 | recsize=(120,60,3), font=('simsun', 60), color=4, box=5) 63 | # 将色彩索引表套用在paper上 64 | paper = paper.lookup(lut) 65 | 66 | from PIL import Image 67 | Image.formarray(paper).show() 68 | ``` 69 | ![](http://idoc.imagepy.org/gis/09.png) 70 | 71 | 72 | 73 | ## 根据面积进行等级划分 74 | 75 | ```python 76 | # 读取矢量文件及计算影像空间信息 77 | shandong = gio.read_shp('../data/shape/shandong.shp') 78 | shandong = shandong.to_crs(3857) 79 | box = gutil.shp2box(shandong, (3600, 2400), 0.1, 1) 80 | paper = gnp.frombox(*box, dtype=np.uint8) 81 | 82 | # 取各个城市的面积 83 | areas = shandong.area 84 | # 制作一个长度为100的索引表,3级60个,2级30个,1级10个 85 | grade_lut = np.array([3]*60 + [2]*30 + [1]*10, dtype=np.uint8) 86 | # 将面积缩放到0-100 87 | vs = (areas-areas.min())/(areas.max()-areas.min())*99 88 | # 套用等级索引,为每个城市划分等级 89 | grade = grade_lut[vs.astype(int)] 90 | # 这里颜色传入一个序列,代表依次使用这些颜色绘制 91 | gdraw.draw_polygon(paper, shandong, grade, 0) 92 | 93 | # ===== 其他装饰信息 ===== 94 | gdraw.draw_polygon(paper, shandong, 4, 2) 95 | gdraw.draw_ruler(paper, 80, 50, -80, -50, 1, 4326, ('times', 32), 4, 2, 5) 96 | gdraw.draw_lab(paper, shandong, 'name', 4, ('simhei', 32), 'ct') 97 | gdraw.draw_unit(paper, -180, -100, 0.3, 30, ('times', 48), 4, 'km', 3, anc='r') 98 | gdraw.draw_text(paper, '山东省', 180, 120, 4, ('simkai', 128)) 99 | gdraw.draw_N(paper, -240, 240, ('simhei', 100), 2, 100, 4) 100 | 101 | # ===== 图例绘制 ===== 102 | body = [('图例', 'simhei', 72), 103 | ('rect', 1, '特大城市'), 104 | ('rect', 2, '中型城市'), 105 | ('rect', 3, '一般城市')] 106 | # 底图,位置,内容,空隙,矩形尺寸及线宽,字体字号颜色,外边框宽度 107 | gdraw.draw_style(paper, 150, -90, body, mar=(20, 30), 108 | recsize=(120,60,2), font=('simsun', 60, 4), color=4, box=0) 109 | 110 | # ===== 色彩索引 ===== 111 | lut = np.array([[255,255,255], 112 | [255,200,100], 113 | [255,255,128], 114 | [255,255,200], 115 | [0 ,0 ,0 ]], dtype=np.uint8) 116 | 117 | # ===== 套用假彩色 ===== 118 | paper = paper.lookup(lut) 119 | 120 | from PIL import Image 121 | Image.formarray(paper).show() 122 | ``` 123 | 124 | ![](http://idoc.imagepy.org/gis/10.png) 125 | 126 | 这里我们使用一个技巧,取得每个城市的面积,然后为面积划分等级,用等级当作颜色进行多边形绘制,最后统一进行色彩映射。其实如果数据中带有等级之类的数值属性,我们也可以传入一个字符串当作颜色,函数会根据字符串查找对应的列当作颜色进行绘制。 127 | 128 | 129 | 130 | ## 土地利用类型图绘制 131 | 132 | ```python 133 | # ===== look up table ===== 134 | lut = np.array([[0 ,0 ,0 ], 135 | [168,168,0 ], 136 | [20 ,119,73 ], 137 | [169,208,95 ], 138 | [56 ,168,0 ], 139 | [126,206,244], 140 | [0 ,86 ,154], 141 | [112,168,0 ], 142 | [147,47 ,20 ], 143 | [202,202,202], 144 | [0 ,255,197], 145 | [255,255,255]], dtype=np.uint8) 146 | 147 | # ===== read shape file and make a paper ===== 148 | liaoning = gio.read_shp('../data/shape/shandong.shp') 149 | liaoning = liaoning.to_crs(3857) 150 | box = gutil.shp2box(liaoning, (3600, 2400), 0.15, 1) 151 | paper = gnp.frombox(*box, dtype=np.uint8) 152 | 153 | # ===== match the class tif into paper 154 | fs = glob('../data/class/*.tif') 155 | idx = gmt.build_index(fs) 156 | gmt.match_idx(idx, paper, out='in', order=0) 157 | 158 | # ===== draw polygon as mask ===== 159 | msk = paper * 0 160 | gdraw.draw_polygon(msk, liaoning, 255, 0) 161 | paper[msk==0] = 11 162 | 163 | body = [('图例', 'simhei', 72), 164 | ('rect', 1, '农田'), 165 | ('rect', 2, '森林'), 166 | ('rect', 3, '草地'), 167 | ('rect', 4, '灌丛'), 168 | ('rect', 5, '湿地'), 169 | ('rect', 6, '水体'), 170 | ('rect', 7, '苔原'), 171 | ('rect', 8, '隔水层'), 172 | ('rect', 9, '裸地'), 173 | ('rect', 10, '冰雪')] 174 | # 底图,位置,内容,空隙,矩形尺寸及线宽,字体字号颜色,外边框宽度 175 | gdraw.draw_style(paper, 60, -60, body, mar=(20, 30), 176 | recsize=(120,60,0), font=('simsun', 60, 0), color=0, box=0) 177 | 178 | # ===== 其他装饰信息 ===== 179 | gdraw.draw_unit(paper, -120, -60, 0.3, 30, ('times', 48), 0, 'km', 3, anc='r') 180 | gdraw.draw_text(paper, '山东省土地利用类型', 80, 60, 0, ('simkai', 128)) 181 | gdraw.draw_N(paper, -240, 240, ('simhei', 100), 2, 100, 0) 182 | gdraw.draw_polygon(paper, liaoning, 0, 2) 183 | gdraw.draw_bound(paper, 5, 5, -5, -5, 0, 2, clear=None) 184 | 185 | # ===== 套用假彩色 ===== 186 | paper = paper.lookup(lut) 187 | 188 | from PIL import Image 189 | Image.formarray(paper).show() 190 | 191 | ``` 192 | 193 | ![](http://idoc.imagepy.org/gis/11.png) 194 | 195 | -------------------------------------------------------------------------------- /doc/dem_forest_statistic.md: -------------------------------------------------------------------------------- 1 | # 综合应用:森林覆盖率统计 2 | 3 | 这里我们用一个综合性的例子串联 geonumpy 的各种使用方法,当然这其中离不开 numpy,scipy,pandas 等经典科学计算库的支持。 4 | 5 | 6 | 7 | ## 引入依赖 8 | 9 | ```python 10 | import geonumpy as gnp 11 | import geonumpy.io as gio 12 | import geonumpy.util as gutil 13 | import geonumpy.draw as gdraw 14 | import geonumpy.match as gmt 15 | import numpy as np 16 | import scipy.ndimage as ndimg 17 | import matplotlib.pyplot as plt 18 | from PIL import Image 19 | from glob import glob 20 | ``` 21 | 22 | 23 | 24 | ## 用地类型拼接 25 | 26 | ```python 27 | def match_class(df): 28 | shandong = df.to_crs(3857) 29 | box = gutil.shp2box(shandong, (3600, 2400), 0.15, 1) 30 | paper = gnp.frombox(*box, dtype=np.uint8) 31 | idx = gmt.build_index(glob('../data/class/*.tif')) 32 | gmt.match_idx(idx, paper, out='in', order=0) 33 | gio.write_tif(paper, '../data/result/shandong_class.tif') 34 | ``` 35 | 36 | ![](http://idoc.imagepy.org/gis/13.png) 37 | 38 | 由于用地类型拼接是比较耗时的工作,因而我们将拼接结果保存下来。 39 | 40 | 41 | 42 | ## 市级行政区标记 43 | 44 | ```python 45 | def city_label(df): 46 | shandong = df.to_crs(3857) 47 | box = gutil.shp2box(shandong, (3600, 2400), 0.15, 1) 48 | paper = gnp.frombox(*box, dtype=np.uint8) 49 | # 这里我们使用顺序递增的序号当作颜色依次绘制每个城市,得到标记图层 50 | gdraw.draw_polygon(paper, shandong, np.arange(len(shandong))+1, 0) 51 | gio.write_tif(paper, '../data/result/shandong_label.tif') 52 | ``` 53 | 54 | ![](http://idoc.imagepy.org/gis/14.png) 55 | 56 | 这里我们用同样的尺寸绘制标记图像,我们采用多边形绘制,但颜色传入的是一个 arange 序号,同样,我们将标记结果存储,以便后续使用。 57 | 58 | 59 | 60 | ## 用地类型及标记展示 61 | 62 | ```python 63 | def show_class_label(cls, lab): 64 | ax1, ax2 = plt.subplot(121), plt.subplot(122) 65 | ax1.imshow(cls) 66 | ax2.imshow(lab) 67 | ax1.set_title('shandong class') 68 | ax2.set_title('shandong label') 69 | plt.show() 70 | ``` 71 | 72 | ![](http://idoc.imagepy.org/gis/12.png) 73 | 74 | 读取两张图,并绘制出来。 75 | 76 | 77 | 78 | ## 各个城市森林覆盖率计算 79 | 80 | ```python 81 | def statistic(cls, lab, df): 82 | # 用lab作为标记,对cls==2的森林做求和统计 83 | forest = ndimg.sum(cls==2, lab, np.arange(lab.max())+1) 84 | # 像素统计,得到每个城市的面积 85 | total = np.bincount(lab.ravel())[1:] 86 | # 将城市面积,森林面积,森林覆盖率赋值到df 87 | df['area'] = total 88 | df['forest'] = forest 89 | df['ratio'] = forest/total 90 | return df 91 | 92 | >>> name area forest ratio 93 | 0 济南市 101390 11575.0 0.114163 94 | 1 青岛市 140587 6844.0 0.048682 95 | 2 淄博市 75245 12162.0 0.161632 96 | 3 枣庄市 55496 4718.0 0.085015 97 | 4 东营市 93881 80.0 0.000852 98 | 5 烟台市 179707 19820.0 0.110291 99 | 6 潍坊市 203411 11056.0 0.054353 100 | 7 济宁市 138547 3371.0 0.024331 101 | 8 泰安市 98093 8124.0 0.082819 102 | 9 威海市 73943 9339.0 0.126300 103 | 10 日照市 66440 4800.0 0.072246 104 | 11 莱芜市 28966 4380.0 0.151212 105 | 12 临沂市 214153 15699.0 0.073307 106 | 13 德州市 134892 654.0 0.004848 107 | 14 聊城市 110581 485.0 0.004386 108 | 15 滨州市 119506 1250.0 0.010460 109 | 16 菏泽市 150702 874.0 0.005800 110 | ``` 111 | 112 | 113 | ## 绘制森林覆盖率专题图 114 | 115 | ```python 116 | def draw_ratio(cls, lab, df): 117 | shandong = df.to_crs(3857) 118 | paper = cls.copy() 119 | paper[lab==0] = 12 120 | # 地类的色彩索引 121 | lut = np.array([[0 ,0 ,0 ], 122 | [168,168,0 ], 123 | [20 ,119,73 ], 124 | [169,208,95 ], 125 | [56 ,168,0 ], 126 | [126,206,244], 127 | [0 ,86 ,154], 128 | [112,168,0 ], 129 | [147,47 ,20 ], 130 | [202,202,202], 131 | [0 ,255,197], 132 | [20 ,255,73 ], 133 | [255,255,255]], dtype=np.uint8) 134 | # 地类的图例名称 135 | body = [('图例', 'simhei', 72), 136 | ('rect', 1, '农田'), 137 | ('rect', 2, '森林'), 138 | ('rect', 3, '草地'), 139 | ('rect', 4, '灌丛'), 140 | ('rect', 5, '湿地'), 141 | ('rect', 6, '水体'), 142 | ('rect', 7, '苔原'), 143 | ('rect', 8, '隔水层'), 144 | ('rect', 9, '裸地'), 145 | ('rect', 10, '冰雪')] 146 | 147 | # 为了视觉效果,我们将地类假彩色映射减淡处理 148 | lut[1:-2] = lut[1:-2] * 0.6 + 255 * 0.4 149 | 150 | # 构造一个列,作为标注,我们用森林覆盖率的百分数形式 151 | shandong['lab'] = shandong['ratio'].apply(lambda x:'%.2f%%'%(x*100)) 152 | gdraw.draw_lab(paper, shandong, 'lab', 0, ('simhei', 32), 'ct') 153 | 154 | # 绘制图例 155 | gdraw.draw_style(paper, 60, -60, body, mar=(20, 30), 156 | recsize=(120,60,0), font=('simsun', 60, 0), color=0, box=0) 157 | 158 | # 底图,位置,内容,空隙,矩形尺寸及线宽,字体字号颜色,外边框宽度 159 | gdraw.draw_unit(paper, -120, -60, 0.3, 30, ('times', 48), 0, 'km', 3, anc='r') 160 | gdraw.draw_text(paper, '山东省森林覆盖率统计', 80, 60, 0, ('simkai', 128)) 161 | gdraw.draw_N(paper, -240, 240, ('simhei', 100), 2, 100, 0) 162 | gdraw.draw_polygon(paper, shandong, 0, 2) 163 | # 将覆盖率超过10%的城市用绿色描边 164 | gdraw.draw_polygon(paper, shandong[shandong['ratio']>0.1], 11, 8) 165 | gdraw.draw_bound(paper, 5, 5, -5, -5, 0, 2, clear=None) 166 | # 进行假彩色映射 167 | return paper.lookup(lut) 168 | ``` 169 | 170 | ![](http://idoc.imagepy.org/gis/15.png) 171 | 172 | 最后我们绘制森林覆盖率统计结果的专题图。 -------------------------------------------------------------------------------- /doc/dem_geoarray.md: -------------------------------------------------------------------------------- 1 | # GeoArray 2 | 3 | GeoArray 是地理图像处理的基础,继承于ndarray,携带坐标系与投影矩阵。 4 | 5 | 6 | 7 | ## 创建 GeoArray 对象 8 | 9 | ```python 10 | import geonumpy as gnp 11 | import numpy as np 12 | 13 | mat = np.array([[0,1,0],[0,0,1]]) 14 | garr = gnp.geoarray(np.ones((5,5)), crs=4326, mat=mat) 15 | 16 | print(garr.crs) 17 | >>> 4326 18 | 19 | print(garr.mat) 20 | >>> [[0 1 0] 21 | [0 0 1]] 22 | ``` 23 | 创建 GeoArray 对象时,需要传递一个2-3D的ndarray对象,同时指定参考系crs,以及投影矩阵mat。 24 | 25 | * crs可以是egpn码,如同这里 4326 代表 wgs1984 投影,也可以是 wkt 字符串 26 | 27 | * mat 是投影矩阵,指明像素坐标到投影坐标的转换关系,其中左边一列是平移项,右边 2x2 是旋转缩放。 28 | 29 | 30 | 31 | ## GeoArray 的运算 32 | 33 | ```python 34 | garr2 = garr+1 35 | 36 | print(garr2.crs) 37 | >>> 4326 38 | 39 | print(garr2.mat) 40 | >>> [[0 1 0] 41 | [0 0 1]] 42 | ``` 43 | 44 | GeoArray 对象在进行运算,例如 +, -, *, /,或 np.sin, np.log 的时候,结果会自动保持 crs 与 mat。 45 | 46 | 47 | 48 | ## GeoArray 的切片 49 | 50 | ```python 51 | garr3 = garr[1::2,1::2] 52 | 53 | print(garr3.crs) 54 | >>> 4326 55 | 56 | print(garr3.mat) 57 | >>> [[1 2 0] 58 | [1 0 2]] 59 | ``` 60 | 61 | GeoArray 在进行数组切片的时候,会自动转换 mat,这里我们从1,1点开始切片,间隔 2,所以结果左边列变成了 1,1,而右侧原本单位矩阵,扩大了两倍。 62 | 63 | 64 | 65 | ## 获取 box 及从 box 创建 66 | 67 | ```python 68 | box = garr.getbox() 69 | print(box) 70 | >>> ((5, 5), 71 | 4326, 72 | array([[0, 1, 0], 73 | [0, 0, 1]]), 74 | 1) 75 | 76 | garr4 = gnp.frombox(*box, dtype=np.uint8) 77 | print(garr4.crs) 78 | >>> 4326 79 | 80 | print(garr4.mat) 81 | >>> [[1 2 0] 82 | [1 0 2]] 83 | ``` 84 | 85 | Box 可以理解为 GeoArray 的基础信息,是 (尺寸,参考,投影矩阵,通道数) 的元组。我们可以用 getbox 获取当前 GeoArray 的基础信息,也可以用 gnp.frombox 从边界信息构造 GeoArray 对象,但是注意,dtype 不属于空间信息,因而 gnp.frombox 需要传入一个 dtype 参数,默认为 np.uint8。 86 | 87 | 88 | 89 | ## 多通道处理 90 | 91 | ```python 92 | garr_mc = gnp.geoarray(np.ones((5,5,3)), crs=4326, mat=mat) 93 | >>> garr_mc.channels() 94 | 3 95 | >>> garr_mc.channels(0) 96 | GeoArray([[1., 1., 1., 1., 1.], 97 | [1., 1., 1., 1., 1.], 98 | [1., 1., 1., 1., 1.], 99 | [1., 1., 1., 1., 1.], 100 | [1., 1., 1., 1., 1.]]) 101 | ``` 102 | 103 | GeoArray 对象通过 channels() 方法可以获取通道数,而channels(n) 表示获取第 n 个通道的切片。 -------------------------------------------------------------------------------- /doc/dem_io.md: -------------------------------------------------------------------------------- 1 | # geonumpy.io 2 | 3 | io 提供了一系列常用数据类型的读取,除了影像数据之外,也重命名了geopandas读取shapefile的方法。 4 | 5 | 6 | 7 | ## 读写shapefile 8 | 9 | ```python 10 | import geonumpy.io as gio 11 | 12 | gdf = gio.read_shp('../data/shape/shandong.shp') 13 | 14 | print(gdf.crs) 15 | >>> {'proj': 'longlat', 'ellps': 'GRS80', 'no_defs': True} 16 | 17 | print(gdf) 18 | >>> gdf 19 | name geometry 20 | 0 济南市 POLYGON ((116.5148065247014 36.38448656000679,... 21 | 1 青岛市 (POLYGON ((120.9778956949305 36.42131830969231... 22 | 2 淄博市 POLYGON ((117.7029971845725 36.65135262502366,... 23 | 3 枣庄市 POLYGON ((117.1832330898353 34.82091945456921,... 24 | 4 东营市 (POLYGON ((118.2108902247041 37.38519372011416... 25 | 5 烟台市 (POLYGON ((120.2516180601255 37.11630801969937... 26 | 6 潍坊市 POLYGON ((118.2356711848773 36.41432422458337,... 27 | 7 济宁市 POLYGON ((116.1290081102362 35.63315157458555,... 28 | 8 泰安市 POLYGON ((116.2236255245983 36.16986052983964,... 29 | 9 威海市 (POLYGON ((121.7099395947366 37.12126350001057... 30 | 10 日照市 POLYGON ((118.8712478898783 35.39324951465341,... 31 | 11 莱芜市 POLYGON ((117.6633433146424 36.52794210504419,... 32 | 12 临沂市 POLYGON ((117.6154824151814 35.13172327500916,... 33 | 13 德州市 POLYGON ((115.8603655451338 37.07219694962959,... 34 | 14 聊城市 POLYGON ((115.3349590553162 36.37903970541811,... 35 | 15 滨州市 POLYGON ((117.3156585848759 37.50292387473706,... 36 | 16 菏泽市 POLYGON ((115.1253803253418 34.99438765516618,... 37 | 38 | gdf.plot() 39 | plt.show() 40 | ``` 41 | ![](http://idoc.imagepy.org/gis/01.png) 42 | 43 | read_shp 是调用 geopandas 的 read_file 方法,返回的是 GeoDataFrame 对象,带有 crs 以及一个 geometry 列。 44 | 45 | ```python 46 | gdf_wgs = gdf.to_crs(4326) 47 | gio.write_shp(gdf_wgs, '../data/result/shandong_wgs.shp') 48 | ``` 49 | 50 | 这里我们将 gdf 转为wgs1984 坐标,并重新存储。(更多用法请参见geopandas的文档) 51 | 52 | 53 | 54 | ## 读写 tif/hdf 55 | 56 | ```python 57 | path = '../data/landsat/LC08_L1TP_122033_20190506_20190506_01_RT_B5.TIF' 58 | landsat = gio.read_tif(path) 59 | 60 | >>> landsat.shape 61 | (7821, 7691) 62 | 63 | >>> landsat.crs 64 | 'PROJCS["WGS 84 / UTM zone 50N",GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",117],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AXIS["Easting",EAST],AXIS["Northing",NORTH],AUTHORITY["EPSG","32650"]]' 65 | 66 | >>> landsat.mat 67 | array([[ 4.556850e+05, 3.000000e+01, 0.000000e+00], 68 | [ 4.423215e+06, 0.000000e+00, -3.000000e+01]]) 69 | 70 | plt.imshow(landsat, cmap='gray') 71 | plt.show() 72 | ``` 73 | 74 | ![landsat](http://idoc.imagepy.org/gis/02.png) 75 | 76 | 我们可以用 read_tif 方法将 tif 文件读取为 GeoArray 对象,对于多通道,可以加入chan参数读取指定通道,默认读取全部通道。 77 | 78 | ```python 79 | gio.write_tif(landsat, '../data/result/landsat_new.tif') 80 | ``` 81 | 82 | 同样我们可以将 GeoArray 对象写成 tif 文件。 83 | 84 | ```python 85 | # read the hdf's first channel, if pass a list, means read these channels 86 | modis = gio.read_hdf('../data/modis/MOD09Q1.A2019017.h28v05.006.2019030120612.hdf', 0) 87 | # read_raster can select the reader by filename, read both tif and hdf 88 | modis = gio.read_raster('../data/modis/MOD09Q1.A2019017.h28v05.006.2019030120612.hdf') 89 | ``` 90 | 91 | 同样的,我们可以用 read_hdf, write_hdf 对hdf文件进行读写,read_raster 可以同时读取 tif/hdf。 92 | 93 | 94 | 95 | ## 读写影像空间信息 96 | 97 | ```python 98 | path = '../data/landsat/LC08_L1TP_122033_20190506_20190506_01_RT_B5.TIF' 99 | box = gio.read_tif_box(path) 100 | 101 | print(box) 102 | >>> ((7821, 7691), 103 | 'PROJCS["WGS 84 / UTM zone 50N",GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",117],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AXIS["Easting",EAST],AXIS["Northing",NORTH],AUTHORITY["EPSG","32650"]]', 104 | array([[ 4.556850e+05, 3.000000e+01, 0.000000e+00], 105 | [ 4.423215e+06, 0.000000e+00, -3.000000e+01]]), 106 | ['Channel:0']) 107 | ``` 108 | 109 | read_tif_box 并不直接读取像素信息,只读取相关的空间信息,它的好处是,速度快,并且不会占用大量内存,而用空间信息,我们可以进行简单的位置判断(比如match模块下的 build_idx 创建空间索引),在必要的时候再读取像素信息,或者使用 gnp.frombox 实例化。与之类似的,我们也可以使用 read_hdf_box 读取 hdf 的空间信息。或者使用 read_raster_box 自动识别类型。 -------------------------------------------------------------------------------- /doc/dem_match.md: -------------------------------------------------------------------------------- 1 | # geonumpy.match 2 | 3 | 影像的重投影,拼接,采样是一个最普通的需求,match 模块提供这些功能。这里有必要说明一下match模块的设计思想,对于投影来说,总是需要两个 GeoArray 对象,一个source,一个image,match_one 函数可以实现把 source 映射到 image。而我们不总是拥有 image,所以 image 的产生有如下几种方式: 4 | 5 | * 天然拥有,有些时候,我们可以得到要映射到的图像,拥有尺寸以及空间参考,那么直接使用。 6 | * 通过 shapefile 产生,有些时候,我们有映射目标的矢量文件,那么读取矢量,并且使用 util.shp2box获取结果图像的空间信息,再用gnp.frombox实例化。 7 | * 从source产生还有些时候,我们没有目标图像,也没有对应的矢量文件,那么我们需要通过source自动计算,具体方法是先计通过 util.box2shp 得到 source 的边界矢量,然后 to_crs 对边界矢量进行投影,再使用上面的方法获得 image。 8 | 9 | 对于拼接,geonumpy 是当作多图投影处理的,本质是将多张图投影到同一个目标图像上。 10 | 11 | 12 | 13 | ## 素材展示 14 | 15 | ```python 16 | fs = glob('../data/modis/*.hdf') 17 | ax1 = plt.subplot(131) 18 | ax1.imshow(gio.read_hdf(fs[0], 0)) 19 | ax2 = plt.subplot(132) 20 | ax2.imshow(gio.read_hdf(fs[1], 0)) 21 | ax3 = plt.subplot(133) 22 | ax3.imshow(gio.read_hdf(fs[2], 0)) 23 | plt.show() 24 | ``` 25 | 26 | ![](http://idoc.imagepy.org/gis/06.png) 27 | 28 | 29 | 30 | ## 单图投影 31 | 32 | ```python 33 | import geonumpy.io as gio 34 | import geonumpy.util as gutil 35 | import geonumpy.match as gmt 36 | 37 | # 读取山东省 shapefile 38 | shandong = gio.read_shp('../data/shape/shandong.shp') 39 | # 转为 web 墨卡托 投影 40 | shandong = shandong.to_crs(3857) 41 | # 缩放到 2048*1536大小,边距0.05,计算相关空间信息 42 | box = gutil.shp2box(shandong, (2048,1536), 0.05, 1) 43 | # 用空间信息实例化 GeoArray 对象 44 | paper = gnp.frombox(*box, dtype=np.int16) 45 | # 读取影像的 0 通道 46 | path = '../data/modis/MOD09Q1.A2019017.h27v05.006.2019030120430.hdf' 47 | raster = gio.read_hdf(path, 0) 48 | # 将 raster 投影到 paper 49 | gmt.match_one(raster, paper, out='in') 50 | 51 | plt.imshow(paper) 52 | plt.show() 53 | ``` 54 | ![](http://idoc.imagepy.org/gis/03.png) 55 | 56 | match_one 用于单图投影,out 默认为 auto, 表示返回与 raster 相同类型的结果,也可以为 dtype,指定返回结果类型,in 表示 在 des 上进行投影。 57 | 58 | **gutil.shp2box(shandong, (2048,1536), 0.05, 1)** 其中第二个参数是图像尺寸或比例尺,如果输入tuple,则指定最终尺寸,比例尺自动计算,如输入数字,则作为比例尺,尺寸自动计算。 59 | 60 | 61 | 62 | ## 多图投影 63 | 64 | ```python 65 | # 读取山东省 shapefile 66 | shandong = gio.read_shp('../data/shape/shandong.shp') 67 | # 转为 web 墨卡托 投影 68 | shandong = shandong.to_crs(3857) 69 | # 缩放到 2048*1536大小,边距0.05,计算相关空间信息 70 | box = gutil.shp2box(shandong, (2048,1536), 0.05, 1) 71 | # 用空间信息实例化 GeoArray 对象 72 | paper = gnp.frombox(*box, dtype=np.int16) 73 | # 读取所有影像的 0 通道 74 | fs = glob('../data/modis/*.hdf') 75 | rasters = [gio.read_hdf(i, 0) for i in fs] 76 | # 将 rasters 投影到 paper 77 | gmt.match_multi(rasters, paper, out='in') 78 | 79 | plt.imshow(paper) 80 | plt.show() 81 | ``` 82 | 83 | ![](http://idoc.imagepy.org/gis/04.png) 84 | 85 | 与单图类似,这里读取一个 GeoArray 的序列,用 match_multi 进行投影,而 out 使用 in,重复投影点,函数会以最大值为准,因而我们得到了完整的拼接结果。 86 | 87 | 88 | 89 | ## 创建空间索引 90 | 91 | ```python 92 | fs = glob('../data/modis/*.hdf') 93 | idx = gmt.build_index(fs) 94 | 95 | >>> idx.columns 96 | Index(['geometry', 'shape', 'mat', 'channels', 'path'], dtype='object') 97 | 98 | idx.plot() 99 | plt.show() 100 | ``` 101 | 102 | ![landsat](http://idoc.imagepy.org/gis/05.png) 103 | 104 | 这里调用 io.read_raster_info 读取每个影像的空间信息,创建对应的多边形矢量,而不直接读取像素,而这个空间索引却包含了影像所处的位置信息,投影信息,以及对应的文件路径。遥感影像通常很大,我们无法一次性全部读取到内存,因而这个空间索引就有重要的作用。 105 | 106 | 107 | 108 | ## 通过索引批量匹配 109 | 110 | ```python 111 | # 读取山东省 shapefile 112 | shandong = gio.read_shp('../data/shape/shandong.shp') 113 | # 转为 web 墨卡托 投影 114 | shandong = shandong.to_crs(3857) 115 | # 缩放到 2048*1536大小,边距0.05,计算相关空间信息 116 | box = gutil.shp2box(shandong, (2048,1536), 0.05, 1) 117 | # 用空间信息实例化 GeoArray 对象 118 | paper = gnp.frombox(*box, dtype=np.int16) 119 | # 创建空间索引 120 | fs = glob('../data/modis/*.hdf') 121 | idx = gmt.build_index(fs) 122 | # 根据索引将 rasters 投影到 paper 123 | gmt.match_idx(idx, paper, out='in', chan=[0]) 124 | 125 | plt.imshow(paper) 126 | plt.show() 127 | ``` 128 | 129 | ![](http://idoc.imagepy.org/gis/04.png) 130 | 131 | 通过 match_idx 我们也拼接得到了同样的结果,与 match_multi 非常类似,但是有几点不同,match_idx 可以通过空间索引,自动查找相关的地块,在需要的时候读取,并及时释放。对于遥感数据,计算机很难将全部图像载入内存,即便载入,也需要判断需要投影的块,而使用 idx 空间索引,就可以很好解决以上问题。 132 | 133 | 134 | 135 | ## 单图投影转换 136 | 137 | ```python 138 | # 读取图像 139 | path = '../data/modis/MOD09Q1.A2019017.h27v05.006.2019030120430.hdf' 140 | raster = gio.read_hdf(path, 0) 141 | # 获取图像的边界多边形,并转换到 web 墨卡托投影 142 | outshp = gutil.box2shp(*raster.getbox()).to_crs(3857) 143 | # 通过边界多边形,以 1:1000 计算图像空间信息 144 | box = gutil.shp2box(outshp, 1000, 0, 1) 145 | # 根据空间信息创建图像 146 | paper = gnp.frombox(*box, dtype=np.int16) 147 | # 将 rasters 投影到 paper 148 | gmt.match_one(raster, paper, out='in') 149 | 150 | plt.imshow(paper) 151 | plt.show() 152 | ``` 153 | 154 | ![](http://idoc.imagepy.org/gis/07.png) -------------------------------------------------------------------------------- /doc/dem_pretreat.md: -------------------------------------------------------------------------------- 1 | # geonumpy.pretreat 2 | 3 | 遥感影像需要很多预处理工作,比如大气校正,去条带等 4 | 5 | 6 | 7 | ## 去条带 8 | 9 | ```python 10 | path = '../data/landsat-gap/LE07_L1TP_123037_20180721_20180816_01_T1_sr_ndvi.tif' 11 | img = gio.read_tif(path) 12 | # 去条带 13 | degapimg = gpt.degap(img.copy(), img==-9999, 10) 14 | 15 | # 绘制处理前后的图并展示 16 | ax1 = plt.subplot(121) 17 | ax1.set_title('ori image') 18 | ax1.imshow(img, cmap='gray') 19 | 20 | ax2 = plt.subplot(122) 21 | ax2.set_title('degaped image') 22 | ax2.imshow(degapimg, cmap='gray') 23 | plt.show() 24 | 25 | ``` 26 | ![](http://idoc.imagepy.org/gis/16.png) 27 | 28 | 部分旧传感器返回的数据中带有条带,degap 函数可以有效的处理条带。 -------------------------------------------------------------------------------- /doc/index.md: -------------------------------------------------------------------------------- 1 | # geonumpy 地理数据工具包 2 | 3 | geonumpy的编写目的是提供一套开源,易用,高效的地理数据处理库。包括矢量数据,图像数据的读取,图像拼接,重采样,地图绘制等。 4 | 5 | 6 | 7 | ## pygis的几个重要依赖 8 | 9 | 1. Numpy:作为科学计算库,自然离不开numpy 10 | 2. pandas, scipy, scikit-image,这几个常用库为我们提供了表格与图像处理功能 11 | 3. gdal,fiona:gdal是一套功能强大的地理数据处理库,可以读取矢量,图像数据,但功能相对比较底层,代码不够pythonic,而fiona是基于gdal的更方便的数据读取库。 12 | 4. shapely,geopandas:shapely是python的计算几何库,实现了矢量算法,geopandas是shapely与pandas的结合,可以方便的带着属性进行几何运算,实现类似arcgis里的图形关系运算。 13 | 5. pyproj:著名的地图投影项目,提供各种地理坐标系转换。 14 | 15 | 16 | 17 | ## geonumpy API 文档 18 | 19 | **[GeoArray: 带投影的数组](api_geoarray.md)** 20 | 21 | 1. [GeoArray](api_geoarray.md#Class GeoArray) 22 | 2. [geoarray](api_geoarray.md#geoarray) 23 | 3. [frombox](api_geoarray.md#frombox) 24 | 25 | **[io: 数据读取与存储](api_io.md)** 26 | 27 | 1. [read_shp](api_io.md#read_shp) 28 | 2. [write_shp](api_io.md#write_shp) 29 | 3. [read_tif ](api_io.md#read_tif) 30 | 4. [read_hdf](api_io.md#read_hdf) 31 | 5. [write_tif](api_io.md#write_tif) 32 | 6. [write_hdf(未实现)](api_io.md#write_hdf) 33 | 7. [read_tif_info ](api_io.md#read_tif_info) 34 | 8. [read_hdf_info](api_io.md#read_hdf_info) 35 | 9. [read_raster](api_io.md#read_raster) 36 | 10. [read_raster_info](api_io.md#read_raster_info) 37 | 38 | **[util: 几个辅助函数](api_util.md)** 39 | 40 | 1. [shp2box](api_util.md#shp2box) 41 | 2. [box2shp](api_util.md#box2shp) 42 | 43 | **[match: 配准与拼接](api_match.md)** 44 | 45 | 1. [match_one](api_match.md#match_one) 46 | 2. [match_multi](api_match.md#match_multi) 47 | 3. [build_index](api_match.md#build_index) 48 | 4. [match_idx](api_match.md#match_idx) 49 | 50 | **[draw: 地图绘制](api_draw.md)** 51 | 52 | 1. [draw_polygon](api_draw.md#draw_polygon) 53 | 54 | 2. [draw_line](api_draw.md#draw_line) 55 | 56 | 3. [draw_text](api_draw.md#draw_text) 57 | 58 | 4. [draw_lab](api_draw.md#draw_lab) 59 | 60 | 5. [draw_unit](api_draw.md#draw_unit) 61 | 62 | 6. [draw_N](api_draw.md#draw_N) 63 | 64 | 7. [draw_bound](api_draw.md#draw_bound) 65 | 66 | 8. [draw_ruler](api_draw.md#draw_ruler) 67 | 68 | 9. [draw_style](api_draw.md#draw_style) 69 | 70 | **[pretreat: 预处理](pretreat.md)** 71 | 72 | 1. [大气校正(未实现)](pretreat.md) 73 | 2. [degap](pretreat.md#degap) 74 | 75 | **[indicate: 常规指标](indicate.md)** 76 | 77 | 1. [ndvi](indicate.md#ndvi) 78 | 79 | 2. what should be added here! any issue is welcom! 80 | 81 | 82 | **[download: 影像下载](api_download.md)** 83 | 84 | 1. [modis_search](api_download.md#modis_search) 85 | 2. [modis_download](api_download.md#modis_download) 86 | 3. [landsat_search](lapi_download.md#landsat_search) 87 | 4. [landsat_download(未实现)](api_download.md#landsat_download) 88 | 5. [sentinal_search(未实现)](lapi_download.md) 89 | 6. [sentinal_download(未实现)](api_download.md) 90 | 91 | 92 | 93 | ## geonumpy 用户手册 94 | 95 | **[GeoArray: 带投影的数组](dem_geoarray.md)** 96 | 97 | 1. [创建 GeoArray 对象](dem_geoarray.md#创建-GeoArray-对象) 98 | 2. [GeoArray 的运算](dem_geoarray.md#GeoArray-的运算) 99 | 3. [切片自动处理投影矩阵](dem_geoarray.md#GeoArray-的切片) 100 | 4. [生成方法](dem_geoarray.md#获取-box-及从-box-创建) 101 | 5. [多通道处理](dem_geoarray.md#多通道处理) 102 | 103 | **[io: 数据读取与存储](dem_io.md)** 104 | 105 | 1. [读写shapefile](dem_io.md#读写shapefile) 106 | 2. [读写 tif/hdf](dem_io.md#读写-tif/hdf) 107 | 3. [读写影像空间信息](dem_io.md#读写影像空间信息) 108 | 109 | **[match: 拼接与配准](dem_match.md)** 110 | 111 | 1. [素材展示](dem_match.md#素材展示) 112 | 2. [单图投影](dem_match.md#单图投影) 113 | 3. [多图投影](dem_match.md#多图投影) 114 | 4. [创建空间索引](dem_match.md#创建空间索引) 115 | 5. [通过索引批量匹配](dem_match.md#通过索引批量匹配) 116 | 6. [投影转换](dem_match.md#单图投影转换) 117 | 118 | **[draw: 地图绘制](dem_draw.md)** 119 | 120 | 1. [矢量图绘制](dem_draw.md#矢量图绘制) 121 | 2. [图例的使用](dem_draw.md#图例的使用) 122 | 3. [根据面积进行等级划分](dem_draw.md#根据面积进行等级划分) 123 | 4. [土地利用类型图绘制](dem_draw.md#土地利用类型图绘制) 124 | 125 | **[pretreat: 预处理](dem_pretreat.md)** 126 | 127 | 1. [去条带](dem_pretreat.md#去条带) 128 | 129 | **[综合应用:森林覆盖率统计](dem_forest_statistic.md)** 130 | 131 | 1. [用地类型拼接](dem_forest_statistic.md#用地类型拼接) 132 | 2. [市级行政区标记](dem_forest_statistic.md#市级行政区标记) 133 | 3. [用地类型及标记展示](dem_forest_statistic.md#用地类型及标记展示) 134 | 4. [各个城市森林覆盖率计算](dem_forest_statistic.md#各个城市森林覆盖率计算) 135 | 5. [绘制森林覆盖率专题图](dem_forest_statistic.md#绘制森林覆盖率专题图) 136 | 137 | 138 | 139 | -------------------------------------------------------------------------------- /geonumpy/__init__.py: -------------------------------------------------------------------------------- 1 | from .base import * -------------------------------------------------------------------------------- /geonumpy/base/__init__.py: -------------------------------------------------------------------------------- 1 | from .geonumpy import * -------------------------------------------------------------------------------- /geonumpy/base/geonumpy.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from numpy.linalg import inv 3 | 4 | class GeoArray(np.ndarray): 5 | def __new__(cls, input_array, crs=4326, mat=None): 6 | obj = np.asarray(input_array).view(cls) 7 | obj.crs, obj.mat = crs, mat.reshape((2,3)) 8 | return obj 9 | 10 | def __array_finalize__(self, obj): 11 | if obj is None: return 12 | self.crs = getattr(obj, 'crs', '') 13 | self.mat = getattr(obj, 'mat', None) 14 | 15 | def __getitem__(self, item): 16 | sliced = True & isinstance(item, tuple) 17 | if sliced: 18 | sliced &= len(item) in (2, 3) 19 | sliced &= isinstance(item[1], slice) 20 | sliced &= isinstance(item[0], slice) 21 | def gs(s): return (s.start or 0, s.step or 1) 22 | if sliced: 23 | (s1,d1), (s2,d2) = gs(item[0]), gs(item[1]) 24 | obj = super(GeoArray, self).__getitem__(item) 25 | if not obj.mat is None: 26 | m, offset = obj.mat[:,1:], obj.mat[:,:1] 27 | o = np.dot(m, [[s2],[s1]]) + offset 28 | t = m * [d2, d1] 29 | obj.mat = np.hstack((o,t)) 30 | return obj 31 | if not sliced: 32 | return super().__getitem__(item).__array__() 33 | 34 | def __array_wrap__(self, out_arr, context=None): 35 | if out_arr.shape[:2] != self.shape[:2]: 36 | out_arr = out_arr.__array__() 37 | return out_arr 38 | 39 | @property 40 | def imat(self): 41 | imat = np.vstack((self.mat[:,[1,2,0]], [[0,0,1]])) 42 | return np.linalg.inv(imat)[:2,[2,0,1]] 43 | 44 | @property 45 | def imat1(self): return self.imat.ravel()[[1,2,4,5,0,3]] 46 | 47 | def project(self, x, y): 48 | x += 0.5; y += 0.5 49 | m, offset = self.mat[:,1:], self.mat[:,:1] 50 | xy = np.array([x, y]).reshape((2,-1)) 51 | return np.dot(m, xy) + offset 52 | 53 | def invpro(self, e, n): 54 | m, offset = self.mat[:,1:], self.mat[:,:1] 55 | en = np.array([e, n]).reshape((2,-1)) 56 | return np.dot(inv(m), en - offset) - 0.5 57 | 58 | def channels(self, n=None): 59 | if n is None: 60 | return 1 if self.ndim==2 else self.shape[2] 61 | else: 62 | return self if self.ndim==2 else self[:,:,n] 63 | 64 | def lookup(self, lut): 65 | return GeoArray(lut[self], self.crs, self.mat) 66 | 67 | def getbox(self): 68 | return (self.shape[:2], self.crs, self.mat) 69 | 70 | 71 | def frombox(shp, crs, mat, chan=1, dtype=np.uint8): 72 | if chan>1: shp += (chan,) 73 | return GeoArray(np.zeros(shp, dtype=dtype), crs, mat) 74 | 75 | def geoarray(arr, crs=None, mat=np.array([[1,1,0],[1,0,1]])): 76 | return GeoArray(arr, crs, mat) 77 | 78 | 79 | if __name__ == '__main__': 80 | prj = np.array([0,1,0, 0,0,1]) 81 | a = GeoArray(np.ones((5,5)), crs=4326, mat=prj) 82 | print(a.crs) 83 | print(a.mat) 84 | b = a+1 85 | print(a.crs) 86 | print(a.mat) 87 | c = a[1::2,1::2] 88 | print(c.crs) 89 | print(c.mat) 90 | -------------------------------------------------------------------------------- /geonumpy/download/__init__.py: -------------------------------------------------------------------------------- 1 | ''' 2 | from modis_download import search as modis_search 3 | from modis_download import download as modis_download 4 | from landsat_search import search as landsat_search 5 | from landsat_download import download as landsat_download 6 | ''' -------------------------------------------------------------------------------- /geonumpy/download/landsat_download.py: -------------------------------------------------------------------------------- 1 | import requests, json, re 2 | from bs4 import BeautifulSoup 3 | client = requests.session() 4 | 5 | url_index = 'https://espa.cr.usgs.gov/index/' 6 | url_login = 'https://ers.cr.usgs.gov/login/' 7 | url_order = 'https://espa.cr.usgs.gov/ordering/status/' 8 | url_download = 'https://espa.cr.usgs.gov/ordering/order-status/' 9 | 10 | def login(username, pwd): 11 | rst = client.get(url_login) 12 | cont = rst.content.decode('utf-8') 13 | loc = cont.index('==') 14 | token = cont[loc-6:loc+2] 15 | data = {'username':username,'password':pwd,'csrf_token':token} 16 | rst = client.post(url_login, data) 17 | if rst.status_code!=502: print('Error:login') 18 | 19 | def list_order(): 20 | rst = client.get(url_order) 21 | cont = rst.content.decode('utf-8') 22 | p = re.compile('>espa-kongwp@radi.ac.cn-\d+-\d+-\d+<') 23 | return [i[1:-1] for i in p.findall(cont)] 24 | 25 | def list_file(orderid): 26 | rst = client.get(url_download+orderid) 27 | cont = rst.content.decode('utf-8') 28 | patten = re.compile('https://edclpdsftp.*tar.gz') 29 | return patten.findall(cont) 30 | 31 | def download(files): 32 | for imgurl in files: 33 | print('downloading', imgurl.split('/')[-1], '...', end='') 34 | try: 35 | rst = client.get(imgurl) 36 | f = open('download/'+imgurl.split('/')[-1], 'wb') 37 | f.write(rst.content) 38 | f.close() 39 | print('ok') 40 | except: print('failed!') 41 | print('completed!') 42 | 43 | if __name__ == '__main__': 44 | login('weiping_RADI', '123ca#456') # 登录 45 | orders = list_order() # 列举订单 46 | files = list_file(orders[0]) # 列举某个订单中的文件 47 | download(files) # 下载文件 48 | -------------------------------------------------------------------------------- /geonumpy/download/landsat_search.py: -------------------------------------------------------------------------------- 1 | import requests, json, time 2 | from bs4 import BeautifulSoup 3 | client = requests.session() 4 | 5 | url_rc = 'https://earthexplorer.usgs.gov/ajax/wrstolatlng?path=%.3d&row=%.3d&type=WRS2&wrsType=Point' 6 | url_set = 'https://earthexplorer.usgs.gov/tabs/save' 7 | url_index = 'https://earthexplorer.usgs.gov/result/index' 8 | 9 | data_set_date = ''' 10 | {"tab":1,"destination":2,"coordinates":[ 11 | {"c":"0","a":%.4f,"o":%.4f}], 12 | "format":"dms","dStart":"%s","dEnd":"%s", 13 | "searchType":"Std","num":"1000", 14 | "includeUnknownCC":"1","maxCC":100,"minCC":0, 15 | "months":%s,"pType":"polygon"}''' 16 | 17 | data_set_L8 = ''' 18 | {"tab":3,"destination":4,"criteria":{"12864":{ 19 | "select_20522_5":["%s"],"select_20515_5":["%s"], 20 | "select_20510_4":[""],"select_20517_4":[""], 21 | "select_20518_4":[""],"select_20513_3":[""], 22 | "select_20519_3":[""]}},"selected":"12864"}''' 23 | 24 | data_set_L7 = ''' 25 | {"tab":3,"destination":4,"criteria":{"12267":{ 26 | "select_19893_5":["%s"],"select_19883_5":["%s"], 27 | "select_19890_3":[""],"select_19886_4":[""], 28 | "select_19892_3":[""],"select_19885_3":[""]}},"selected":"12267"}''' 29 | 30 | data_set_L45 = ''' 31 | {"tab":3,"destination":4,"criteria":{"12266":{ 32 | "select_19881_5":["%s"],"select_19874_5":["%s"], 33 | "select_19880_3":[""],"select_19876_4":[""], 34 | "select_19877_3":[""],"select_25173_3":[""], 35 | "select_19875_3":[""]}},"selected":"12266"}''' 36 | 37 | def get_latlon_byrc(path, row): 38 | rst = client.get(url_rc%(path, row)) 39 | return rst.json()['coordinates'][0] 40 | 41 | def set_rc_date(coord, start, end, month=None): 42 | if month is None: month = ['']+list(range(12)) 43 | month = json.dumps(["%s"%i for i in month]) 44 | data = data_set_date%(coord[0], coord[1], start, end, month) 45 | data = data.replace(' ', '').replace('\n', '') 46 | rst = client.post(url_set, {'data':data}) 47 | if rst.content != b'1': print('Error:set rc data') 48 | 49 | def set_criterial(product, landcloud='', scenecloud=''): 50 | datastr = {8:data_set_L8, 7:data_set_L7, 45:data_set_L45}[product] 51 | data = datastr%(landcloud, scenecloud) 52 | data = data.replace(' ', '').replace('\n', '') 53 | rst = client.post(url_set, {'data':data}) 54 | if rst.content != b'1': print('Error:set criterial data') 55 | 56 | def get_content(product, page): 57 | cid = {7:12267, 8:12864, 45:12266}[product] 58 | rst = client.post(url_index, {'collectionId':cid, 'pageNum':page}) 59 | return rst.content.decode('utf-8') 60 | 61 | def parse_record(cont): 62 | soup = BeautifulSoup(cont, 'html.parser') 63 | records = soup.findAll(attrs={'class':'metadata', 'data-url':True}) 64 | return [i.text for i in records[1::3]] 65 | 66 | def search(product, prs, start, end, month=None, lcloud='', scloud='', page=1): 67 | records = [] 68 | for path, row in prs: 69 | client.cookies.clear() 70 | print('searching %.3d %.3d, ...'%(path, row)) 71 | coord = get_latlon_byrc(path, row) 72 | set_rc_date(coord, start, end, month) 73 | set_criterial(product, lcloud, scloud) 74 | for i in range(page): 75 | cont = get_content(product, i+1) 76 | rds = parse_record(cont) 77 | print(len(rds)) 78 | records.extend(rds) 79 | if len(rds)<10: break 80 | return records 81 | 82 | def to_order(records): 83 | f = open('order.txt', 'w') 84 | f.write('\n'.join(records)) 85 | f.close() 86 | 87 | # 这里输入需要下载的行列号 88 | blocks = [(123,43), (119,37), (122,37)] 89 | 90 | if __name__ == '__main__': 91 | # 等级(7,8),行列号,起始,结束日期,月份限定,陆地云量,传感器云量,页数 92 | records = search(7, blocks, '06/30/2018', '07/30/2019', None, 10, 10, 1) 93 | for i in records: print(i) # 打印 94 | #to_order(records) # 存储为订单文件 95 | 96 | -------------------------------------------------------------------------------- /geonumpy/download/modis_download.py: -------------------------------------------------------------------------------- 1 | import urllib.request as request 2 | import os, datetime, json 3 | 4 | url = 'https://ladsweb.modaps.eosdis.nasa.gov/archive/allData' 5 | 6 | def get_lev_pro_year(level, product, year): 7 | ref = url + '/%s/%s/%s.json'%(level, product, year) 8 | req = request.Request(ref) 9 | response = request.urlopen(req) 10 | return json.loads(response.read().decode('utf-8')) 11 | 12 | def get_list(level, product, year, day): 13 | ref = url + '/%s/%s/%s/%s.json'%(level, product, year, day) 14 | req = request.Request(ref) 15 | response = request.urlopen(req) 16 | return json.loads(response.read().decode('utf-8')) 17 | 18 | def get_all(level, product, areas, year, days = (0,365)): 19 | print('searching... %s level %s %s'%(product, level, year)) 20 | if isinstance(days, list): days = ['%.3d'%i for i in days] 21 | else: 22 | buf = [i['name'] for i in get_lev_pro_year(level, product, year)] 23 | days = [i for i in buf if int(i)>=days[0] and int(i)<=days[1]] 24 | files = [] 25 | for day in days: 26 | print('\t%s day ...'%day, end=' ') 27 | lst = [i['name'] for i in get_list(level, product, year, day)] 28 | files.extend([(level, product, year, day), i] for i in lst) 29 | print('done') 30 | print() 31 | files = [i for i in files if sum([a in i[1] for a in areas])] 32 | return files 33 | 34 | def download_one(pre, name, des): 35 | ref = url + '/%s/%s/%s/%s/%s'%(pre+(name,)) 36 | req = request.Request(ref) 37 | response = request.urlopen(req) 38 | html = response.read() 39 | f = open('%s/%s'%(des,name), 'wb') 40 | f.write(html) 41 | f.close() 42 | 43 | def search(product, level, areas, terms): 44 | files = [] 45 | for pro in product: 46 | for year, days in terms: 47 | files.extend(get_all(level, pro, areas, year, days)) 48 | print('%s new files found!'%len(files)) 49 | return files 50 | 51 | def download(files, des): 52 | succ = 0 53 | for pre, name in files: 54 | try: 55 | print('\t', name, end=' ... ') 56 | download_one(pre, name, des) 57 | print('done') 58 | succ += 1 59 | except: 60 | print('failed!') 61 | print('download completed, succ %s,failed %s'%(succ, len(files)-succ)) 62 | 63 | 64 | 65 | if __name__ == '__main__': 66 | files = search(['MOD09Q1', 'MOD11A2'], level=6, areas=['h25v05', 'h27v05', 'h28v05'], terms=[(2019, (0,30))]) 67 | download(files, '') 68 | -------------------------------------------------------------------------------- /geonumpy/download/tile_online_download.py: -------------------------------------------------------------------------------- 1 | import geopandas as pd 2 | from .. import io as gio 3 | from .. import util as gutil 4 | from .. import geoarray 5 | 6 | import numpy as np 7 | from shapely.geometry import Polygon, GeometryCollection 8 | from imageio import imread, imsave 9 | from math import ceil, floor 10 | 11 | max_lon = 85.0511287798 12 | max_dis = 20037508.343 13 | 14 | def build_grid_idx(level=0, box=None): 15 | n = 2**level 16 | 17 | ls = np.mgrid[0:n] 18 | 19 | minlat, minlon, maxlat, maxlon = box.bounds 20 | # print(minlat, minlon, maxlat, maxlon) 21 | step = max_dis * 2 / n 22 | 23 | sr = floor((minlat+max_dis)/step) 24 | er = ceil((maxlat+max_dis)/step) 25 | 26 | sc = floor((max_dis-maxlon)/step) 27 | ec = ceil((max_dis-minlon)/step) 28 | 29 | rcs = np.mgrid[sr:er, sc:ec].reshape(2,-1) 30 | 31 | # rgs = np.mgrid[-max_dis+sr*step:-max_dis+er*step:(n+1)*1j] 32 | 33 | rgs = np.mgrid[floor(minlat/step)*step:ceil(maxlat/step)*step:(er-sr+1)*1j, 34 | ceil(maxlon/step)*step:floor(minlon/step)*step:(ec-sc+1)*1j] 35 | 36 | print(sr, er, sc, ec) 37 | p1, p2 = rgs[:,:-1,:-1], rgs[:,:-1,1:] 38 | p3, p4 = rgs[:,1:,1:], rgs[:,1:,:-1] 39 | pts = np.array([p1, p2, p3, p4]) 40 | plgs = pts.reshape(4,2,-1).transpose(2,0,1) 41 | 42 | plgs = [Polygon(i) for i in plgs] 43 | print(len(plgs), rcs.shape) 44 | data = {'geometry':plgs, 'x':rcs[0], 'y':rcs[1]} 45 | zs = np.ones(len(plgs), dtype=np.uint8) 46 | data['z'] = zs * level 47 | return pd.GeoDataFrame(data, crs=3857) 48 | 49 | def build_geo_tif(img, crs, box): 50 | h, w = img.shape[:2] 51 | mat = [[box[0], (box[2]-box[0])/(w-1), 0], 52 | [box[3], 0, -(box[3]-box[1])/(h-1)]] 53 | return geoarray(img, crs=crs, mat=np.array(mat)) 54 | 55 | def build_online_tiles(level=3, box=None): 56 | if not box is None: 57 | shp = gutil.box2shp(*box).to_crs(3857)[0] 58 | if level is None and not box is None: 59 | a,b,c,d = shp.bounds 60 | l = ((c-a)**2 + (d-b)**2)**0.5 61 | s = (box[0][0]**2 + box[0][1]**2)**0.5 62 | rg = max_dis*2 / 256 / 2 ** np.arange(13) 63 | k = max(l/s, rg[-1]+1e-3) 64 | level = np.argmax(rg <= k) 65 | print(level) 66 | ts = build_grid_idx(level, shp) 67 | if not box is None: ts = ts[ts.intersects(shp)] 68 | return ts 69 | 70 | urls = {'arcgis blue':'https://map.geoq.cn/ArcGIS/rest/services/ChinaOnlineCommunity/MapServer/tile/{z}/{y}/{x}', 71 | 'arcgis gray':'https://map.geoq.cn/ArcGIS/rest/services/ChinaOnlineStreetGray/MapServer/tile/{z}/{y}/{x}', 72 | 'arcgis dark':'https://map.geoq.cn/ArcGIS/rest/services/ChinaOnlineStreetPurplishBlue/MapServer/tile/{z}/{y}/{x}', 73 | 'gaode':'https://webst01.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}', 74 | 'google':'http://mt1.google.com/vt/lyrs=s&x={x}&y={y}&z={z}'} 75 | 76 | def download_online_tiles(tiles, path, src='gaode'): 77 | for i,g,x,y,z in tiles[['geometry','x','y','z']].to_records(): 78 | img = imread(urls[src].format(x=x, y=y, z=z)) 79 | img = build_geo_tif(img, 3857, g.bounds) 80 | gio.write_tif(img, '%s/%ld_%d_%d.tif'%(path,z,y,x)) 81 | 82 | if __name__ == '__main__': 83 | import geonumpy.draw as gdraw 84 | import geonumpy.match as gmt 85 | import geonumpy.io as gio 86 | 87 | from glob import glob 88 | 89 | box = gio.read_tif_box('./3.背景TIF.tif') 90 | buf = gnp.frombox(*box[:3], 3) 91 | 92 | tiles = build_online_tiles(level=7, box=box[:3]) 93 | download_online_tiles(tiles, './tiles', 'gaode') 94 | 95 | idx = gmt.build_index(glob('./tiles/*.tif')) 96 | rst = gmt.match_idx(idx, box[:3]) 97 | gio.write_tif(rst, 'b.tif') 98 | 99 | -------------------------------------------------------------------------------- /geonumpy/draw/__init__.py: -------------------------------------------------------------------------------- 1 | from .draw import * -------------------------------------------------------------------------------- /geonumpy/draw/draw.py: -------------------------------------------------------------------------------- 1 | from shapely.affinity import affine_transform 2 | from shapely.geometry import GeometryCollection 3 | from PIL import Image, ImageDraw, ImageFont 4 | from shapely.geometry import Polygon, LineString 5 | import numpy as np, pandas as pd, pyproj 6 | from numba import jit 7 | 8 | def draw_polygon(raster, shape, color, width): 9 | shape = shape.to_crs(raster.crs) 10 | img = Image.fromarray(raster) 11 | draw = ImageDraw.Draw(img) 12 | geoms = shape['geometry'].affine_transform(raster.imat1) 13 | if isinstance(color, np.ndarray): colors = color.tolist() 14 | elif isinstance(color, pd.Series): colors = color.to_list() 15 | elif isinstance(color, str): colors = shape[color].to_list() 16 | else: colors = [color] * len(geoms) 17 | for g, c in zip(geoms, colors): 18 | gs = g #affine_transform(g, m) 19 | if isinstance(gs, Polygon): gs = [gs] 20 | if isinstance(gs, LineString): gs = [gs] 21 | for g in gs: 22 | pts = np.array(g.exterior.xy).T.astype(np.int).reshape((-1,2)) 23 | pts = [tuple(i) for i in pts] 24 | if width==0: draw.polygon(pts, c) 25 | if width!=0: draw.line(pts, c, width, joint='curve') 26 | raster[:] = np.array(img) 27 | 28 | def draw_line(raster, shape, color, width): 29 | shape = shape.to_crs(raster.crs) 30 | m = raster.mat 31 | m = [1/m[0,1], 0, 0, -1/m[0,1], -m[0,0]/m[0,1], m[1,0]/m[0,1]] 32 | img = Image.fromarray(raster) 33 | draw = ImageDraw.Draw(img) 34 | geoms = shape['geometry'] 35 | if isinstance(color, pd.Series): colors = color.to_list() 36 | elif isinstance(color, str): colors = shape[color].to_list() 37 | else: colors = [color] * len(geoms) 38 | for g, c in zip(geoms, colors): 39 | gs = affine_transform(g, m) 40 | if isinstance(gs, LineString): gs = [gs] 41 | for g in gs: 42 | pts = np.array(g.xy).T.astype(np.int).reshape((-1,2)) 43 | pts = [tuple(i) for i in pts] 44 | draw.line(pts, c, width, joint='curve') 45 | raster[:] = np.array(img) 46 | 47 | def draw_mask(img, msk): 48 | weight = msk[:,:,3]/255 49 | np.multiply(img.T, 1-weight.T, out=img.T, casting='unsafe') 50 | np.add(img.T, msk[:,:,:3].T*weight.T, out=img.T, casting='unsafe') 51 | 52 | @jit 53 | def draw_mask(img, msk): 54 | h, w = img.shape[:2] 55 | for r in range(h): 56 | for c in range(w): 57 | wg = msk[r,c,3]/255.0 58 | img[r,c] = img[r,c]*(1-wg) + msk[r,c,:3]*wg 59 | return img 60 | 61 | def draw_unit(raster, x, y, w, h, ft, color, unit, lw, anc='left'): 62 | y = y-h 63 | img = Image.fromarray(raster) 64 | d = ImageDraw.Draw(img) 65 | def f(x, dir=0): 66 | v = raster.shape[1-dir] 67 | if abs(x)>1: x = int(x) 68 | else: x = int(v * x) 69 | return x if x>=0 else v+x 70 | step = int(round(f(w,0)*raster.mat[0,1]/10000)) 71 | if step>100: 72 | n = 10**len(str(step))/10 73 | step = int(np.round(step/n)*n) 74 | cell = int(step * 1000 / raster.mat[0,1]) 75 | offsetx = 0 if anc=='left' else -cell *10 76 | for s, e, c in zip((0,1,3,6), (1,3,6,10), (1,0,1,0)): 77 | d.rectangle([f(x)+s*cell+offsetx, f(y,1), 78 | f(x)+e*cell+offsetx, f(y,1)+f(h)], 79 | fill=[color, None][c], outline=color, width=lw) 80 | font = ImageFont.truetype(*ft) 81 | for s in (0,1,3,6,10): 82 | w, h = d.textsize(str(step*(s))+['',' '+unit][s==10], font) 83 | d.text((f(x)+s*cell-w//2+offsetx, f(y,1)-ft[1]*1.2), 84 | str(step*(s))+['',' '+unit][s==10], font=font, fill=color, align='center') 85 | raster[:] = np.array(img) 86 | 87 | def draw_N(raster, x, y, ft, lw, h, color): 88 | img = Image.fromarray(raster) 89 | d = ImageDraw.Draw(img) 90 | def f(x, dir=0): 91 | v = raster.shape[1-dir] 92 | if abs(x)>1: x = int(x) 93 | else: x = int(v * x) 94 | return x if x>=0 else v+x 95 | x, y = f(x), f(y, 1) 96 | d.polygon([x, y, x, y+h, x-h/2, y+h*5//3], color) 97 | d.line([x, y, x-h/2, y+h*5//3, x, y+h, x+h/2, y+h*5//3, x, y], color, lw) 98 | font = ImageFont.truetype(*ft) 99 | w,h = d.textsize('N', font) 100 | x, y = x-w//2, y-h*1.2 101 | d.text((x, y), 'N', font=font, fill=color) 102 | raster[:] = np.array(img) 103 | 104 | def draw_text(raster, txt, xs, ys, color, ft, anc='lt', align='left'): 105 | img = Image.fromarray(raster) 106 | d = ImageDraw.Draw(img) 107 | if isinstance(txt, str): txt = [txt] 108 | if not hasattr(xs, '__len__'): xs = [xs] 109 | if not hasattr(ys, '__len__'): ys = [ys] 110 | def f(x, dir=0): 111 | v = raster.shape[1-dir] 112 | if abs(x)>1: x = int(x) 113 | else: x = int(v * x) 114 | return x if x>=0 else v+x 115 | font = ImageFont.truetype(*ft) 116 | for t, x,y in zip(txt, xs, ys): 117 | x, y = f(x), f(y,1) 118 | w,h = d.textsize(t, font) 119 | if anc=='ct': x, y = x-w//2, y-h*3/5 120 | if anc=='lb': y = y-h 121 | if anc=='lt': x, y = x, y 122 | if anc=='rb': x, y = x-w, y-h 123 | if anc=='rt': x = x-w 124 | d.text((x, y), t, font=font, fill=color, align=align, spacing=ft[1]*0.3) 125 | raster[:] = np.array(img) 126 | 127 | def draw_lab(raster, shp, name, color, font, anc): 128 | gs = shp['geometry'].centroid 129 | m = raster.mat 130 | m = [1/m[0,1], 0, 0, -1/m[0,1], -m[0,0]/m[0,1], m[1,0]/m[0,1]] 131 | pos = [(int(p.x), int(p.y)) for p in [affine_transform(i, m) for i in gs]] 132 | return draw_text(raster, shp[name], *list(zip(*pos)), color, font, anc) 133 | 134 | def draw_style(raster, x, y, body, mar, recsize, font, color, box): 135 | body = body[::-1] 136 | img = Image.fromarray(raster) 137 | d = ImageDraw.Draw(img) 138 | tcolor = color 139 | def f(x, dir=0): 140 | v = raster.shape[1-dir] 141 | if abs(x)>1: x = int(x) 142 | else: x = int(v * x) 143 | return x if x>=0 else v+x 144 | cury, maxx, (w, h, lw) = f(y,1)-mar[1], 0, recsize 145 | font = ImageFont.truetype(*font[:2]) 146 | for i in range(len(body)): 147 | item = body[i] 148 | if item[0]=='line': 149 | _, c, t = item 150 | d.line([f(x)+mar[0], cury-f(h,1)//2, f(x)+mar[0]+f(w), 151 | cury-f(h,1)//2], fill=c, width=lw) 152 | tw, th = d.textsize(t, font) 153 | maxx = max(maxx, f(x)+mar[0]*3+f(w)+tw) 154 | d.text([f(x)+mar[0]*2+f(w), cury-f(h,1)//2-th*3/5], t, 155 | font=font, fill=tcolor) 156 | cury -= mar[1]+f(h,1) 157 | elif item[0]=='rect': 158 | _, c, t = item 159 | d.rectangle([f(x)+mar[0], cury, f(x)+mar[0]+f(w), cury-f(h,1)], 160 | fill=c, outline=[None, tcolor][lw>0], width=lw) 161 | tw, th = d.textsize(t, font) 162 | maxx = max(maxx, f(x)+mar[0]*3+f(w)+tw) 163 | d.text([f(x)+mar[0]*2+f(w), cury-f(h,1)//2-th*3/5], t, 164 | font=font, fill=tcolor) 165 | cury -= mar[1]+f(h,1) 166 | elif item[0]=='circle': 167 | _, c, t = item 168 | d.ellipse([f(x)+mar[0]+f(w)//2-f(h,1)//5, cury-f(h,1)//2-f(h,1)//5, 169 | f(x)+mar[0]+f(w)//2+f(h,1)//5, cury-f(h,1)//2+f(h,1)//5], 170 | fill=c, outline=[None, tcolor][lw>0], width=lw) 171 | tw, th = d.textsize(t, font) 172 | maxx = max(maxx, f(x)+mar[0]*3+f(w)+tw) 173 | d.text([f(x)+mar[0]*2+f(w), cury-f(h,1)//2-th*3/5], t, 174 | font=font, fill=tcolor) 175 | cury -= mar[1]+f(h,1) 176 | elif item[0]=='blank': 177 | cury -= item[1] 178 | else: 179 | print(item) 180 | t, tf, ts = item 181 | tf = ImageFont.truetype(tf, ts) 182 | tw, th = d.textsize(t, tf) 183 | d.text([f(x)+mar[0]*2, cury-f(h,1)//2-th*3/5], 184 | t, font=tf, fill=tcolor) 185 | cury -= mar[1]+f(h,1) 186 | if box>0: 187 | d.rectangle([f(x), f(y,1), maxx, cury], outline=tcolor, width=box) 188 | raster[:] = np.array(img) 189 | 190 | def draw_ruler(raster, left, top, right, bot, step, crs, font, color, w, dh): 191 | img = Image.fromarray(raster) 192 | d = ImageDraw.Draw(img) 193 | def f(x, dir=0): 194 | v = raster.shape[1-dir] 195 | if abs(x)>1: x = int(x) 196 | else: x = int(v * x) 197 | return x if x>=0 else v+x 198 | left, top, right, bot = f(left), f(top,1), f(right), f(bot,1) 199 | d.rectangle([left, top, right, bot], outline=color, width=w) 200 | pts = np.array([(left,top),(right,top),(right,bot),(left,bot)]) 201 | prj1, prj2 = pyproj.CRS(raster.crs), pyproj.CRS(crs) 202 | ct = pyproj.Transformer.from_crs(prj1, prj2, always_xy=True) 203 | pts = np.dot(raster.mat[:,1:], pts.T) + raster.mat[:,:1] 204 | xs, ys = ct.transform(*pts) 205 | 206 | a, b = int(xs[0]//step), int(xs[1]//step) 207 | tab = [i*step for i in range(a+1, b+1)] 208 | nps = ct.transform(tab, np.linspace(ys[0], ys[1], len(tab)), direction='INVERSE') 209 | nps = np.dot(np.linalg.inv(raster.mat[:,1:]), nps - raster.mat[:,:1]) 210 | font = ImageFont.truetype(*font) 211 | for x,e in zip(nps[0],tab): 212 | d.line([int(x), top, int(x), top-dh], color, w) 213 | tw, th = d.textsize('%d°E'%e, font) 214 | d.text((int(x-tw//2), top-dh-th*1.2), '%d°E'%e, font=font, fill=color) 215 | 216 | a, b = int(xs[3]//step), int(xs[2]//step) 217 | tab = [i*step for i in range(a+1, b+1)] 218 | nps = ct.transform(tab, np.linspace(ys[3], ys[2], len(tab)), direction='INVERSE') 219 | nps = np.dot(np.linalg.inv(raster.mat[:,1:]), nps - raster.mat[:,:1]) 220 | for x,e in zip(nps[0],tab): 221 | d.line([int(x), bot, int(x), bot+dh], color, w) 222 | tw, th = d.textsize('%d°E'%e, font) 223 | d.text((int(x-tw//2), bot+dh+th*0.0), '%d°E'%e, font=font, fill=color) 224 | 225 | a, b = int(ys[3]//step), int(ys[0]//step) 226 | tab = [i*step for i in range(a+1, b+1)] 227 | nps = ct.transform(np.linspace(xs[3], xs[0], len(tab)), tab, direction='INVERSE') 228 | nps = np.dot(np.linalg.inv(raster.mat[:,1:]), nps - raster.mat[:,:1]) 229 | for y,n in zip(nps[1],tab): 230 | d.line([left, int(y), left-dh, int(y)], color, w) 231 | tw, th = d.textsize('%d°N'%n, font) 232 | d.text((left-dh-tw-th*0.2, int(y-th*3/5)), '%d°N'%n, font=font, fill=color) 233 | 234 | a, b = int(ys[2]//step), int(ys[1]//step) 235 | tab = [i*step for i in range(a+1, b+1)] 236 | nps = ct.transform(np.linspace(xs[2], xs[1], len(tab)), tab, direction='INVERSE') 237 | nps = np.dot(np.linalg.inv(raster.mat[:,1:]), nps - raster.mat[:,:1]) 238 | for y,n in zip(nps[1],tab): 239 | d.line([right, int(y), right+dh, int(y)], color, w) 240 | tw, th = d.textsize('%d°N'%n, font) 241 | d.text((right+dh+th*0.2, int(y-th*3/5)), '%d°N'%n, font=font, fill=color) 242 | raster[:] = np.array(img) 243 | 244 | def draw_bound(raster, left, top, right, bot, c, lw, clear=None): 245 | def f(x, dir=0): 246 | v = raster.shape[1-dir] 247 | if abs(x)>1: x = int(x) 248 | else: x = int(v * x) 249 | return x if x>=0 else v+x 250 | left, top, right, bot = f(left), f(top,1), f(right), f(bot,1) 251 | if not clear is None: 252 | raster[:top] = clear 253 | raster[bot:] = clear 254 | raster[:,:left] = clear 255 | raster[:,right:] = clear 256 | img = Image.fromarray(raster) 257 | d = ImageDraw.Draw(img) 258 | d.rectangle([left, top, right, bot], outline=c, width=lw) 259 | raster[:] = np.array(img) -------------------------------------------------------------------------------- /geonumpy/indicate/geo_indicate.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | def ndvi(raster1, raster2): 4 | b1 = np.clip(raster1[0], 1, 1e8) 5 | b2 = np.clip(raster2[0], 1, 1e8) 6 | ndvi = (((b2-b1)/(b2+b1)+1)/2*255+0.5).astype(np.uint8) 7 | return (ndvi, raster1[1], raster1[2]) -------------------------------------------------------------------------------- /geonumpy/io/__init__.py: -------------------------------------------------------------------------------- 1 | from .geoio import * -------------------------------------------------------------------------------- /geonumpy/io/geoio.py: -------------------------------------------------------------------------------- 1 | import geopandas as gpd 2 | from osgeo import gdal, osr, ogr 3 | from osgeo import gdal_array 4 | import matplotlib.pyplot as plt 5 | import numpy as np 6 | import pandas as pd 7 | import pyproj 8 | from ..base import GeoArray 9 | 10 | def read_shp(path, encoding='utf-8'): 11 | return gpd.read_file(path, encoding=encoding) 12 | 13 | def write_shp(shp, path, encoding='utf-8'): 14 | shp.to_file(path, encoding) 15 | 16 | def read_hdf(path, chans=None): 17 | ds = gdal.Open(path) 18 | sds = ds.GetSubDatasets() 19 | imgs = [] 20 | if chans is None: chans = range(len(sds)) 21 | if isinstance(chans, int): chans = [chans] 22 | for i in chans: 23 | ds = gdal.Open(sds[i][0]) 24 | img = ds.ReadAsArray() 25 | m = ds.GetGeoTransform() 26 | m = np.array(m).reshape((2,3)) 27 | prj = ds.GetProjection() 28 | imgs.append(img) 29 | if len(chans) == 1: imgs = imgs[0] 30 | else: imgs = np.array(imgs).transpose((1,2,0)) 31 | return GeoArray(imgs, prj, m) 32 | 33 | def read_tif(path, chans=None): 34 | ds = gdal.Open(path) 35 | if chans is None: chans = range(ds.RasterCount) 36 | if isinstance(chans, int): chans = [chans] 37 | prj = ds.GetProjection() 38 | m = ds.GetGeoTransform() 39 | m = np.array(m).reshape((2,3)) 40 | imgs = [ds.GetRasterBand(i+1).ReadAsArray() for i in chans] 41 | if len(chans)==1: imgs = imgs[0] 42 | else: imgs = np.array(imgs).transpose((1,2,0)) 43 | return GeoArray(imgs, prj, m) 44 | 45 | def read_raster(path, chans=None): 46 | if 'hdf' in path.lower(): return read_hdf(path, chans) 47 | if 'tif' in path.lower(): return read_tif(path, chans) 48 | 49 | def read_tif_box(path): 50 | ds = gdal.Open(path) 51 | prj = ds.GetProjection() 52 | m = ds.GetGeoTransform() 53 | m = np.array(m).reshape((2,3)) 54 | #chans = ['Channel:%d'%i for i in range(ds.RasterCount)] 55 | rs = range(ds.RasterCount) 56 | chans = [ds.GetRasterBand(i+1).GetDescription() for i in rs] 57 | shape = (ds.RasterYSize, ds.RasterXSize) 58 | return (shape, prj, m, chans) 59 | 60 | def read_hdf_box(path): 61 | ds = gdal.Open(path) 62 | sds = ds.GetSubDatasets() 63 | rs = gdal.Open(sds[0][0]) 64 | prj = rs.GetProjection() 65 | m = rs.GetGeoTransform() 66 | m = np.array(m).reshape((2,3)) 67 | chans = [i[1] for i in sds] 68 | shape = (rs.RasterYSize, rs.RasterXSize) 69 | return (shape, prj, m, chans) 70 | 71 | def read_raster_box(path): 72 | if 'hdf' in path.lower(): return read_hdf_box(path) 73 | if 'tif' in path.lower(): return read_tif_box(path) 74 | 75 | 76 | def write_tif(raster, path, compress=None, nodata=None): 77 | #if isinstance(raster, tuple): raster = [raster] 78 | driver = gdal.GetDriverByName("GTiff") 79 | tps = {np.uint8:gdal.GDT_Byte, np.int16:gdal.GDT_Int16, 80 | np.int32:gdal.GDT_Int32, np.uint16:gdal.GDT_UInt16, 81 | np.uint32:gdal.GDT_UInt32, np.float32:gdal.GDT_Float32, 82 | np.float64:gdal.GDT_Float64} 83 | options = [] if compress is None else ['COMPRESS=%s'%compress] 84 | tif = driver.Create(path, raster.shape[1], raster.shape[0], 85 | raster.channels(), tps[raster.dtype.type], options=options) 86 | tif.SetGeoTransform(raster.mat.ravel()) 87 | crs = osr.SpatialReference() 88 | crs.ImportFromProj4(pyproj.CRS(raster.crs).to_proj4()) 89 | tif.SetProjection(crs.ExportToWkt()) 90 | for i in range(raster.channels()): 91 | b = tif.GetRasterBand(i+1) 92 | b.WriteArray(raster.channels(i)) 93 | if not nodata is None: b.SetNoDataValue(nodata) 94 | 95 | def array_as_shp(raster, path, field='class'): 96 | tif = gdal_array.OpenArray(raster) 97 | tif.SetGeoTransform(raster.mat.ravel()) 98 | crs = osr.SpatialReference() 99 | crs.ImportFromProj4(pyproj.CRS(raster.crs).to_proj4()) 100 | tif.SetProjection(crs.ExportToWkt()) 101 | for i in range(raster.channels()): 102 | tif.GetRasterBand(i+1).WriteArray(raster.channels(i)) 103 | band = tif.GetRasterBand(1) 104 | driver = ogr.GetDriverByName("ESRI Shapefile") 105 | outDatasource = driver.CreateDataSource(path) 106 | srs = osr.SpatialReference() 107 | srs.ImportFromWkt( tif.GetProjectionRef() ) 108 | outLayer = outDatasource.CreateLayer(path, srs) 109 | newField = ogr.FieldDefn(field, ogr.OFTInteger) 110 | outLayer.CreateField(newField) 111 | gdal.Polygonize(band, None, outLayer, 0, [],callback=None) 112 | outDatasource.Destroy() 113 | 114 | def show_raster(raster, c=0): 115 | plt.imshow(raster.imgs[c]) 116 | 117 | def show_shp(shp): 118 | shp.plot() 119 | plt.show() 120 | 121 | if __name__ == '__main__': 122 | shp = read_shp('../../tasks/country_china_wheat_2018/region.shp') 123 | #plot_shp(shp) 124 | -------------------------------------------------------------------------------- /geonumpy/match/__init__.py: -------------------------------------------------------------------------------- 1 | from .match import * -------------------------------------------------------------------------------- /geonumpy/match/match.py: -------------------------------------------------------------------------------- 1 | from osgeo import gdal, osr 2 | import pyproj 3 | import geopandas as gpd 4 | from glob import glob 5 | import numpy as np 6 | from numpy.linalg import inv 7 | import geonumpy as gnp 8 | import geonumpy.util as gutil 9 | import geonumpy.io as gio 10 | from math import ceil, floor 11 | from scipy.ndimage import map_coordinates 12 | 13 | def mp2pm(boxs, m1, prj1, prj2, m2, t1 = lambda x:x, t2 = lambda x:x): 14 | p1 = osr.SpatialReference() 15 | p1.ImportFromProj4(pyproj.CRS(prj1).to_proj4()) 16 | p2 = osr.SpatialReference() 17 | p2.ImportFromProj4(pyproj.CRS(prj2).to_proj4()) 18 | box = t1(np.dot(m1[:,1:], np.array(boxs).T) + m1[:,:1]) 19 | ct = osr.CoordinateTransformation(p1, p2) 20 | box = t2(np.array(ct.TransformPoints(box.T)).T) 21 | return np.dot(inv(m2[:,1:]), box[:2]-m2[:,:1]).T 22 | ''' 23 | prj1 = pyproj.CRS(wkt1) 24 | prj2 = pyproj.CRS(wkt2) 25 | ct = pyproj.Transformer.from_crs(prj1, prj2) 26 | print(ct.transform(116, 39)) 27 | ''' 28 | 29 | def match_one(raster, out, chan='all', step=10, order=1): 30 | if chan=='all': chan = list(range(raster.channels())) 31 | if isinstance(chan, int): chan = [chan] 32 | if isinstance(out, tuple): 33 | out = gnp.frombox(*out, len(chan), raster.dtype) 34 | imgd, prjd, md = out, out.crs, out.mat 35 | img, prjs, ms = raster, raster.crs, raster.mat 36 | hs, ws = img.shape[:2] 37 | xx = np.linspace(0,ws,100).reshape((-1,1))*[1,0] 38 | yy = np.linspace(0,hs,100).reshape((-1,1))*[0,1] 39 | xy = np.vstack((xx, xx+[0,hs], yy, yy+[ws,0])) 40 | xy = mp2pm(xy, ms, prjs, prjd, md)#.astype(np.int) 41 | 42 | (left, top), (right, bot) = xy.min(axis=0), xy.max(axis=0) 43 | left, right = np.clip((left, right), 0, imgd.shape[1]) 44 | top, bot = np.clip((top, bot), 0, imgd.shape[0]) 45 | hb, wb = bot-top, right-left # 像素数目 46 | nh, nw = int(hb//step+1), int(wb//step+1) 47 | xy = np.mgrid[top:bot:nh*1j, left:right:nw*1j].reshape((2,-1)).T 48 | 49 | xs, ys = mp2pm(xy[:,::-1], md, prjd, prjs, ms).T 50 | 51 | xs.shape = ys.shape = (nh, nw)#block.shape 52 | 53 | intleft, intright = floor(left), ceil(right) 54 | inttop, intbot = floor(top), ceil(bot) 55 | rc = np.mgrid[inttop:intbot, intleft:intright].reshape((2,-1)) 56 | frc = rc.astype(np.float32) + 0.5 57 | frc[0] = (frc[0]-top)/(bot-top)*(nh-1) 58 | frc[1] = (frc[1]-left)/(right-left)*(nw-1) 59 | xs = map_coordinates(xs, frc, order=1) 60 | ys = map_coordinates(ys, frc, order=1) 61 | for i in chan: 62 | #range(img.channels()): 63 | rcs, dcs = img.channels(i), imgd.channels(i) 64 | vs = map_coordinates(rcs, np.array([ys, xs])-0.5, order=order)#prefilter=False) 65 | vs[np.isnan(vs)] = 0 66 | dcs[tuple(rc)] = np.maximum(dcs[tuple(rc)], vs) 67 | return imgd 68 | 69 | def match_multi(rasters, out, chan='all', step=10, order=1): 70 | if chan=='all': chan = list(range(rasters[0].channels())) 71 | if isinstance(chan, int): chan = [chan] 72 | if isinstance(out, tuple): 73 | out = gnp.frombox(*out, len(chan), rasters[0].dtype) 74 | for raster in rasters: match_one(raster, out, chan, step, order) 75 | return out 76 | 77 | def build_index(fs): 78 | boxes, bcrs = [], None 79 | for i in fs: 80 | isfile = isinstance(i, str) 81 | if isfile: shp, crs, m, chans = gio.read_raster_box(i) 82 | else: (shp, crs, m), chans = i.getbox(), i.channels() 83 | if bcrs is None: bcrs = crs 84 | box = gutil.box2shp(shp, crs, m).to_crs(bcrs) 85 | boxes.append([box[0], shp, m, chans, ['memory', i][isfile]]) 86 | columns = ['geometry', 'shape', 'mat', 'channels', 'path'] 87 | return gpd.GeoDataFrame(boxes, columns=columns, crs=bcrs) 88 | 89 | def match_idx(idx, out, chan='all', step=10, order=1): 90 | if chan=='all': chan = list(range(len(idx['channels'][0]))) 91 | if isinstance(chan, int): chan = [chan] 92 | if isinstance(out, tuple): shape, crs, mat = out 93 | else: shape, crs, mat = out.shape, out.crs, out.mat 94 | idx = idx.to_crs(gutil.makecrs(crs).to_proj4()) 95 | box = gutil.box2shp(shape, crs, mat)[0] 96 | for i in idx.index: 97 | if box.intersects(idx.loc[i]['geometry']): 98 | print(chan) 99 | raster = gio.read_raster(idx.loc[i]['path'], chan) 100 | if isinstance(out, tuple): 101 | out = gnp.frombox(*out, len(chan), raster.dtype) 102 | print('merge ...', idx.loc[i]['path'], end=' ') 103 | match_one(raster, out, chan, step, order) 104 | print('end') 105 | return out 106 | 107 | if __name__ == '__main__': 108 | wkt = 'PROJCS["China_Lambert_Conformal_Conic",GEOGCS["GCS_Beijing_1954",DATUM["Beijing_1954",SPHEROID["Krassowsky_1940",6378245,298.3,AUTHORITY["EPSG","7024"]],AUTHORITY["EPSG","6214"]],PRIMEM["Greenwich",0],UNIT["degree",0.0174532925199433]],PROJECTION["Lambert_Conformal_Conic_2SP"],PARAMETER["standard_parallel_1",30],PARAMETER["standard_parallel_2",62],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",105],PARAMETER["false_easting",0],PARAMETER["false_northing",0],UNIT["metre",1,AUTHORITY["EPSG","9001"]]]' 109 | wktesri = open('全国县界行政区划2010_project.prj').read() 110 | from pygis.draw import make_paper 111 | idx = build_index('../data/class') 112 | 113 | china = read_shp('./全国县界行政区划2010_project.shp') 114 | paper = make_paper(china[china['Province']=='河南省'], (1024,768)) 115 | paper = (paper[0], wkt, paper[2]) 116 | match_idx(paper, idx) 117 | -------------------------------------------------------------------------------- /geonumpy/pretreat/__init__.py: -------------------------------------------------------------------------------- 1 | from .gaprepair import * -------------------------------------------------------------------------------- /geonumpy/pretreat/gaprepair.py: -------------------------------------------------------------------------------- 1 | from scipy.ndimage import distance_transform_edt as edt 2 | import numpy as np 3 | 4 | def degap(img, msk, r=0): 5 | dis, indices = edt(msk, return_indices=True) 6 | if r!=0: msk = msk & (dis 72.004) & (lng < 137.8347) 66 | msk &= (lat > 0.8293) & (lat < 55.8271) 67 | return not msk 68 | 69 | def lat2y(lat): 70 | rad = np.clip(lat, -85.0511287798, 85.0511287798) * np.pi / 180 71 | return np.log(np.tan(np.pi / 4 + rad / 2)) / np.pi * 180 72 | 73 | def y2lat(y): 74 | return (np.arctan(np.exp(y / 180 * np.pi)) - np.pi / 4) * 2 / np.pi * 180 75 | 76 | def gaode_adjust(xy): 77 | return np.array([xy[:,0], lat2y(xy[:,1])]).T 78 | 79 | def gaode_adjust_(xy): 80 | return np.array([xy[:,0], y2lat(xy[:,1])]).T 81 | 82 | def wgs2gaode(xy): 83 | return gaode_adjust(wgs84togcj02(xy)) 84 | 85 | def gaode2wgs(xy): 86 | return gcj02towgs84(gaode_adjust_(xy)) 87 | 88 | if __name__ == '__main__': 89 | lng, lat = 120, 35 90 | lnglat = np.array([[120, 35],[120,35]]) 91 | rst = wgs84togcj02(lnglat) 92 | -------------------------------------------------------------------------------- /geonumpy/test/download_test.py: -------------------------------------------------------------------------------- 1 | import geonumpy.download as gdl 2 | 3 | def modis_test(): 4 | imgs = gdl.modis_search(['MOD09Q1', 'MOD11A2'], 5 | level=6, 6 | areas=['h25v05', 'h27v05', 'h28v05'], 7 | terms=[(2019, (0,30))]) 8 | 9 | gdl.download(imgs, './download/') 10 | 11 | def landsat_test(): 12 | blocks = [(123,43), (119,37), (122,37)] 13 | # 等级(7,8),行列号,起始,结束日期,月份限定,陆地云量,传感器云量,页数 14 | records = gdl.landsat_search(7, blocks, '06/30/2018', '07/30/2019', None, 10, 10, 1) 15 | for i in records: print(i) # 打印 16 | -------------------------------------------------------------------------------- /geonumpy/test/draw_test.py: -------------------------------------------------------------------------------- 1 | import geonumpy as gnp 2 | import geonumpy.io as gio 3 | import geonumpy.util as gutil 4 | import geonumpy.draw as gdraw 5 | import geonumpy.match as gmt 6 | import numpy as np 7 | import matplotlib.pyplot as plt 8 | from PIL import Image 9 | from glob import glob 10 | 11 | def draw_simple(): 12 | shandong = gio.read_shp('../data/shape/shandong.shp') 13 | shandong = shandong.to_crs(3857) 14 | box = gutil.shp2box(shandong, (3600, 2400), 0.1) 15 | paper = gnp.frombox(*box, chan=1, dtype=np.uint8) 16 | paper[:] = 255 17 | gdraw.draw_polygon(paper, shandong, 0, 2) 18 | gdraw.draw_ruler(paper, 80, 50, -80, -50, 1, 4326, ('times', 32), 0, 2, 5) 19 | gdraw.draw_lab(paper, shandong, 'name', 0, ('simhei', 32), 'ct') 20 | gdraw.draw_unit(paper, -180, -100, 0.3, 30, ('times', 48), 0, 'km', 3, anc='r') 21 | gdraw.draw_text(paper, '山东省', 180, 120, 0, ('simkai', 128)) 22 | gdraw.draw_N(paper, -240, 240, ('simhei', 100), 2, 100, 0) 23 | return paper 24 | 25 | def draw_style(): 26 | paper = gnp.geoarray(np.zeros((480,1024), dtype=np.uint8)) 27 | 28 | body = [('Style', 'simhei', 72), 29 | ('blank',50), 30 | ('line', 1, 'this is line style'), 31 | ('circle', 2, 'this is circle style'), 32 | ('rect', 3, 'this is rect style')] 33 | 34 | lut = np.array([[255,255,255], 35 | [255,0 ,0 ], 36 | [0 ,255,0 ], 37 | [0 ,0 ,255], 38 | [0 ,0 ,0 ]], dtype=np.uint8) 39 | gdraw.draw_style(paper,128,-20, body, mar=(20, 30), 40 | recsize=(120,60,3), font=('simsun', 60), color=4, box=5) 41 | return paper.lookup(lut) 42 | 43 | def draw_grade(): 44 | shandong = gio.read_shp('../data/shape/shandong.shp') 45 | shandong = shandong.to_crs(3857) 46 | box = gutil.shp2box(shandong, (3600, 2400), 0.1) 47 | paper = gnp.frombox(*box, dtype=np.uint8) 48 | 49 | areas = shandong.area 50 | grade_lut = np.array([3]*60 + [2]*30 + [1]*10, dtype=np.uint8) 51 | vs = (areas-areas.min())/(areas.max()-areas.min())*99 52 | grade = grade_lut[vs.astype(int)] 53 | print(grade) 54 | 55 | gdraw.draw_polygon(paper, shandong, grade, 0) 56 | gdraw.draw_polygon(paper, shandong, 4, 2) 57 | 58 | gdraw.draw_ruler(paper, 80, 50, -80, -50, 1, 4326, ('times', 32), 4, 2, 5) 59 | gdraw.draw_lab(paper, shandong, 'name', 4, ('simhei', 32), 'ct') 60 | gdraw.draw_unit(paper, -180, -100, 0.3, 30, ('times', 48), 4, 'km', 3, anc='r') 61 | gdraw.draw_text(paper, '山东省', 180, 120, 4, ('simkai', 128)) 62 | gdraw.draw_N(paper, -240, 240, ('simhei', 100), 2, 100, 4) 63 | 64 | body = [('图例', 'simhei', 72), 65 | ('rect', 1, '特大城市'), 66 | ('rect', 2, '中型城市'), 67 | ('rect', 3, '一般城市')] 68 | # 底图,位置,内容,空隙,矩形尺寸及线宽,字体字号颜色,外边框宽度 69 | gdraw.draw_style(paper, 150, -90, body, mar=(20, 30), 70 | recsize=(120,60,2), font=('simsun', 60, 4), color=4, box=0) 71 | 72 | lut = np.array([[255,255,255], 73 | [255,200,100], 74 | [255,255,128], 75 | [255,255,200], 76 | [0 ,0 ,0 ]], dtype=np.uint8) 77 | 78 | return paper.lookup(lut) 79 | 80 | def draw_class(): 81 | # ===== look up table ===== 82 | lut = np.array([[0 ,0 ,0 ], 83 | [168,168,0 ], 84 | [20 ,119,73 ], 85 | [169,208,95 ], 86 | [56 ,168,0 ], 87 | [126,206,244], 88 | [0 ,86 ,154], 89 | [112,168,0 ], 90 | [147,47 ,20 ], 91 | [202,202,202], 92 | [0 ,255,197], 93 | [255,255,255]], dtype=np.uint8) 94 | 95 | # ===== read shape file and make a paper ===== 96 | liaoning = gio.read_shp('../data/shape/shandong.shp') 97 | liaoning = liaoning.to_crs(3857) 98 | box = gutil.shp2box(liaoning, (3600, 2400), 0.15) 99 | paper = gnp.frombox(*box, dtype=np.uint8) 100 | 101 | # ===== match the class tif into paper 102 | fs = glob('../data/class/*.tif') 103 | idx = gmt.build_index(fs) 104 | gmt.match_idx(idx, out=paper, order=0) 105 | 106 | msk = paper * 0 107 | gdraw.draw_polygon(msk, liaoning, 255, 0) 108 | paper[msk==0] = 11 109 | 110 | body = [('图例', 'simhei', 72), 111 | ('rect', 1, '农田'), 112 | ('rect', 2, '森林'), 113 | ('rect', 3, '草地'), 114 | ('rect', 4, '灌丛'), 115 | ('rect', 5, '湿地'), 116 | ('rect', 6, '水体'), 117 | ('rect', 7, '苔原'), 118 | ('rect', 8, '隔水层'), 119 | ('rect', 9, '裸地'), 120 | ('rect', 10, '冰雪')] 121 | # 底图,位置,内容,空隙,矩形尺寸及线宽,字体字号颜色,外边框宽度 122 | gdraw.draw_style(paper, 60, -60, body, mar=(20, 30), 123 | recsize=(120,60,0), font=('simsun', 60, 0), color=0, box=0) 124 | 125 | gdraw.draw_unit(paper, -120, -60, 0.3, 30, ('times', 48), 0, 'km', 3, anc='r') 126 | 127 | gdraw.draw_text(paper, '山东省土地利用类型', 80, 60, 0, ('simkai', 128)) 128 | 129 | gdraw.draw_N(paper, -240, 240, ('msyh', 100), 2, 100, 0) 130 | 131 | gdraw.draw_polygon(paper, liaoning, 0, 2) 132 | 133 | gdraw.draw_bound(paper, 5, 5, -5, -5, 0, 2, clear=None) 134 | 135 | return paper.lookup(lut) 136 | 137 | 138 | if __name__ == '__main__': 139 | 140 | rst = draw_simple() 141 | rst = draw_style() 142 | rst = draw_grade() 143 | rst = draw_class() 144 | 145 | Image.fromarray(rst).save('../doc/imgs/00.png') 146 | Image.fromarray(rst).show() 147 | -------------------------------------------------------------------------------- /geonumpy/test/forest_statistic.py: -------------------------------------------------------------------------------- 1 | import geonumpy as gnp 2 | import geonumpy.io as gio 3 | import geonumpy.util as gutil 4 | import geonumpy.draw as gdraw 5 | import geonumpy.match as gmt 6 | import numpy as np 7 | import scipy.ndimage as ndimg 8 | import matplotlib.pyplot as plt 9 | from PIL import Image 10 | from glob import glob 11 | 12 | def match_class(df): 13 | shandong = df.to_crs(3857) 14 | box = gutil.shp2box(shandong, (3600, 2400), 0.15) 15 | paper = gnp.frombox(*box, dtype=np.uint8) 16 | idx = gmt.build_index(glob('../data/class/*.tif')) 17 | gmt.match_idx(idx, out=paper, order=0) 18 | gio.write_tif(paper, '../data/result/shandong_class.tif') 19 | 20 | def city_label(df): 21 | shandong = df.to_crs(3857) 22 | box = gutil.shp2box(shandong, (3600, 2400), 0.15) 23 | paper = gnp.frombox(*box, dtype=np.uint8) 24 | gdraw.draw_polygon(paper, shandong, np.arange(len(shandong))+1, 0) 25 | gio.write_tif(paper, '../data/result/shandong_label.tif') 26 | 27 | def show_class_label(cls, lab): 28 | ax1, ax2 = plt.subplot(121), plt.subplot(122) 29 | ax1.imshow(cls) 30 | ax2.imshow(lab) 31 | ax1.set_title('shandong class') 32 | ax2.set_title('shandong label') 33 | plt.show() 34 | 35 | def statistic(cls, lab, df): 36 | forest = ndimg.sum(cls==2, lab, np.arange(lab.max())+1) 37 | total = np.bincount(lab.ravel())[1:] 38 | df['area'] = total 39 | df['forest'] = forest 40 | df['ratio'] = forest/total 41 | return df 42 | 43 | def draw_ratio(cls, lab, df): 44 | shandong = df.to_crs(3857) 45 | paper = cls.copy() 46 | paper[lab==0] = 12 47 | 48 | lut = np.array([[0 ,0 ,0 ], 49 | [168,168,0 ], 50 | [20 ,119,73 ], 51 | [169,208,95 ], 52 | [56 ,168,0 ], 53 | [126,206,244], 54 | [0 ,86 ,154], 55 | [112,168,0 ], 56 | [147,47 ,20 ], 57 | [202,202,202], 58 | [0 ,255,197], 59 | [20 ,119,73 ], 60 | [255,255,255]], dtype=np.uint8) 61 | 62 | body = [('图例', 'simhei', 72), 63 | ('rect', 1, '农田'), 64 | ('rect', 2, '森林'), 65 | ('rect', 3, '草地'), 66 | ('rect', 4, '灌丛'), 67 | ('rect', 5, '湿地'), 68 | ('rect', 6, '水体'), 69 | ('rect', 7, '苔原'), 70 | ('rect', 8, '隔水层'), 71 | ('rect', 9, '裸地'), 72 | ('rect', 10, '冰雪')] 73 | 74 | lut[1:-2] = lut[1:-2] * 0.6 + 255 * 0.4 75 | 76 | shandong['lab'] = shandong['ratio'].apply(lambda x:'%.2f%%'%(x*100)) 77 | gdraw.draw_polygon(paper, shandong, 0, 2) 78 | gdraw.draw_polygon(paper, shandong[shandong['ratio']>0.1], 11, 8) 79 | gdraw.draw_lab(paper, shandong, 'lab', 0, ('simhei', 48), 'ct') 80 | # 底图,位置,内容,空隙,矩形尺寸及线宽,字体字号颜色,外边框宽度 81 | gdraw.draw_style(paper, 60, -60, body, mar=(20, 30), 82 | recsize=(120,60,0), font=('simsun', 60, 0), color=0, box=0) 83 | 84 | gdraw.draw_unit(paper, -120, -60, 0.3, 30, ('times', 48), 0, 'km', 3, anc='r') 85 | gdraw.draw_text(paper, '山东省森林覆盖率统计', 80, 60, 0, ('simkai', 128)) 86 | gdraw.draw_N(paper, -240, 240, ('simhei', 100), 2, 100, 0) 87 | gdraw.draw_bound(paper, 5, 5, -5, -5, 0, 2, clear=None) 88 | return paper.lookup(lut) 89 | 90 | if __name__ == '__main__': 91 | shandong = gio.read_shp('../data/shape/shandong.shp') 92 | match_class(shandong) 93 | city_label(shandong) 94 | 95 | cls = gio.read_tif('../data/result/shandong_class.tif') 96 | lab = gio.read_tif('../data/result/shandong_label.tif') 97 | 98 | tab = statistic(cls, lab, shandong) 99 | print(tab[['name', 'area', 'forest', 'ratio']]) 100 | 101 | rst = draw_ratio(cls, lab, shandong) 102 | 103 | Image.fromarray(rst).show() 104 | Image.fromarray(rst).save('../data/result/shandong_forest.png') 105 | -------------------------------------------------------------------------------- /geonumpy/test/geoarray_test.py: -------------------------------------------------------------------------------- 1 | import geonumpy as gnp 2 | import numpy as np 3 | 4 | if __name__ == '__main__': 5 | mat = np.array([[0,1,0],[0,0,1]]) 6 | garr = gnp.geoarray(np.ones((5,5)), crs=4326, mat=mat) 7 | print(garr.crs) 8 | print(garr.mat) 9 | 10 | garr2 = garr+1 11 | print(garr2.crs) 12 | print(garr2.mat) 13 | 14 | garr3 = garr[1::2,1::2] 15 | print(garr3.crs) 16 | print(garr3.mat) 17 | 18 | box = garr.getbox() 19 | print(box) 20 | 21 | garr4 = gnp.frombox(*box, dtype=np.uint8) 22 | print(garr4.getbox()) 23 | 24 | garr_mc = gnp.geoarray(np.ones((5,5,3)), crs=4326, mat=mat) 25 | print(garr_mc.crs) 26 | print(garr_mc.mat) 27 | 28 | -------------------------------------------------------------------------------- /geonumpy/test/geoio_test.py: -------------------------------------------------------------------------------- 1 | import geonumpy.io as gio 2 | import matplotlib.pyplot as plt 3 | 4 | 5 | if __name__ == '__main__': 6 | gdf = gio.read_shp('../data/shape/shandong.shp') 7 | print(gdf) 8 | gdf.plot() 9 | plt.show() 10 | 11 | print('read modis hdf data:') 12 | modis = gio.read_hdf('../data/modis/MOD09Q1.A2019017.h28v05.006.2019030120612.hdf', 0) 13 | print(modis.shape, '\n', modis.crs, '\n', modis.mat) 14 | plt.imshow(modis, cmap='gray') 15 | plt.show() 16 | 17 | print('\nread landsat tif data:') 18 | landsat = gio.read_tif('../data/landsat/LC08_L1TP_122033_20190506_20190506_01_RT_B5.TIF') 19 | print(modis.shape, '\n', modis.crs, '\n', modis.mat) 20 | plt.imshow(landsat, cmap='gray') 21 | plt.show() 22 | 23 | print('\nsave modis as tif') 24 | gio.write_tif(modis, '../data/modis/MOD09Q1.A2019017.h25v05.006.2019030120448.tif') 25 | 26 | box = gio.read_tif_box('../data/landsat/LC08_L1TP_122033_20190506_20190506_01_RT_B5.TIF') 27 | print(box) 28 | -------------------------------------------------------------------------------- /geonumpy/test/match_test.py: -------------------------------------------------------------------------------- 1 | import geonumpy.io as gio 2 | import numpy as np 3 | import geonumpy as gnp 4 | import geonumpy.match as gmt 5 | from glob import glob 6 | import geonumpy.util as gutil 7 | import geonumpy.draw as gdraw 8 | import matplotlib.pyplot as plt 9 | import geopandas as gpd 10 | 11 | def all_source(): 12 | fs = glob('../data/modis/*.hdf') 13 | ax1 = plt.subplot(131) 14 | ax1.imshow(gio.read_hdf(fs[0], 0)) 15 | ax2 = plt.subplot(132) 16 | ax2.imshow(gio.read_hdf(fs[1], 0)) 17 | ax3 = plt.subplot(133) 18 | ax3.imshow(gio.read_hdf(fs[2], 0)) 19 | plt.show() 20 | 21 | def match_one_test(): 22 | shandong = gio.read_shp('../data/shape/shandong.shp') 23 | shandong = shandong.to_crs(3857) 24 | info = gutil.shp2box(shandong, (2048,1536), 0.05) 25 | #paper = gnp.frombox(*info, dtype=np.int16) 26 | path = '../data/modis/MOD09Q1.A2019017.h27v05.006.2019030120430.hdf' 27 | raster = gio.read_hdf(path) 28 | print(raster.shape) 29 | rst = gmt.match_one(raster, info, 0) 30 | print(rst.shape) 31 | plt.imshow(rst) 32 | plt.show() 33 | 34 | def match_multi_test(): 35 | shandong = gio.read_shp('../data/shape/shandong.shp') 36 | shandong = shandong.to_crs(3857) 37 | info = gutil.shp2box(shandong, (2048,1536), 0.05) 38 | fs = glob('../data/modis/*.hdf') 39 | rasters = [gio.read_hdf(i, 0) for i in fs] 40 | rst = gmt.match_multi(rasters, info, 0) 41 | plt.imshow(rst) 42 | plt.show() 43 | 44 | def build_idx_test(): 45 | fs = glob('../data/modis/*.hdf') 46 | idx = gmt.build_index(fs) 47 | idx.plot() 48 | plt.show() 49 | 50 | def match_idx_test(): 51 | shandong = gio.read_shp('../data/shape/shandong.shp') 52 | shandong = shandong.to_crs(3857) 53 | info = gutil.shp2box(shandong, (2048,768*2), 0.05) 54 | 55 | fs = glob('../data/modis/*.hdf') 56 | idx = gmt.build_index(fs) 57 | 58 | rst = gmt.match_idx(idx, info, chan=[0]) 59 | plt.imshow(rst) 60 | plt.show() 61 | 62 | def crs_trans_test(): 63 | path = '../data/modis/MOD09Q1.A2019017.h27v05.006.2019030120430.hdf' 64 | raster = gio.read_hdf(path, 0) 65 | outshp = gutil.box2shp(*raster.getbox()).to_crs(3857) 66 | box = gutil.shp2box(outshp, 1000, 0) 67 | paper = gnp.frombox(*box, dtype=np.int16) 68 | gmt.match_one(raster, out=paper) 69 | 70 | plt.imshow(paper) 71 | plt.show() 72 | 73 | if __name__ == '__main__': 74 | all_source() 75 | match_one_test() 76 | match_multi_test() 77 | 78 | build_idx_test() 79 | 80 | match_idx_test() 81 | 82 | crs_trans_test() 83 | -------------------------------------------------------------------------------- /geonumpy/test/pretreat_test.py: -------------------------------------------------------------------------------- 1 | import geonumpy.io as gio 2 | import geonumpy.pretreat as gpt 3 | import matplotlib.pyplot as plt 4 | 5 | def degap(): 6 | img = gio.read_tif('../data/landsat-gap/LE07_L1TP_123037_20180721_20180816_01_T1_sr_ndvi.tif') 7 | degapimg = gpt.degap(img.copy(), img==-9999, 10) 8 | 9 | ax1 = plt.subplot(121) 10 | ax1.set_title('ori image') 11 | ax1.imshow(img, cmap='gray') 12 | 13 | ax2 = plt.subplot(122) 14 | ax2.set_title('degaped image') 15 | ax2.imshow(degapimg, cmap='gray') 16 | plt.show() 17 | 18 | if __name__ == '__main__': 19 | degap() 20 | -------------------------------------------------------------------------------- /geonumpy/util/__init__.py: -------------------------------------------------------------------------------- 1 | from .util import * -------------------------------------------------------------------------------- /geonumpy/util/util.py: -------------------------------------------------------------------------------- 1 | from shapely.geometry import GeometryCollection 2 | from shapely.geometry import Polygon 3 | import geopandas as gpd 4 | import pyproj, numpy as np 5 | 6 | def makecrs(prj): return pyproj.CRS(prj) 7 | 8 | def shp2box(shape, scale, margin=0.05): 9 | if isinstance(shape, gpd.GeoDataFrame): 10 | shape = shape['geometry'] 11 | shapes = shape.values 12 | kmargin = margin/(1-2*margin) 13 | geoms = list(shape.values) 14 | bounds = GeometryCollection(geoms).bounds 15 | l,t,r,b = bounds 16 | w,h = r-l, b-t 17 | if isinstance(scale, tuple): 18 | ox, oy = (l+r)/2, (t+b)/2 19 | W, H = np.array(scale) * (1-margin*2) 20 | scale = max(w/W, h/H) 21 | if w/h > W/H: h = w/W*H 22 | else: w = h/H*W 23 | l, t, r, b = (ox-w/2, oy-h/2, ox+w/2, oy+h/2) 24 | offsetx, offsety = l-w*kmargin, b+h*kmargin 25 | shp = np.array((h,w))*(1+(kmargin*2))/scale 26 | 27 | shp = (tuple(shp.round().astype(np.int))) 28 | m = np.array([offsetx, scale, 0, offsety, 0, -scale]).reshape((2,3)) 29 | return (shp, shape.crs, m) 30 | 31 | def box2shp(shape, crs, m): 32 | hs, ws = shape[:2] 33 | xx = np.linspace(0,ws,100).reshape((-1,1))*[1,0] 34 | yy = np.linspace(0,hs,100).reshape((-1,1))*[0,1] 35 | xy = np.vstack((xx, yy+[ws,0], xx[::-1]+[0,hs],yy[::-1])) 36 | xy = np.dot(m[:,1:], xy.T) + m[:,:1] 37 | return gpd.GeoSeries([Polygon(xy.T)], crs=pyproj.CRS(crs).to_proj4()) 38 | 39 | def shp_bounds(shape): 40 | bds = shape.bounds 41 | return [bds['minx'].min(), bds['miny'].min(), 42 | bds['maxx'].max(), bds['maxy'].max()] 43 | 44 | def box_boundx(shape, crs, m): 45 | return shp_bounds(box2shp(shape, crs, m)) 46 | 47 | if __name__ == '__main__': 48 | shp = read_shp('../../tasks/country_china_wheat_2018/region.shp') 49 | from time import time 50 | start = time() 51 | raster = shp2raster(shp, 250, 0, style='lab') 52 | print(time()-start) 53 | plt.imshow(raster.imgs[0]) 54 | plt.show() -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | 4 | pip install shapely 5 | pip install fiona 6 | 7 | 8 | if which apt-get > /dev/null; 9 | then 10 | # ubuntu 11 | sudo apt-get install libgdal1i libgdal1-dev libgdal-dev \ 12 | libevent-dev \ 13 | build-essential \ 14 | python-dev python3-dev 15 | sudo add-apt-repository ppa:ubuntugis && sudo apt-get update 16 | sudo apt-get install gdal-bin 17 | sudo apt-get -y install gcc gcc-c++ # gcc version 18 | sudo pip install --upgrade setuptools 19 | sudo pip install gdal 20 | pip install -e geonumpy 21 | elif which yum > /dev/null; 22 | then 23 | # centos 24 | sudo yum -y install gcc gcc-c++ 25 | sudo yum install python-devel python3-devel 26 | sudo yum install libevent-devel openssl-devel libffi-devel 27 | pip install -e geonumpy 28 | elif which brew > /dev/null; 29 | then 30 | # Mac 31 | # TODO 32 | pip install -e geonumpy 33 | fi 34 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | scikit-image 2 | numpy 3 | scipy 4 | matplotlib 5 | pandas 6 | geopandas -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | 4 | from setuptools import setup 5 | from setuptools import find_packages 6 | import setuptools 7 | 8 | setup( 9 | name="geonumpy", 10 | version="0.09", 11 | author="YXDragon", 12 | author_email="yxdragon@imagepy.org", 13 | license = "BSD 3-Clause", 14 | url = "http://imagepy.org/", 15 | description = "combine geo crs and mat with numpy array", 16 | install_requires = [ 17 | "scikit-image", 18 | "numpy", 19 | "scipy", 20 | "matplotlib", 21 | "pandas", 22 | "geopandas", 23 | ], 24 | packages=setuptools.find_packages(), 25 | ) 26 | --------------------------------------------------------------------------------