PlanWise applies algorithms and geospatial optimisation techniques to existing data on population, road networks and health facilities, so health care planners can better understand the unmet health needs of their constituents and better locate future health facilities. Together, InSTEDD and Concern Worldwide are developing PlanWise, to apply such algorithms to help governments visualize how potential future facilities will improve access to healthcare in underserved areas.
PlanWise is a single page web application implemented in Clojure and Clojurescript, backed with PostgreSQL database storing both relational and spatial information, and GeoTiff raster files for demographics data.
Server side:
Client side:
Deployment:
The webserver is multi-threaded and there is no lock contention on any operation, so it can handle multiple concurrent requests. The PostgreSQL database can also handle multiple concurrent connections. Pre-processing of facilities is parallelized to make use of multiple cores, and can be easily extended to multiple hosts.
Beyond that, there are three main dimensions for scaling PlanWise:
The production deployment of PlanWise uses demographics datasets from WorldPop.
Instructions for setting up a development environment using Docker.
Build the required Docker images:
$ docker-compose build
Start the Docker compose stack defined in docker-compose.yml
$ docker-compose up
This will start the PostgreSQL/PostGIS database, the MapServer/MapCache containers and a headless nREPL container.
Some scripts might require a bit more than 2gb of memory. Increase the default docker limit if import-osm is run inside a container.
The mapserver and mapcache containers for development will use the map data in
the data
folder.
Run inside the app
container:
$ docker-compose run app bash
app$ scripts/bootstrap-dev.sh
There is a bit of geographical data needed to have a functional environment.
First, the global friction layer needs to be download as described here. This is used to compute walking and car travel time.
Second, the administrative hierarchy of selected countries needs to be
downloaded. Follow this procedure to populate
the data/geojson
directory.
Third, download and register country population datasets to use as demand raster source. While doing this last step the administrative hierarchies will be registered and friction layer will be sliced per country. If you don't need demand raster sources you will still need to register the administrative hierarchies and slice the friction layer. Check this procedure to see how these steps are done.
Additionally, the project requires GUISSO
information (identifier and secret) to establish the OAuth flow with resourcemap.
Register your development host in GUISSO, and set the environment variables in
a docker-compose.override.yml
:
version: '2'
services:
app:
environment:
- GUISSO_CLIENT_ID=YOURID
- GUISSO_CLIENT_SECRET=YOURSECRET
Or you can set these values in the local dev/resources/local.edn
, which is
more useful if you plan to run the application outside Docker (see below):
{:duct.core/include ["dev.edn"]
:planwise.component/auth
{:guisso-client-id "YOURID"
:guisso-client-secret "YOURSECRET"}}
To avoid Docker from starting Leiningen, put in your
docker-compose.override.yml
the following configuration:
version: '2'
services:
app:
command: /bin/true
You need to have Leiningen installed. In Mac OSX using Homebrew, just run:
$ brew install leiningen
The following environment variables are used by the project scripts, and it is suggested to set them before starting development:
export RASTER_ISOCHRONES=false
export CALCULATE_DEMAND=false
export POSTGRES_PASSWORD="planwise"
export POSTGRES_USER=planwise
export POSTGRES_DB=planwise
export POSTGRES_HOST=localhost
export POSTGRES_PORT=5433
Default values are set in the file env
, so you can simply run:
$ source ./env
The project needs GDAL 2.x with support for PostgreSQL and Java bindings. On Mac OSX you can use the osgeo4mac Homebrew tap to get it.
$ brew tap osgeo/osgeo4mac
$ brew install gdal2 --with-swig-java --with-postgresql
Since gdal2
is keg-only, you need to force link it with brew link --force gdal2
and add /usr/local/opt/gdal2/bin
to your PATH environment
variable.
You also need to add the make the JNI libraries discoverable by the JVM. For
development, an easy and non-intrusive way of doing it is adding the
java.library.path
system property in your profiles.clj
. Eg.
;; Local profile overrides
{:profiles/dev {:jvm-opts ["-Djava.library.path=/usr/local/opt/gdal2/lib"]}}
The application has some C++ binaries which are run in the context of the
application. If running the application outside Docker, you'll need to compile
these. Otherwise, these are automatically built by bootstrap-dev.sh
.
$ brew install cmake boost
$ scripts/build-binaries
NPM dependencies are handled by npm
and updated via the package.json
file.
Install NPM dependencies before firing up the REPL or compiling the project:
$ npm install
NB: npm install
is ran automatically when executing (go)
from the REPL.
Connect to the running REPL inside the Docker container from your editor/IDE or from the command line:
$ lein repl :connect
Or, if running outside Docker, start up the REPL with lein repl
.
Load the development namespace and start the system:
user=> (dev)
:loaded
dev=> (go)
By default this creates a web server at http://localhost:3000. Note that
these 3 commands are ran when invoking scripts/dev
.
When you make changes to your source files, use reset
to reload any
modified files and reset the server.
dev=> (reset)
:reloading (...)
:resumed
If you want to access a ClojureScript REPL, make sure that the server is running and there is a browser with the application loaded.
$ shadow-cljs cljs-repl :app
shadow-cljs - config: /app/client/shadow-cljs.edn cli version: 2.8.59 node: v9.11.2
shadow-cljs - connected to server
cljs.user=> (js/alert "hi")
nil
The shadow-cljs
is installed by ./client/package.json
in
./client/node_modules/.bin/shadow-cljs
. In a docker development environment
you can execute shadow-cljs from the client service container.
The database engine is PostgreSQL, using PostGIS and pgRouting. It is included in the default Docker compose file.
Database schema is managed through migrations via
ragtime. In
development, they are run automatically on system startup and after every
(reset)
.
With a running system, there is a (rollback-1)
function to manually rollback
the last migration, though will be rarely needed since the migrations are
rebased automatically on each reset. The expected workflow is:
(reset)
to apply the migration(reset)
again, to rollback the old version and apply the modified
migrationThe function (rollback-1)
can be used to manually rollback until a specific
migration. This would be needed if switching the development branch and
restarting the REPL at the same time. Otherwise the system should be able to
rebase the migrations when resetting.
Migrations can be executed outside the REPL via the lein migrate
task.
Migration files are located in resources/migrations
, and follow the
NUM-name.(up|down).sql
naming convention, where NUM
is a 3-digit incremental
ID.
Additionally, SQL functions located in resources/planwise/plpgsql
are
regenerated on every lein migrate
, or can be manually loaded from the REPL by
running (load-sql)
.
Running the tests require a separate scratch database.
$ docker-compose exec db createdb planwise-test -U planwise
The connection configuration is located in the environment variable
TEST_DATABASE_URL
, with the default being as specified in
test/resources/test.edn
.
Testing is fastest through the REPL, as you avoid environment startup time.
dev=> (test)
...
But you can also run tests through Leiningen.
lein test
Use the Planwise Tools Docker image to manage geographic and base source sets in the database. In development, this can be spawned by running:
docker-compose run tools
Then follow the instructions given in scripts/tools/README.md
.
Planwise supports Intercom as its CRM platform. To load the Intercom chat widget, simply start Planwise with the env variable INTERCOM_APP_ID
set to your Intercom app id (https://www.intercom.com/help/faqs-and-troubleshooting/getting-set-up/where-can-i-find-my-workspace-id-app-id).
Planwise will forward any conversation with a logged user identifying them through their email address. Anonymous, unlogged users will also be able to communicate.
If you don't want to use Intercom, you can simply omit INTERCOM_APP_ID
or set it to ''
.
To test the feature in development, add the INTERCOM_APP_ID
variable and its value to the corresponding edn
file.
Sample files for docker cloud and docker compose are provided in the root folder, which make use of the project's Docker image.
After setting up the stack, DB data can be provisioned by running the scripts described in the Database section of this document.
Copyright © 2016 InSTEDD
This software is released under the GPLv3 license. See LICENSE.md.