├── .dockerignore ├── .github └── workflows │ └── build.yml ├── .gitignore ├── Dockerfile ├── LICENSE.md ├── README.md ├── build.sh ├── client ├── README.md ├── dev │ └── src │ │ ├── build.clj │ │ ├── build │ │ └── sass.clj │ │ └── dev │ │ └── user.cljs ├── package-lock.json ├── package.json ├── resources │ ├── css │ │ ├── data-table.css │ │ └── mapbox.css │ ├── js │ │ └── externs.js │ └── sass │ │ ├── _global.scss │ │ ├── _grid.scss │ │ ├── _layout.scss │ │ ├── _reset.scss │ │ ├── _responsive.scss │ │ ├── _variables.scss │ │ ├── landing.scss │ │ └── site2.scss ├── shadow-cljs.edn ├── src │ ├── leaflet │ │ ├── controls.cljs │ │ ├── core.cljs │ │ └── layers.cljs │ └── planwise │ │ └── client │ │ ├── api.cljs │ │ ├── asdf.cljs │ │ ├── components │ │ ├── common.cljs │ │ └── common2.cljs │ │ ├── config.cljs │ │ ├── core.cljs │ │ ├── coverage.cljs │ │ ├── db.cljs │ │ ├── effects.cljs │ │ ├── handlers.cljs │ │ ├── mapping.cljs │ │ ├── modal │ │ ├── modal.cljs │ │ └── modal.svg │ │ ├── projects2 │ │ ├── api.cljs │ │ ├── components │ │ │ ├── common.cljs │ │ │ ├── create.cljs │ │ │ ├── dashboard.cljs │ │ │ ├── listings.cljs │ │ │ └── settings.cljs │ │ ├── core.cljs │ │ ├── db.cljs │ │ ├── handlers.cljs │ │ ├── subs.cljs │ │ └── views.cljs │ │ ├── providers_set │ │ ├── api.cljs │ │ ├── components │ │ │ ├── dropdown.cljs │ │ │ └── new_provider_set.cljs │ │ ├── db.cljs │ │ ├── handlers.cljs │ │ ├── subs.cljs │ │ └── views.cljs │ │ ├── regions │ │ ├── api.cljs │ │ ├── handlers.cljs │ │ └── subs.cljs │ │ ├── routes.cljs │ │ ├── scenarios │ │ ├── api.cljs │ │ ├── changeset.cljs │ │ ├── db.cljs │ │ ├── edit.cljs │ │ ├── handlers.cljs │ │ ├── subs.cljs │ │ └── views.cljs │ │ ├── sources │ │ ├── api.cljs │ │ ├── components │ │ │ └── dropdown.cljs │ │ ├── db.cljs │ │ ├── handlers.cljs │ │ ├── sources.svg │ │ ├── subs.cljs │ │ └── views.cljs │ │ ├── styles.cljs │ │ ├── subs.cljs │ │ ├── ui │ │ ├── common.cljs │ │ ├── dialog.cljs │ │ ├── filter_select.cljs │ │ ├── macros.clj │ │ └── rmwc.cljs │ │ ├── utils.cljs │ │ └── views.cljs └── test │ └── planwise │ └── client │ ├── scenarios_test.cljs │ └── utils_test.cljs ├── common ├── README.md └── src │ └── planwise │ ├── common.cljc │ └── model │ ├── coverage.cljc │ └── project.cljc ├── cpp ├── .gitignore ├── CMakeLists.txt ├── aggregate-population ├── aggregate-population.cpp ├── bin-trampoline.sh ├── walking-coverage └── walking-coverage.cpp ├── data ├── populations │ └── .gitkeep ├── regions │ └── .gitkeep └── scenarios │ └── .gitkeep ├── dev ├── resources │ └── dev.edn └── src │ ├── dev.clj │ ├── planwise │ ├── repl.clj │ └── virgil.clj │ └── user.clj ├── doc ├── coverage_algo.md └── polygon_clipping_algorithms.md ├── docker-cloud.sample.yml ├── docker-compose.ci.yml ├── docker-compose.production.yml ├── docker-compose.yml ├── docker-env ├── env ├── java └── planwise │ └── engine │ └── Algorithm.java ├── mapserver ├── .dockerignore ├── .gitignore ├── Dockerfile.mapcache ├── Dockerfile.mapserver ├── README.md ├── docker-compose.yml ├── mapcache.xml ├── partition.py └── planwise.map ├── project.clj ├── resources ├── migrations │ ├── 001-extensions.up.sql │ ├── 002-ways_nodes.down.sql │ ├── 002-ways_nodes.up.sql │ ├── 003-facilities.down.sql │ ├── 003-facilities.up.sql │ ├── 004-preprocess_functions.down.sql │ ├── 004-preprocess_functions.up.sql │ ├── 005-projects.down.sql │ ├── 005-projects.up.sql │ ├── 006-facilities_type_column.down.sql │ ├── 006-facilities_type_column.up.sql │ ├── 007-users.down.sql │ ├── 007-users.up.sql │ ├── 008-regions.down.sql │ ├── 008-regions.up.sql │ ├── 009-add_region_to_projects.down.sql │ ├── 009-add_region_to_projects.up.sql │ ├── 010-tokens.down.sql │ ├── 010-tokens.up.sql │ ├── 011-facility_types.down.sql │ ├── 011-facility_types.up.sql │ ├── 012-add_facilities_count_to_projects.down.sql │ ├── 012-add_facilities_count_to_projects.up.sql │ ├── 013-process_single_facility.down.sql │ ├── 013-process_single_facility.up.sql │ ├── 014-projects_filters_and_stats.down.sql │ ├── 014-projects_filters_and_stats.up.sql │ ├── 015-apply_traffic_factor.down.sql │ ├── 015-apply_traffic_factor.up.sql │ ├── 016-add_preview_geom_to_regions.down.sql │ ├── 016-add_preview_geom_to_regions.up.sql │ ├── 017-facilities_polygons_index.down.sql │ ├── 017-facilities_polygons_index.up.sql │ ├── 018-add_owner_user_to_projects.down.sql │ ├── 018-add_owner_user_to_projects.up.sql │ ├── 019-add_id_to_facilities_polygons.down.sql │ ├── 019-add_id_to_facilities_polygons.up.sql │ ├── 020-facility_polygons_regions.down.sql │ ├── 020-facility_polygons_regions.up.sql │ ├── 021-facility_polygons_data.down.sql │ ├── 021-facility_polygons_data.up.sql │ ├── 022-add_population_to_regions.down.sql │ ├── 022-add_population_to_regions.up.sql │ ├── 023-add_facilities_polygon_constraint.down.sql │ ├── 023-add_facilities_polygon_constraint.up.sql │ ├── 024-add_facilities_processing_status.down.sql │ ├── 024-add_facilities_processing_status.up.sql │ ├── 025-add_max_population_to_regions.down.sql │ ├── 025-add_max_population_to_regions.up.sql │ ├── 026-datasets.down.sql │ ├── 026-datasets.up.sql │ ├── 027-dataset_references.down.sql │ ├── 027-dataset_references.up.sql │ ├── 028-project_share.down.sql │ ├── 028-project_share.up.sql │ ├── 029-add_raster_pixel_area_to_regions.down.sql │ ├── 029-add_raster_pixel_area_to_regions.up.sql │ ├── 030-facilities-type-id-contraint.down.sql │ ├── 030-facilities-type-id-contraint.up.sql │ ├── 031-add_import_job_to_datasets.down.sql │ ├── 031-add_import_job_to_datasets.up.sql │ ├── 032-add_projects_state.down.sql │ ├── 032-add_projects_state.up.sql │ ├── 033-add_capacity_to_facilities.down.sql │ ├── 033-add_capacity_to_facilities.up.sql │ ├── 034-add_code_to_facility_types.down.sql │ ├── 034-add_code_to_facility_types.up.sql │ ├── 035-datasets2.down.sql │ ├── 035-datasets2.up.sql │ ├── 036-projects2.down.sql │ ├── 036-projects2.up.sql │ ├── 037-add-config-to-projects2.down.sql │ ├── 037-add-config-to-projects2.up.sql │ ├── 038-add_coverage_to_datasets2.down.sql │ ├── 038-add_coverage_to_datasets2.up.sql │ ├── 039-sites2_coverage.down.sql │ ├── 039-sites2_coverage.up.sql │ ├── 040-population_sources.down.sql │ ├── 040-population_sources.up.sql │ ├── 041-populations.down.sql │ ├── 041-populations.up.sql │ ├── 042-add-dataset-id-to-projects2.down.sql │ ├── 042-add-dataset-id-to-projects2.up.sql │ ├── 043-add-population-source-id-to-projects2.down.sql │ ├── 043-add-population-source-id-to-projects2.up.sql │ ├── 044-add_projects2_state.down.sql │ ├── 044-add_projects2_state.up.sql │ ├── 045-scenarios.down.sql │ ├── 045-scenarios.up.sql │ ├── 046-add-region-id-to-projects2.down.sql │ ├── 046-add-region-id-to-projects2.up.sql │ ├── 047-add_dataset_version_to_projects2.down.sql │ ├── 047-add_dataset_version_to_projects2.up.sql │ ├── 048-add_state_to_scenarios.down.sql │ ├── 048-add_state_to_scenarios.up.sql │ ├── 049-add_engine_config_to_projects2.down.sql │ ├── 049-add_engine_config_to_projects2.up.sql │ ├── 050-add_deleted_at_to_projects2.down.sql │ ├── 050-add_deleted_at_to_projects2.up.sql │ ├── 051-rename_datasets_to_providers_set.down.sql │ ├── 051-rename_datasets_to_providers_set.up.sql │ ├── 052-add_sources_set.down.sql │ ├── 052-add_sources_set.up.sql │ ├── 053-add_sources.down.sql │ ├── 053-add_sources.up.sql │ ├── 054-add_providers_data_to_scenarios.down.sql │ ├── 054-add_providers_data_to_scenarios.up.sql │ ├── 055-update-population-source-id-to-projects2.down.sql │ ├── 055-update-population-source-id-to-projects2.up.sql │ ├── 056-add_owner_to_sources.down.sql │ ├── 056-add_owner_to_sources.up.sql │ ├── 057-nullable_raster_file_sources.down.sql │ ├── 057-nullable_raster_file_sources.up.sql │ ├── 058-add_sources_data_to_scenarios.down.sql │ ├── 058-add_sources_data_to_scenarios.up.sql │ ├── 059-add_new_providers_geom_to_scenarios.down.sql │ ├── 059-add_new_providers_geom_to_scenarios.up.sql │ ├── 060-add_error_message_to_scenarios.down.sql │ ├── 060-add_error_message_to_scenarios.up.sql │ ├── 061-remove_deleted_at_from_projects2.down.sql │ ├── 061-remove_deleted_at_from_projects2.up.sql │ ├── 062-coverage_contexts.down.sql │ ├── 062-coverage_contexts.up.sql │ ├── 063-add_geo_coverage_to_scenarios.down.sql │ ├── 063-add_geo_coverage_to_scenarios.up.sql │ ├── 064-add_coverage_to_projects2.down.sql │ ├── 064-add_coverage_to_projects2.up.sql │ ├── 065-add_population_under_coverage_to_scenarios.down.sql │ ├── 065-add_population_under_coverage_to_scenarios.up.sql │ ├── 066-rename_investment_to_effort.down.sql │ ├── 066-rename_investment_to_effort.up.sql │ ├── 067-add_providers_set_version_index.down.sql │ ├── 067-add_providers_set_version_index.up.sql │ ├── 068-add_regions_name_index.down.sql │ └── 068-add_regions_name_index.up.sql ├── planwise │ ├── cli.edn │ ├── config.edn │ ├── errors │ │ ├── 403.html │ │ ├── 404.html │ │ └── 500.html │ ├── plpgsql │ │ ├── coverage.sql │ │ ├── fix-isolated.sql │ │ ├── functions.sql │ │ ├── isochrones.sql │ │ ├── patches.sql │ │ └── regions-preview.sql │ ├── prod.edn │ ├── public │ │ ├── favicon.ico │ │ ├── images │ │ │ ├── illustration.svg │ │ │ ├── logo-transparent.png │ │ │ ├── logo.png │ │ │ ├── planwise-icon.svg │ │ │ ├── project-background1@2x.png │ │ │ ├── project-thumbnail1@2x.png │ │ │ ├── project-thumbnail2@2x.png │ │ │ └── project-thumbnail3@2x.png │ │ ├── js │ │ │ └── leaflet.ext.js │ │ ├── providers-sample.csv │ │ ├── robots.txt │ │ └── sources-sample.csv │ └── sql │ │ ├── coverage │ │ ├── coverage.sql │ │ ├── friction.sql │ │ ├── pgrouting.sql │ │ └── simple.sql │ │ ├── projects2.sql │ │ ├── providers_set.sql │ │ ├── regions.sql │ │ ├── scenarios.sql │ │ ├── sources.sql │ │ └── users.sql └── svg │ ├── README.md │ ├── icons.svg │ └── icons │ ├── ambulance.svg │ ├── arrow-back.svg │ ├── arrow-down.svg │ ├── arrow-up.svg │ ├── box.svg │ ├── bulb.svg │ ├── bus.svg │ ├── car.svg │ ├── check-circle-wizard.svg │ ├── check-circle.svg │ ├── close.svg │ ├── cross-circle.svg │ ├── delete.svg │ ├── demographics.svg │ ├── download.svg │ ├── error.svg │ ├── exit.svg │ ├── filter.svg │ ├── hospital.svg │ ├── key-arrow-down.svg │ ├── key-arrow-left.svg │ ├── key-arrow-right.svg │ ├── location.svg │ ├── logo.svg │ ├── logo2.svg │ ├── mail-outline.svg │ ├── pickup.svg │ ├── portfolio.svg │ ├── refresh.svg │ ├── remove-circle.svg │ ├── remove.svg │ ├── repair.svg │ ├── scenario.svg │ ├── search.svg │ ├── share.svg │ ├── signout.svg │ ├── transport-means.svg │ ├── walk.svg │ └── warning.svg ├── scripts ├── Dockerfile ├── bootstrap-dev.sh ├── build-binaries ├── crop-source-raster ├── dev ├── friction │ ├── README.md │ └── load-friction-raster ├── geojson │ ├── README.md │ ├── gadm2geojson │ ├── gadm2geojson.js │ ├── package-lock.json │ └── package.json ├── migrate ├── population │ ├── README.md │ ├── load-regions │ └── scripts-design.svg ├── raster-pixel-size ├── resize-raster └── tools │ ├── README.md │ ├── docker-entrypoint.sh │ ├── readme │ ├── tools │ ├── update-region-previews │ └── update-source-sets ├── src └── planwise │ ├── auth.clj │ ├── auth │ └── guisso.clj │ ├── boundary │ ├── auth.clj │ ├── coverage.clj │ ├── engine.clj │ ├── file_store.clj │ ├── jobrunner.clj │ ├── mailer.clj │ ├── maps.clj │ ├── projects2.clj │ ├── providers_set.clj │ ├── regions.clj │ ├── runner.clj │ ├── scenarios.clj │ ├── sources.clj │ └── users.clj │ ├── component │ ├── auth.clj │ ├── coverage.clj │ ├── coverage │ │ ├── friction.clj │ │ ├── greedy_search.clj │ │ ├── rasterize.clj │ │ └── simple.clj │ ├── engine.clj │ ├── file_store.clj │ ├── jobrunner.clj │ ├── mailer.clj │ ├── maps.clj │ ├── projects2.clj │ ├── providers_set.clj │ ├── regions.clj │ ├── runner.clj │ ├── scenarios.clj │ ├── sources.clj │ ├── taskmaster.clj │ └── users.clj │ ├── config.clj │ ├── configuration │ └── templates.clj │ ├── database.clj │ ├── endpoint │ ├── auth.clj │ ├── coverage.clj │ ├── home.clj │ ├── monitor.clj │ ├── projects2.clj │ ├── providers_set.clj │ ├── regions.clj │ ├── scenarios.clj │ └── sources.clj │ ├── engine │ ├── common.clj │ ├── demand.clj │ ├── raster.clj │ └── suggestions.clj │ ├── main.clj │ ├── middleware.clj │ ├── model │ ├── ident.clj │ ├── project_review.cljc │ ├── projects2.clj │ ├── providers.clj │ ├── scenarios.clj │ └── users.clj │ ├── router.clj │ ├── tasks │ ├── build_icons.clj │ └── recompute_scenarios.clj │ └── util │ ├── collections.clj │ ├── files.clj │ ├── geo.clj │ ├── hash.clj │ ├── numbers.clj │ ├── ring.clj │ └── str.clj └── test ├── planwise ├── boundary │ └── coverage_test.clj ├── component │ ├── jobrunner_test.clj │ ├── providers_set_test.clj │ ├── regions_test.clj │ ├── scenarios_test.clj │ ├── taskmaster_test.clj │ └── users_test.clj ├── endpoint │ └── home_test.clj ├── engine │ └── raster_test.clj ├── model │ └── scenarios_test.clj ├── test_system.clj └── test_utils.clj └── resources ├── other-sites.csv ├── pointe-noire-byte.tif ├── pointe-noire-float.tif ├── sites.csv └── test.edn /.dockerignore: -------------------------------------------------------------------------------- 1 | data 2 | mapserver 3 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | test: 7 | runs-on: ubuntu-22.04 8 | steps: 9 | - uses: actions/checkout@v4 10 | 11 | - name: Set environment up 12 | run: | 13 | mv docker-compose.ci.yml docker-compose.override.yml 14 | docker compose pull 15 | docker compose up --wait db 16 | docker compose build 17 | docker compose exec db createdb planwise-test -U postgres 18 | docker compose run --rm client npm install 19 | 20 | - name: Run specs 21 | env: 22 | JVM_OPTS: -Xmx3200m 23 | run: | 24 | docker compose up --wait db 25 | docker compose run --rm -e JVM_OPTS app lein test 26 | docker compose run --rm client npm run test 27 | docker compose run --rm app lein check-format 28 | 29 | build: 30 | needs: test 31 | runs-on: ubuntu-22.04 32 | env: 33 | DOCKER_REPOSITORY: 'instedd/planwise' 34 | DOCKER_USER: ${{ secrets.DOCKER_USER }} 35 | DOCKER_PASS: ${{ secrets.DOCKER_PASS }} 36 | steps: 37 | - uses: actions/checkout@v4 38 | - name: Build image & push to Docker Hub 39 | run: ./build.sh 40 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /logs 3 | /classes 4 | /checkouts 5 | /data 6 | pom.xml 7 | pom.xml.asc 8 | *.jar 9 | *.class 10 | /.env 11 | /.docker-env 12 | /.lein-* 13 | /.nrepl-port 14 | 15 | /.dir-locals.el 16 | /client/.dir-locals.el 17 | /profiles.clj 18 | /dev/src/local.clj 19 | /dev/resources/local.edn 20 | 21 | *.pyc 22 | .idea 23 | resources/planwise/public/data/ 24 | /mapserver/data/* 25 | 26 | /cpp/*.dSYM 27 | /cpp/*.o 28 | /cpp/build-*/ 29 | 30 | /docker-compose.override.yml 31 | 32 | node_modules 33 | /scripts/population/target 34 | /scripts/population/data 35 | /client/.shadow-cljs 36 | /client/target 37 | .clj-kondo/ 38 | .lsp/ -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM clojure:lein-2.8.1 AS base 2 | 3 | RUN echo 'deb http://archive.debian.org/debian stretch main\n\ 4 | deb http://archive.debian.org/debian-security stretch/updates main' > /etc/apt/sources.list 5 | 6 | RUN apt update && \ 7 | apt install -y build-essential cmake \ 8 | libboost-timer-dev libboost-program-options-dev \ 9 | libboost-filesystem-dev \ 10 | libpq-dev libgdal-dev postgresql-client libpq-dev \ 11 | gdal-bin python-gdal libgdal-java \ 12 | && \ 13 | curl -sL https://deb.nodesource.com/setup_9.x | bash - && \ 14 | apt-get install -y nodejs 15 | 16 | WORKDIR /app 17 | 18 | FROM base as build 19 | 20 | COPY . /app 21 | 22 | RUN cd client && npm install && npm run release 23 | RUN lein uberjar 24 | RUN scripts/build-binaries --release 25 | 26 | FROM openjdk:8u242-jre-stretch 27 | 28 | RUN echo 'deb http://archive.debian.org/debian stretch main\n\ 29 | deb http://archive.debian.org/debian-security stretch/updates main' > /etc/apt/sources.list 30 | 31 | # Install package dependencies and add precompiled binary 32 | RUN apt-get update && \ 33 | apt-get -y install postgresql-client gdal-bin python-gdal libgdal-java && \ 34 | apt-get clean && \ 35 | rm -rf /var/lib/apt/lists/* 36 | 37 | # Add scripts 38 | COPY --from=build /app/scripts/ /app/scripts/ 39 | ENV SCRIPTS_PATH /app/scripts/ 40 | 41 | # Add project compiled binaries 42 | COPY --from=build /app/cpp/build-linux-x86_64/aggregate-population /app/bin/aggregate-population 43 | COPY --from=build /app/cpp/build-linux-x86_64/walking-coverage /app/bin/walking-coverage 44 | ENV BIN_PATH /app/bin/ 45 | 46 | # Add uberjar with app 47 | COPY --from=build /app/target/uberjar/planwise-standalone.jar /app/lib/ 48 | ENV JAR_PATH /app/lib/planwise-standalone.jar 49 | 50 | # Add app version file 51 | COPY --from=build /app/resources/planwise/version /app/VERSION 52 | 53 | # Expose JNI libs to app 54 | ENV LD_LIBRARY_PATH=/usr/lib/jni 55 | 56 | # Exposed port 57 | ENV PORT 80 58 | EXPOSE $PORT 59 | 60 | # Data and tmp folders 61 | ENV DATA_PATH /data/ 62 | ENV TMP_PATH /tmp/ 63 | 64 | CMD ["java", "-jar", "/app/lib/planwise-standalone.jar"] 65 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -eo pipefail 3 | 4 | source <(curl -s https://raw.githubusercontent.com/manastech/ci-docker-builder/d3406587def914918666ef41c0637d6b739fdf7d/build.sh) 5 | 6 | dockerSetup 7 | echo $VERSION > VERSION 8 | echo $VERSION > resources/planwise/version 9 | 10 | if [[ -z "$DOCKER_TAG" ]]; then 11 | echo "Not building because DOCKER_TAG is undefined" 12 | exit 0 13 | fi 14 | 15 | dockerBuildAndPush 16 | dockerBuildAndPush -d mapserver -s "-mapserver" -o "-f mapserver/Dockerfile.mapserver" 17 | dockerBuildAndPush -d mapserver -s "-mapcache" -o "-f mapserver/Dockerfile.mapcache" 18 | dockerBuildAndPush -d scripts -s "-tools" 19 | -------------------------------------------------------------------------------- /client/README.md: -------------------------------------------------------------------------------- 1 | # Development 2 | 3 | 1. Run `npm install` 4 | 2. To start a watch process with Hot Code Reload, run `npm run watch` 5 | 6 | * Running `npm run build` will rebuild both CSS and JS files for development 7 | * Running `npm run build-sass` will rebuild the CSS only 8 | 9 | 3. (optional) Connect CIDER with `cider-connect-cljs`, selecting connection type `shadow` and build `:app` 10 | -------------------------------------------------------------------------------- /client/dev/src/build.clj: -------------------------------------------------------------------------------- 1 | (ns build 2 | (:require [hawk.core :as hawk] 3 | [shadow.cljs.devtools.api :as shadow] 4 | [build.sass :as sass])) 5 | 6 | (def sass-options 7 | {:source-paths ["resources/sass"] 8 | :output-path "target/planwise/public/css" 9 | :include-paths ["node_modules"] 10 | :output-style :nested 11 | :source-map? true}) 12 | 13 | (defn sass 14 | [] 15 | (sass/build-all 16 | (merge sass-options {:output-style :nested}))) 17 | 18 | (defn sass-release 19 | [] 20 | (sass/build-all 21 | (merge sass-options {:output-style :compressed}))) 22 | 23 | (defn watch-sass 24 | {:shadow/requires-server true} 25 | [] 26 | (hawk/watch! [{:paths ["resources/sass"] 27 | :filter hawk/file? 28 | :handler (fn [ctx e] (sass))}])) 29 | 30 | (defn watch 31 | {:shadow/requires-server true} 32 | [] 33 | (sass) 34 | (watch-sass) 35 | (shadow/watch :app)) 36 | 37 | (defn build 38 | [] 39 | (sass) 40 | (shadow/compile :app)) 41 | 42 | (defn release 43 | [] 44 | (sass-release) 45 | (shadow/release :app)) 46 | -------------------------------------------------------------------------------- /client/dev/src/dev/user.cljs: -------------------------------------------------------------------------------- 1 | (ns dev.user 2 | (:require [schema.core :as s] 3 | [re-frame.core :as rf] 4 | [planwise.client.core :as client])) 5 | 6 | ;; Enable Schema validations client-side 7 | (s/set-fn-validation! true) 8 | 9 | (js/console.info "Starting in development mode") 10 | 11 | (enable-console-print!) 12 | 13 | (defn ^:dev/after-load remount 14 | [] 15 | (rf/clear-subscription-cache!) 16 | (client/mount-root)) 17 | -------------------------------------------------------------------------------- /client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "build-sass": "shadow-cljs clj-run build/sass", 4 | "build": "shadow-cljs clj-run build/build", 5 | "watch": "shadow-cljs clj-run build/watch", 6 | "release": "shadow-cljs clj-run build/release", 7 | "test": "shadow-cljs compile test && node target/test/node-tests.js" 8 | }, 9 | "dependencies": { 10 | "create-react-class": "^15.7.0", 11 | "highlight.js": "10.4.1", 12 | "react": "16.8.6", 13 | "react-dom": "16.8.6", 14 | "react-flip-move": "^3.0.3", 15 | "react-highlight.js": "1.0.7", 16 | "react-intercom": "^1.0.15", 17 | "rmwc": "1.4.1" 18 | }, 19 | "devDependencies": { 20 | "shadow-cljs": "^2.8.58" 21 | }, 22 | "license": "GPL-3.0" 23 | } 24 | -------------------------------------------------------------------------------- /client/resources/sass/_grid.scss: -------------------------------------------------------------------------------- 1 | /* ============================================================================= 2 | GRID 3 | ============================================================================= */ 4 | 5 | .grid { 6 | display: grid; 7 | position: relative; 8 | grid-gap: 3rem; 9 | &.x2 { 10 | grid-template-columns: 1fr 1fr; 11 | grid-template-areas: "c1 c2"; 12 | } 13 | &.x3 { 14 | grid-template-columns: repeat(3, 1fr); 15 | grid-template-areas: "c1 c2 c3"; 16 | } 17 | &.x4 { 18 | grid-template-columns: repeat(4, 1fr); 19 | grid-template-areas: "c1 c2 c3 c4"; 20 | } 21 | &.centered { 22 | justify-content: center; 23 | } 24 | &.v-centered { 25 | align-items: center; 26 | height: 100%; 27 | } 28 | .section { 29 | padding-left: 0; 30 | padding-right: 0; 31 | } 32 | } 33 | 34 | .cell { 35 | padding: .5rem 0; 36 | &.span2 { 37 | grid-area: auto / auto / auto / span 2; 38 | } 39 | &.span3 { 40 | grid-area: auto / auto / auto / span 3; 41 | } 42 | &.span4 { 43 | grid-area: auto / auto / auto / span 4; 44 | } 45 | } 46 | 47 | -------------------------------------------------------------------------------- /client/resources/sass/_variables.scss: -------------------------------------------------------------------------------- 1 | /* ============================================================================= 2 | VARIABLES 3 | ============================================================================= */ 4 | 5 | @import url('https://fonts.googleapis.com/css?family=Roboto:300,400,500,700'); 6 | @import url('https://fonts.googleapis.com/icon?family=Material+Icons'); 7 | 8 | 9 | 10 | $sans-family: "Roboto", sans-serif; 11 | 12 | $primary-color: #FF5722; 13 | $secondary-color: #5D5D5D; 14 | 15 | $black: #000000; 16 | $white: #FFFFFF; 17 | 18 | $gray: #9E9E9E; 19 | $gray-medium: #cdcdcd; 20 | $gray-light: #E5E5E5; 21 | $gray-lighten: #f1f1f1; 22 | $gray-dark: #424242; 23 | $gray-darker: #333333; 24 | $blue-link: #2196F3; 25 | 26 | $btn-outline: $primary-color; 27 | $btn-outline-dark: darken($primary-color, 10%); 28 | 29 | $header: 100px; 30 | $side-padd: 5%; 31 | 32 | $error-red: #F1453D; 33 | -------------------------------------------------------------------------------- /client/resources/sass/landing.scss: -------------------------------------------------------------------------------- 1 | @import 'variables'; 2 | @import 'reset'; 3 | @import 'grid'; 4 | @import 'layout'; 5 | @import 'global'; 6 | @import 'responsive'; -------------------------------------------------------------------------------- /client/shadow-cljs.edn: -------------------------------------------------------------------------------- 1 | ;; shadow-cljs configuration 2 | {:source-paths 3 | ["dev/src" 4 | "src" 5 | "../common/src" 6 | "test"] 7 | 8 | :dependencies 9 | [[reagent "0.8.1" 10 | :exclusions [cljsjs/react cljsjs/react-dom]] 11 | [reagent-forms "0.5.43"] 12 | [reagent-utils "0.3.3"] 13 | [re-frame "0.10.9"] 14 | [re-com "2.1.0"] 15 | [day8.re-frame/http-fx "0.1.5"] 16 | [crate "0.2.4"] 17 | [cljs-ajax "0.7.3"] 18 | [clj-commons/secretary "1.2.4"] 19 | [venantius/accountant "0.2.4"] 20 | [prismatic/schema "1.1.7"] 21 | [day8.re-frame/re-frame-10x "0.5.2" 22 | :exclusions [cljsjs/react cljsjs/react-dom]] 23 | [binaryage/devtools "0.9.10"] 24 | 25 | [io.bit3/jsass "5.5.6"] 26 | [org.slf4j/slf4j-nop "1.7.25"] 27 | 28 | [cider/cider-nrepl "0.26.0"]] 29 | 30 | :http 31 | {:port 9630} 32 | 33 | :nrepl 34 | {:middleware [cider.nrepl/cider-middleware 35 | cider.piggieback/wrap-cljs-repl]} 36 | 37 | :builds 38 | {:app {:target :browser 39 | :output-dir "target/planwise/public/js" 40 | :asset-path "/js" 41 | :modules {:main {:entries [planwise.client.core]}} 42 | :compiler-options {:externs ["resources/js/externs.js"]} 43 | :dev {:closure-defines {"re_frame.trace.trace_enabled_QMARK_" true}} 44 | :devtools {:watch-dir "target/planwise/public/css" 45 | :watch-path "/css" 46 | :preloads [dev.user 47 | day8.re-frame-10x.preload]}} 48 | 49 | :test {:target :node-test 50 | :output-to "target/test/node-tests.js"}}} 51 | -------------------------------------------------------------------------------- /client/src/leaflet/controls.cljs: -------------------------------------------------------------------------------- 1 | (ns leaflet.controls 2 | (:require [crate.core :as crate])) 3 | 4 | 5 | (def ^:private MapboxLogo 6 | (js/L.Control.extend 7 | #js {:onAdd #(crate/html [:a.mapbox-logo 8 | {:href "https://www.mapbox.com/about/maps" 9 | :target "_blank"} 10 | "MapBox"])})) 11 | 12 | (defn- mapbox-logo 13 | [options] 14 | (new MapboxLogo (clj->js (merge {:position "topright"} options)))) 15 | 16 | (defn- reference-table-content 17 | [{:keys [hide-actions?] :as options}] 18 | (crate/html 19 | [:div.map-reference-table 20 | (when-not hide-actions? 21 | [:div 22 | [:h1 "Actions"] 23 | [:ul 24 | [:li [:i.material-icons "domain"] "New provider"] 25 | [:li [:i.material-icons "arrow_upward"] "Upgrade provider"] 26 | [:li [:i.material-icons "add"] "Increase capacity"]] 27 | [:hr]]) 28 | [:h1 "Provider capacity"] 29 | [:ul 30 | [:li [:div.marker-provider-icon.idle-capacity] "Excess"] 31 | [:li [:div.marker-provider-icon.at-capacity] "Covered"] 32 | [:li [:div.marker-provider-icon.unsatisfied] "Shortage"]] 33 | [:hr] 34 | [:h1 "Unsatisfied demand"] 35 | [:ul.scale 36 | [:li.q1 "Q1"] 37 | [:li.q2 "Q2"] 38 | [:li.q3 "Q3"] 39 | [:li.q4 "Q4"]]])) 40 | 41 | (def ^:private ReferenceTable 42 | (js/L.Control.extend 43 | #js {:onAdd (fn [] 44 | (let [options (js->clj (.-options (js-this)) :keywordize-keys true)] 45 | (reference-table-content options)))})) 46 | 47 | (defn- reference-table 48 | [options] 49 | (new ReferenceTable (clj->js (merge {:position "bottomright"} options)))) 50 | 51 | 52 | (defn create-control 53 | [control-def] 54 | (let [[type props] (if (vector? control-def) control-def [control-def {}])] 55 | (case type 56 | :zoom (.zoom js/L.control) 57 | :attribution (.attribution js/L.control #js {:prefix false}) 58 | :mapbox-logo (mapbox-logo props) 59 | :reference-table (reference-table props) 60 | (throw (ex-info "Invalid control type" control-def))))) 61 | -------------------------------------------------------------------------------- /client/src/planwise/client/api.cljs: -------------------------------------------------------------------------------- 1 | (ns planwise.client.api 2 | (:require [ajax.core :as ajax] 3 | [re-frame.core :as rf] 4 | [clojure.string :as string] 5 | [planwise.client.config :as config])) 6 | 7 | ;; Set default interceptor for adding CSRF token to all non-GET requests 8 | 9 | (def csrf-token 10 | (atom (.-value (.getElementById js/document "__anti-forgery-token")))) 11 | 12 | (def csrf-token-interceptor 13 | (ajax/to-interceptor {:name "CSRF Token Interceptor" 14 | :request (fn [req] 15 | (if (not= "GET" (:method req)) 16 | (assoc-in req [:headers "X-CSRF-Token"] @csrf-token) 17 | req))})) 18 | 19 | ;; Add interceptor to send JWT encrypted token authentication with all requests 20 | 21 | (def jwe-token 22 | (atom config/jwe-token)) 23 | 24 | (def jwe-token-interceptor 25 | (ajax/to-interceptor {:name "JWE Token Interceptor" 26 | :request (fn [req] 27 | (let [auth-value (str "Token " @jwe-token)] 28 | (assoc-in req [:headers "Authorization"] auth-value)))})) 29 | 30 | (swap! ajax/default-interceptors into [csrf-token-interceptor jwe-token-interceptor]) 31 | 32 | 33 | ;; Default event handlers for http-xhrio/api effects 34 | 35 | (rf/reg-event-fx 36 | :http-no-on-success 37 | (fn [_ [_ data]] 38 | (rf/console :log "Unhandled successful API response: " data))) 39 | 40 | (rf/reg-event-fx 41 | :http-no-on-failure 42 | (fn [_ [_ {:keys [status] :as response}]] 43 | (rf/console :error (str "Got HTTP response " status ":") response))) 44 | 45 | 46 | ;; Debugging utility functions 47 | 48 | (defn truncate 49 | ([s] 50 | (truncate s 200)) 51 | ([s max-length] 52 | (let [s (str s)] 53 | (if (> (count s) max-length) 54 | (str (subs s 0 (- max-length 3)) "...") 55 | s)))) 56 | 57 | 58 | ;; Authentication APIs 59 | 60 | (def signout 61 | {:method :delete 62 | :uri "/logout"}) 63 | -------------------------------------------------------------------------------- /client/src/planwise/client/asdf.cljs: -------------------------------------------------------------------------------- 1 | (ns planwise.client.asdf 2 | [:refer-clojure :exclude [reset! swap! update]] 3 | [:require [schema.core :as s]]) 4 | 5 | (s/defschema AsdfState (s/enum :invalid :reloading :valid)) 6 | 7 | (s/defrecord AsdfBase 8 | [state :- AsdfState 9 | value :- s/Any]) 10 | 11 | (defn Asdf 12 | [value-schema] 13 | {:state AsdfState 14 | :value value-schema}) 15 | 16 | (defn new 17 | [value] 18 | (map->AsdfBase {:state :invalid :value value})) 19 | 20 | (defn value 21 | [asdf] 22 | (:value asdf)) 23 | 24 | (defn state 25 | [asdf] 26 | (:state asdf)) 27 | 28 | (defn valid? 29 | [asdf] 30 | (= :valid (:state asdf))) 31 | 32 | (defn reloading? 33 | [asdf] 34 | (= :reloading (:state asdf))) 35 | 36 | (defn should-reload? 37 | [asdf] 38 | (= :invalid (:state asdf))) 39 | 40 | (defn reset! 41 | [asdf value] 42 | (assoc asdf 43 | :state :valid 44 | :value value)) 45 | 46 | (defn swap! 47 | [asdf swap-fn & args] 48 | (assoc asdf 49 | :state :valid 50 | :value (apply swap-fn (:value asdf) args))) 51 | 52 | (defn invalidate! 53 | ([asdf] 54 | (assoc asdf :state :invalid)) 55 | ([asdf update-fn & args] 56 | (assoc asdf 57 | :state :invalid 58 | :value (apply update-fn (:value asdf) args)))) 59 | 60 | (defn reload! 61 | [asdf] 62 | (assoc asdf :state :reloading)) 63 | 64 | (defn update 65 | "Updates the value in the asdf preserving its current state" 66 | [asdf update-fn & args] 67 | (assoc asdf :value (apply update-fn (:value asdf) args))) 68 | -------------------------------------------------------------------------------- /client/src/planwise/client/components/common.cljs: -------------------------------------------------------------------------------- 1 | (ns planwise.client.components.common) 2 | 3 | ;; SVG based icons 4 | 5 | (defn icon [icon-name & [icon-class]] 6 | (let [icon-name (some-> icon-name name) 7 | icon-class (or icon-class "icon")] 8 | [:svg {:class icon-class 9 | ; Support for :xlinkHref is present in React 0.14+, but reagent 0.5.1 is bundled with 0.13 10 | ; Change the dangerouslySetInnerHTML call to the following content after upgrading 11 | ; [:use {:xlinkHref (str "#icon-" icon-name)}]]) 12 | :dangerouslySetInnerHTML {:__html (str "")}}])) 13 | 14 | -------------------------------------------------------------------------------- /client/src/planwise/client/config.cljs: -------------------------------------------------------------------------------- 1 | (ns planwise.client.config 2 | (:require [planwise.model.project])) 3 | 4 | (def global-config 5 | (aget js/window "_CONFIG")) 6 | 7 | (def jwe-token 8 | (aget global-config "jwe-token")) 9 | 10 | (def user-email 11 | (aget global-config "identity")) 12 | 13 | (def mapserver-url 14 | (aget global-config "mapserver-url")) 15 | 16 | (def app-version 17 | (aget global-config "app-version")) 18 | 19 | (def intercom-app-id 20 | (aget global-config "intercom-app-id")) 21 | -------------------------------------------------------------------------------- /client/src/planwise/client/core.cljs: -------------------------------------------------------------------------------- 1 | (ns planwise.client.core 2 | (:require-macros [secretary.core :refer [defroute]]) 3 | (:require [reagent.core :as reagent :refer [atom]] 4 | [re-frame.core :as rf] 5 | [secretary.core :as secretary] 6 | [accountant.core :as accountant] 7 | 8 | [planwise.client.effects] 9 | [planwise.client.routes] 10 | [planwise.client.handlers] 11 | [planwise.client.subs] 12 | [planwise.client.views :as views])) 13 | 14 | 15 | ;; ------------------------- 16 | ;; Initialize app 17 | 18 | (defn mount-root [] 19 | (reagent/render [views/planwise-app] 20 | (.getElementById js/document "app"))) 21 | 22 | (defn- ^:export main [] 23 | (rf/dispatch-sync [:initialise-db]) 24 | (accountant/configure-navigation! 25 | {:nav-handler 26 | (fn [path] 27 | (secretary/dispatch! path)) 28 | :path-exists? 29 | (fn [path] 30 | (secretary/locate-route path))}) 31 | (accountant/dispatch-current!) 32 | (mount-root)) 33 | -------------------------------------------------------------------------------- /client/src/planwise/client/db.cljs: -------------------------------------------------------------------------------- 1 | (ns planwise.client.db 2 | (:require [planwise.client.sources.db :as sources] 3 | [planwise.client.projects2.db :as projects2] 4 | [planwise.client.scenarios.db :as scenarios] 5 | [planwise.client.providers-set.db :as providers-set])) 6 | 7 | (def initial-db 8 | {;; Navigation (current page) 9 | :current-page :home 10 | ;; Map of navigation params (ie. :page, :id, :section, etc) 11 | :page-params {} 12 | 13 | ;; Projects2 14 | :projects2 projects2/initial-db 15 | 16 | ;; Regions id => {:keys [id name admin-level & [geojson preview-geojson]]} 17 | :regions {} 18 | 19 | ;; Providers Set 20 | :providers-set providers-set/initial-db 21 | 22 | :coverage {} 23 | 24 | :sources sources/initial-db 25 | 26 | :scenarios scenarios/initial-db}) 27 | -------------------------------------------------------------------------------- /client/src/planwise/client/handlers.cljs: -------------------------------------------------------------------------------- 1 | (ns planwise.client.handlers 2 | (:require [planwise.client.db :as db] 3 | [planwise.client.api :as api] 4 | [planwise.client.routes :as routes] 5 | [planwise.client.projects2.handlers] 6 | [planwise.client.providers-set.handlers] 7 | [planwise.client.sources.handlers] 8 | [planwise.client.scenarios.handlers] 9 | [planwise.client.coverage] 10 | [planwise.client.regions.handlers :as regions] 11 | [re-frame.core :as rf])) 12 | 13 | ;; Event handlers 14 | ;; ----------------------------------------------------------------------- 15 | 16 | (rf/reg-event-fx 17 | :initialise-db 18 | (fn [_ _] 19 | {:dispatch-n [[:regions/load-regions] 20 | [:coverage/load-algorithms]] 21 | :db db/initial-db})) 22 | 23 | (rf/reg-event-fx 24 | :navigate 25 | (fn [{:keys [db]} [_ {page :page, :as params}]] 26 | {:db (assoc db :current-page page :page-params params) 27 | :route-change params})) 28 | 29 | (rf/reg-event-fx 30 | :signout 31 | (fn [_ [_]] 32 | {:api (assoc api/signout 33 | :on-success [:after-signout]) 34 | :intercom-logout true})) 35 | 36 | (rf/reg-event-fx 37 | :after-signout 38 | (fn [_ [_ data]] 39 | (let [url (or (:redirect-to data) (routes/home))] 40 | {:location url}))) 41 | 42 | (rf/reg-event-fx 43 | :message-posted 44 | (fn [_ [_ message]] 45 | (cond 46 | (#{"react-devtools-content-script" 47 | "react-devtools-bridge" 48 | "react-devtools-detector"} 49 | (aget message "source")) 50 | nil ; ignore React dev tools messages 51 | 52 | true 53 | (rf/console :warn "Invalid message received " message)))) 54 | -------------------------------------------------------------------------------- /client/src/planwise/client/modal/modal.cljs: -------------------------------------------------------------------------------- 1 | (ns planwise.client.modal.modal 2 | (:require [re-frame.core :as rf] 3 | [planwise.client.ui.common :as ui] 4 | [planwise.client.components.common2 :as common2] 5 | [planwise.client.ui.rmwc :as m])) 6 | 7 | (def in-modal-path (rf/path [:modal-window])) 8 | 9 | ;; ---------------------------------------------------------------------------- 10 | ;; Subscription 11 | (rf/reg-sub 12 | :modal/state 13 | (fn [db _] 14 | (get-in db [:modal-window :state]))) 15 | 16 | ;; ---------------------------------------------------------------------------- 17 | ;; Handlers 18 | (rf/reg-event-db 19 | :modal/show 20 | in-modal-path 21 | (fn [db _] 22 | (assoc-in db [:state] {:open? true}))) 23 | 24 | (rf/reg-event-db 25 | :modal/hide 26 | in-modal-path 27 | (fn [db _] 28 | (assoc-in db [:state] {:open? false}))) 29 | 30 | ;; ---------------------------------------------------------------------------- 31 | ;; View 32 | (defn modal-view 33 | [attrs content] 34 | (fn [attrs _] 35 | (let [state @(rf/subscribe [:modal/state])] 36 | [m/Dialog {:open (:open? state)} 37 | [m/DialogSurface 38 | [m/DialogHeader 39 | [m/DialogHeaderTitle (:title attrs)]] 40 | [m/DialogBody content] 41 | [m/DialogFooter 42 | [m/DialogFooterButton {:on-click (fn [] ((:cancel-fn attrs)))} 43 | "Cancel"] 44 | [m/DialogFooterButton {:on-click (fn [] ((:accept-fn attrs))) 45 | :disabled (not (:accept-enabled? attrs))} 46 | (:accept-label attrs)]]]]))) 47 | -------------------------------------------------------------------------------- /client/src/planwise/client/projects2/api.cljs: -------------------------------------------------------------------------------- 1 | (ns planwise.client.projects2.api) 2 | 3 | ;; ---------------------------------------------------------------------------- 4 | ;; API methods 5 | 6 | (defn- create-project! 7 | [defaults] 8 | {:method :post 9 | :section :index 10 | :params {:project defaults} 11 | :uri "/api/projects2"}) 12 | 13 | (defn- list-projects 14 | [] 15 | {:method :get 16 | :section :index 17 | :uri (str "/api/projects2")}) 18 | 19 | (defn- list-templates 20 | [] 21 | {:method :get 22 | :section :index 23 | :uri (str "/api/projects2/templates")}) 24 | 25 | (defn- update-project 26 | [project-id project] 27 | {:method :put 28 | :section :show 29 | :params {:project project} 30 | :uri (str "/api/projects2/" project-id)}) 31 | 32 | (defn- get-project 33 | [project-id] 34 | {:method :get 35 | :uri (str "/api/projects2/" project-id)}) 36 | 37 | (defn- start-project! 38 | [project-id] 39 | {:method :post 40 | :uri (str "/api/projects2/" project-id "/start")}) 41 | 42 | (defn- reset-project! 43 | [project-id] 44 | {:method :post 45 | :uri (str "/api/projects2/" project-id "/reset")}) 46 | 47 | (defn- delete-project! 48 | [project-id] 49 | {:method :delete 50 | :uri (str "/api/projects2/" project-id)}) 51 | -------------------------------------------------------------------------------- /client/src/planwise/client/projects2/components/common.cljs: -------------------------------------------------------------------------------- 1 | (ns planwise.client.projects2.components.common 2 | (:require [planwise.client.ui.dialog :refer [dialog]])) 3 | 4 | (defn delete-project-dialog 5 | [{:keys [open? id cancel-fn delete-fn]}] 6 | [dialog {:open? open? 7 | :title "Delete Project" 8 | :class "narrow" 9 | :delete-fn delete-fn 10 | :cancel-fn cancel-fn 11 | :content [:p.dialog-prompt 12 | "Do you want to delete this project?" 13 | [:br] 14 | [:strong "This action cannot be undone."]]}]) 15 | 16 | (defn reset-project-dialog 17 | [{:keys [open? id cancel-fn accept-fn]}] 18 | [dialog {:open? open? 19 | :title "Reset Project" 20 | :class "narrow" 21 | :acceptable? true 22 | :accept-fn accept-fn 23 | :cancel-fn cancel-fn 24 | :content [:p.dialog-prompt 25 | "Do you want to reset this project? " 26 | "This will delete all your current scenarios, but will allow changes in the project settings." 27 | [:br] 28 | [:strong "This action cannot be undone."]]}]) 29 | -------------------------------------------------------------------------------- /client/src/planwise/client/projects2/components/create.cljs: -------------------------------------------------------------------------------- 1 | (ns planwise.client.projects2.components.create 2 | (:require [re-frame.core :refer [subscribe dispatch] :as rf] 3 | [planwise.client.asdf :as asdf] 4 | [reagent.core :as r] 5 | [re-com.core :as rc] 6 | [planwise.client.components.common2 :as common2] 7 | [planwise.client.routes :as routes] 8 | [planwise.client.utils :as utils] 9 | [planwise.client.ui.common :as ui] 10 | [planwise.client.ui.rmwc :as m] 11 | [planwise.client.mapping :refer [static-image fullmap-region-geo]] 12 | [planwise.client.components.common :as common])) 13 | (def project-templates 14 | [{:description "Plan facilities based on ground access" 15 | :icon "directions_walk" 16 | :key "plan" 17 | :defaults {:name "ground"}} 18 | {:description "Plan diagnostic services & sample referrals" 19 | :icon "call_split" 20 | :key "diagnosis" 21 | :defaults {:name "sample"}}]) 22 | 23 | (defn project-section-template-selector 24 | [] 25 | [ui/fixed-width (common2/nav-params) 26 | (let [templates (subscribe [:projects2/templates]) 27 | scratch-template (first (filter #(not (contains? % :description)) @templates)) 28 | sample-templates (filter #(contains? % :description) @templates)] 29 | (dispatch [:projects2/get-templates-list]) 30 | [:div.template-container 31 | (if (some? sample-templates) 32 | [:h2 "Start from a template"]) 33 | (if (some? sample-templates) 34 | [:div.row 35 | (map (fn [template] 36 | [:a.action {:key (:key template) :onClick #(dispatch [:projects2/new-project (:defaults template)])} 37 | [m/Icon {} (:icon template)] 38 | [:div (:description template)]]) 39 | sample-templates)]) 40 | [:hr] 41 | [:h2 "Start from scratch"] 42 | [:div.row 43 | [:a.action {:onClick #(dispatch [:projects2/new-project (:defaults scratch-template)])} 44 | [m/Icon {} "folder_open"] 45 | [:div "Follow a wizard through all available settings"]]]])]) 46 | -------------------------------------------------------------------------------- /client/src/planwise/client/projects2/core.cljs: -------------------------------------------------------------------------------- 1 | (ns planwise.client.projects2.core) 2 | 3 | (def sections-base 4 | [{:step "goal" :title "Goal" :spec :planwise.model.project/goal-step} 5 | {:step "consumers" :title "Consumers" :spec :planwise.model.project/consumers-step} 6 | {:step "providers" :title "Providers" :spec :planwise.model.project/providers-step} 7 | {:step "coverage" :title "Coverage" :spec :planwise.model.project/coverage-step} 8 | {:step "actions" :title "Actions" :spec :planwise.model.project/actions-step} 9 | {:step "review" :title "Review" :spec :planwise.model.project/review-step}]) 10 | 11 | (def sections 12 | (->> sections-base 13 | (#(list % (concat (drop 1 %) (repeat nil)))) 14 | (apply mapv (fn [step next] 15 | (assoc step :next-step (:step next)))))) 16 | -------------------------------------------------------------------------------- /client/src/planwise/client/projects2/db.cljs: -------------------------------------------------------------------------------- 1 | (ns planwise.client.projects2.db 2 | (:require [schema.core :as s] 3 | [planwise.client.asdf :as asdf])) 4 | 5 | (def initial-db 6 | {:current-project nil 7 | :list nil 8 | :open-dialog nil 9 | :source-types #{"raster" "points"}}) 10 | -------------------------------------------------------------------------------- /client/src/planwise/client/projects2/subs.cljs: -------------------------------------------------------------------------------- 1 | (ns planwise.client.projects2.subs 2 | (:require [re-frame.core :as rf] 3 | [clojure.string :as string] 4 | [planwise.client.asdf :as asdf] 5 | [planwise.client.utils :as utils])) 6 | 7 | (rf/reg-sub 8 | :projects2/current-project 9 | (fn [db _] 10 | (get-in db [:projects2 :current-project]))) 11 | 12 | (rf/reg-sub 13 | :projects2/templates 14 | (fn [db _] 15 | (get-in db [:projects2 :templates]))) 16 | 17 | (rf/reg-sub 18 | :projects2/list 19 | (fn [db _] 20 | (some->> (get-in db [:projects2 :list]) 21 | (sort-by (comp string/lower-case :name))))) 22 | 23 | (rf/reg-sub 24 | :projects2/source-types 25 | (fn [db _] 26 | (get-in db [:projects2 :source-types]))) 27 | 28 | (rf/reg-sub 29 | :projects2/tags :<- [:projects2/current-project] 30 | (fn [current-project [_]] 31 | (get-in current-project [:config :providers :tags]))) 32 | 33 | (rf/reg-sub 34 | :projects2/build-actions :<- [:projects2/current-project] 35 | (fn [current-project [_]] 36 | (get-in current-project [:config :actions :build]))) 37 | 38 | (rf/reg-sub 39 | :projects2/upgrade-actions :<- [:projects2/current-project] 40 | (fn [current-project [_]] 41 | (get-in current-project [:config :actions :upgrade]))) 42 | 43 | (rf/reg-sub 44 | :projects2/new-project-coverage :<- [:projects2/current-project] 45 | (fn [current-project [_]] 46 | (get-in current-project [:coverage-algorithm]))) 47 | 48 | (rf/reg-sub 49 | :projects2/open-dialog 50 | (fn [db _] 51 | (get-in db [:projects2 :open-dialog]))) 52 | -------------------------------------------------------------------------------- /client/src/planwise/client/providers_set/api.cljs: -------------------------------------------------------------------------------- 1 | (ns planwise.client.providers-set.api) 2 | 3 | ;; ---------------------------------------------------------------------------- 4 | ;; API methods 5 | 6 | (def load-providers-set 7 | {:method :get 8 | :uri "/api/providers"}) 9 | 10 | (defn create-provider-set-with-csv 11 | [{:keys [name csv-file]}] 12 | (let [form-data (doto (js/FormData.) 13 | (.append "name" name) 14 | (.append "file" csv-file))] 15 | 16 | {:method :post 17 | :uri "/api/providers" 18 | :body form-data})) 19 | 20 | (defn delete-provider-set 21 | [id] 22 | {:method :delete 23 | :params {:id id} 24 | :uri "/api/providers"}) 25 | -------------------------------------------------------------------------------- /client/src/planwise/client/providers_set/components/dropdown.cljs: -------------------------------------------------------------------------------- 1 | (ns planwise.client.providers-set.components.dropdown 2 | (:require [re-frame.core :as rf] 3 | [planwise.client.asdf :as asdf] 4 | [planwise.client.ui.filter-select :as filter-select])) 5 | 6 | (defn providers-set-dropdown-component 7 | [attrs] 8 | (let [list (rf/subscribe [:providers-set/list]) 9 | props (merge {:choices @(rf/subscribe [:providers-set/dropdown-options]) 10 | :label-fn :label 11 | :render-fn (fn [provider-set] [:div.option-row 12 | [:span (:label provider-set)]])} 13 | attrs)] 14 | (when (asdf/should-reload? @list) 15 | (rf/dispatch [:providers-set/load-providers-set])) 16 | (into [filter-select/single-dropdown] (mapcat identity props)))) 17 | -------------------------------------------------------------------------------- /client/src/planwise/client/providers_set/db.cljs: -------------------------------------------------------------------------------- 1 | (ns planwise.client.providers-set.db 2 | (:require [planwise.client.asdf :as asdf] 3 | [planwise.client.utils :refer [remove-by-id]] 4 | [clojure.spec.alpha :as s])) 5 | 6 | (s/def ::view-state #{:list :create-dialog :creating}) 7 | (s/def ::last-error (s/nilable string?)) 8 | (s/def ::name string?) 9 | (s/def ::coverage keyword?) 10 | 11 | (def initial-new-provider-set 12 | {:name "" 13 | :js-file nil 14 | :coverage nil}) 15 | 16 | (def initial-db 17 | {:view-state :list 18 | :last-error nil 19 | :list (asdf/new nil) 20 | :new-provider-set initial-new-provider-set}) 21 | 22 | (defn show-dialog? 23 | [state] 24 | (#{:create-dialog :creating} state)) 25 | -------------------------------------------------------------------------------- /client/src/planwise/client/providers_set/subs.cljs: -------------------------------------------------------------------------------- 1 | (ns planwise.client.providers-set.subs 2 | (:require [re-frame.core :as rf] 3 | [planwise.client.asdf :as asdf] 4 | [planwise.client.utils :as utils])) 5 | 6 | (rf/reg-sub 7 | :providers-set/list 8 | (fn [db _] 9 | (get-in db [:providers-set :list]))) 10 | 11 | (rf/reg-sub 12 | :providers-set/dropdown-options 13 | (fn [db _] 14 | (let [providers (get-in db [:providers-set :list :value])] 15 | (->> providers 16 | (map (fn [provider-set] 17 | (let [{:keys [id name]} provider-set] 18 | {:value id :label name :id id}))) 19 | (sort-by :label) 20 | (into [{:value nil :label "None" :id nil}]))))) 21 | 22 | (rf/reg-sub 23 | :providers-set/view-state 24 | (fn [db _] 25 | (get-in db [:providers-set :view-state]))) 26 | 27 | (rf/reg-sub 28 | :providers-set/last-error 29 | (fn [db _] 30 | (get-in db [:providers-set :last-error]))) 31 | 32 | (rf/reg-sub 33 | :providers-set/new-provider-set-state 34 | (fn [db _] 35 | (get-in db [:providers-set :new-provider-set :state]))) 36 | 37 | (rf/reg-sub 38 | :providers-set/new-provider-set-name 39 | (fn [db _] 40 | (get-in db [:providers-set :new-provider-set :name]))) 41 | 42 | (rf/reg-sub 43 | :providers-set/new-provider-set-js-file 44 | (fn [db _] 45 | (get-in db [:providers-set :new-provider-set :js-file]))) 46 | 47 | 48 | (rf/reg-sub 49 | :providers-set/delete-selected-provider-set 50 | (fn [db _] 51 | (get-in db [:providers-set :selected-provider]))) 52 | -------------------------------------------------------------------------------- /client/src/planwise/client/regions/api.cljs: -------------------------------------------------------------------------------- 1 | (ns planwise.client.regions.api 2 | (:require [clojure.string :as str])) 3 | 4 | (def load-regions 5 | {:method :get 6 | :uri "/api/regions"}) 7 | 8 | (defn load-regions-with-preview 9 | [ids] 10 | {:method :get 11 | :uri (str "/api/regions/" (str/join "," ids) "/with-preview")}) 12 | 13 | (defn load-regions-with-geo 14 | [ids] 15 | {:method :get 16 | :uri (str "/api/regions/" (str/join "," ids) "/with-geo")}) 17 | -------------------------------------------------------------------------------- /client/src/planwise/client/regions/handlers.cljs: -------------------------------------------------------------------------------- 1 | (ns planwise.client.regions.handlers 2 | (:require [re-frame.core :as rf] 3 | [planwise.client.regions.api :as api])) 4 | 5 | (def in-regions (rf/path [:regions])) 6 | 7 | (rf/reg-event-fx 8 | :regions/load-regions 9 | (fn [_ _] 10 | {:api (assoc api/load-regions 11 | :on-success [:regions/regions-loaded])})) 12 | 13 | (defn ids-for-regions-without 14 | [field regions ids] 15 | (->> ids 16 | (filter some?) 17 | (remove (fn [id] (get-in regions [id field]))) 18 | seq)) 19 | 20 | (rf/reg-event-fx 21 | :regions/load-regions-with-preview 22 | in-regions 23 | (fn [{:keys [db]} [_ region-ids]] 24 | (when-some [missing-region-ids (ids-for-regions-without :preview-geojson db region-ids)] 25 | {:api (assoc (api/load-regions-with-preview missing-region-ids) 26 | :on-success [:regions/regions-loaded])}))) 27 | 28 | (rf/reg-event-fx 29 | :regions/load-regions-with-geo 30 | in-regions 31 | (fn [{:keys [db]} [_ region-ids]] 32 | (when-some [missing-region-ids (ids-for-regions-without :geojson db region-ids)] 33 | {:api (assoc (api/load-regions-with-geo missing-region-ids) 34 | :on-success [:regions/regions-loaded])}))) 35 | 36 | (rf/reg-event-db 37 | :regions/regions-loaded 38 | in-regions 39 | (fn [db [_ regions-data]] 40 | (reduce 41 | (fn [db {id :id :as region}] 42 | (update db id #(merge % region))) 43 | db regions-data))) 44 | -------------------------------------------------------------------------------- /client/src/planwise/client/regions/subs.cljs: -------------------------------------------------------------------------------- 1 | (ns planwise.client.regions.subs 2 | (:require [re-frame.core :as rf])) 3 | 4 | (rf/reg-sub 5 | :regions/list 6 | (fn [db _] 7 | (sort-by :name (vals (:regions db))))) 8 | 9 | (rf/reg-sub 10 | :regions/preview-geojson 11 | (fn [db [_ region-id]] 12 | (get-in db [:regions region-id :preview-geojson]))) 13 | 14 | (rf/reg-sub 15 | :regions/geojson 16 | (fn [db [_ region-id]] 17 | (get-in db [:regions region-id :geojson]))) 18 | 19 | -------------------------------------------------------------------------------- /client/src/planwise/client/scenarios/api.cljs: -------------------------------------------------------------------------------- 1 | (ns planwise.client.scenarios.api) 2 | 3 | (defn load-scenario 4 | [id] 5 | {:method :get 6 | :uri (str "/api/scenarios/" id)}) 7 | 8 | (defn copy-scenario 9 | [id] 10 | {:method :post 11 | :uri (str "/api/scenarios/" id "/copy")}) 12 | 13 | (defn update-scenario 14 | [id scenario] 15 | {:method :put 16 | :params {:scenario scenario} 17 | :uri (str "/api/scenarios/" id)}) 18 | 19 | (defn load-scenarios 20 | [id] 21 | {:method :get 22 | :uri (str "/api/projects2/" id "/scenarios")}) 23 | 24 | (defn suggested-locations-for-new-provider 25 | [id] 26 | {:method :get 27 | :timeout 90000 28 | :uri (str "/api/scenarios/" id "/suggested-locations")}) 29 | 30 | (defn suggested-providers-to-improve 31 | [id] 32 | {:method :get 33 | :timeout 90000 34 | :uri (str "/api/scenarios/" id "/suggested-providers")}) 35 | 36 | (defn get-provider-geom 37 | [id provider-id] 38 | {:method :get 39 | :uri (str "/api/scenarios/" id "/geometry/" provider-id)}) 40 | 41 | (defn get-suggestion-geom 42 | [id iteration] 43 | {:method :get 44 | :uri (str "/api/scenarios/" id "/coverage/suggestion/" iteration)}) 45 | 46 | (defn- delete-scenario 47 | [id] 48 | {:method :delete 49 | :uri (str "/api/scenarios/" id)}) 50 | 51 | (defn download-scenario-sources 52 | [id] 53 | (str "/api/scenarios/" id "/sources")) 54 | 55 | (defn download-scenario-providers 56 | [id] 57 | (str "/api/scenarios/" id "/providers")) 58 | -------------------------------------------------------------------------------- /client/src/planwise/client/sources/api.cljs: -------------------------------------------------------------------------------- 1 | (ns planwise.client.sources.api) 2 | 3 | ;; ---------------------------------------------------------------------------- 4 | ;; API methods 5 | 6 | (def load-sources 7 | {:method :get 8 | :uri "/api/sources"}) 9 | 10 | (defn create-source-with-csv 11 | [{:keys [name unit csv-file]}] 12 | (let [form-data (doto (js/FormData.) 13 | (.append "name" name) 14 | (.append "unit" unit) 15 | (.append "csvfile" csv-file))] 16 | {:method :post 17 | :uri "/api/sources" 18 | :body form-data})) 19 | -------------------------------------------------------------------------------- /client/src/planwise/client/sources/components/dropdown.cljs: -------------------------------------------------------------------------------- 1 | (ns planwise.client.sources.components.dropdown 2 | (:require [re-frame.core :as rf] 3 | [re-frame.core :refer [dispatch subscribe]] 4 | [planwise.client.asdf :as asdf] 5 | [planwise.client.components.common2 :as common2] 6 | [planwise.client.utils :as utils] 7 | [planwise.client.ui.filter-select :as filter-select] 8 | [planwise.client.ui.rmwc :as m])) 9 | 10 | (def in-sources (rf/path [:sources])) 11 | 12 | (rf/reg-sub 13 | :sources/dropdown-options 14 | (fn [_] 15 | [(subscribe [:sources/list]) 16 | (subscribe [:projects2/source-types])]) 17 | (fn [[list types] _] 18 | (filter (fn [{:keys [type]}] (types type)) list))) 19 | 20 | ;; ---------------------------------------------------------------------------- 21 | ;; Views 22 | 23 | (defn sources-dropdown-component 24 | [attrs] 25 | (let [props (merge {:choices @(rf/subscribe [:sources/dropdown-options]) 26 | :label-fn :name 27 | :render-fn (fn [source] [:div.option-row 28 | [:span (:name source)] 29 | [:span.option-context (:value source)]])} 30 | attrs)] 31 | (into [filter-select/single-dropdown] (mapcat identity props)))) 32 | -------------------------------------------------------------------------------- /client/src/planwise/client/sources/db.cljs: -------------------------------------------------------------------------------- 1 | (ns planwise.client.sources.db 2 | (:require [planwise.client.asdf :as asdf])) 3 | 4 | (def initial-db 5 | {:list (asdf/new nil)}) 6 | -------------------------------------------------------------------------------- /client/src/planwise/client/sources/handlers.cljs: -------------------------------------------------------------------------------- 1 | (ns planwise.client.sources.handlers 2 | (:require [re-frame.core :refer [register-handler dispatch] :as rf] 3 | [planwise.client.asdf :as asdf] 4 | [planwise.client.sources.api :as api])) 5 | 6 | (def in-sources (rf/path [:sources])) 7 | 8 | (rf/reg-event-fx 9 | :sources/load 10 | in-sources 11 | (fn [{:keys [db]} [_]] 12 | {:api (assoc api/load-sources 13 | :on-success [:sources/loaded]) 14 | :db (update db :list asdf/reload!)})) 15 | 16 | (rf/reg-event-db 17 | :sources/loaded 18 | in-sources 19 | (fn [db [_ sources]] 20 | (update db :list asdf/reset! sources))) 21 | 22 | (rf/reg-event-db 23 | :sources.new/update 24 | in-sources 25 | (fn [db [_ changes]] 26 | (update db :new #(merge % changes)))) 27 | 28 | (rf/reg-event-fx 29 | :sources.new/create 30 | in-sources 31 | (fn [{:keys [db]}] 32 | (let [new-source (get db :new)] 33 | {:api (assoc (api/create-source-with-csv new-source) 34 | :on-success [:sources.new/created] 35 | :on-failure [:sources.new/failed])}))) 36 | 37 | (rf/reg-event-db 38 | :sources.new/discard 39 | in-sources 40 | (fn [db] 41 | (rf/dispatch [:modal/hide]) 42 | (dissoc db :new))) 43 | 44 | (rf/reg-event-db 45 | :sources.new/created 46 | in-sources 47 | (fn [db [_ created-source]] 48 | (rf/dispatch [:modal/hide]) 49 | (-> db 50 | (dissoc :new) 51 | (update :list #(asdf/swap! % conj created-source))))) 52 | 53 | (rf/reg-event-db 54 | :sources.new/failed 55 | in-sources 56 | (fn [db [_ err]] 57 | (assoc-in db [:new :current-error] (:status-text err)))) 58 | -------------------------------------------------------------------------------- /client/src/planwise/client/sources/subs.cljs: -------------------------------------------------------------------------------- 1 | (ns planwise.client.sources.subs 2 | (:require [re-frame.core :as rf] 3 | [planwise.client.asdf :as asdf] 4 | [clojure.string :as str])) 5 | 6 | (rf/reg-sub 7 | :sources/list-as-asdf 8 | (fn [db _] 9 | (let [sources (get-in db [:sources :list])] 10 | (when (asdf/should-reload? sources) 11 | (rf/dispatch [:sources/load])) 12 | sources))) 13 | 14 | (rf/reg-sub 15 | :sources/list 16 | (fn [_] 17 | (rf/subscribe [:sources/list-as-asdf])) 18 | (fn [sources] 19 | (asdf/value sources))) 20 | 21 | (rf/reg-sub 22 | :sources.new/data 23 | (fn [db _] 24 | (get-in db [:sources :new]))) 25 | 26 | (rf/reg-sub 27 | :sources.new/valid? 28 | (fn [_] 29 | (rf/subscribe [:sources.new/data])) 30 | (fn [new-source] 31 | (let [name (:name new-source) 32 | csv-file (:csv-file new-source)] 33 | (not (or (str/blank? name) 34 | (nil? csv-file)))))) 35 | 36 | (rf/reg-sub 37 | :sources.new/current-error 38 | (fn [db _] 39 | (get-in db [:sources :new :current-error]))) 40 | -------------------------------------------------------------------------------- /client/src/planwise/client/styles.cljs: -------------------------------------------------------------------------------- 1 | (ns planwise.client.styles) 2 | 3 | (def orange "#ff8400") 4 | (def dark-orange "#ac5900") 5 | (def green "#819f51") 6 | (def black "#111111") 7 | (def light-grey "#b7b7b7") 8 | 9 | ; See https://bl.ocks.org/mbostock/5577023 10 | (def facility-types-palette 11 | ["#377eb8" "#4daf4a" "#984ea3" "#ff7f00" "#a65628" "#f781bf"]) 12 | 13 | (def invalid-facility-type "#999999") 14 | -------------------------------------------------------------------------------- /client/src/planwise/client/subs.cljs: -------------------------------------------------------------------------------- 1 | (ns planwise.client.subs 2 | (:require [re-frame.core :as rf] 3 | [planwise.client.coverage] 4 | [planwise.client.projects2.subs] 5 | [planwise.client.sources.subs] 6 | [planwise.client.providers-set.subs] 7 | [planwise.client.regions.subs] 8 | [planwise.client.scenarios.subs])) 9 | 10 | 11 | ;; Subscriptions 12 | ;; ------------------------------------------------------- 13 | 14 | (rf/reg-sub 15 | :current-page 16 | (fn [db _] 17 | (:current-page db))) 18 | 19 | (rf/reg-sub 20 | :page-params 21 | (fn [db _] 22 | (:page-params db))) 23 | -------------------------------------------------------------------------------- /client/src/planwise/client/ui/dialog.cljs: -------------------------------------------------------------------------------- 1 | (ns planwise.client.ui.dialog 2 | (:require [reagent.core :as r] 3 | [planwise.client.ui.rmwc :as m] 4 | [planwise.client.ui.common :as ui] 5 | [planwise.client.utils :as utils])) 6 | 7 | (defn dialog 8 | [{:keys [open? class title content accept-fn cancel-fn delete-fn acceptable?]}] 9 | (r/with-let [] 10 | [m/Dialog {:open open? 11 | :on-accept accept-fn 12 | :on-close cancel-fn 13 | :className class} 14 | [m/DialogSurface 15 | [m/DialogHeader 16 | [m/DialogHeaderTitle title] 17 | [ui/close-button {:on-click cancel-fn}]] 18 | [m/DialogBody 19 | [:form.vertical {:on-submit (utils/prevent-default accept-fn)} 20 | content]] 21 | [m/DialogFooter 22 | (when (some? delete-fn) 23 | [:div {:class (when (some? accept-fn) "flex-spacer")} 24 | [m/Button {:on-click delete-fn} "Delete"]]) 25 | (when (some? cancel-fn) [m/DialogFooterButton {:cancel true} "Cancel"]) 26 | (when (some? accept-fn) [m/DialogFooterButton {:accept true 27 | :unelevated true 28 | :disabled (not acceptable?)} "OK"])]]] 29 | (finally 30 | ;; NB. this is a hack/fix to avoid the scroll lock when a RMWC Dialog is 31 | ;; destroyed while running the hide transition. In that case the HTML body 32 | ;; had the `mdc-dialog-scroll-lock` class added because the dialog was 33 | ;; open, but MDC fails to remove it if the dialog is destroyed before the 34 | ;; transition end for the closing animation event occurs 35 | (js/document.body.classList.remove "mdc-dialog-scroll-lock")))) 36 | -------------------------------------------------------------------------------- /client/src/planwise/client/ui/macros.clj: -------------------------------------------------------------------------------- 1 | (ns planwise.client.ui.macros) 2 | 3 | (def rmwc-tags 4 | '[Button 5 | Checkbox 6 | Chip 7 | ChipIcon 8 | ChipSet 9 | ChipText 10 | Dialog 11 | DialogSurface 12 | DialogHeader 13 | DialogHeaderTitle 14 | DialogBody 15 | DialogFooter 16 | DialogFooterButton 17 | DialogBackdrop 18 | Elevation 19 | Fab 20 | FormField 21 | Grid 22 | GridCell 23 | GridInner 24 | GridList 25 | GridTile 26 | GridTileIcon 27 | GridTilePrimary 28 | GridTilePrimaryContent 29 | GridTileSecondary 30 | GridTileTitle 31 | Icon 32 | List 33 | ListDivider 34 | ListItem 35 | ListItemGraphic 36 | ListItemMeta 37 | ListItemSecondaryText 38 | ListItemText 39 | Menu 40 | MenuAnchor 41 | MenuItem 42 | Radio 43 | Ripple 44 | Select 45 | SimpleDialog 46 | SimpleMenu 47 | Slider 48 | Switch 49 | Tab 50 | TabBar 51 | TabIcon 52 | TextField 53 | TextFieldHelperText 54 | TextFieldIcon 55 | Theme 56 | Toolbar 57 | ToolbarRow 58 | ToolbarSection 59 | ToolbarTitle 60 | Typography]) 61 | 62 | (defn rmwc-ui-react-import [tname] 63 | `(def ~tname 64 | (reagent/adapt-react-class ~(symbol (str "rmwc/" (name tname)))))) 65 | 66 | (defmacro export-rmwc [] 67 | `(do 68 | ~@(map rmwc-ui-react-import rmwc-tags))) 69 | -------------------------------------------------------------------------------- /client/src/planwise/client/ui/rmwc.cljs: -------------------------------------------------------------------------------- 1 | (ns planwise.client.ui.rmwc 2 | (:refer-clojure :exclude [List]) 3 | (:require-macros [planwise.client.ui.macros :refer [export-rmwc]]) 4 | (:require rmwc 5 | [reagent.core :as reagent])) 6 | 7 | (export-rmwc) 8 | -------------------------------------------------------------------------------- /client/test/planwise/client/scenarios_test.cljs: -------------------------------------------------------------------------------- 1 | (ns planwise.client.scenarios-test 2 | (:require [planwise.client.scenarios.db :as sut] 3 | [cljs.test :as t :refer-macros [deftest is]])) 4 | 5 | (deftest new-provider-from-change-test 6 | (let [change {:id "foo" 7 | :name "New Provider 1" 8 | :action "create-provider" 9 | :location {:lat 0 :lon 0}}] 10 | (is (= {:id "foo" 11 | :name "New Provider 1" 12 | :matches-filters true 13 | :location {:lat 0 :lon 0} 14 | :change change} 15 | (sut/new-provider-from-change change))))) 16 | -------------------------------------------------------------------------------- /client/test/planwise/client/utils_test.cljs: -------------------------------------------------------------------------------- 1 | (ns planwise.client.utils-test 2 | (:require [cljs.test :as t :refer-macros [deftest is]] 3 | [clojure.string :as str] 4 | [planwise.client.utils :as sut] 5 | [planwise.common :as common])) 6 | 7 | 8 | (deftest pluralize-test 9 | (is (= "1 site" (common/pluralize 1 "site"))) 10 | (is (= "2 sites" (common/pluralize 2 "site"))) 11 | (is (= "1 person" (common/pluralize 1 "person" "people"))) 12 | (is (= "2 people" (common/pluralize 2 "person" "people")))) 13 | 14 | (deftest update-by-id-test 15 | (let [coll [{:id 1 :name "foo"} 16 | {:id 2 :name "bar"} 17 | {:id 3 :name "quux"}]] 18 | (is (= [{:id 1 :name "foo"} 19 | {:id 2 :name "BAR"} 20 | {:id 3 :name "quux"}] 21 | (sut/update-by-id coll 2 update :name str/upper-case))))) 22 | -------------------------------------------------------------------------------- /common/README.md: -------------------------------------------------------------------------------- 1 | This directory contains Clojure code common to both the server and client applications. 2 | 3 | -------------------------------------------------------------------------------- /common/src/planwise/common.cljc: -------------------------------------------------------------------------------- 1 | (ns planwise.common 2 | (:require [clojure.string :as string :refer [lower-case]])) 3 | 4 | (defn is-budget 5 | [analysis-type] 6 | (= analysis-type "budget")) 7 | 8 | (def is-action (complement is-budget)) 9 | 10 | (defn project-has-budget? 11 | [project] 12 | (= "budget" (get-in project [:config :analysis-type]))) 13 | 14 | (def currency-symbol "$") 15 | 16 | (defn pluralize 17 | ([count singular] 18 | (pluralize count singular (str singular "s"))) 19 | ([count singular plural] 20 | (let [noun (if (= 1 count) singular plural)] 21 | (str count " " noun)))) 22 | 23 | (defn- get-project-unit 24 | [project path value lowercase?] 25 | ((if lowercase? lower-case identity) (or (not-empty (get-in project path)) value))) 26 | 27 | (defn get-consumer-unit 28 | ([project] 29 | (get-consumer-unit project true)) 30 | ([project lowercase?] 31 | (get-project-unit project [:config :demographics :unit-name] "total population" lowercase?))) 32 | 33 | (defn get-demand-unit 34 | ([project] 35 | (get-demand-unit project true)) 36 | ([project lowercase?] 37 | (get-project-unit project [:config :demographics :demand-unit] "targets" lowercase?))) 38 | 39 | (defn get-provider-unit 40 | ([project] 41 | (get-provider-unit project true)) 42 | ([project lowercase?] 43 | (get-project-unit project [:config :providers :provider-unit] "providers" lowercase?))) 44 | 45 | (defn get-capacity-unit 46 | ([project] 47 | (get-capacity-unit project true)) 48 | ([project lowercase?] 49 | (get-project-unit project [:config :providers :capacity-unit] "units" lowercase?))) 50 | 51 | (defn sanitize-tag 52 | [tag] 53 | (-> tag 54 | string/trim 55 | (string/replace #"\s+" "-") 56 | (string/replace #"[^a-zA-Z0-9.-]" ""))) 57 | -------------------------------------------------------------------------------- /common/src/planwise/model/coverage.cljc: -------------------------------------------------------------------------------- 1 | (ns planwise.model.coverage 2 | (:require [clojure.spec.alpha :as s])) 3 | 4 | (s/def ::algorithm keyword?) 5 | (s/def ::base-criteria (s/keys :req-un [::algorithm])) 6 | 7 | (defmulti criteria-algo :algorithm) 8 | (s/def ::coverage-criteria (s/multi-spec criteria-algo :algorithm)) 9 | 10 | (def buffer-distance-values 11 | (range 0 301 5)) 12 | 13 | 14 | ;; Specs ===================================================================== 15 | ;; 16 | 17 | (s/def ::driving-time #{30 60 90 120}) 18 | (s/def ::driving-friction-criteria (s/keys :req-un [::driving-time])) 19 | 20 | (s/def ::distance (set buffer-distance-values)) 21 | (s/def ::simple-buffer-criteria (s/keys :req-un [::distance])) 22 | 23 | (s/def ::walking-time #{60 120 180}) 24 | (s/def ::walking-friction-criteria (s/keys :req-un [::walking-time])) 25 | 26 | (s/def ::drive-walk-friction-criteria (s/or :walking-friction-criteria ::walking-friction-criteria 27 | :driving-friction-criteria ::driving-friction-criteria)) 28 | 29 | (defmethod criteria-algo :simple-buffer [_] 30 | (s/merge ::base-criteria ::simple-buffer-criteria)) 31 | (defmethod criteria-algo :walking-friction [_] 32 | (s/merge ::base-criteria ::walking-friction-criteria)) 33 | (defmethod criteria-algo :driving-friction [_] 34 | (s/merge ::base-criteria ::driving-friction-criteria)) 35 | (defmethod criteria-algo :drive-walk-friction [_] 36 | (s/merge ::base-criteria ::drive-walk-friction-criteria)) 37 | 38 | 39 | (defn valid-coverage-criteria? 40 | [algorithm filter-options] 41 | (s/valid? ::coverage-criteria 42 | (assoc filter-options :algorithm (keyword algorithm)))) 43 | -------------------------------------------------------------------------------- /cpp/.gitignore: -------------------------------------------------------------------------------- 1 | *.tif 2 | -------------------------------------------------------------------------------- /cpp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.7) 2 | project(planwise) 3 | 4 | include(FindBoost) 5 | set(Boost_USE_STATIC_LIBS ON) 6 | find_package(Boost COMPONENTS timer program_options filesystem) 7 | if(NOT Boost_FOUND) 8 | message(FATAL_ERROR "Boost not found") 9 | endif() 10 | 11 | find_program(GDAL_CONFIG gdal-config) 12 | if(NOT GDAL_CONFIG) 13 | message(FATAL_ERROR "GDAL not found") 14 | endif() 15 | 16 | exec_program(${GDAL_CONFIG} ARGS --cflags OUTPUT_VARIABLE GDAL_CFLAGS) 17 | exec_program(${GDAL_CONFIG} ARGS --libs OUTPUT_VARIABLE GDAL_LIBS) 18 | 19 | option(BENCHMARK "add timing benchmarks" OFF) 20 | 21 | if(BENCHMARK) 22 | add_definitions(-DBENCHMARK) 23 | endif() 24 | 25 | add_compile_options(${GDAL_CFLAGS}) 26 | include_directories(${Boost_INCLUDE_DIRS}) 27 | 28 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -std=c++11") 29 | 30 | add_executable(aggregate-population aggregate-population.cpp) 31 | target_link_libraries(aggregate-population ${GDAL_LIBS} ${Boost_LIBRARIES}) 32 | 33 | add_executable(walking-coverage walking-coverage.cpp) 34 | target_link_libraries(walking-coverage ${GDAL_LIBS} ${Boost_LIBRARIES}) 35 | -------------------------------------------------------------------------------- /cpp/aggregate-population: -------------------------------------------------------------------------------- 1 | bin-trampoline.sh -------------------------------------------------------------------------------- /cpp/bin-trampoline.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | base_dir=$(dirname $0) 4 | base_name=$(basename $0) 5 | bin_dir=$base_dir/build-`uname -s | tr '[A-Z]' '[a-z]'`-`uname -m` 6 | 7 | binary=$bin_dir/$base_name 8 | 9 | if [ ! -x $binary ]; then 10 | echo "ERROR: Binary executable $binary is missing!" 11 | echo "Compile it using scripts/build-binaries." 12 | exit 1 13 | fi 14 | 15 | exec $binary $* 16 | -------------------------------------------------------------------------------- /cpp/walking-coverage: -------------------------------------------------------------------------------- 1 | bin-trampoline.sh -------------------------------------------------------------------------------- /data/populations/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/instedd/planwise/6da765a09481c6b86169dab585bbea71aabe120e/data/populations/.gitkeep -------------------------------------------------------------------------------- /data/regions/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/instedd/planwise/6da765a09481c6b86169dab585bbea71aabe120e/data/regions/.gitkeep -------------------------------------------------------------------------------- /data/scenarios/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/instedd/planwise/6da765a09481c6b86169dab585bbea71aabe120e/data/scenarios/.gitkeep -------------------------------------------------------------------------------- /dev/resources/dev.edn: -------------------------------------------------------------------------------- 1 | {:duct.core/environment :development 2 | :duct.core/include ["planwise/config"] 3 | 4 | :duct.module/sql 5 | {:database-url #duct/env ["DATABASE_URL" Str :or "jdbc:postgresql://localhost:5433/planwise?user=planwise&password=planwise"]} 6 | 7 | :planwise.auth/base-secret 8 | "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20" 9 | 10 | :planwise.component/runner 11 | {:bin #duct/env ["BIN_PATH" Str :or "cpp/"]} 12 | 13 | :planwise.component/mailer 14 | {:config {:mock? true}} 15 | 16 | :planwise.component/maps 17 | {:config {}}} 18 | -------------------------------------------------------------------------------- /dev/src/dev.clj: -------------------------------------------------------------------------------- 1 | (ns dev 2 | (:refer-clojure :exclude [test]) 3 | (:require [clojure.repl :refer :all] 4 | [fipp.edn :refer [pprint]] 5 | [clojure.tools.namespace.repl :refer [refresh]] 6 | [clojure.java.io :as io] 7 | [duct.core :as duct] 8 | [duct.core.repl :as duct-repl] 9 | [eftest.runner :as eftest] 10 | [clojure.spec.alpha :as s] 11 | [integrant.core :as ig] 12 | [integrant.repl :refer [clear halt init prep]] 13 | [integrant.repl.state :refer [config system]] 14 | [taoensso.timbre :as timbre] 15 | [planwise.config] 16 | [planwise.repl :refer :all] 17 | [planwise.virgil])) 18 | 19 | (duct/load-hierarchy) 20 | 21 | ;; Logging configuration for development 22 | (timbre/merge-config! {:level :debug 23 | :ns-blacklist ["com.zaxxer.hikari.*" 24 | "org.apache.http.*" 25 | "org.eclipse.jetty.*" 26 | "org.openid4java.*"]}) 27 | 28 | (defn read-config [] 29 | (duct/read-config (io/resource "dev.edn"))) 30 | 31 | (clojure.tools.namespace.repl/set-refresh-dirs "dev/src" "src" "test") 32 | (planwise.virgil/set-java-source-dirs! ["java"]) 33 | 34 | (when (io/resource "local.clj") 35 | (load "local")) 36 | 37 | (integrant.repl/set-prep! (comp duct/prep read-config)) 38 | 39 | (s/check-asserts true) 40 | -------------------------------------------------------------------------------- /dev/src/planwise/repl.clj: -------------------------------------------------------------------------------- 1 | (ns planwise.repl 2 | (:refer-clojure :exclude [test]) 3 | (:require [integrant.core :as ig] 4 | [integrant.repl :as igr] 5 | [integrant.repl.state :refer [config system]] 6 | [eftest.runner :as eftest] 7 | [planwise.database :as database] 8 | [planwise.tasks.build-icons :as build-icons] 9 | [ragtime.core :as ragtime] 10 | [ragtime.jdbc :as rag-jdbc] 11 | [duct.migrator.ragtime :as dmr] 12 | [buddy.core.nonce :as nonce] 13 | [planwise.virgil]) 14 | (:import org.apache.commons.codec.binary.Hex)) 15 | 16 | (defn db 17 | [] 18 | (let [[_ database] (ig/find-derived-1 system :duct.database/sql)] 19 | database)) 20 | 21 | (defn run-tests 22 | [tests] 23 | (eftest/run-tests tests {:multithread? false})) 24 | 25 | (defn test 26 | ([] 27 | (run-tests (eftest/find-tests "test"))) 28 | ([pattern] 29 | (let [pattern (re-pattern pattern) 30 | filterer (fn [var-name] 31 | (->> (str var-name) 32 | (re-find pattern))) 33 | tests (->> (eftest/find-tests "test") 34 | (filter filterer))] 35 | (run-tests tests)))) 36 | 37 | (defn load-sql 38 | [] 39 | (database/load-sql-functions (db))) 40 | 41 | (defn rollback-1 42 | [] 43 | (let [[_ index] (ig/find-derived-1 system :duct.migrator/ragtime) 44 | [_ logger] (ig/find-derived-1 system :duct/logger) 45 | store (rag-jdbc/sql-database (:spec (db))) 46 | options {:reporter (dmr/logger-reporter logger)}] 47 | (ragtime/rollback-last store index 1 options))) 48 | 49 | (defn build-icons 50 | [] 51 | (build-icons/process-svgs)) 52 | 53 | (defn gen-base-secret 54 | [] 55 | (-> (nonce/random-bytes 32) 56 | Hex/encodeHex 57 | String.)) 58 | 59 | (defn go 60 | [] 61 | (igr/prep) 62 | (igr/init)) 63 | 64 | (defn compile-java 65 | [] 66 | (planwise.virgil/recompile-all-java)) 67 | 68 | (defn reset 69 | [] 70 | (planwise.virgil/refresh) 71 | (igr/reset)) 72 | -------------------------------------------------------------------------------- /dev/src/user.clj: -------------------------------------------------------------------------------- 1 | (ns user) 2 | 3 | (defn dev 4 | "Load and switch to the 'dev' namespace." 5 | [] 6 | (require 'dev) 7 | (in-ns 'dev) 8 | :loaded) 9 | -------------------------------------------------------------------------------- /doc/coverage_algo.md: -------------------------------------------------------------------------------- 1 | # Coverage algorithms 2 | 3 | Coverage algorithms should calculate the coverage area of a site in a geographic 4 | point given some criteria. Each algorithm can apply a different strategy to 5 | compute the resulting coverage and accept different criteria parameters. 6 | 7 | For example, the coverage for car driving would accept a maximum driving time 8 | and use OSM road network data. The coverage for walking distance would also 9 | accept a maximum travel time but use a capped raster friction layer. A 10 | hipotetical FM radio coverage algorithm would take a minimum signal strength as 11 | a cut criteria and use the digital elevation model for the computation. 12 | 13 | The expected output format for the coverage is both in vector format as a 14 | polygon and in raster format. The algorithms should accept parameters 15 | controlling the resolution and/or quality of the output. Eg. a simplification 16 | threshold for vector responses, grid resolution and origin/snap coordinates for 17 | raster responses and spatial reference system for both. 18 | 19 | Several instances of a single algorithm can be declared, each with a different 20 | data set, for example to compute coverages in different areas, or with different 21 | precision. Hence, each instance of an algorithm is only aplicable to a 22 | restricted geographical area. The choice of data set may also constrain the 23 | allowed criteria parameters, depending on the algorithm implementation. 24 | -------------------------------------------------------------------------------- /docker-cloud.sample.yml: -------------------------------------------------------------------------------- 1 | db: 2 | image: starefossen/pgrouting:9.4-2.1-2.1 3 | autorestart: always 4 | environment: 5 | POSTGRES_PASSWORD: PLANWISE_POSTGRES_PASSWORD 6 | POSTGRES_USER: planwise 7 | POSTGRES_DB: routing 8 | 9 | web: 10 | image: instedd/planwise:latest 11 | autorestart: always 12 | autoredeploy: true 13 | command: /bin/sh -c "/app/scripts/migrate && java -jar $JAR_PATH" 14 | environment: 15 | DATABASE_URL: "jdbc:postgresql://db/routing?user=planwise&password=PLANWISE_POSTGRES_PASSWORD" 16 | POSTGRES_PASSWORD: PLANWISE_POSTGRES_PASSWORD 17 | POSTGRES_USER: planwise 18 | POSTGRES_DB: routing 19 | POSTGRES_HOST: db 20 | volumes_from: 21 | - mapserver-data 22 | 23 | mapcache: 24 | image: instedd/planwise-mapcache:kenya 25 | autorestart: always 26 | autoredeploy: true 27 | 28 | mapserver: 29 | image: instedd/planwise-mapserver:kenya 30 | autorestart: always 31 | autoredeploy: true 32 | volumes_from: 33 | - mapserver-data 34 | 35 | mapserver-data: 36 | image: instedd/planwise-mapserver:kenya-data 37 | autoredeploy: true 38 | command: /bin/true 39 | -------------------------------------------------------------------------------- /docker-compose.ci.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | 3 | services: 4 | app: 5 | environment: 6 | DATABASE_URL: "jdbc:postgresql://db/planwise?user=postgres&password=planwise" 7 | TEST_DATABASE_URL: "jdbc:postgresql://db/planwise-test?user=postgres&password=planwise" 8 | POSTGRES_USER: postgres 9 | 10 | db: 11 | environment: 12 | POSTGRES_USER: postgres 13 | -------------------------------------------------------------------------------- /docker-compose.production.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | 3 | services: 4 | app: 5 | container_name: planwise-prod-app 6 | image: instedd/planwise:latest 7 | environment: 8 | CALCULATE_DEMAND: 'true' 9 | DATABASE_URL: "jdbc:postgresql://db/planwise?user=planwise&password=planwise" 10 | MAIL_SENDER: planwise-test@instedd.org 11 | PORT: 3000 12 | POSTGRES_DB: planwise 13 | POSTGRES_HOST: db 14 | POSTGRES_PASSWORD: planwise 15 | POSTGRES_USER: planwise 16 | RASTER_ISOCHRONES: 'true' 17 | env_file: ".docker-env" 18 | volumes: 19 | - data:/data 20 | depends_on: 21 | - db 22 | ports: 23 | - "3000:3000" 24 | 25 | db: 26 | container_name: planwise-prod-db 27 | image: starefossen/pgrouting:10.1-2.4-2.5 28 | environment: 29 | POSTGRES_PASSWORD: planwise 30 | POSTGRES_USER: planwise 31 | POSTGRES_DB: routing 32 | volumes: 33 | - db:/var/lib/postgresql/data 34 | 35 | mapcache: 36 | container_name: planwise-prod-mapcache 37 | image: instedd/planwise-mapcache:kenya 38 | pid: host 39 | 40 | mapserver: 41 | container_name: planwise-prod-mapserver 42 | image: instedd/planwise-mapserver:kenya 43 | pid: host 44 | volumes: 45 | - data:/data 46 | 47 | volumes: 48 | db: 49 | data: 50 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | 3 | services: 4 | app: 5 | build: 6 | context: . 7 | target: base 8 | platform: linux/amd64 9 | volumes: 10 | - ".:/app" 11 | - m2:/root/.m2 12 | - "lein:/root/.lein" 13 | depends_on: 14 | - db 15 | env_file: 16 | - ./docker-env 17 | command: "lein repl :headless" 18 | ports: 19 | - "47480:47480" 20 | - "3000:3000" 21 | 22 | client: 23 | build: 24 | context: . 25 | target: base 26 | platform: linux/amd64 27 | volumes: 28 | - ".:/app" 29 | - m2:/root/.m2 30 | - node_modules:/app/client/node_modules 31 | working_dir: "/app/client" 32 | command: "sh -c 'npm install && npm run watch'" 33 | ports: 34 | - "9630:9630" 35 | 36 | db: 37 | image: starefossen/pgrouting:10.1-2.4-2.5 38 | environment: 39 | POSTGRES_PASSWORD: planwise 40 | POSTGRES_USER: planwise 41 | POSTGRES_DB: planwise 42 | volumes: 43 | - db:/var/lib/postgresql/data 44 | ports: 45 | - "5433:5432" 46 | 47 | mapcache: 48 | image: camptocamp/mapcache:1.4 49 | volumes: 50 | - "./mapserver/mapcache.xml:/mapcache/mapcache.xml:ro" 51 | depends_on: 52 | - mapserver 53 | ports: 54 | - "5002:80" 55 | 56 | mapserver: 57 | image: camptocamp/mapserver:7.0 58 | volumes: 59 | - "./data:/data:ro" 60 | - "./mapserver/planwise.map:/etc/mapserver/planwise.map:ro" 61 | ports: 62 | - "5001:80" 63 | 64 | tools: 65 | image: instedd/planwise-tools 66 | platform: linux/amd64 67 | build: 68 | context: scripts 69 | env_file: 70 | - ./docker-env 71 | volumes: 72 | - "./data:/data" 73 | depends_on: 74 | - db 75 | 76 | volumes: 77 | db: 78 | lein: 79 | m2: 80 | node_modules: 81 | -------------------------------------------------------------------------------- /docker-env: -------------------------------------------------------------------------------- 1 | DATABASE_URL="jdbc:postgresql://db/planwise?user=planwise&password=planwise" 2 | TEST_DATABASE_URL="jdbc:postgresql://db/planwise-test?user=planwise&password=planwise" 3 | POSTGRES_HOST=db 4 | POSTGRES_PORT=5432 5 | POSTGRES_USER=planwise 6 | POSTGRES_PASSWORD=planwise 7 | POSTGRES_DB=planwise 8 | BIN_PATH=cpp/ 9 | -------------------------------------------------------------------------------- /env: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | export POSTGRES_HOST=localhost 4 | export POSTGRES_PORT=5433 5 | export POSTGRES_USER=planwise 6 | export POSTGRES_PASSWORD=planwise 7 | export POSTGRES_DB=planwise 8 | export RASTER_ISOCHRONES=true 9 | 10 | -------------------------------------------------------------------------------- /mapserver/.dockerignore: -------------------------------------------------------------------------------- 1 | data 2 | -------------------------------------------------------------------------------- /mapserver/.gitignore: -------------------------------------------------------------------------------- 1 | *.png 2 | -------------------------------------------------------------------------------- /mapserver/Dockerfile.mapcache: -------------------------------------------------------------------------------- 1 | FROM camptocamp/mapcache:1.4 2 | 3 | # Add mapcache.xml 4 | ADD mapcache.xml /mapcache/mapcache.xml 5 | 6 | EXPOSE 80 7 | -------------------------------------------------------------------------------- /mapserver/Dockerfile.mapserver: -------------------------------------------------------------------------------- 1 | FROM camptocamp/mapserver:7.0 2 | 3 | ADD planwise.map /etc/mapserver/planwise.map 4 | 5 | VOLUME /data 6 | 7 | EXPOSE 80 8 | -------------------------------------------------------------------------------- /mapserver/docker-compose.yml: -------------------------------------------------------------------------------- 1 | mapcache: 2 | image: instedd/planwise-mapcache 3 | # build: . 4 | # dockerfile: Dockerfile.mapcache 5 | volumes: 6 | - "./mapcache.xml:/mapcache/mapcache.xml:ro" 7 | links: 8 | - mapserver 9 | ports: 10 | - "5002:80" 11 | 12 | mapserver: 13 | image: instedd/planwise-mapserver 14 | # build: . 15 | # dockerfile: Dockerfile.mapserver 16 | volumes: 17 | - "../data:/data:ro" 18 | - "./planwise.map:/etc/mapserver/mapserver.map:ro" 19 | ports: 20 | - "5001:80" 21 | -------------------------------------------------------------------------------- /mapserver/partition.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | width = 9601 4 | height = 12179 5 | tilesize = 2048 6 | filename = 'KEN_popmap15_v2b' 7 | 8 | os.chdir('./data') 9 | 10 | for i in range(0, width, tilesize): 11 | for j in range(0, height, tilesize): 12 | size_x = (width - i) if i + tilesize > width else tilesize 13 | size_y = (height - j) if j + tilesize > height else tilesize 14 | gdaltranString = "gdal_translate -of GTIFF -srcwin {0} {1} {2} {3} {4}.tif {4}_{0}_{1}.tif".format(i, j, size_x, size_y, filename) 15 | os.system(gdaltranString) 16 | os.system("gdaladdo {0}_{1}_{2}.tif -r average 2 4 8 16 32".format(filename, i, j)) 17 | 18 | os.system("gdaltindex {0}.shp {0}_*".format(filename)) 19 | -------------------------------------------------------------------------------- /resources/migrations/001-extensions.up.sql: -------------------------------------------------------------------------------- 1 | CREATE EXTENSION IF NOT EXISTS postgis; 2 | CREATE EXTENSION IF NOT EXISTS pgrouting; 3 | -------------------------------------------------------------------------------- /resources/migrations/002-ways_nodes.down.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS ways_nodes; 2 | -------------------------------------------------------------------------------- /resources/migrations/002-ways_nodes.up.sql: -------------------------------------------------------------------------------- 1 | -- Creates a new table `ways_nodes` to contain all the points for all the 2 | -- `ways` table. Used for alpha shape computation. 3 | 4 | CREATE TABLE ways_nodes ( 5 | id SERIAL, 6 | gid BIGINT NOT NULL, 7 | lon NUMERIC(11,8), 8 | lat NUMERIC(11,8)); 9 | 10 | CREATE INDEX ways_nodes_gid ON ways_nodes(gid); 11 | -------------------------------------------------------------------------------- /resources/migrations/003-facilities.down.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS facilities; 2 | -------------------------------------------------------------------------------- /resources/migrations/003-facilities.up.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE facilities ( 2 | id BIGINT PRIMARY KEY, 3 | name VARCHAR(255) NOT NULL, 4 | lat NUMERIC(11,8) NOT NULL, 5 | lon NUMERIC(11,8) NOT NULL, 6 | the_geom GEOMETRY(Point, 4326) NOT NULL); 7 | 8 | CREATE INDEX facilities_the_geom_idx ON facilities USING gist (the_geom); 9 | -------------------------------------------------------------------------------- /resources/migrations/004-preprocess_functions.down.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS facilities_polygons; 2 | -------------------------------------------------------------------------------- /resources/migrations/004-preprocess_functions.up.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE facilities_polygons ( 2 | facility_id integer not null, 3 | threshold integer not null, 4 | method varchar not null, 5 | the_geom geometry(polygon, 4326) 6 | ); 7 | -------------------------------------------------------------------------------- /resources/migrations/005-projects.down.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS projects; 2 | -------------------------------------------------------------------------------- /resources/migrations/005-projects.up.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE projects ( 2 | id BIGSERIAL PRIMARY KEY, 3 | goal VARCHAR(255) NOT NULL); 4 | -------------------------------------------------------------------------------- /resources/migrations/006-facilities_type_column.down.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE facilities DROP COLUMN type; 2 | -------------------------------------------------------------------------------- /resources/migrations/006-facilities_type_column.up.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE facilities ADD COLUMN type varchar(255); 2 | -------------------------------------------------------------------------------- /resources/migrations/007-users.down.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS users; 2 | -------------------------------------------------------------------------------- /resources/migrations/007-users.up.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE users ( 2 | id BIGSERIAL PRIMARY KEY, 3 | email VARCHAR(255) UNIQUE NOT NULL, 4 | full_name VARCHAR(255), 5 | last_login TIMESTAMP WITH TIME ZONE, 6 | created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT current_timestamp 7 | ); 8 | 9 | CREATE INDEX users_email_idx ON users (email); 10 | -------------------------------------------------------------------------------- /resources/migrations/008-regions.down.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS regions; 2 | -------------------------------------------------------------------------------- /resources/migrations/008-regions.up.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE regions ( 2 | id BIGSERIAL PRIMARY KEY, 3 | country VARCHAR(255) NOT NULL, 4 | name VARCHAR(255) NOT NULL, 5 | admin_level INT, 6 | the_geom geometry(MultiPolygon,4326) 7 | ); 8 | 9 | CREATE INDEX regions_country_idx ON regions (country); 10 | -------------------------------------------------------------------------------- /resources/migrations/009-add_region_to_projects.down.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE projects DROP COLUMN region_id; 2 | -------------------------------------------------------------------------------- /resources/migrations/009-add_region_to_projects.up.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE projects ADD COLUMN region_id BIGINT NULL REFERENCES regions(id); 2 | -------------------------------------------------------------------------------- /resources/migrations/010-tokens.down.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS tokens; 2 | -------------------------------------------------------------------------------- /resources/migrations/010-tokens.up.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE tokens ( 2 | id BIGSERIAL PRIMARY KEY, 3 | user_id BIGINT NOT NULL REFERENCES users(id), 4 | scope VARCHAR(255) NOT NULL, 5 | token VARCHAR(255) NOT NULL, 6 | refresh_token VARCHAR(255) NOT NULL, 7 | expires TIMESTAMP WITH TIME ZONE 8 | ); 9 | -------------------------------------------------------------------------------- /resources/migrations/011-facility_types.down.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE facility_types; 2 | 3 | ALTER TABLE facilities DROP COLUMN type_id; 4 | ALTER TABLE facilities ADD COLUMN type TYPE varchar(255); 5 | -------------------------------------------------------------------------------- /resources/migrations/011-facility_types.up.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE facility_types ( 2 | id SERIAL PRIMARY KEY, 3 | name VARCHAR(255) NOT NULL); 4 | 5 | ALTER TABLE facilities DROP COLUMN type; 6 | ALTER TABLE facilities ADD COLUMN type_id INTEGER REFERENCES facility_types(id); 7 | 8 | -------------------------------------------------------------------------------- /resources/migrations/012-add_facilities_count_to_projects.down.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE projects DROP COLUMN facilities_count; 2 | -------------------------------------------------------------------------------- /resources/migrations/012-add_facilities_count_to_projects.up.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE projects ADD COLUMN facilities_count integer; 2 | -------------------------------------------------------------------------------- /resources/migrations/013-process_single_facility.down.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE facilities_polygons DROP COLUMN starting_node; 2 | -------------------------------------------------------------------------------- /resources/migrations/014-projects_filters_and_stats.down.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE projects ADD COLUMN facilities_count integer; 2 | ALTER TABLE projects DROP COLUMN stats; 3 | ALTER TABLE projects DROP COLUMN filters; 4 | -------------------------------------------------------------------------------- /resources/migrations/014-projects_filters_and_stats.up.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE projects ADD COLUMN filters text; 2 | ALTER TABLE projects ADD COLUMN stats text; 3 | ALTER TABLE projects DROP COLUMN facilities_count; 4 | -------------------------------------------------------------------------------- /resources/migrations/015-apply_traffic_factor.down.sql: -------------------------------------------------------------------------------- 1 | DO $$ 2 | BEGIN 3 | IF EXISTS (SELECT 1 FROM pg_class WHERE relname = 'ways') THEN 4 | UPDATE ways 5 | SET cost_s = length_m / (maxspeed_forward * 5 / 18) * 1.0; 6 | END IF; 7 | END; 8 | $$ 9 | -------------------------------------------------------------------------------- /resources/migrations/015-apply_traffic_factor.up.sql: -------------------------------------------------------------------------------- 1 | DO $$ 2 | BEGIN 3 | IF EXISTS (SELECT 1 FROM pg_class WHERE relname = 'ways') THEN 4 | UPDATE ways 5 | SET cost_s = length_m / (maxspeed_forward * 5.0 / 18) * 1.5; 6 | END IF; 7 | END; 8 | $$ 9 | -------------------------------------------------------------------------------- /resources/migrations/016-add_preview_geom_to_regions.down.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE regions 2 | DROP COLUMN IF EXISTS preview_geom; 3 | -------------------------------------------------------------------------------- /resources/migrations/016-add_preview_geom_to_regions.up.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE regions 2 | ADD COLUMN preview_geom geometry(MultiPolygon,4326); 3 | 4 | DO $$ 5 | BEGIN 6 | PERFORM calculate_regions_previews(); 7 | END; 8 | $$ 9 | -------------------------------------------------------------------------------- /resources/migrations/017-facilities_polygons_index.down.sql: -------------------------------------------------------------------------------- 1 | DROP INDEX facilities_polygons_facility_method_threshold; 2 | -------------------------------------------------------------------------------- /resources/migrations/017-facilities_polygons_index.up.sql: -------------------------------------------------------------------------------- 1 | CREATE INDEX facilities_polygons_facility_method_threshold 2 | ON facilities_polygons(facility_id, method, threshold); 3 | -------------------------------------------------------------------------------- /resources/migrations/018-add_owner_user_to_projects.down.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE projects DROP COLUMN owner_id; 2 | -------------------------------------------------------------------------------- /resources/migrations/018-add_owner_user_to_projects.up.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE projects ADD COLUMN owner_id BIGINT REFERENCES users(id); 2 | -------------------------------------------------------------------------------- /resources/migrations/019-add_id_to_facilities_polygons.down.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE facilities_polygons 2 | DROP COLUMN IF EXISTS id; 3 | -------------------------------------------------------------------------------- /resources/migrations/019-add_id_to_facilities_polygons.up.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE facilities_polygons ADD COLUMN id BIGSERIAL PRIMARY KEY; 2 | -------------------------------------------------------------------------------- /resources/migrations/020-facility_polygons_regions.down.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS facilities_polygons_regions; 2 | -------------------------------------------------------------------------------- /resources/migrations/020-facility_polygons_regions.up.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE facilities_polygons_regions ( 2 | facility_polygon_id BIGINT REFERENCES facilities_polygons(id) ON DELETE CASCADE, 3 | region_id INT REFERENCES regions(id) ON DELETE CASCADE, 4 | area FLOAT, 5 | population INT); 6 | 7 | CREATE INDEX facilities_polygons_regions_facility_polygon_id 8 | ON facilities_polygons_regions(facility_polygon_id); 9 | 10 | CREATE INDEX facilities_polygons_regions_region_id 11 | ON facilities_polygons_regions(region_id); 12 | 13 | INSERT INTO facilities_polygons_regions(facility_polygon_id, region_id, area) 14 | SELECT fp.id, r.id, ST_Area(ST_Intersection(fp.the_geom, r.the_geom)) 15 | FROM regions AS r INNER JOIN facilities_polygons AS fp 16 | ON ST_Intersects(fp.the_geom, r.the_geom); 17 | -------------------------------------------------------------------------------- /resources/migrations/021-facility_polygons_data.down.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE facilities_polygons 2 | DROP COLUMN IF EXISTS area; 3 | 4 | ALTER TABLE facilities_polygons 5 | DROP COLUMN IF EXISTS population; 6 | -------------------------------------------------------------------------------- /resources/migrations/021-facility_polygons_data.up.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE facilities_polygons 2 | ADD COLUMN area FLOAT; 3 | 4 | ALTER TABLE facilities_polygons 5 | ADD COLUMN population INT; 6 | -------------------------------------------------------------------------------- /resources/migrations/022-add_population_to_regions.down.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE regions 2 | DROP COLUMN IF EXISTS total_population; 3 | -------------------------------------------------------------------------------- /resources/migrations/022-add_population_to_regions.up.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE regions 2 | ADD COLUMN total_population INT NULL; 3 | -------------------------------------------------------------------------------- /resources/migrations/023-add_facilities_polygon_constraint.down.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE facilities_polygons 2 | DROP CONSTRAINT facilities_polygons_facility_id_fkey; 3 | 4 | -------------------------------------------------------------------------------- /resources/migrations/023-add_facilities_polygon_constraint.up.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE facilities_polygons 2 | ADD CONSTRAINT facilities_polygons_facility_id_fkey 3 | FOREIGN KEY (facility_id) 4 | REFERENCES facilities(id) 5 | ON DELETE CASCADE; 6 | -------------------------------------------------------------------------------- /resources/migrations/024-add_facilities_processing_status.down.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE facilities 2 | DROP COLUMN IF EXISTS processing_status; 3 | -------------------------------------------------------------------------------- /resources/migrations/024-add_facilities_processing_status.up.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE facilities 2 | ADD COLUMN processing_status VARCHAR; 3 | 4 | UPDATE facilities AS f 5 | SET processing_status = 6 | CASE 7 | WHEN EXISTS (SELECT 1 FROM facilities_polygons fp WHERE fp.facility_id = f.id LIMIT 1) 8 | THEN 'ok' 9 | ELSE 10 | 'no-road-network' 11 | END 12 | WHERE f.processing_status IS NULL; 13 | 14 | -- The following version indeed calculates if the facility is too far from the 15 | -- road network, but takes too long to run. So we use the version above, that 16 | -- assumes that, if we are running migrations, there are no facilities pending 17 | -- processing, so they have polygons iif they are close to the road network. 18 | 19 | -- UPDATE facilities AS f 20 | -- SET processing_status = 21 | -- CASE 22 | -- WHEN ST_Distance( 23 | -- ST_GeogFromWKB(f.the_geom), 24 | -- ST_GeogFromWKB(( 25 | -- SELECT wvp.the_geom 26 | -- FROM ways_vertices_pgr AS wvp 27 | -- WHERE wvp.id = closest_node(f.the_geom) 28 | -- LIMIT 1 29 | -- ))) > 1000 THEN 'no-road-network' 30 | -- ELSE 'ok' 31 | -- END 32 | -- WHERE f.processing_status IS NULL; 33 | -------------------------------------------------------------------------------- /resources/migrations/025-add_max_population_to_regions.down.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE regions 2 | DROP COLUMN IF EXISTS max_population; 3 | -------------------------------------------------------------------------------- /resources/migrations/025-add_max_population_to_regions.up.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE regions 2 | ADD COLUMN max_population INT NULL; 3 | -------------------------------------------------------------------------------- /resources/migrations/026-datasets.down.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS datasets; 2 | -------------------------------------------------------------------------------- /resources/migrations/026-datasets.up.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS datasets ( 2 | id BIGSERIAL PRIMARY KEY, 3 | name VARCHAR(255) NOT NULL, 4 | description VARCHAR(255), 5 | owner_id BIGINT NOT NULL REFERENCES users(id), 6 | collection_id BIGINT, 7 | import_mappings TEXT, 8 | import_result TEXT 9 | ); 10 | -------------------------------------------------------------------------------- /resources/migrations/027-dataset_references.down.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE facilities DROP COLUMN id CASCADE; 2 | ALTER TABLE facilities RENAME COLUMN site_id TO id; 3 | ALTER TABLE facilities DROP COLUMN IF EXISTS dataset_id; 4 | ALTER TABLE facility_types DROP COLUMN IF EXISTS dataset_id; 5 | ALTER TABLE projects DROP COLUMN IF EXISTS dataset_id; 6 | -------------------------------------------------------------------------------- /resources/migrations/027-dataset_references.up.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE facility_types 2 | ADD COLUMN dataset_id BIGINT NOT NULL 3 | REFERENCES datasets(id) ON DELETE CASCADE; 4 | 5 | ALTER TABLE facilities 6 | DROP CONSTRAINT IF EXISTS facilities_pkey CASCADE; 7 | 8 | ALTER TABLE facilities 9 | RENAME COLUMN id TO site_id; 10 | 11 | ALTER TABLE facilities 12 | ADD COLUMN id SERIAL PRIMARY KEY; 13 | 14 | ALTER TABLE facilities 15 | ADD COLUMN dataset_id BIGINT NOT NULL 16 | REFERENCES datasets(id) ON DELETE CASCADE; 17 | 18 | ALTER TABLE facilities_polygons 19 | ADD CONSTRAINT facilities_polygons_facility_id_pkey 20 | FOREIGN KEY (facility_id) 21 | REFERENCES facilities(id) ON DELETE CASCADE; 22 | 23 | ALTER TABLE projects 24 | ADD COLUMN dataset_id BIGINT NOT NULL 25 | REFERENCES datasets(id) ON DELETE RESTRICT; 26 | 27 | CREATE INDEX facilities_dataset_id_idx ON facilities(dataset_id); 28 | CREATE INDEX projects_dataset_id_idx ON projects(dataset_id); 29 | -------------------------------------------------------------------------------- /resources/migrations/028-project_share.down.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS project_shares; 2 | 3 | ALTER TABLE projects 4 | DROP COLUMN IF EXISTS share_token; 5 | -------------------------------------------------------------------------------- /resources/migrations/028-project_share.up.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS project_shares ( 2 | project_id BIGINT REFERENCES projects(id) ON DELETE CASCADE, 3 | user_id BIGINT REFERENCES users(id) ON DELETE CASCADE, 4 | PRIMARY KEY (project_id, user_id) 5 | ); 6 | 7 | CREATE INDEX project_shares_project_id_idx ON project_shares(project_id); 8 | CREATE INDEX project_shares_user_id_idx ON project_shares(user_id); 9 | 10 | ALTER TABLE projects 11 | ADD COLUMN share_token VARCHAR(60) NULL; 12 | 13 | CREATE EXTENSION IF NOT EXISTS pgcrypto; 14 | 15 | UPDATE projects SET share_token = gen_random_uuid() 16 | WHERE share_token IS NULL; 17 | 18 | CREATE INDEX proje_sharing_token_idx ON projects(share_token); 19 | -------------------------------------------------------------------------------- /resources/migrations/029-add_raster_pixel_area_to_regions.down.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE regions 2 | DROP COLUMN IF EXISTS raster_pixel_area; 3 | -------------------------------------------------------------------------------- /resources/migrations/029-add_raster_pixel_area_to_regions.up.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE regions 2 | ADD COLUMN raster_pixel_area INT NULL; 3 | -------------------------------------------------------------------------------- /resources/migrations/030-facilities-type-id-contraint.down.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE facilities ALTER COLUMN type_id DROP NOT NULL; 2 | -------------------------------------------------------------------------------- /resources/migrations/030-facilities-type-id-contraint.up.sql: -------------------------------------------------------------------------------- 1 | DELETE FROM facilities WHERE type_id IS NULL; 2 | ALTER TABLE facilities ALTER COLUMN type_id SET NOT NULL; 3 | -------------------------------------------------------------------------------- /resources/migrations/031-add_import_job_to_datasets.down.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE datasets 2 | DROP COLUMN IF EXISTS import_job; 3 | -------------------------------------------------------------------------------- /resources/migrations/031-add_import_job_to_datasets.up.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE datasets 2 | ADD COLUMN import_job TEXT; 3 | -------------------------------------------------------------------------------- /resources/migrations/032-add_projects_state.down.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE projects DROP COLUMN state; 2 | -------------------------------------------------------------------------------- /resources/migrations/032-add_projects_state.up.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE projects ADD COLUMN state TEXT; 2 | -------------------------------------------------------------------------------- /resources/migrations/033-add_capacity_to_facilities.down.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE facilities DROP COLUMN capacity; 2 | -------------------------------------------------------------------------------- /resources/migrations/033-add_capacity_to_facilities.up.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE facilities ADD COLUMN capacity INTEGER DEFAULT 0; 2 | -------------------------------------------------------------------------------- /resources/migrations/034-add_code_to_facility_types.down.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE facility_types DROP COLUMN IF EXISTS code; 2 | -------------------------------------------------------------------------------- /resources/migrations/034-add_code_to_facility_types.up.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE facility_types ADD COLUMN code VARCHAR(255); 2 | -------------------------------------------------------------------------------- /resources/migrations/035-datasets2.down.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS sites2; 2 | 3 | DROP TABLE IF EXISTS datasets2; 4 | -------------------------------------------------------------------------------- /resources/migrations/035-datasets2.up.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS datasets2 ( 2 | id BIGSERIAL PRIMARY KEY, 3 | name VARCHAR(255) NOT NULL, 4 | "last-version" INT, 5 | "owner-id" BIGINT NOT NULL REFERENCES users(id) 6 | ); 7 | 8 | CREATE TABLE IF NOT EXISTS sites2 ( 9 | id INT NOT NULL, 10 | version INT, 11 | "dataset-id" BIGINT NOT NULL REFERENCES datasets2(id), 12 | name VARCHAR(255) NOT NULL, 13 | lat NUMERIC(11,8) NOT NULL, 14 | lon NUMERIC(11,8) NOT NULL, 15 | the_geom GEOMETRY(Point, 4326) NOT NULL, 16 | capacity INT, 17 | type TEXT, 18 | tags TEXT 19 | ); 20 | -------------------------------------------------------------------------------- /resources/migrations/036-projects2.down.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS projects2; 2 | -------------------------------------------------------------------------------- /resources/migrations/036-projects2.up.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS projects2 ( 2 | id BIGSERIAL PRIMARY KEY, 3 | "owner-id" BIGINT NOT NULL REFERENCES users(id), 4 | name VARCHAR(255) NOT NULL 5 | ); 6 | -------------------------------------------------------------------------------- /resources/migrations/037-add-config-to-projects2.down.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE projects2 DROP COLUMN config; 2 | -------------------------------------------------------------------------------- /resources/migrations/037-add-config-to-projects2.up.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE projects2 ADD COLUMN config TEXT; 2 | -------------------------------------------------------------------------------- /resources/migrations/038-add_coverage_to_datasets2.down.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE datasets2 DROP COLUMN "coverage-algorithm"; 2 | -------------------------------------------------------------------------------- /resources/migrations/038-add_coverage_to_datasets2.up.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE datasets2 ADD COLUMN "coverage-algorithm" VARCHAR(100); 2 | -------------------------------------------------------------------------------- /resources/migrations/039-sites2_coverage.down.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE "sites2_coverage"; 2 | 3 | ALTER TABLE "sites2" DROP COLUMN "processing-status"; 4 | ALTER TABLE "sites2" DROP COLUMN "id"; 5 | ALTER TABLE "sites2" RENAME COLUMN "source-id" TO "id"; 6 | -------------------------------------------------------------------------------- /resources/migrations/039-sites2_coverage.up.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE "sites2" RENAME COLUMN "id" TO "source-id"; 2 | ALTER TABLE "sites2" ADD COLUMN "id" SERIAL PRIMARY KEY; 3 | ALTER TABLE "sites2" ADD COLUMN "processing-status" VARCHAR; 4 | 5 | CREATE TABLE "sites2_coverage" ( 6 | "id" SERIAL PRIMARY KEY, 7 | "site-id" INTEGER REFERENCES sites2(id), 8 | "algorithm" VARCHAR(100) NOT NULL, 9 | "options" VARCHAR NOT NULL, 10 | "geom" GEOMETRY(Polygon,4326), 11 | "raster" VARCHAR 12 | ); 13 | 14 | -------------------------------------------------------------------------------- /resources/migrations/040-population_sources.down.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS population_sources; 2 | -------------------------------------------------------------------------------- /resources/migrations/040-population_sources.up.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS population_sources ( 2 | id BIGSERIAL PRIMARY KEY, 3 | name VARCHAR(255) NOT NULL, 4 | tif_file VARCHAR(255) NOT NULL 5 | ); 6 | -------------------------------------------------------------------------------- /resources/migrations/041-populations.down.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS populations; 2 | -------------------------------------------------------------------------------- /resources/migrations/041-populations.up.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS populations ( 2 | id BIGSERIAL PRIMARY KEY, 3 | source_id BIGINT NOT NULL REFERENCES population_sources(id), 4 | region_id BIGINT NOT NULL REFERENCES regions(id), 5 | total_population INT NULL, 6 | max_population INT NULL, 7 | raster_pixel_area INT NULL 8 | ); 9 | -------------------------------------------------------------------------------- /resources/migrations/042-add-dataset-id-to-projects2.down.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE projects2 DROP COLUMN "dataset-id"; 2 | -------------------------------------------------------------------------------- /resources/migrations/042-add-dataset-id-to-projects2.up.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE projects2 ADD COLUMN "dataset-id" BIGINT REFERENCES datasets2(id) DEFAULT NULL; 2 | -------------------------------------------------------------------------------- /resources/migrations/043-add-population-source-id-to-projects2.down.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE projects2 DROP COLUMN "population-source-id""; 2 | -------------------------------------------------------------------------------- /resources/migrations/043-add-population-source-id-to-projects2.up.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE projects2 ADD COLUMN "population-source-id" BIGINT REFERENCES population_sources(id) DEFAULT NULL; 2 | -------------------------------------------------------------------------------- /resources/migrations/044-add_projects2_state.down.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE projects DROP COLUMN state; 2 | -------------------------------------------------------------------------------- /resources/migrations/044-add_projects2_state.up.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE projects2 2 | ADD COLUMN state VARCHAR(255); 3 | -------------------------------------------------------------------------------- /resources/migrations/045-scenarios.down.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS scenarios; 2 | -------------------------------------------------------------------------------- /resources/migrations/045-scenarios.up.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS scenarios ( 2 | id BIGSERIAL PRIMARY KEY, 3 | name VARCHAR(255) NOT NULL, 4 | "project-id" BIGINT NOT NULL REFERENCES projects2(id), 5 | label VARCHAR(255) NULL, 6 | investment NUMERIC(12, 2) NULL, 7 | "demand-coverage" BIGINT NULL, 8 | changeset TEXT NOT NULL 9 | ); 10 | -------------------------------------------------------------------------------- /resources/migrations/046-add-region-id-to-projects2.down.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE "projects2" DROP COLUMN "region-id"; 2 | -------------------------------------------------------------------------------- /resources/migrations/046-add-region-id-to-projects2.up.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE "projects2" ADD COLUMN "region-id" BIGINT REFERENCES regions(id); 2 | -------------------------------------------------------------------------------- /resources/migrations/047-add_dataset_version_to_projects2.down.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE "projects2" DROP COLUMN "dataset-version"; 2 | -------------------------------------------------------------------------------- /resources/migrations/047-add_dataset_version_to_projects2.up.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE "projects2" ADD COLUMN "dataset-version" INTEGER; 2 | -------------------------------------------------------------------------------- /resources/migrations/048-add_state_to_scenarios.down.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE "scenarios" DROP COLUMN "updated-at"; 2 | ALTER TABLE "scenarios" DROP COLUMN "state"; 3 | ALTER TABLE "scenarios" DROP COLUMN "raster"; 4 | -------------------------------------------------------------------------------- /resources/migrations/048-add_state_to_scenarios.up.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE "scenarios" ADD COLUMN "raster" VARCHAR(255); 2 | ALTER TABLE "scenarios" ADD COLUMN "state" VARCHAR(100); 3 | ALTER TABLE "scenarios" ADD COLUMN "updated-at" TIMESTAMP; 4 | -------------------------------------------------------------------------------- /resources/migrations/049-add_engine_config_to_projects2.down.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE "projects2" DROP COLUMN "engine-config"; 2 | -------------------------------------------------------------------------------- /resources/migrations/049-add_engine_config_to_projects2.up.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE "projects2" ADD COLUMN "engine-config" TEXT; 2 | -------------------------------------------------------------------------------- /resources/migrations/050-add_deleted_at_to_projects2.down.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE "projects2" DROP COLUMN "deleted-at"; 2 | -------------------------------------------------------------------------------- /resources/migrations/050-add_deleted_at_to_projects2.up.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE "projects2" ADD COLUMN "deleted-at" TIMESTAMP DEFAULT NULL; 2 | -------------------------------------------------------------------------------- /resources/migrations/051-rename_datasets_to_providers_set.down.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE providers_coverage 2 | RENAME COLUMN "provider-id" TO "site-id"; 3 | 4 | ALTER TABLE "providers_coverage" 5 | RENAME TO "sites2_coverage"; 6 | 7 | ALTER TABLE providers 8 | RENAME TO sites2; 9 | 10 | ALTER TABLE projects2 11 | RENAME COLUMN "provider-set-id" TO "dataset-id"; 12 | 13 | ALTER TABLE projects2 14 | RENAME COLUMN "provider-set-version" TO "dataset-version"; 15 | 16 | AlTER TABLE sites2 17 | RENAME COLUMN "provider-set-id" TO "dataset-id"; 18 | 19 | ALTER TABLE providers_set 20 | RENAME TO datasets2; -------------------------------------------------------------------------------- /resources/migrations/051-rename_datasets_to_providers_set.up.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE datasets2 2 | RENAME TO providers_set; 3 | 4 | AlTER TABLE sites2 5 | RENAME COLUMN "dataset-id" TO "provider-set-id"; 6 | 7 | ALTER TABLE projects2 8 | RENAME COLUMN "dataset-version" TO "provider-set-version"; 9 | 10 | ALTER TABLE projects2 11 | RENAME COLUMN "dataset-id" TO "provider-set-id"; 12 | 13 | ALTER TABLE sites2 14 | RENAME TO providers; 15 | 16 | ALTER TABLE "sites2_coverage" 17 | RENAME TO "providers_coverage"; 18 | 19 | ALTER TABLE providers_coverage 20 | RENAME COLUMN "site-id" TO "provider-id"; 21 | -------------------------------------------------------------------------------- /resources/migrations/052-add_sources_set.down.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS source_set; 2 | -------------------------------------------------------------------------------- /resources/migrations/052-add_sources_set.up.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS source_set ( 2 | id BIGSERIAL PRIMARY KEY, 3 | name VARCHAR(255) NOT NULL, 4 | type TEXT NOT NULL, 5 | unit VARCHAR(255) NOT NULL, 6 | raster_file VARCHAR(255) NOT NULL 7 | ); 8 | -------------------------------------------------------------------------------- /resources/migrations/053-add_sources.down.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS sources; -------------------------------------------------------------------------------- /resources/migrations/053-add_sources.up.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS sources ( 2 | id BIGSERIAL PRIMARY KEY, 3 | set_id BIGINT NOT NULL REFERENCES source_set(id), 4 | name VARCHAR(255) NOT NULL, 5 | type TEXT NOT NULL, 6 | the_geom geometry(POINT, 4326) NOT NULL, 7 | quantity INT NULL 8 | ); 9 | -------------------------------------------------------------------------------- /resources/migrations/054-add_providers_data_to_scenarios.down.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE scenarios DROP COLUMN "providers-data"; -------------------------------------------------------------------------------- /resources/migrations/054-add_providers_data_to_scenarios.up.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE scenarios 2 | ADD COLUMN "providers-data" TEXT; 3 | -------------------------------------------------------------------------------- /resources/migrations/055-update-population-source-id-to-projects2.down.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE projects2 2 | DROP COLUMN IF EXISTS "source-set-id", 3 | ADD COLUMN "population-source-id" BIGINT REFERENCES population_sources(id) DEFAULT NULL; 4 | -------------------------------------------------------------------------------- /resources/migrations/055-update-population-source-id-to-projects2.up.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE projects2 2 | DROP COLUMN IF EXISTS "population-source-id", 3 | ADD COLUMN "source-set-id" BIGINT REFERENCES source_set(id) DEFAULT NULL; 4 | -------------------------------------------------------------------------------- /resources/migrations/056-add_owner_to_sources.down.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE source_set 2 | DROP COLUMN "owner-id"; 3 | -------------------------------------------------------------------------------- /resources/migrations/056-add_owner_to_sources.up.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE source_set 2 | ADD COLUMN "owner-id" BIGINT REFERENCES users(id) DEFAULT NULL; 3 | -------------------------------------------------------------------------------- /resources/migrations/057-nullable_raster_file_sources.down.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE source_set 2 | ALTER COLUMN raster_file SET NOT NULL; 3 | -------------------------------------------------------------------------------- /resources/migrations/057-nullable_raster_file_sources.up.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE source_set 2 | ALTER COLUMN raster_file DROP NOT NULL; 3 | 4 | -------------------------------------------------------------------------------- /resources/migrations/058-add_sources_data_to_scenarios.down.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE scenarios 2 | DROP COLUMN "sources-data"; 3 | -------------------------------------------------------------------------------- /resources/migrations/058-add_sources_data_to_scenarios.up.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE scenarios 2 | ADD COLUMN "sources-data" TEXT; 3 | -------------------------------------------------------------------------------- /resources/migrations/059-add_new_providers_geom_to_scenarios.down.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE scenarios 2 | DROP COLUMN "new-providers-geom"; 3 | -------------------------------------------------------------------------------- /resources/migrations/059-add_new_providers_geom_to_scenarios.up.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE scenarios 2 | ADD COLUMN "new-providers-geom" TEXT; 3 | -------------------------------------------------------------------------------- /resources/migrations/060-add_error_message_to_scenarios.down.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE scenarios 2 | DROP COLUMN "error-message"; 3 | -------------------------------------------------------------------------------- /resources/migrations/060-add_error_message_to_scenarios.up.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE scenarios 2 | ADD COLUMN "error-message" TEXT; 3 | -------------------------------------------------------------------------------- /resources/migrations/061-remove_deleted_at_from_projects2.down.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE "projects2" ADD COLUMN "deleted-at" TIMESTAMP DEFAULT NULL; 2 | -------------------------------------------------------------------------------- /resources/migrations/061-remove_deleted_at_from_projects2.up.sql: -------------------------------------------------------------------------------- 1 | DELETE FROM "scenarios" s 2 | WHERE s."project-id" IN (SELECT id FROM "projects2" p WHERE p."deleted-at" is NOT NULL); 3 | DELETE FROM "projects2" WHERE "deleted-at" is NOT NULL; 4 | ALTER TABLE "projects2" DROP COLUMN IF EXISTS "deleted-at"; 5 | -------------------------------------------------------------------------------- /resources/migrations/062-coverage_contexts.down.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE coverages; 2 | DROP TABLE coverage_contexts; 3 | -------------------------------------------------------------------------------- /resources/migrations/062-coverage_contexts.up.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE coverage_contexts ( 2 | id BIGSERIAL PRIMARY KEY, -- id is internal and used for cross-reference with coverages only 3 | cid VARCHAR(255) UNIQUE, -- stringified version of the context id 4 | region_id BIGINT REFERENCES regions(id) ON DELETE CASCADE, 5 | options TEXT NOT NULL 6 | ); 7 | 8 | CREATE TABLE coverages ( 9 | context_id BIGINT REFERENCES coverage_contexts(id) ON DELETE CASCADE, 10 | lid VARCHAR(255) NOT NULL, -- stringified version of the location id 11 | location GEOMETRY(Point, 4326) NOT NULL, 12 | coverage GEOMETRY(MultiPolygon, 4326) NOT NULL, 13 | raster_file VARCHAR(255), -- name of the raster file, which is relative to the context directory 14 | 15 | CONSTRAINT id_with_context UNIQUE (context_id, lid) 16 | ); 17 | -------------------------------------------------------------------------------- /resources/migrations/063-add_geo_coverage_to_scenarios.down.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE scenarios DROP COLUMN "geo-coverage"; 2 | -------------------------------------------------------------------------------- /resources/migrations/063-add_geo_coverage_to_scenarios.up.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE scenarios ADD COLUMN "geo-coverage" FLOAT DEFAULT NULL; 2 | -------------------------------------------------------------------------------- /resources/migrations/064-add_coverage_to_projects2.down.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE projects2 DROP COLUMN "coverage-algorithm"; 2 | ALTER TABLE providers_set ADD COLUMN "coverage-algorithm" VARCHAR(100); 3 | -------------------------------------------------------------------------------- /resources/migrations/064-add_coverage_to_projects2.up.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE projects2 ADD COLUMN "coverage-algorithm" VARCHAR(100); 2 | UPDATE projects2 AS p2 3 | SET "coverage-algorithm" = ( 4 | SELECT "coverage-algorithm" 5 | FROM providers_set 6 | WHERE p2."provider-set-id" = providers_set.id 7 | ); 8 | ALTER TABLE providers_set DROP COLUMN "coverage-algorithm"; 9 | -------------------------------------------------------------------------------- /resources/migrations/065-add_population_under_coverage_to_scenarios.down.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE scenarios DROP COLUMN "population-under-coverage"; 2 | -------------------------------------------------------------------------------- /resources/migrations/065-add_population_under_coverage_to_scenarios.up.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE scenarios ADD COLUMN "population-under-coverage" FLOAT DEFAULT NULL; 2 | -------------------------------------------------------------------------------- /resources/migrations/066-rename_investment_to_effort.down.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE scenarios 2 | RENAME COLUMN "effort" TO "investment"; 3 | -------------------------------------------------------------------------------- /resources/migrations/066-rename_investment_to_effort.up.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE scenarios 2 | RENAME COLUMN "investment" TO "effort"; 3 | -------------------------------------------------------------------------------- /resources/migrations/067-add_providers_set_version_index.down.sql: -------------------------------------------------------------------------------- 1 | DROP INDEX providers_set_version; 2 | -------------------------------------------------------------------------------- /resources/migrations/067-add_providers_set_version_index.up.sql: -------------------------------------------------------------------------------- 1 | CREATE INDEX providers_set_version ON providers ("provider-set-id", "version"); 2 | -------------------------------------------------------------------------------- /resources/migrations/068-add_regions_name_index.down.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE regions DROP CONSTRAINT "regions_name_index"; 2 | -------------------------------------------------------------------------------- /resources/migrations/068-add_regions_name_index.up.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE regions ADD CONSTRAINT "regions_name_index" UNIQUE ("country", "name", "admin_level"); 2 | -------------------------------------------------------------------------------- /resources/planwise/cli.edn: -------------------------------------------------------------------------------- 1 | {:duct.core/include ["planwise/config"] 2 | 3 | :duct.logger/timbre 4 | {:level :info 5 | :ns-blacklist ["duct.database.sql.hikaricp"]}} 6 | -------------------------------------------------------------------------------- /resources/planwise/errors/403.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |