diff --git a/.github/workflows/flake8.yml b/.github/workflows/flake8.yml new file mode 100644 index 0000000..dd88038 --- /dev/null +++ b/.github/workflows/flake8.yml @@ -0,0 +1,18 @@ +name: flake8 Lint + +on: [push, pull_request] + +jobs: + flake8-lint: + runs-on: ubuntu-latest + name: Lint with flake8 + steps: + - uses: actions/checkout@v1 + - uses: ricardochaves/python-lint@v1.3.0 + with: + use-pylint: false + use-pycodestyle: false + use-flake8: true + use-black: false + use-mypy: false + use-isort: false diff --git a/.github/workflows/python-test-suite.yml b/.github/workflows/python-test-suite.yml index f9952d4..51fb73d 100644 --- a/.github/workflows/python-test-suite.yml +++ b/.github/workflows/python-test-suite.yml @@ -28,13 +28,6 @@ jobs: - name: Install Mapshader code run: | $CONDA/bin/pip install -e . - - name: Lint with flake8 - run: | - # $CONDA/bin/conda install flake8 - # stop the build if there are Python syntax errors or undefined names - # $CONDA/bin/flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics - # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide - # $CONDA/bin/flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics - name: Test with pytest run: | $CONDA/bin/pip install pytest diff --git a/conda.recipe/run_test.py b/conda.recipe/run_test.py index 7e342f8..1042d3d 100644 --- a/conda.recipe/run_test.py +++ b/conda.recipe/run_test.py @@ -1 +1,4 @@ -import mapshader;mapshader.test() +import mapshader + + +mapshader.test() diff --git a/mapshader/__init__.py b/mapshader/__init__.py index 049f32d..5546248 100644 --- a/mapshader/__init__.py +++ b/mapshader/__init__.py @@ -32,5 +32,10 @@ def hello(services=None): print('\tServices', file=sys.stdout) print('\t--------\n', file=sys.stdout) for s in services: - service_msg = f'\t > {s.name} - {s.service_type} - {s.source.geometry_type} - {s.source.description}' + service_msg = '\t > {0} - {1} - {2} - {3}'.format( + s.name, + s.service_type, + s.source.geometry_type, + s.source.description + ) print(service_msg, file=sys.stdout) diff --git a/mapshader/commands/__init__.py b/mapshader/commands/__init__.py new file mode 100644 index 0000000..214d9a4 --- /dev/null +++ b/mapshader/commands/__init__.py @@ -0,0 +1,17 @@ +from pkg_resources import iter_entry_points as entry_points + +import click +from click_plugins import with_plugins + + +@with_plugins(cmd for cmd in list(entry_points('mapshader.commands'))) +@click.group(context_settings=dict(help_option_names=['-h', '--help'])) +def main(args=None): + ''' + mapshader command line interface. + ''' + pass + + +if __name__ == '__main__': + main() diff --git a/mapshader/commands/examples.py b/mapshader/commands/examples.py new file mode 100644 index 0000000..a27c3ef --- /dev/null +++ b/mapshader/commands/examples.py @@ -0,0 +1,31 @@ +import click +import pyct.cmd + + +@click.command( + context_settings=dict(help_option_names=['-h', '--help']), + help='Copy examples and fetch data to the supplied path.', + short_help='Copy examples and fetch data to the supplied path.', +) +@click.option( + '--path', + 'path', + default='.', + show_default=True, + help='Relative path to copy the examples.', +) +@click.option( + '--force', + 'force', + is_flag=True, + help='Force overwrite examples if they already exist.', +) +def examples(path, force): + try: + pyct.cmd.substitute_main( + 'mapshader', + cmds='examples', + args=dict(path=path, fornce=force), + ) + except ValueError as e: + raise click.BadArgumentUsage(e) diff --git a/mapshader/commands/tif_to_netcdf.py b/mapshader/commands/tif_to_netcdf.py new file mode 100644 index 0000000..5bec56c --- /dev/null +++ b/mapshader/commands/tif_to_netcdf.py @@ -0,0 +1,149 @@ +from os import path +import sys + +import click +import xarray as xr + +from mapshader.transforms import cast +from mapshader.transforms import flip_coords +from mapshader.transforms import orient_array +from mapshader.transforms import reproject_raster +from mapshader.transforms import squeeze + + +@click.command( + no_args_is_help=True, + context_settings=dict(help_option_names=['-h', '--help']), + short_help='Convert GeoTIFF raster file format into a NetCDF file.', + help=( + 'Convert GeoTIFF raster file format into a NetCDF file ' + 'given the `FILEPATH` relative path.' + ), +) +@click.argument( + 'filepath', + type=str, + required=True, +) +@click.option( + '--x', + type=str, + default='x', + show_default=True, + help='The x dimension name.', +) +@click.option( + '--y', + type=str, + default='y', + show_default=True, + help='The y dimension name.', +) +@click.option( + '--chunks', + type=tuple, + default=(512, 512), + show_default=True, + help='Coerce into dask arrays with the given chunks.', +) +@click.option( + '--data_variable', + type=str, + default='data', + show_default=True, + help='The data variable name.', +) +@click.option( + '--fill_na', + type=int, + default=-9999, + show_default=True, + help='Fill NaN values with the given value.', +) +@click.option( + '-c', + '--cast', + 'dtype', + default='int16', + show_default=True, + help='Cast the data to the given type.', +) +@click.option( + '-r', + '--reproject', + 'crs', + type=int, + default=3857, + show_default=True, + help='Reproject the data to the given CRS.', +) +def tif_to_netcdf( + filepath, + x, + y, + chunks, + data_variable, + fill_na, + dtype, + crs, +): + ''' + Convert GeoTIFF raster file format into a NetCDF file given the + `FILEPATH` relative path. + + Parameters + ---------- + filepath : str + GeoTIFF raster file relative path. + x : str + The x dimension name. + y : str + The y dimension name. + chunks : tuple of int + The dask array chunk size for the x and y dimension. + data_variable : str + The data variable name. + fill_na : int or float + Fill NaN values with the given value. + dtype : str + Cast the data to the given type. + crs : int + Reproject the data to the given CRS. + ''' + input_file = path.abspath(path.expanduser(filepath)) + output_file = input_file.replace('.tif', '.nc') + + print( + 'Converting {0} from GeoTIFF to NetCDF file'.format(input_file), + file=sys.stdout, + ) + + arr = xr.open_rasterio(input_file) + + # Check if the given dimensions exist + for dimension in (x, y): + if dimension not in arr.dims: + raise click.BadParameter( + "The dimension name {} doesn't exist.".format(dimension) + ) + + arr = squeeze(arr, [d for d in arr.dims if d != x and d != y]) + arr = cast(arr, dtype=dtype) + arr = orient_array(arr) + arr = flip_coords(arr, dim=y) + arr = reproject_raster(arr, epsg=crs) + + dataset = xr.Dataset( + data_vars={data_variable: (['y', 'x'], arr.chunk(chunks))}, + coords={'x': arr.coords[x], 'y': arr.coords[y]}, + ) + dataset.attrs = dict(name=data_variable) + dataset.to_netcdf( + path=output_file, + encoding={data_variable: {'_FillValue': fill_na}}, + ) + + print( + 'Conversion complete: {0}'.format(output_file), + file=sys.stdout, + ) diff --git a/mapshader/core.py b/mapshader/core.py index 75d8074..cc60d09 100644 --- a/mapshader/core.py +++ b/mapshader/core.py @@ -278,7 +278,7 @@ def to_raster(source: MapSource, return create_agg(source, xmin, ymin, xmax, ymax, None, None, None, height, width) -def render_map(source: MapSource, +def render_map(source: MapSource, # noqa: C901 xmin: float = None, ymin: float = None, xmax: float = None, ymax: float = None, x: float = None, y: float = None, diff --git a/mapshader/flask_app.py b/mapshader/flask_app.py index 5af5ca6..c7a8432 100644 --- a/mapshader/flask_app.py +++ b/mapshader/flask_app.py @@ -74,12 +74,6 @@ def flask_to_geojson(source: MapSource): if not source.is_loaded: source.load() - q = request.args.get('q') - limit = request.args.get('limit') - offset = request.args.get('offset') - simplify = request.args.get('simplify') - bbox = request.args.get('bbox') - resp = render_geojson(source) return resp @@ -128,58 +122,89 @@ def service_page(service: MapService): plot = build_previewer(service) script, div = components(dict(preview=plot)) - template = Template(''' - - - - - {{service.name}} - {{ resources }} - {{ script }} - - - - {{ psutils_html }} -
-

{{service.name}}

-
-
Client URL: {{service.client_url}}
-
Description: {{service.source.description}}
-
Geometry Type: {{service.source.geometry_type.capitalize()}}
-
-
-
- {% for key in div.keys() %} - {{ div[key] }} - {% endfor %} -
-
-
-

Details

-
-
Data Path: {{service.source.filepath}}
-
Span: {{service.source.span}}
-
Overviews: {{service.source.overviews.keys()}}
-
Aggregation Method: {{service.source.agg_func}}
-
Colormap Interpolation Method: {{service.source.shade_how}}
-
- - - ''') + template = Template( + ''' + + + + + + {{service.name}} + {{ resources }} + {{ script }} + + + + {{ psutils_html }} +
+

{{service.name}}

+
+
Client URL: + {{service.client_url}} +
+
Description: + {{service.source.description}} +
+
Geometry Type: + {{service.source.geometry_type.capitalize()}} +
+
+
+
+ {% for key in div.keys() %} + {{ div[key] }} + {% endfor %} +
+
+
+

Details

+
+
+ + Data Path: + + {{service.source.filepath}} +
+
+ + Span: + + {{service.source.span}} +
+
+ + Overviews: + + {{service.source.overviews.keys()}} +
+
+ + Aggregation Method: + + {{service.source.agg_func}} +
+
+ + Colormap Interpolation Method: + + {{service.source.shade_how}} +
+
+ + + ''' + ) resources = INLINE.render() html = template.render(resources=resources, diff --git a/mapshader/io.py b/mapshader/io.py index 92ae6d3..de2af9b 100644 --- a/mapshader/io.py +++ b/mapshader/io.py @@ -1,12 +1,8 @@ -import rioxarray -import xarray as xr -import datashader as ds -import geopandas as gpd -import numpy as np -from affine import Affine - from os.path import expanduser +import geopandas as gpd +import numpy as np +import xarray as xr def load_raster(file_path, xmin=None, ymin=None, diff --git a/mapshader/mercator.py b/mapshader/mercator.py index 0f2e7b4..685a7f0 100644 --- a/mapshader/mercator.py +++ b/mapshader/mercator.py @@ -54,14 +54,12 @@ def to_ogc_tile_metadata(self, output_file_path): ''' pass - def to_esri_tile_metadata(self, output_file_path): ''' Create ESRI tile metadata JSON ''' pass - def is_valid_tile(self, x, y, z): if x < 0 or x >= math.pow(2, z): @@ -72,18 +70,15 @@ def is_valid_tile(self, x, y, z): return True - # TODO ngjit? def _get_resolution(self, z): return self.initial_resolution / (2 ** z) - def get_resolution_by_extent(self, extent, height, width): x_rs = (extent[2] - extent[0]) / width y_rs = (extent[3] - extent[1]) / height return [x_rs, y_rs] - def get_level_by_extent(self, extent, height, width): x_rs = (extent[2] - extent[0]) / width y_rs = (extent[3] - extent[1]) / height @@ -100,21 +95,18 @@ def get_level_by_extent(self, extent, height, width): i += 1 return (i-1) - def pixels_to_meters(self, px, py, level): res = self._get_resolution(level) mx = (px * res) - self.x_origin_offset my = (py * res) - self.y_origin_offset return (mx, my) - def meters_to_pixels(self, mx, my, level): res = self._get_resolution(level) px = (mx + self.x_origin_offset) / res py = (my + self.y_origin_offset) / res return (px, py) - def pixels_to_tile(self, px, py, level): tx = math.ceil(px / self.tile_size) tx = tx if tx == 0 else tx - 1 @@ -122,19 +114,15 @@ def pixels_to_tile(self, px, py, level): # convert from TMS y coordinate return (int(tx), invert_y_tile(int(ty), level)) - def pixels_to_raster(self, px, py, level): map_size = self.tile_size << level return (px, map_size - py) - def meters_to_tile(self, mx, my, level): px, py = self.meters_to_pixels(mx, my, level) return self.pixels_to_tile(px, py, level) - def get_tiles_by_extent(self, extent, level): - # unpack extent and convert to tile coordinates xmin, ymin, xmax, ymax = extent # note y coordinates are reversed since they are in opposite direction to meters @@ -151,10 +139,12 @@ def get_tiles_by_extent(self, extent, level): return tiles - def get_tile_meters(self, tx, ty, level): - ty = invert_y_tile(ty, level) # convert to TMS for conversion to meters + ty = invert_y_tile(ty, level) # convert to TMS for conversion to meters xmin, ymin = self.pixels_to_meters(tx * self.tile_size, ty * self.tile_size, level) - xmax, ymax = self.pixels_to_meters((tx + 1) * self.tile_size, (ty + 1) * self.tile_size, level) + xmax, ymax = self.pixels_to_meters( + (tx + 1) * self.tile_size, + (ty + 1) * self.tile_size, + level, + ) return (xmin, ymin, xmax, ymax) - diff --git a/mapshader/sources.py b/mapshader/sources.py index 10c90c1..a8d74fe 100644 --- a/mapshader/sources.py +++ b/mapshader/sources.py @@ -16,7 +16,7 @@ class MapSource(object): - def __init__(self, + def __init__(self, # noqa: C901 name=None, description=None, filepath=None, diff --git a/mapshader/tests/fixtures/shade.nc b/mapshader/tests/fixtures/shade.nc new file mode 100644 index 0000000..5d703eb Binary files /dev/null and b/mapshader/tests/fixtures/shade.nc differ diff --git a/mapshader/tests/fixtures/shade.tif b/mapshader/tests/fixtures/shade.tif new file mode 100644 index 0000000..9ea0f3f Binary files /dev/null and b/mapshader/tests/fixtures/shade.tif differ diff --git a/mapshader/tests/test_tif_to_netcdf.py b/mapshader/tests/test_tif_to_netcdf.py new file mode 100644 index 0000000..8b2a34c --- /dev/null +++ b/mapshader/tests/test_tif_to_netcdf.py @@ -0,0 +1,81 @@ +import filecmp +from os import path +import shutil + +from click import BadParameter +from click.testing import CliRunner +from rasterio.errors import RasterioIOError + +from mapshader.commands.tif_to_netcdf import tif_to_netcdf +from mapshader.tests.data import FIXTURES_DIR + + +def test_invalid_y_dimension_name(): + runner = CliRunner() + input_file = path.join(FIXTURES_DIR, 'shade.tif') + + result = runner.invoke( + tif_to_netcdf, [input_file, '--x', '1'], standalone_mode=False + ) + assert isinstance(result.exception, BadParameter) + + +def test_invalid_x_dimension_name(): + runner = CliRunner() + input_file = path.join(FIXTURES_DIR, 'shade.tif') + + result = runner.invoke( + tif_to_netcdf, [input_file, '--x', '1'], standalone_mode=False + ) + assert isinstance(result.exception, BadParameter) + + +def test_invalid_input_file(): + runner = CliRunner() + input_file = path.join(FIXTURES_DIR, 'counties_3857.gpkg') + + result = runner.invoke(tif_to_netcdf, [input_file], standalone_mode=False) + assert isinstance(result.exception, RasterioIOError) + + +def test_invalid_input_file_path(): + runner = CliRunner() + input_file = path.join(FIXTURES_DIR, 'nd.tif') + + result = runner.invoke(tif_to_netcdf, [input_file], standalone_mode=False) + assert isinstance(result.exception, RasterioIOError) + + +def test_invalid_dtype_cast(): + runner = CliRunner() + input_file = path.join(FIXTURES_DIR, 'shade.tif') + + result = runner.invoke( + tif_to_netcdf, [input_file, '--cast', 'int2'], standalone_mode=False + ) + assert isinstance(result.exception, TypeError) + + +def test_invalid_reprojection(): + runner = CliRunner() + input_file = path.join(FIXTURES_DIR, 'shade.tif') + + result = runner.invoke( + tif_to_netcdf, [input_file, '--reproject', '123'], standalone_mode=False + ) + assert isinstance(result.exception, ValueError) + + +def test_valid_conversion(tmpdir): + runner = CliRunner() + + input_filename = 'shade.tif' + output_filename = 'shade.nc' + + input_filepath = tmpdir.join(input_filename).strpath + output_filepath = tmpdir.join(output_filename).strpath + expected_output_filepath = path.join(FIXTURES_DIR, output_filename) + + shutil.copy2(path.join(FIXTURES_DIR, input_filename), input_filepath) + runner.invoke(tif_to_netcdf, [input_filepath], standalone_mode=False) + assert filecmp.cmp(output_filepath, expected_output_filepath) diff --git a/mapshader/transforms.py b/mapshader/transforms.py index e153895..6b15ab5 100644 --- a/mapshader/transforms.py +++ b/mapshader/transforms.py @@ -13,7 +13,10 @@ wgs84_proj_str = '+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs' -us_national_equal_area_str = '+proj=laea +lat_0=45 +lon_0=-100 +x_0=0 +y_0=0 +a=6370997 +b=6370997 +units=m +no_defs' +us_national_equal_area_str = ( + '+proj=laea +lat_0=45 +lon_0=-100 +x_0=0 +y_0=0 +a=6370997 ' + '+b=6370997 +units=m +no_defs' +) projections = { 3857: wb_proj_str, diff --git a/mapshader/utils.py b/mapshader/utils.py index e9e4936..7f69c57 100644 --- a/mapshader/utils.py +++ b/mapshader/utils.py @@ -40,7 +40,7 @@ def find_and_set_categoricals(df): def psutil_fetching(): # CPU logs - cpu_usage_percentage = psutil.cpu_percent(interval = 1) + cpu_usage_percentage = psutil.cpu_percent(interval=1) cpu_number_logical = psutil.cpu_count() cpu_number_physical = psutil.cpu_count(logical=False) @@ -49,16 +49,16 @@ def psutil_fetching(): cpu_per_cpu_percentage = psutil.cpu_percent(interval=1, percpu=True) # Disks - disk_usage = psutil.disk_usage("/") # Root disk usage + disk_usage = psutil.disk_usage("/") # Root disk usage log = { 'cpu': { - 'cpu_usage_percentage': cpu_usage_percentage, - 'cpu_number_logical': cpu_number_logical, - 'cpu_number_physical': cpu_number_physical, - 'cpu_per_cpu_percentage': cpu_per_cpu_percentage, - 'cpu_times': cpu_times._asdict(), + 'cpu_usage_percentage': cpu_usage_percentage, + 'cpu_number_logical': cpu_number_logical, + 'cpu_number_physical': cpu_number_physical, + 'cpu_per_cpu_percentage': cpu_per_cpu_percentage, + 'cpu_times': cpu_times._asdict(), }, 'memory': psutil.virtual_memory()._asdict(), 'disk': disk_usage._asdict(), @@ -68,138 +68,138 @@ def psutil_fetching(): def psutils_html(): return ''' - -
-
-
- - CPU:  - 0,0% - -
-
-
-
-
-
-
- - MEMORY:  - 0,0% - -
-
-
-
-
-
-
- - DISK:  - 0,0% - -
-
-
-
-
-
- - ''' \ No newline at end of file + +
+
+
+ + CPU:  + 0,0% + +
+
+
+
+
+
+
+ + MEMORY:  + 0,0% + +
+
+
+
+
+
+
+ + DISK:  + 0,0% + +
+
+
+
+
+
+ + ''' diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..a207dee --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,6 @@ +[build-system] +requires = [ + 'pyct', + 'setuptools', + 'wheel', +] diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 0000000..c1fa878 --- /dev/null +++ b/pytest.ini @@ -0,0 +1,2 @@ +[pytest] +addopts = -p no:warnings \ No newline at end of file diff --git a/scripts/tif_to_netcdf.py b/scripts/tif_to_netcdf.py deleted file mode 100644 index 05b4bf2..0000000 --- a/scripts/tif_to_netcdf.py +++ /dev/null @@ -1,68 +0,0 @@ -import xarray as xr - -from mapshader.transforms import squeeze -from mapshader.transforms import cast -from mapshader.transforms import orient_array -from mapshader.transforms import flip_coords -from mapshader.transforms import reproject_raster - - -def run_float(input_file, output_file, chunks=(512, 512), - name='data', scale_factor=0.1, fill_value=-9999): - arr = xr.open_rasterio(input_file) - arr = squeeze(arr, 'band') - arr = cast(arr, dtype='float64') - arr = orient_array(arr) - arr = flip_coords(arr, dim='y') # do we need this? - arr = reproject_raster(arr, epsg=3857) - - dataset = xr.Dataset({name: (['y', 'x'], arr.chunk(chunks))}, - coords={'x': arr.coords['x'], - 'y': arr.coords['y']}) - dataset.attrs = dict(name=name) - dataset.to_netcdf(output_file, encoding={'data': {'dtype': 'int16', - 'scale_factor': 0.1, - '_FillValue': -9999}}) - - -def run_int(input_file, output_file, chunks=(512, 512), - name='data', fill_value=-9999): - arr = xr.open_rasterio(input_file) - arr = squeeze(arr, 'band') - arr = orient_array(arr) - arr = flip_coords(arr, dim='y') # do we need this? - arr = reproject_raster(arr, epsg=3857) - - dataset = xr.Dataset({name: (['y', 'x'], arr.chunk(chunks))}, - coords={'x': arr.coords['x'], - 'y': arr.coords['y']}) - dataset.attrs = dict(name=name) - dataset.to_netcdf(output_file, encoding={'data': {'dtype': 'int16', - '_FillValue': fill_value}}) - - -if __name__ == '__main__': - - import sys - from argparse import ArgumentParser - from os import path - - parser = ArgumentParser() - parser.add_argument('-i') - parser.add_argument('-o') - parser.add_argument('-f') - parsed = parser.parse_args() - - input_file = path.abspath(path.expanduser(parsed.i)) - print(f'Converting {input_file} from TIFF to NetCDF File', file=sys.stdout) - - if not parsed.o: - output_file = input_file.replace('.tif', '.nc') - else: - output_file = path.abspath(path.expanduser(parsed.o)) - - if parsed.f: - run_float(input_file, output_file) - else: - run_int(input_file, output_file) - print(f'Conversion Complete: {output_file}', file=sys.stdout) diff --git a/setup.py b/setup.py index 7be3ca0..13a1c3b 100644 --- a/setup.py +++ b/setup.py @@ -1,33 +1,69 @@ +import os from setuptools import setup -setup(name='mapshader', - use_scm_version={ - "write_to": "mapshader/_version.py", - "write_to_template": '__version__ = "{version}"', - "tag_regex": r"^(?Pv)?(?P[^\+]+)(?P.*)?$", - }, - description='Simple Python GIS Web Services', - url='https://github.com/makepath/mapshader', - packages=['mapshader', - 'mapshader.tests'], - install_requires=['xarray-spatial', - 'datashader', - 'geopandas', - 'click', - 'jinja2', - 'spatialpandas', - 'pytest', - 'tbb', - 'rtree', - 'rioxarray', - 'matplotlib', - 'descartes', - 'flask', - 'flask-cors>=3.0.10', - 'rasterio', - 'jupyter', - 'pyarrow', 'psutil'], - zip_safe=False, - classifiers=["Programming Language :: Python :: 3", - "License :: OSI Approved :: MIT License", - "Operating System :: OS Independent"], - include_package_data=True) +import shutil +import sys + +import pyct.build + + +setup_args = dict( + name='mapshader', + use_scm_version={ + 'write_to': 'mapshader/_version.py', + 'write_to_template': '__version__ = "{version}"', + 'tag_regex': r'^(?Pv)?(?P[^\+]+)(?P.*)?$', + }, + description='Simple Python GIS Web Services', + url='https://github.com/makepath/mapshader', + packages=[ + 'mapshader', + 'mapshader.tests', + ], + install_requires=[ + 'xarray-spatial', + 'datashader', + 'geopandas', + 'click', + 'click_plugins', + 'jinja2', + 'spatialpandas', + 'pytest', + 'tbb', + 'rtree', + 'rioxarray', + 'matplotlib', + 'descartes', + 'flask', + 'flask-cors>=3.0.10', + 'rasterio', + 'jupyter', + 'pyarrow', + 'psutil', + 'pyct', + ], + zip_safe=False, + classifiers=[ + 'Programming Language :: Python :: 3', + 'License :: OSI Approved :: MIT License', + 'Operating System :: OS Independent', + ], + include_package_data=True, + entry_points=''' + [console_scripts] + mapshader=mapshader.commands:main + + [mapshader.commands] + examples=mapshader.commands.examples:examples + tif_to_netcdf=mapshader.commands.tif_to_netcdf:tif_to_netcdf + ''', +) + +if __name__ == '__main__': + example_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), + 'mapshader', 'examples') + if 'develop' not in sys.argv: + pyct.build.examples(example_path, __file__, force=True) + setup(**setup_args) + + if os.path.isdir(example_path): + shutil.rmtree(example_path)