Package: geoshiny
GeoshinyThis is a tool to create customized maps using OpenStreetMap data.NOTE: this software is under development, the interface and the functionalities can and probably will change.FeaturesGenerate customized maps of cities and small regionsOutputs:SVGPNGmany othersStore a filtered intermediate representation in JSONL to easily generate images without a databaseInput dataFirst you need a PostGIS database with the data to represent in flex format. To create that, get a PDB file of the region you are interested to represent (you can also import the whole planet.osm file) and use osm2pgsql with the flex output to run thepgosm-flexscripts.If you have Docker, you can find a script that populates a PostGIS instance for you inscript_utils/database_from_pbf.sh, run it with the pbf file name as an argument.The connection string for this database has to be put in the environment variablePGIS_CONN_STRexport PGIS_CONN_STR=postgres://username:password@localhost:5432/osm_dataInstallationYou will probably need to install thegeoslibrary on your system, on Ubuntu for example is the packagelibgeos-dev, on macOS using bres it'sgeos.The default installation does not require GDAL and cannot generate geoTIFF. That functionality is in thegeotiffextra and you will need the GDAL libraries installed on your computer, the procedure depends on the OS and can be tricky.Then, requirements.txt dependencies need to be installed, for example withpython3 -m pip install --no-binary Shapely -r requirements.txtthere are a lot of bugs related to the way GDAL is installed, for example you may need to reinstall it after installing numpy, and avoid using the wheel but let it build (python3 -m pip install --no-binary GDAL GDAL).Also, GDAL seems to need to see the gdal commands when building, or it fails at runtime. So if you are using a virtualenv you'll need to activate it first or alter you PATH if you are in a CI pipeline (check the Makefile for examples).I still have to find reliable ways to install and import GDAL and Shapely without thisimport order problem, so for now I use only Shapely.UsageNOTE: this is under development, usage will change soonThe library expects two callbacks,representationandrenderer.Therepresentationis called for each map feature in the given extent, receives the OSM tags as an input and returnsNonefor features to be ignored or an arbitrary Python object (usually a dictionary) with the relevant information to represent. Can also alter the Shapely geometry.Therendererwill receive the output of therepresentationfunction and produce the matplotlib attributes like color and alpha.So one takes care of decidingwhatto represent and the other ofhowto represent it. This decoupling allows to change representation and store intermediate values in a file.
Usingfile_to_representationyou can generate the representation once and render different extents with different styles easily without even running a database instance.importasynciofromgeoshiny.database_extractimportraw_data_from_extentfromgeoshiny.typesimport(ExtentDegrees,ObjectStyle,)fromgeoshiny.draw_helpersimport(data_to_representation,data_to_representation_file,file_to_representation,representation_to_figure,)defrepresentation(osm_id:int,geom,tags:dict)->Optional[dict]:iftags.get("bicycle")=="designated":returndict(path_type="bike")if"water"intags:returndict(surface_type="water")iftags.get("landuse")=="grass":returndict(surface_type="grass")iftags.get("leisure")=="park":returndict(surface_type="grass")[...]defrenderer(osm_id:int,shape:BaseGeometry,d:dict):water_style=ObjectStyle(facecolor="blue",edgecolor="darkblue",linewidth=0.1)grass_style=ObjectStyle(facecolor="green",linewidth=0.1)ifd.get("surface_type")=="water":returnwater_style[...]# northern part of Rostock, Germanyextent=ExtentDegrees(latmin=54.0960,latmax=54.2046,lonmin=12.0029,lonmax=12.1989,)# this directly renders to a filegenerate_chart('generated.png',extent,representation,renderer,)# but you can run the steps yourselfloop=asyncio.get_event_loop()db_data=loop.run_until_complete(raw_data_from_extent(extent))reprs=data_to_representation(db_data,entity_callback=representation)db_img=representation_to_figure(reprs,extent,renderer,figsize=3000)db_img.savefig("image.png")db_img.savefig("image.svg")data_to_representation_file(db_data,'somefile.jsonl',entity_callback=representation,)img2=representation_to_figure(file_to_representation('somefile.jsonl'),extent,renderer,figsize=3000,)img2.savefig("image2.png")img2.savefig("image2.svg")TestingNOTE: this will also probably change, I'm looking at ways to run the tests without git-lfsTo run the test you need git-lfs and docker installed. If you didn't have git-lfs enabled before cloning the repo, you have to dogit lfs pullto download the SQL dump file first.
Usemake test-from-zeroto perform a complete integration test, it will use a dump of a small postgis DB of around 200 MB to create a dockerized postgis instance and run scripts against that. The instance id deleted after the test or in case of errors.TODO"Proper" automated tests with a reasonable data fixtureExamine the possibility of removing GDAL or making it optional, it's a pain to installAdd labeling/textXKCD style output (from matplotlib, should work out of the box)layers/pipelines to further process the outputOffer both async and sync access if possible, hiding the loop to sync usersVisual comparison of output images (may require opencv as a test dependency, is it worth it?)Helper to generate world files (https://en.wikipedia.org/wiki/World_file)3D output (check QGIS formats / glTF)tileset output?Create and document helpers to make the usage simpler (once the interface is stabilized)Examples and screenshot gallerySpatialite support?Optional pipeline definition interface to combine processing steps?Graph functionalities (e.g. show road distance from a set of POI) ?MoviePy integration exampleIntegrated webserver/UI? Maybe better off as a separate thing