├── .gitignore ├── README.md ├── ansible ├── geoserver.yml └── roles │ ├── geoserver │ ├── defaults │ │ └── main.yml │ └── tasks │ │ └── main.yml │ ├── oracle-java │ ├── defaults │ │ └── main.yml │ ├── tasks │ │ └── main.yml │ └── templates │ │ └── java_home.sh.j2 │ └── tomcat │ ├── defaults │ └── main.yml │ ├── handlers │ └── main.yml │ ├── tasks │ └── main.yml │ ├── templates │ ├── context.xml.j2 │ ├── java-ld.conf.j2 │ ├── server.xml.j2 │ ├── tomcat-users.xml.j2 │ ├── tomcat.conf.j2 │ ├── tomcat.service.j2 │ ├── tomcat.sysv.j2 │ └── web.xml.j2 │ └── vars │ └── main.yml ├── bash ├── download_abs_geographies.sh └── unzip_abs_geographies.sh ├── client-app ├── .env.production ├── .gitignore ├── README.md ├── package.json ├── public │ ├── favicon.ico │ └── index.html ├── src │ ├── components │ │ ├── AppHeader.js │ │ ├── InfoPanel.js │ │ ├── OlMap.js │ │ ├── PaperHeader.js │ │ ├── SelectStat.js │ │ ├── SelectStatControl.js │ │ └── SidePanel.js │ ├── containers │ │ └── app │ │ │ └── App.js │ ├── external │ │ ├── mapbox-gl-style-spec │ │ │ ├── deref.js │ │ │ ├── feature_filter │ │ │ │ ├── README.md │ │ │ │ └── index.js │ │ │ ├── function │ │ │ │ ├── color_spaces.js │ │ │ │ └── index.js │ │ │ └── util │ │ │ │ ├── extend.js │ │ │ │ ├── get_type.js │ │ │ │ ├── interpolate.js │ │ │ │ ├── parse_color.js │ │ │ │ ├── ref_properties.js │ │ │ │ └── unbundle_jsonlint.js │ │ ├── stylefunction.js │ │ └── util.js │ ├── index.css │ ├── index.js │ ├── modules │ │ ├── index.js │ │ └── map.js │ ├── registerServiceWorker.js │ └── store.js └── yarn.lock ├── cloudformation ├── 1-vpc-setup.yaml ├── 2-security-groups.yaml ├── 3-db-setup.yaml ├── 4-admin.yaml └── 5-geoserver.yaml ├── images ├── client_end_state.png ├── client_select_statistic_control.png ├── client_stage_1.png ├── client_stage_2.png ├── cloudformation_admin_params.png ├── cloudformation_db_params.png ├── cloudformation_geoserver_params.png ├── cloudformation_sg_params.png ├── cloudformation_vpc_params.png ├── color_brewer.png ├── db_schema.png ├── geoserver_add_postgis_store.png ├── geoserver_add_workspace.png ├── geoserver_bounding_boxes.png ├── geoserver_chain_settings.png ├── geoserver_enable_mvt.png ├── geoserver_filter_chains.png ├── geoserver_layer_preview.png ├── geoserver_new_layer_table.png ├── geoserver_styles.png ├── maputnik_add_filter.png ├── maputnik_add_layer.png ├── maputnik_add_source.png ├── maputnik_duplicate_layer.png ├── putty_pk.png ├── putty_session.png ├── putty_username.png ├── puttygen.png ├── qgis_import_layer_file_button.png ├── qgis_import_vector_layer.png ├── qgis_postgis_connection.png ├── qgis_select_features.png ├── qgis_select_features_dialog.png ├── react_redux.png └── workshop-architecture.png ├── packer └── geoserver.json ├── python ├── abs_loader.py ├── generate_abs_mb_style.py └── style_generator_config │ ├── point_blue.json │ ├── point_green.json │ ├── point_orange.json │ ├── point_pink.json │ ├── point_purple.json │ ├── poly_blue.json │ ├── poly_green.json │ ├── poly_orange.json │ ├── poly_pink.json │ └── poly_purple.json ├── section-1-setup.md ├── section-2-aws.md ├── section-3-data.md ├── section-4-geoserver.md ├── section-5-publish.md ├── section-6-style.md ├── section-7-1-skeleton-app.md ├── section-7-2-ui.md ├── section-7-3-finish-app.md ├── section-7-4-publish-app.md ├── section-7.md ├── section-8-clean.md ├── sql ├── geoserver-consolidate.sql ├── geoserver-stats.sql ├── geoserver-views.sql └── jenks.sql └── styles ├── point_blue.json ├── point_green.json ├── point_orange.json ├── point_pink.json ├── point_purple.json ├── poly_blue.json ├── poly_green.json ├── poly_orange.json ├── poly_pink.json └── poly_purple.json /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | *.retry 3 | 4 | # Vim swap files 5 | .*.sw? 6 | 7 | # Python bytecode 8 | *.py[co] 9 | 10 | # MacOS desktop services 11 | .DS_Store 12 | 13 | data 14 | manifest.json 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 0 to 100 on AWS – Building a full stack web mapping application with PostGIS, GeoServer, OpenLayers and ReactJS 2 | 3 | #### FOSS4G SotM Oceania - Afternoon Workshop #1 - 20th November 2018 4 | 5 | > Please note that this repository is **archived** and **read only**. 6 | > It is available as a reference, but is no longer actively maintained. 7 | 8 | 9 | ## Introduction 10 | 11 | This 3.5 hour workshop is intended to provide an overview and hands-on 12 | experience in deploying a full stack web mapping application using open source 13 | geospatial technologies including GeoServer, PostGIS and OpenLayers. 14 | 15 | By the end of the workshop the aim is to have a fully functional web app that 16 | allows you to interactively explore a set of ABS statistics across Victoria. 17 | 18 | ![Workshop Architecture Overview](images/client_end_state.png) 19 | 20 | | Section | Expected time to complete | 21 | | --- | --- | 22 | | [1. Prequisites and setup](section-1-setup.md) | 10 min | 23 | | [2. Deploy AWS resources](section-2-aws.md) | 30 min | 24 | | [3. Import data into PostGIS](section-3-data.md) | 30 min | 25 | | [4. Deploy GeoServer instance](section-4-geoserver.md) | 10 min | 26 | | [5. Publish vector tiles](section-5-publish.md) | 10 min | 27 | | [6. Style vector tiles](section-6-style.md) | 25 min | 28 | | [7. Client application](section-7.md) | | 29 | | [7.1. Skeleton app](section-7-1-skeleton-app.md) | 35 min | 30 | | [7.2. Redux and UI](section-7-2-ui.md) | 20 min | 31 | | [7.3. Finish app](section-7-3-finish-app.md) | 30 min | 32 | | [7.4. Publish app](section-7-4-publish-app.md) | 5 min | 33 | | [8. Cleanup](section-8-clean.md) | | 34 | 35 | 36 | ## Feedback 37 | 38 | We would love to get some feedback on the workshop. If you could spare a minute to fill in a [very short survey](https://geoplex.typeform.com/to/LJyqtr) after the workshop it would be appreciated. 39 | 40 | ## Acknowledgements 41 | 42 | ABS data used with permission from the [Australian Bureau of Statistics](http://www.abs.gov.au). 43 | 44 | Jenks classification PL/PgSQL functions from the [PostgreSQL extension for CartoDB](https://github.com/CartoDB/cartodb-postgresql). 45 | 46 | ## Credits 47 | 48 | ### Authors/Presenters 49 | 50 | * Joseph O'Connell 51 | * Brendan Jurd / [github](https://github.com/direvus) 52 | * Brent Schiller / [github](https://github.com/b-schiller) 53 | 54 | ### Reviewers 55 | 56 | * Chris Thomas 57 | * Chris Comer 58 | 59 | *** 60 | 61 | **Next**: [1. Prequisites and setup](section-1-setup.md) 62 | -------------------------------------------------------------------------------- /ansible/geoserver.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Install and Configure Geoserver and Dependencies 3 | hosts: all 4 | gather_facts: yes 5 | become: true 6 | 7 | vars: 8 | geoserver_data_dir: /efs/geoserver/data 9 | geoserver_logs_dir: /var/log/geoserver 10 | 11 | roles: 12 | - role: roles/oracle-java 13 | 14 | - role: roles/tomcat 15 | tomcat_catalina_opts: "-Xms512M -Xmx2048M -server -XX:+UseParallelGC -XX:SoftRefLRUPolicyMSPerMB=36000 \ 16 | -DGEOSERVER_DATA_DIR={{ geoserver_data_dir }} \ 17 | -DGEOSERVER_NODE_OPTS=id:$host_ip \ 18 | -DGEOSERVER_LOG_LOCATION={{ geoserver_logs_dir }}/geoserver.log \ 19 | -Xbootclasspath/a:/opt/tomcat/lib/marlin-0.9.1-Unsafe.jar \ 20 | -Xbootclasspath/p:/opt/tomcat/lib/marlin-0.9.1-Unsafe-sun-java2d.jar \ 21 | -Dsun.java2d.renderer=org.marlin.pisces.PiscesRenderingEngine" 22 | tomcat_java_opts: "-Djava.awt.headless=true" 23 | 24 | - role: roles/geoserver 25 | create_data_dir: false 26 | -------------------------------------------------------------------------------- /ansible/roles/geoserver/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | geoserver_major_version: 2 3 | geoserver_minor_version: 13 4 | geoserver_bugfix_version: 2 5 | geoserver_version: "{{geoserver_major_version}}.{{geoserver_minor_version}}.{{geoserver_bugfix_version}}" 6 | geoserver_service_name: tomcat 7 | tomcat_user: tomcat 8 | tomcat_group: tomcat 9 | geoserver_webapp_dir: /opt/tomcat/webapps/geoserver 10 | geoserver_data_dir: /opt/geoserver/data 11 | create_data_dir: true 12 | geoserver_logs_dir: /var/log/geoserver 13 | 14 | geoserver_war_url: "https://downloads.sourceforge.net/project/geoserver/GeoServer/{{ geoserver_version }}/geoserver-{{ geoserver_version }}-war.zip" 15 | extensions_baseurl: "https://downloads.sourceforge.net/project/geoserver/GeoServer/{{ geoserver_version }}/extensions/" 16 | community_extensions_baseurl: "http://ares.opengeo.org/geoserver/master/community-latest/" 17 | jai_url: "http://download.java.net/media/jai/builds/release/1_1_3/jai-1_1_3-lib-linux-amd64.tar.gz" 18 | jai_imageio_url: "http://download.java.net/media/jai-imageio/builds/release/1.1/jai_imageio-1_1-lib-linux-amd64.tar.gz" 19 | marlin_renderer_url: "https://github.com/bourgesl/marlin-renderer/releases/download/v0_9_1/marlin-0.9.1-Unsafe.jar" 20 | marlin_sun_java2d_url: "https://github.com/bourgesl/marlin-renderer/releases/download/v0_9_1/marlin-0.9.1-Unsafe-sun-java2d.jar" 21 | 22 | geoserver_setup_filename: "geoserver-{{ geoserver_version }}-war.zip" 23 | geoserver_extensions: 24 | - "geoserver-{{ geoserver_version }}-wps-plugin.zip" 25 | - "geoserver-{{ geoserver_version }}-css-plugin.zip" 26 | - "geoserver-{{ geoserver_version }}-ysld-plugin.zip" 27 | - "geoserver-{{ geoserver_version }}-vectortiles-plugin.zip" 28 | 29 | community_extensions: 30 | - "geoserver-{{ geoserver_major_version }}.{{ geoserver_minor_version }}-SNAPSHOT-mbstyle-plugin.zip" -------------------------------------------------------------------------------- /ansible/roles/geoserver/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Install dependencies 3 | yum: 4 | name: "{{ item }}" 5 | state: latest 6 | with_items: 7 | - zip 8 | - unzip 9 | 10 | - name: Stop Geoserver / Tomcat 11 | service: 12 | name: "{{ geoserver_service_name }}" 13 | state: stopped 14 | 15 | - name: Download Geoserver Zip file 16 | get_url: 17 | url: "{{ geoserver_war_url }}" 18 | dest: /tmp/{{ geoserver_setup_filename }} 19 | mode: 0750 20 | 21 | - name: Extract Geoserver zip file 22 | unarchive: 23 | src: "/tmp/{{ geoserver_setup_filename }}" 24 | remote_src: yes 25 | dest: /tmp 26 | 27 | - name: Create Geoserver webapp directory 28 | file: 29 | path: "{{ geoserver_webapp_dir }}" 30 | state: directory 31 | owner: "{{ tomcat_user }}" 32 | group: "{{ tomcat_group }}" 33 | 34 | - name: Extract geoserver.war file 35 | unarchive: 36 | src: /tmp/geoserver.war 37 | remote_src: yes 38 | dest: "{{ geoserver_webapp_dir }}" 39 | owner: "{{ tomcat_user }}" 40 | group: "{{ tomcat_group }}" 41 | 42 | - name: Download extensions 43 | get_url: 44 | url: "{{ extensions_baseurl }}{{ item }}" 45 | dest: "/tmp/{{ item }}" 46 | mode: 0750 47 | with_items: "{{ geoserver_extensions }}" 48 | 49 | - name: Extract extensions 50 | unarchive: 51 | src: "/tmp/{{ item }}" 52 | remote_src: yes 53 | dest: "{{ geoserver_webapp_dir }}/WEB-INF/lib/" 54 | owner: "{{ tomcat_user }}" 55 | group: "{{ tomcat_group }}" 56 | with_items: "{{ geoserver_extensions }}" 57 | 58 | - name: Download community extensions 59 | get_url: 60 | url: "{{ community_extensions_baseurl }}{{ item }}" 61 | dest: "/tmp/{{ item }}" 62 | mode: 0750 63 | with_items: "{{ community_extensions }}" 64 | 65 | - name: Extract community extensions 66 | unarchive: 67 | src: "/tmp/{{ item }}" 68 | remote_src: yes 69 | dest: "{{ geoserver_webapp_dir }}/WEB-INF/lib/" 70 | owner: "{{ tomcat_user }}" 71 | group: "{{ tomcat_group }}" 72 | with_items: "{{ community_extensions }}" 73 | 74 | - name: Download JAI 75 | get_url: 76 | url: "{{ jai_url }}" 77 | dest: /tmp/jai-1_1_3-lib-linux-amd64.tar.gz 78 | mode: 0750 79 | 80 | - name: Extract JAI .jar files 81 | unarchive: 82 | src: /tmp/jai-1_1_3-lib-linux-amd64.tar.gz 83 | remote_src: yes 84 | dest: /usr/java/latest/jre/lib/ext 85 | extra_opts: ["--wildcards", "*.jar", "--strip-components", "2"] 86 | 87 | - name: Extract JAI .so files 88 | unarchive: 89 | src: /tmp/jai-1_1_3-lib-linux-amd64.tar.gz 90 | remote_src: yes 91 | dest: /usr/java/latest/jre/lib/amd64 92 | extra_opts: ["--wildcards", "*.so", "--strip-components", "2"] 93 | 94 | - name: Download JAI ImageIO 95 | get_url: 96 | url: "{{ jai_imageio_url }}" 97 | dest: /tmp/jai_imageio-1_1-lib-linux-amd64.tar.gz 98 | mode: 0750 99 | 100 | - name: Extract JAI ImageIO .jar files 101 | unarchive: 102 | src: /tmp/jai_imageio-1_1-lib-linux-amd64.tar.gz 103 | remote_src: yes 104 | dest: /usr/java/latest/jre/lib/ext 105 | extra_opts: ["--wildcards", "*.jar", "--strip-components", "2"] 106 | 107 | - name: Extract JAI ImageIO .so files 108 | unarchive: 109 | src: /tmp/jai_imageio-1_1-lib-linux-amd64.tar.gz 110 | remote_src: yes 111 | dest: /usr/java/latest/jre/lib/amd64 112 | extra_opts: ["--wildcards", "*.so", "--strip-components", "2"] 113 | 114 | - name: Remove non-native (Java) JAI files 115 | file: 116 | path: "{{ geoserver_webapp_dir }}/WEB-INF/lib/{{ item }}" 117 | state: absent 118 | with_items: 119 | - jai_codec-1.1.3.jar 120 | - jai_core-1.1.3.jar 121 | - jai_imageio-1.1.jar 122 | 123 | - name: Download Marlin renderer 124 | get_url: 125 | url: "{{ item }}" 126 | dest: /opt/tomcat/lib/ 127 | mode: 0750 128 | owner: "{{ tomcat_user }}" 129 | group: "{{ tomcat_group }}" 130 | with_items: 131 | - "{{ marlin_renderer_url }}" 132 | - "{{ marlin_sun_java2d_url }}" 133 | 134 | - name: Create Geoserver data directory 135 | file: 136 | dest: "{{ geoserver_data_dir }}" 137 | state: directory 138 | owner: "{{ tomcat_user }}" 139 | group: "{{ tomcat_group }}" 140 | mode: 0750 141 | recurse: yes 142 | when: create_data_dir 143 | 144 | - name: Create Geoserver logs directory 145 | file: 146 | dest: "{{ geoserver_logs_dir }}" 147 | state: directory 148 | owner: "{{ tomcat_user }}" 149 | group: "{{ tomcat_group }}" 150 | mode: 0750 151 | recurse: yes 152 | -------------------------------------------------------------------------------- /ansible/roles/oracle-java/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | oracle_java_rpm_url: "http://download.oracle.com/otn-pub/java/jdk/8u181-b13/96a7b8442fe848ef90c96a2fad6ed6d1/jdk-8u181-linux-x64.rpm" 3 | oracle_java_rpm_checksum: "sha256:48af6afd56252f5e6641fd0ad9009c056ee5d178129b4824cef4b976a655d375" 4 | oracle_java_home: /usr/java/latest 5 | -------------------------------------------------------------------------------- /ansible/roles/oracle-java/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Download Oracle Java RPM 3 | become: yes 4 | get_url: 5 | url: "{{ oracle_java_rpm_url }}" 6 | headers: 'Cookie:gpw_e24=http%3A%2F%2Fwww.oracle.com%2F; oraclelicense=accept-securebackup-cookie' 7 | checksum: "{{ oracle_java_rpm_checksum }}" 8 | dest: /tmp/java-linux-x64.rpm 9 | validate_certs: yes 10 | timeout: 10 11 | 12 | - name: Install Oracle Java RPM 13 | become: yes 14 | yum: 15 | name: /tmp/java-linux-x64.rpm 16 | state: present 17 | 18 | - name: Set Java Home Environmental Variables 19 | template: 20 | src: java_home.sh.j2 21 | dest: /etc/profile.d/java_home.sh 22 | -------------------------------------------------------------------------------- /ansible/roles/oracle-java/templates/java_home.sh.j2: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export JAVA_HOME={{ oracle_java_home }} 4 | export PATH={{ oracle_java_home }}/bin:${PATH} 5 | -------------------------------------------------------------------------------- /ansible/roles/tomcat/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | tomcat_version: "8.5.27" 4 | 5 | tomcat_mirrors: 6 | - http://archive.apache.org/dist/tomcat 7 | 8 | # Temporary Storage Directory 9 | tomcat_tmp_storage: /tmp/tomcat-ansible 10 | 11 | java_home: /usr/java/latest 12 | 13 | # Variable That Decides To Install the APR Role Dependency 14 | tomcat_use_apr: true 15 | 16 | tomcat_user_name: tomcat 17 | tomcat_user_group: tomcat 18 | tomcat_user_home: /home/{{ tomcat_user_name }} 19 | tomcat_user_system: false 20 | 21 | tomcat_port_shutdown: 8005 22 | tomcat_port_connector: 8080 23 | tomcat_port_redirect: 8443 24 | tomcat_port_ajp: 8009 25 | 26 | tomcat_java_opts: "" 27 | tomcat_catalina_opts: "" 28 | 29 | tomcat_base_dir: /opt 30 | tomcat_catalina_home: "{{ tomcat_base_dir }}/tomcat" 31 | tomcat_instance_path: "{{ tomcat_base_dir }}/tomcat" 32 | 33 | tomcat_prefer_ipv4: true 34 | tomcat_override_uri_encoding: "" 35 | tomcat_prefer_urandom: true 36 | 37 | # install service with initctl / systemd 38 | tomcat_install_service: true 39 | 40 | tomcat_instance: tomcat 41 | 42 | tomcat_roles: 43 | - manager 44 | - manager-gui 45 | - manager-script 46 | - manager-jmx 47 | - admin 48 | - admin-gui 49 | - admin-script 50 | 51 | tomcat_users: [] 52 | # - name: tomcat 53 | # password: tomcat 54 | # roles: "manager-gui,admin-gui" 55 | # 56 | 57 | tomcat_debug: false 58 | 59 | # This Edits And Allows Ansible To Configure These 60 | # Otherwise it does a default install 61 | tomcat_configure: true 62 | tomcat_configure_configs: "{{ tomcat_configure }}" 63 | tomcat_configure_libs: "{{ tomcat_configure }}" 64 | tomcat_configure_webapps: "{{ tomcat_configure }}" 65 | 66 | # These copy files across and will use basename 67 | tomcat_extra_libs_path: [] 68 | tomcat_webapps_path: [] 69 | 70 | #Removes ROOT webapp Context if false 71 | tomcat_root_context: false 72 | tomcat_manager_context: false 73 | tomcat_hostmanager_context: false 74 | tomcat_docs_context: false 75 | tomcat_examples_context: false 76 | 77 | # Strings That Allow you to modify your 78 | # tomcat instance in a predictable fashion. 79 | tomcat_extra_global_naming_resources: "" 80 | tomcat_context_xml_header_extra: "" 81 | tomcat_context_xml_extra: "" 82 | 83 | 84 | # Disable or enable session persistence 85 | tomcat_disable_persistence_across_restarts: false 86 | 87 | # Custom Configuration Files 88 | tomcat_use_custom_server_xml: false 89 | # tomcat_custom_server_xml: Path 90 | tomcat_use_custom_web_xml: false 91 | # tomcat_custom_web_xml: Path 92 | tomcat_use_custom_context_xml: false 93 | # tomcat_custom_context_xml: Path 94 | tomcat_use_custom_tomcat_users_xml: false 95 | # tomcat_custom_tomcat_users_xml: Path 96 | tomcat_use_custom_manager_context_xml: false 97 | # tomcat_custom_manager_context_xml: Path 98 | 99 | # Tomcat major version 100 | tomcat_version_major: "{{ tomcat_version.0 }}" 101 | tomcat_tar_archive: "apache-tomcat-{{ tomcat_version }}.tar.gz" 102 | 103 | tomcat_instance_directories: 104 | - conf 105 | - logs 106 | - webapps 107 | - temp 108 | - bin 109 | - lib 110 | - work 111 | 112 | tomcat_version_specific: 113 | "8.5.27": 114 | checksum: md5:2d39cb3293ec3308d5235004adf0d134 115 | web_xml_schema_version: 3.1 116 | tomcat_native_version: "1.2.12" 117 | "9.0.0.M18": 118 | checksum: md5:626e16b93de65b2a58714ed50f00d9f9 119 | web_xml_schema_version: 3.1 120 | tomcat_native_version: "1.2.12" 121 | -------------------------------------------------------------------------------- /ansible/roles/tomcat/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Restart Tomcat 4 | service: 5 | name: tomcat 6 | state: restarted 7 | -------------------------------------------------------------------------------- /ansible/roles/tomcat/templates/context.xml.j2: -------------------------------------------------------------------------------- 1 | 2 | 18 | 19 | 20 | 21 | 22 | WEB-INF/web.xml 23 | 24 | {% if tomcat_disable_persistence_across_restarts %} 25 | 26 | {% endif %} 27 | 28 | {% if tomcat_context_xml_extra not in (None, "") %} 29 | {{ tomcat_context_xml_extra }} 30 | {% endif %} 31 | 32 | 33 | -------------------------------------------------------------------------------- /ansible/roles/tomcat/templates/java-ld.conf.j2: -------------------------------------------------------------------------------- 1 | /usr/java/latest/jre/lib/amd64/jli/ -------------------------------------------------------------------------------- /ansible/roles/tomcat/templates/server.xml.j2: -------------------------------------------------------------------------------- 1 | 2 | 18 | 19 | 20 | 21 | {% if tomcat_version_major < 8 %} 22 | 23 | {% endif %} 24 | 25 | 26 | 27 | 28 | 29 | 34 | {% if tomcat_extra_global_naming_resources not in (None, "") %} 35 | {{ tomcat_extra_global_naming_resources }} 36 | {% endif %} 37 | 38 | 39 | 40 | 41 | 48 | 49 | 50 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 62 | 63 | 64 | 66 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /ansible/roles/tomcat/templates/tomcat-users.xml.j2: -------------------------------------------------------------------------------- 1 | 2 | 18 | 19 | {% for role in tomcat_roles %} 20 | 21 | {% endfor %} 22 | {% for user in tomcat_users %} 23 | 24 | {% endfor %} 25 | 26 | -------------------------------------------------------------------------------- /ansible/roles/tomcat/templates/tomcat.conf.j2: -------------------------------------------------------------------------------- 1 | description "Tomcat Server" 2 | 3 | start on runlevel [2345] 4 | stop on runlevel [!2345] 5 | respawn 6 | respawn limit 10 5 7 | 8 | env JAVA_HOME={{ java_home }} 9 | env CATALINA_BASE={{ tomcat_catalina_home }} 10 | env CATALINA_HOME={{ tomcat_catalina_home }} 11 | env LD_LIBRARY_PATH=$LD_LIBRARY_PATH:{{ apr_install_location }}/lib:{{ tomcat_catalina_home}}/lib' 12 | 13 | {% if tomcat_java_opts not in (None, "") %} 14 | env JAVA_OPTS={{ tomcat_java_opts }} 15 | {% else %} 16 | env JAVA_OPTS=-Djava.awt.headless=true 17 | {% endif %} 18 | 19 | {% if tomcat_catalina_opts not in (None, "") %} 20 | env CATALINA_OPTS='{{ tomcat_catalina_opts }}' 21 | {% else %} 22 | env CATALINA_OPTS='-Xms512M -Xmx2048M -server -XX:+UseParallelGC' 23 | {% endif %} 24 | 25 | exec {{ tomcat_instance_path }}/bin/catalina.sh run 26 | 27 | # cleanup temp directory after stop 28 | post-stop script 29 | rm -rf $CATALINA_HOME/temp/* 30 | end script 31 | -------------------------------------------------------------------------------- /ansible/roles/tomcat/templates/tomcat.service.j2: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Tomcat Server 3 | After=network.target 4 | 5 | [Service] 6 | Type=forking 7 | 8 | Restart=on-failure 9 | Environment='TOMCAT_JAVA_HOME={{ java_home }}' 10 | 11 | Environment='CATALINA_HOME={{ tomcat_catalina_home }}' 12 | Environment='CATALINA_BASE={{ tomcat_catalina_home }}' 13 | 14 | {% if tomcat_catalina_opts not in (None, "") %} 15 | Environment='CATALINA_OPTS={{ tomcat_catalina_opts }}' 16 | {% else %} 17 | Environment='CATALINA_OPTS=-Xms512M -Xmx2048M -server -XX:+UseParallelGC' 18 | {% endif %} 19 | 20 | {% if tomcat_java_opts not in (None, "") %} 21 | Environment='JAVA_OPTS={{ tomcat_java_opts }}' 22 | {% else %} 23 | Environment='JAVA_OPTS=-Djava.awt.headless=true' 24 | {% endif %} 25 | 26 | ExecStart={{ tomcat_instance_path }}/bin/startup.sh 27 | ExecStop={{ tomcat_instance_path }}/bin/shutdown.sh 28 | 29 | User={{ tomcat_user_name }} 30 | Group={{ tomcat_user_group }} 31 | 32 | [Install] 33 | WantedBy=multi-user.target 34 | -------------------------------------------------------------------------------- /ansible/roles/tomcat/templates/tomcat.sysv.j2: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ### BEGIN INIT INFO 3 | # Provides: tomcat 4 | # Required-Start: $local_fs $remote_fs $network 5 | # Should-Start: $named $time postgresql sendmail mysql ypclient dhcp radiusd 6 | # Should-Stop: $named $time postgresql sendmail mysql ypclient dhcp radiusd 7 | # Required-Stop: $local_fs $remote_fs $network 8 | # Default-Start: 3 5 9 | # Default-Stop: 0 1 2 6 10 | # X-Interactive: true 11 | # Short-Description: Apache Tomcat 8 Instance 12 | # Description: Start Apache Tomcat 8 Instance 13 | ### END INIT INFO 14 | # 15 | # 16 | # Tomcat 8 start/stop/status init.d script 17 | # Initially forked from: https://gist.github.com/valotas/1000094 18 | # @author: Miglen Evlogiev 19 | # 20 | # Release updates: 21 | # Updated method for gathering pid of the current proccess 22 | # Added usage of CATALINA_BASE 23 | # Added coloring and additional status 24 | # Added check for existence of the tomcat user 25 | # Added termination proccess 26 | 27 | export JAVA_HOME=/usr/java/latest 28 | 29 | export PATH=$JAVA_HOME/bin:$PATH 30 | 31 | export TOMCAT_JAVA_HOME={{ java_home }} 32 | export CATALINA_HOME={{ tomcat_catalina_home }} 33 | export CATALINA_BASE={{ tomcat_catalina_home }} 34 | 35 | {% if tomcat_catalina_opts not in (None, "") %} 36 | export CATALINA_OPTS="{{ tomcat_catalina_opts }}" 37 | {% else %} 38 | export CATALINA_OPTS="-Xms512M -Xmx2048M -server -XX:+UseParallelGC" 39 | {% endif %} 40 | 41 | {% if tomcat_java_opts not in (None, "") %} 42 | export JAVA_OPTS="{{ tomcat_java_opts }}" 43 | {% else %} 44 | export JAVA_OPTS="-Djava.awt.headless=true" 45 | {% endif %} 46 | 47 | export TOMCAT_USER={{ tomcat_user_name }} 48 | 49 | #TOMCAT_USAGE is the message if this script is called without any options 50 | TOMCAT_USAGE="Usage: $0 {\e[00;32mstart\e[00m|\e[00;31mstop\e[00m|\e[00;31mkill\e[00m|\e[00;32mstatus\e[00m|\e[00;31mrestart\e[00m}" 51 | 52 | #SHUTDOWN_WAIT is wait time in seconds for java proccess to stop 53 | SHUTDOWN_WAIT=20 54 | 55 | tomcat_pid() { 56 | echo `ps -fe | grep $CATALINA_BASE | grep -v grep | tr -s " "|cut -d" " -f2` 57 | } 58 | 59 | start() { 60 | pid=$(tomcat_pid) 61 | if [ -n "$pid" ] 62 | then 63 | echo -e "\e[00;31mTomcat is already running (pid: $pid)\e[00m" 64 | else 65 | # Start tomcat 66 | echo -e "\e[00;32mStarting tomcat\e[00m" 67 | #ulimit -n 100000 68 | #umask 007 69 | #/bin/su -p -s /bin/sh $TOMCAT_USER 70 | if [ `user_exists $TOMCAT_USER` = "1" ] 71 | then 72 | /bin/su $TOMCAT_USER -c $CATALINA_HOME/bin/startup.sh 73 | else 74 | sh $CATALINA_HOME/bin/startup.sh 75 | fi 76 | status 77 | fi 78 | return 0 79 | } 80 | 81 | status(){ 82 | pid=$(tomcat_pid) 83 | if [ -n "$pid" ]; then echo -e "\e[00;32mTomcat is running with pid: $pid\e[00m" 84 | else echo -e "\e[00;31mTomcat is not running\e[00m" 85 | fi 86 | } 87 | 88 | terminate() { 89 | echo -e "\e[00;31mTerminating Tomcat\e[00m" 90 | kill -9 $(tomcat_pid) 91 | } 92 | 93 | stop() { 94 | pid=$(tomcat_pid) 95 | if [ -n "$pid" ] 96 | then 97 | echo -e "\e[00;31mStoping Tomcat\e[00m" 98 | #/bin/su -p -s /bin/sh $TOMCAT_USER 99 | sh $CATALINA_HOME/bin/shutdown.sh 100 | 101 | let kwait=$SHUTDOWN_WAIT 102 | count=0; 103 | until [ `ps -p $pid | grep -c $pid` = '0' ] || [ $count -gt $kwait ] 104 | do 105 | echo -n -e "\n\e[00;31mwaiting for processes to exit\e[00m"; 106 | sleep 1 107 | let count=$count+1; 108 | done 109 | 110 | if [ $count -gt $kwait ]; then 111 | echo -n -e "\n\e[00;31mkilling processes didn't stop after $SHUTDOWN_WAIT seconds\e[00m" 112 | terminate 113 | fi 114 | else 115 | echo -e "\e[00;31mTomcat is not running\e[00m" 116 | fi 117 | 118 | return 0 119 | } 120 | 121 | user_exists(){ 122 | if id -u $1 >/dev/null 2>&1; then 123 | echo "1" 124 | else 125 | echo "0" 126 | fi 127 | } 128 | 129 | case $1 in 130 | start) 131 | start 132 | ;; 133 | stop) 134 | stop 135 | ;; 136 | restart) 137 | stop 138 | start 139 | ;; 140 | status) 141 | status 142 | ;; 143 | kill) 144 | terminate 145 | ;; 146 | *) 147 | echo -e $TOMCAT_USAGE 148 | ;; 149 | esac 150 | exit 0 151 | -------------------------------------------------------------------------------- /ansible/roles/tomcat/vars/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | -------------------------------------------------------------------------------- /bash/download_abs_geographies.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Download Australian Bureau of Statistics (ABS) Statistical Area (SA) data at 4 | # levels 1 - 4. 5 | # 6 | # The data will be saved under data/abs/aus_sa.zip, relative to the 7 | # repository root. 8 | 9 | workdir="$(dirname $0)/../data/abs" 10 | url="http://www.abs.gov.au/AUSSTATS/subscriber.nsf/log" 11 | 12 | mkdir -p "$workdir" 13 | ret=$? 14 | if [ $ret -ne 0 ]; then 15 | echo "Failed to create download directory $workdir, abort." 16 | exit $ret 17 | fi 18 | 19 | echo "Downloading ABS SA data to $workdir ..." 20 | 21 | curl -Lo "$workdir/aus_sa1.zip" "$url?openagent&1270055001_sa1_2016_aust_shape.zip&1270.0.55.001&Data%20Cubes&6F308688D810CEF3CA257FED0013C62D&0&July%202016&12.07.2016&Latest" 22 | curl -Lo "$workdir/aus_sa2.zip" "$url?openagent&1270055001_sa2_2016_aust_shape.zip&1270.0.55.001&Data%20Cubes&A09309ACB3FA50B8CA257FED0013D420&0&July%202016&12.07.2016&Latest" 23 | curl -Lo "$workdir/aus_sa3.zip" "$url?openagent&1270055001_sa3_2016_aust_shape.zip&1270.0.55.001&Data%20Cubes&43942523105745CBCA257FED0013DB07&0&July%202016&12.07.2016&Latest" 24 | curl -Lo "$workdir/aus_sa4.zip" "$url?openagent&1270055001_sa4_2016_aust_shape.zip&1270.0.55.001&Data%20Cubes&C65BC89E549D1CA3CA257FED0013E074&0&July%202016&12.07.2016&Latest" 25 | 26 | echo "Done." 27 | -------------------------------------------------------------------------------- /bash/unzip_abs_geographies.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | workdir="$(dirname $0)/../data/abs" 4 | unzip -a "$workdir/vic_mb.zip" -d "$workdir" 5 | unzip -a "$workdir/aus_sa1.zip" -d "$workdir" 6 | unzip -a "$workdir/aus_sa2.zip" -d "$workdir" 7 | unzip -a "$workdir/aus_sa3.zip" -d "$workdir" 8 | unzip -a "$workdir/aus_sa4.zip" -d "$workdir" 9 | -------------------------------------------------------------------------------- /client-app/.env.production: -------------------------------------------------------------------------------- 1 | REACT_APP_BASE_GEOSERVER_URL=http:///geoserver -------------------------------------------------------------------------------- /client-app/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .env.local 15 | .env.development.local 16 | .env.test.local 17 | .env.production.local 18 | 19 | npm-debug.log* 20 | yarn-debug.log* 21 | yarn-error.log* 22 | -------------------------------------------------------------------------------- /client-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "client-app", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@material-ui/core": "^1.5.0", 7 | "immutable": "^3.8.2", 8 | "isomorphic-fetch": "^2.2.1", 9 | "ol": "4.6.5", 10 | "ol-mapbox-style": "2.11.2", 11 | "react": "^16.4.2", 12 | "react-dom": "^16.4.2", 13 | "react-redux": "^5.0.7", 14 | "react-scripts": "1.1.4", 15 | "redux": "^4.0.0", 16 | "redux-thunk": "^2.3.0" 17 | }, 18 | "scripts": { 19 | "start": "react-scripts start", 20 | "build": "react-scripts build", 21 | "test": "react-scripts test --env=jsdom", 22 | "eject": "react-scripts eject" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /client-app/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geoplex/foss4g-oceania-2018-workshop/ca3bb5d73533802f0e665903e2de2530fa2345ca/client-app/public/favicon.ico -------------------------------------------------------------------------------- /client-app/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 11 | 12 | 13 | 22 | Vic Stats Explorer 23 | 24 | 25 | 28 |
29 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /client-app/src/components/AppHeader.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import AppBar from '@material-ui/core/AppBar' 3 | import Toolbar from '@material-ui/core/Toolbar' 4 | import Typography from '@material-ui/core/Typography' 5 | 6 | class AppHeader extends Component { 7 | render() { 8 | return ( 9 | 10 | 11 | 12 | Vic Stats Explorer 13 | 14 | 15 | 16 | ) 17 | } 18 | } 19 | 20 | export default AppHeader; 21 | 22 | 23 | -------------------------------------------------------------------------------- /client-app/src/components/InfoPanel.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { withStyles } from '@material-ui/core/styles'; 3 | import Paper from '@material-ui/core/Paper' 4 | import Table from '@material-ui/core/Table' 5 | import TableBody from '@material-ui/core/TableBody' 6 | import TableCell from '@material-ui/core/TableCell' 7 | import TableRow from '@material-ui/core/TableRow' 8 | import PaperHeader from './PaperHeader' 9 | 10 | const styles = theme => ({ 11 | root: { 12 | width: '90%', 13 | marginLeft: 'auto', 14 | marginRight: 'auto', 15 | marginTop: theme.spacing.unit * 3, 16 | overflowX: 'auto' 17 | }, 18 | table: { 19 | minWidth: 200 20 | }, 21 | cellValue: { 22 | whiteSpace: 'nowrap' 23 | }, 24 | paperHeading: { 25 | background: theme.palette.background.default, 26 | fontWeight: 400 27 | } 28 | }); 29 | 30 | class InfoPanel extends Component { 31 | render() { 32 | let selectedPolygonStyle = this.props.polygonLayer.get( 33 | 'styles' 34 | ).find( 35 | style => style.get('id') === this.props.polygonLayer.get('selectedStyle') 36 | ) 37 | 38 | let selectedPointStyle = this.props.pointLayer.get( 39 | 'styles' 40 | ).find( 41 | style => style.get('id') === this.props.pointLayer.get('selectedStyle') 42 | ) 43 | 44 | let polygonValue = this.props.highlightFeatureProps ? this.props.highlightFeatureProps.get(selectedPolygonStyle.get('displayField')).toFixed(1) : '-' 45 | let pointValue = this.props.highlightFeatureProps ? this.props.highlightFeatureProps.get(selectedPointStyle.get('displayField')).toFixed(1) : '-' 46 | let saValue = this.props.highlightFeatureProps ? + this.props.highlightFeatureProps.get('area_id') + ' (' + this.props.highlightFeatureProps.get('area_type') + ')' : '-' 47 | 48 | return ( 49 | 50 | 51 | 52 | 53 | 54 | 55 | Statistical Area 56 | 57 | {saValue} 58 | 59 | 60 | 61 | {selectedPolygonStyle.get('displayName')} 62 | 63 | {polygonValue} 64 | 65 | 66 | 67 | {selectedPointStyle.get('displayName')} 68 | 69 | {pointValue} 70 | 71 | 72 |
73 |
74 | ) 75 | } 76 | } 77 | 78 | export default withStyles(styles)(InfoPanel); 79 | 80 | 81 | -------------------------------------------------------------------------------- /client-app/src/components/OlMap.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import Map from 'ol/map' 3 | import View from 'ol/view' 4 | import TileLayer from 'ol/layer/tile' 5 | import Stamen from 'ol/source/stamen' 6 | import VectorTileLayer from 'ol/layer/vectortile' 7 | import VectorTileSource from 'ol/source/vectortile' 8 | import MVT from 'ol/format/mvt' 9 | import Style from 'ol/style/style' 10 | import Fill from 'ol/style/fill' 11 | import Stroke from 'ol/style/stroke' 12 | import 'ol/ol.css' 13 | import stylefunction from '../external/stylefunction' 14 | 15 | 16 | class OlMap extends Component { 17 | constructor(props) { 18 | super(props) 19 | this.map = null 20 | this.pointLayer = null 21 | this.polygonLayer = null 22 | this.highlightAreaId = null 23 | this.isDragging = false 24 | } 25 | 26 | componentDidMount() { 27 | let self = this 28 | self.loadMap() 29 | } 30 | 31 | componentDidUpdate(prevProps, prevState) { 32 | let self = this 33 | if (!prevProps.mbStyles && !!self.props.mbStyles) { 34 | self.loadLayers() 35 | } 36 | if (!prevProps.polygonLayer.get('selectedStyle') !== this.props.polygonLayer.get('selectedStyle')) { 37 | this.setVectorTileLayerStyle(this.polygonLayer, this.props.polygonLayer.get('name'), this.props.polygonLayer.get('selectedStyle')) 38 | } 39 | if (!prevProps.pointLayer.get('selectedStyle') !== this.props.pointLayer.get('selectedStyle')) { 40 | this.setVectorTileLayerStyle(this.pointLayer, this.props.pointLayer.get('name'), this.props.pointLayer.get('selectedStyle')) 41 | } 42 | } 43 | 44 | getVectorTileUrl (layerConfig) { 45 | let requestParams = { 46 | REQUEST: 'GetTile', 47 | SERVICE: 'WMTS', 48 | VERSION: '1.0.0', 49 | LAYER: layerConfig.get('workspace') + ':' + layerConfig.get('name'), 50 | STYLE: '', 51 | TILEMATRIX: 'EPSG:900913:{z}', 52 | TILEMATRIXSET: 'EPSG:900913', 53 | FORMAT: 'application/x-protobuf;type=mapbox-vector', 54 | TILECOL: '{x}', 55 | TILEROW: '{y}' 56 | } 57 | let requestParamsString = Object.keys(requestParams).map(x => x + '=' + requestParams[x]).join('&'); 58 | return this.props.baseGeoserverUrl + '/gwc/service/wmts?' + requestParamsString; 59 | } 60 | 61 | handleMapOnSingleClick (event) { 62 | let self = this 63 | let features = self.map.getFeaturesAtPixel(event.pixel); 64 | 65 | if (features && features.length) { 66 | let featureProps = features[0].getProperties() 67 | self.highlightAreaId = featureProps.area_id 68 | self.polygonLayer.setStyle(self.polygonLayer.getStyle()) 69 | this.props.setHighlightFeatureProps(featureProps) 70 | } else { 71 | self.highlightAreaId = null 72 | if (self.polygonLayer) { 73 | self.polygonLayer.setStyle(self.polygonLayer.getStyle()) 74 | } 75 | this.props.setHighlightFeatureProps(null) 76 | } 77 | } 78 | 79 | setVectorTileLayerStyle(layer, source, mbStyleId) { 80 | let self = this 81 | let mbStyle = self.props.mbStyles.get(mbStyleId).get('style').toJS(); 82 | stylefunction(layer, mbStyle, source); 83 | 84 | let origStyleFunction = layer.getStyleFunction() 85 | layer.setStyle((feature, resolution) => { 86 | if (feature.getProperties().area_id === self.highlightAreaId) { 87 | return new Style({ 88 | stroke: new Stroke({ 89 | color: 'rgba(200,20,20,0.8)', 90 | width: 5 91 | }), 92 | fill: new Fill({ 93 | color: 'rgba(200,20,20,0.2)' 94 | }) 95 | }); 96 | } 97 | return origStyleFunction(feature, resolution) 98 | }) 99 | } 100 | 101 | createVectorTileLayer(layerConfig) { 102 | let self = this 103 | return new VectorTileLayer({ 104 | declutter: true, 105 | source: new VectorTileSource({ 106 | format: new MVT(), 107 | url: self.getVectorTileUrl(layerConfig) 108 | }) 109 | }) 110 | } 111 | 112 | loadLayers () { 113 | let self = this 114 | self.polygonLayer = self.createVectorTileLayer(self.props.polygonLayer) 115 | self.pointLayer = self.createVectorTileLayer(self.props.pointLayer) 116 | 117 | self.setVectorTileLayerStyle(self.polygonLayer, self.props.polygonLayer.get('name'), self.props.polygonLayer.get('selectedStyle')) 118 | self.setVectorTileLayerStyle(self.pointLayer, self.props.pointLayer.get('name'), self.props.pointLayer.get('selectedStyle')) 119 | 120 | self.map.getLayers().push(self.polygonLayer) 121 | self.map.getLayers().push(self.pointLayer) 122 | } 123 | 124 | loadMap () { 125 | let self = this 126 | let layers = [ 127 | new TileLayer({ 128 | source: new Stamen({ 129 | layer: 'toner-lite' 130 | }) 131 | }) 132 | ] 133 | self.map = new Map({ 134 | layers: layers, 135 | target: 'map', 136 | view: new View({ 137 | center: self.props.mapOptions.get('center').toJS(), 138 | zoom: self.props.mapOptions.get('zoom') 139 | }) 140 | }) 141 | 142 | self.map.on('singleclick', self.handleMapOnSingleClick, self) 143 | } 144 | 145 | render() { 146 | return ( 147 |
148 | ) 149 | } 150 | 151 | } 152 | 153 | export default OlMap; 154 | 155 | 156 | -------------------------------------------------------------------------------- /client-app/src/components/PaperHeader.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { withStyles } from '@material-ui/core/styles' 3 | import Typography from '@material-ui/core/Typography' 4 | import Divider from '@material-ui/core/Divider' 5 | 6 | const styles = theme => ({ 7 | paperHeading: { 8 | background: theme.palette.background.default, 9 | fontWeight: 400 10 | } 11 | }); 12 | 13 | class PaperHeader extends Component { 14 | render() { 15 | return ( 16 |
17 | 18 | {this.props.title} 19 | 20 | 21 |
22 | ) 23 | } 24 | } 25 | 26 | export default withStyles(styles)(PaperHeader); 27 | 28 | 29 | -------------------------------------------------------------------------------- /client-app/src/components/SelectStat.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { withStyles } from '@material-ui/core/styles'; 3 | import FormControl from '@material-ui/core/FormControl' 4 | import InputLabel from '@material-ui/core/InputLabel' 5 | import Select from '@material-ui/core/Select' 6 | import MenuItem from '@material-ui/core/MenuItem' 7 | 8 | const styles = theme => ({ 9 | formControl: { 10 | margin: theme.spacing.unit, 11 | minWidth: 200 12 | } 13 | }); 14 | 15 | class SelectStat extends Component { 16 | handleChange = event => { 17 | this.props.handleChange(event.target.value) 18 | } 19 | 20 | render() { 21 | let value = this.props.layer.get( 22 | 'styles' 23 | ).find( 24 | style => style.get('id') === this.props.layer.get('selectedStyle') 25 | ).get('id') 26 | 27 | return ( 28 | 29 | {this.props.label} 30 | 42 | 43 | ) 44 | } 45 | 46 | } 47 | 48 | export default withStyles(styles)(SelectStat); 49 | 50 | 51 | -------------------------------------------------------------------------------- /client-app/src/components/SelectStatControl.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { withStyles } from '@material-ui/core/styles'; 3 | import Paper from '@material-ui/core/Paper' 4 | import PaperHeader from './PaperHeader' 5 | import SelectStat from './SelectStat' 6 | 7 | const styles = theme => ({ 8 | root: { 9 | width: '90%', 10 | marginLeft: 'auto', 11 | marginRight: 'auto', 12 | marginTop: theme.spacing.unit * 3, 13 | overflowX: 'auto' 14 | }, 15 | formControl: { 16 | margin: theme.spacing.unit, 17 | minWidth: 200 18 | } 19 | }); 20 | 21 | class SelectStatControl extends Component { 22 | render() { 23 | return ( 24 | 25 | 26 | 32 | 38 | 39 | ) 40 | } 41 | } 42 | 43 | export default withStyles(styles)(SelectStatControl); 44 | 45 | 46 | -------------------------------------------------------------------------------- /client-app/src/components/SidePanel.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import InfoPanel from './InfoPanel' 3 | import SelectStatControl from './SelectStatControl' 4 | 5 | class SidePanel extends Component { 6 | render() { 7 | return ( 8 |
9 | 15 | 20 |
21 | ) 22 | } 23 | } 24 | 25 | export default SidePanel; 26 | 27 | 28 | -------------------------------------------------------------------------------- /client-app/src/containers/app/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { bindActionCreators } from 'redux'; 3 | import { connect } from 'react-redux'; 4 | import { MuiThemeProvider, createMuiTheme } from '@material-ui/core/styles'; 5 | import OlMap from '../../components/OlMap'; 6 | import SidePanel from '../../components/SidePanel'; 7 | import AppHeader from '../../components/AppHeader'; 8 | import { 9 | fetchStyles, 10 | setHighlightFeatureProps, 11 | setPolygonStyle, 12 | setPointStyle 13 | } from '../../modules/map' 14 | 15 | const theme = createMuiTheme(); 16 | 17 | const styles = { 18 | app: { 19 | height: '100%', 20 | width: '100%' 21 | }, 22 | sidePanel: { 23 | height: 'calc(100% - 64px)', 24 | width: 349, 25 | position: 'absolute', 26 | marginTop: 64, 27 | borderRight: '1px solid ' + theme.palette.primary.main 28 | }, 29 | map: { 30 | height: 'calc(100% - 64px)', 31 | width: 'calc(100% - 350px)', 32 | position: 'absolute', 33 | marginTop: 64, 34 | marginLeft: 350 35 | } 36 | } 37 | 38 | class App extends Component { 39 | componentDidMount() { 40 | this.fetchStyles() 41 | } 42 | 43 | fetchStyles() { 44 | let data = { 45 | baseGeoserverUrl: this.props.baseGeoserverUrl 46 | } 47 | let stylesConfig = [ 48 | this.props.polygonLayer.get('styles').toJS(), 49 | this.props.pointLayer.get('styles').toJS() 50 | ] 51 | 52 | data.stylesConfig = [].concat.apply([], stylesConfig) 53 | this.props.fetchStyles(data) 54 | } 55 | 56 | render() { 57 | return ( 58 | 59 |
60 | 61 | 69 | 78 |
79 |
80 | ); 81 | } 82 | } 83 | 84 | const mapStateToProps = state => ({ 85 | baseGeoserverUrl: state.map.get('baseGeoserverUrl'), 86 | mapOptions: state.map.get('mapOptions'), 87 | pointLayer: state.map.get('pointLayer'), 88 | polygonLayer: state.map.get('polygonLayer'), 89 | mbStyles: state.map.get('mbStyles'), 90 | selectedPointStyle: state.map.get('selectedPointStyle'), 91 | selectedPolygonStyle: state.map.get('selectedPolygonStyle'), 92 | highlightFeatureProps: state.map.get('highlightFeatureProps') 93 | }); 94 | 95 | const mapDispatchToProps = dispatch => 96 | bindActionCreators( 97 | { 98 | fetchStyles, 99 | setHighlightFeatureProps, 100 | setPolygonStyle, 101 | setPointStyle 102 | }, 103 | dispatch 104 | ); 105 | 106 | export default connect(mapStateToProps, mapDispatchToProps)(App); 107 | -------------------------------------------------------------------------------- /client-app/src/external/mapbox-gl-style-spec/deref.js: -------------------------------------------------------------------------------- 1 | 2 | const refProperties = require('./util/ref_properties'); 3 | 4 | function deref(layer, parent) { 5 | const result = {}; 6 | 7 | for (const k in layer) { 8 | if (k !== 'ref') { 9 | result[k] = layer[k]; 10 | } 11 | } 12 | 13 | refProperties.forEach((k) => { 14 | if (k in parent) { 15 | result[k] = parent[k]; 16 | } 17 | }); 18 | 19 | return result; 20 | } 21 | 22 | module.exports = derefLayers; 23 | 24 | /** 25 | * Given an array of layers, some of which may contain `ref` properties 26 | * whose value is the `id` of another property, return a new array where 27 | * such layers have been augmented with the 'type', 'source', etc. properties 28 | * from the parent layer, and the `ref` property has been removed. 29 | * 30 | * The input is not modified. The output may contain references to portions 31 | * of the input. 32 | * 33 | * @private 34 | * @param {Array} layers 35 | * @returns {Array} 36 | */ 37 | function derefLayers(layers) { 38 | layers = layers.slice(); 39 | 40 | const map = Object.create(null); 41 | for (let i = 0; i < layers.length; i++) { 42 | map[layers[i].id] = layers[i]; 43 | } 44 | 45 | for (let i = 0; i < layers.length; i++) { 46 | if ('ref' in layers[i]) { 47 | layers[i] = deref(layers[i], map[layers[i].ref]); 48 | } 49 | } 50 | 51 | return layers; 52 | } 53 | -------------------------------------------------------------------------------- /client-app/src/external/mapbox-gl-style-spec/feature_filter/README.md: -------------------------------------------------------------------------------- 1 | ## Filter 2 | 3 | Filter expressions are used to target specific data in a layer. This library implements the semantics specified by the [Mapbox GL JS spec](https://www.mapbox.com/mapbox-gl-style-spec/#filter). 4 | 5 | ### API 6 | 7 | `featureFilter(filter)` 8 | 9 | Given a filter expressed as nested arrays, return a new function 10 | that evaluates whether a given feature (with a .properties or .tags property) 11 | passes its test. 12 | 13 | #### Parameters 14 | 15 | | parameter | type | description | 16 | | --------- | ----- | ---------------- | 17 | | `filter` | Array | mapbox gl filter | 18 | 19 | **Returns** `Function`, filter-evaluating function 20 | 21 | ### Usage 22 | 23 | ``` javascript 24 | var ff = require('feature-filter'); 25 | 26 | // will match a feature with class of street_limited, 27 | // AND an admin_level less than or equal to 3, 28 | // that's NOT a polygon. 29 | var filter = [ 30 | "all", 31 | ["==", "class", "street_limited"], 32 | ["<=", "admin_level", 3], 33 | ["!=", "$type", "Polygon"] 34 | ] 35 | 36 | // will match a feature that has a class of 37 | // wetland OR wetland_noveg. 38 | // ["in", "class", "wetland", "wetland_noveg"] 39 | 40 | // testFilter will be a function that returns a boolean 41 | var testFilter = ff(filter); 42 | 43 | // Layer feature that you're testing. Must have type 44 | // and properties keys. 45 | var feature = { 46 | type: 2, 47 | properties: { 48 | class: "street_limited" 49 | admin_level: 1 50 | } 51 | }; 52 | 53 | // will return a boolean based on whether the feature matched the filter 54 | return testFilter(feature); 55 | ``` 56 | -------------------------------------------------------------------------------- /client-app/src/external/mapbox-gl-style-spec/feature_filter/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | 3 | module.exports = createFilter; 4 | 5 | const types = ['Unknown', 'Point', 'LineString', 'Polygon']; 6 | 7 | /** 8 | * Given a filter expressed as nested arrays, return a new function 9 | * that evaluates whether a given feature (with a .properties or .tags property) 10 | * passes its test. 11 | * 12 | * @private 13 | * @param {Array} filter mapbox gl filter 14 | * @returns {Function} filter-evaluating function 15 | */ 16 | function createFilter(filter) { 17 | return new Function('f', `var p = (f && f.properties || {}); return ${compile(filter)}`); 18 | } 19 | 20 | function compile(filter) { 21 | if (!filter) return 'true'; 22 | const op = filter[0]; 23 | if (filter.length <= 1) return op === 'any' ? 'false' : 'true'; 24 | const str = 25 | op === '==' ? compileComparisonOp(filter[1], filter[2], '===', false) : 26 | op === '!=' ? compileComparisonOp(filter[1], filter[2], '!==', false) : 27 | op === '<' || 28 | op === '>' || 29 | op === '<=' || 30 | op === '>=' ? compileComparisonOp(filter[1], filter[2], op, true) : 31 | op === 'any' ? compileLogicalOp(filter.slice(1), '||') : 32 | op === 'all' ? compileLogicalOp(filter.slice(1), '&&') : 33 | op === 'none' ? compileNegation(compileLogicalOp(filter.slice(1), '||')) : 34 | op === 'in' ? compileInOp(filter[1], filter.slice(2)) : 35 | op === '!in' ? compileNegation(compileInOp(filter[1], filter.slice(2))) : 36 | op === 'has' ? compileHasOp(filter[1]) : 37 | op === '!has' ? compileNegation(compileHasOp(filter[1])) : 38 | 'true'; 39 | return `(${str})`; 40 | } 41 | 42 | function compilePropertyReference(property) { 43 | const ref = 44 | property === '$type' ? 'f.type' : 45 | property === '$id' ? 'f.id' : `p[${JSON.stringify(property)}]`; 46 | return ref; 47 | } 48 | 49 | function compileComparisonOp(property, value, op, checkType) { 50 | const left = compilePropertyReference(property); 51 | const right = property === '$type' ? types.indexOf(value) : JSON.stringify(value); 52 | return (checkType ? `typeof ${left}=== typeof ${right}&&` : '') + left + op + right; 53 | } 54 | 55 | function compileLogicalOp(expressions, op) { 56 | return expressions.map(compile).join(op); 57 | } 58 | 59 | function compileInOp(property, values) { 60 | if (property === '$type') values = values.map((value) => { 61 | return types.indexOf(value); 62 | }); 63 | const left = JSON.stringify(values.sort(compare)); 64 | const right = compilePropertyReference(property); 65 | 66 | if (values.length <= 200) return `${left}.indexOf(${right}) !== -1`; 67 | 68 | return `${'function(v, a, i, j) {' + 69 | 'while (i <= j) { var m = (i + j) >> 1;' + 70 | ' if (a[m] === v) return true; if (a[m] > v) j = m - 1; else i = m + 1;' + 71 | '}' + 72 | 'return false; }('}${right}, ${left},0,${values.length - 1})`; 73 | } 74 | 75 | function compileHasOp(property) { 76 | return property === '$id' ? '"id" in f' : `${JSON.stringify(property)} in p`; 77 | } 78 | 79 | function compileNegation(expression) { 80 | return `!(${expression})`; 81 | } 82 | 83 | // Comparison function to sort numbers and strings 84 | function compare(a, b) { 85 | return a < b ? -1 : a > b ? 1 : 0; 86 | } 87 | 88 | /* eslint-enable */ -------------------------------------------------------------------------------- /client-app/src/external/mapbox-gl-style-spec/function/color_spaces.js: -------------------------------------------------------------------------------- 1 | 2 | // Constants 3 | const Xn = 0.950470, // D65 standard referent 4 | Yn = 1, 5 | Zn = 1.088830, 6 | t0 = 4 / 29, 7 | t1 = 6 / 29, 8 | t2 = 3 * t1 * t1, 9 | t3 = t1 * t1 * t1, 10 | deg2rad = Math.PI / 180, 11 | rad2deg = 180 / Math.PI; 12 | 13 | // Utilities 14 | function xyz2lab(t) { 15 | return t > t3 ? Math.pow(t, 1 / 3) : t / t2 + t0; 16 | } 17 | 18 | function lab2xyz(t) { 19 | return t > t1 ? t * t * t : t2 * (t - t0); 20 | } 21 | 22 | function xyz2rgb(x) { 23 | return 255 * (x <= 0.0031308 ? 12.92 * x : 1.055 * Math.pow(x, 1 / 2.4) - 0.055); 24 | } 25 | 26 | function rgb2xyz(x) { 27 | x /= 255; 28 | return x <= 0.04045 ? x / 12.92 : Math.pow((x + 0.055) / 1.055, 2.4); 29 | } 30 | 31 | // LAB 32 | function rgbToLab(rgbColor) { 33 | const b = rgb2xyz(rgbColor[0]), 34 | a = rgb2xyz(rgbColor[1]), 35 | l = rgb2xyz(rgbColor[2]), 36 | x = xyz2lab((0.4124564 * b + 0.3575761 * a + 0.1804375 * l) / Xn), 37 | y = xyz2lab((0.2126729 * b + 0.7151522 * a + 0.0721750 * l) / Yn), 38 | z = xyz2lab((0.0193339 * b + 0.1191920 * a + 0.9503041 * l) / Zn); 39 | 40 | return [ 41 | 116 * y - 16, 42 | 500 * (x - y), 43 | 200 * (y - z), 44 | rgbColor[3] 45 | ]; 46 | } 47 | 48 | function labToRgb(labColor) { 49 | let y = (labColor[0] + 16) / 116, 50 | x = isNaN(labColor[1]) ? y : y + labColor[1] / 500, 51 | z = isNaN(labColor[2]) ? y : y - labColor[2] / 200; 52 | y = Yn * lab2xyz(y); 53 | x = Xn * lab2xyz(x); 54 | z = Zn * lab2xyz(z); 55 | return [ 56 | xyz2rgb(3.2404542 * x - 1.5371385 * y - 0.4985314 * z), // D65 -> sRGB 57 | xyz2rgb(-0.9692660 * x + 1.8760108 * y + 0.0415560 * z), 58 | xyz2rgb(0.0556434 * x - 0.2040259 * y + 1.0572252 * z), 59 | labColor[3] 60 | ]; 61 | } 62 | 63 | // HCL 64 | function rgbToHcl(rgbColor) { 65 | const labColor = rgbToLab(rgbColor); 66 | const l = labColor[0], 67 | a = labColor[1], 68 | b = labColor[2]; 69 | const h = Math.atan2(b, a) * rad2deg; 70 | return [ 71 | h < 0 ? h + 360 : h, 72 | Math.sqrt(a * a + b * b), 73 | l, 74 | rgbColor[3] 75 | ]; 76 | } 77 | 78 | function hclToRgb(hclColor) { 79 | const h = hclColor[0] * deg2rad, 80 | c = hclColor[1], 81 | l = hclColor[2]; 82 | return labToRgb([ 83 | l, 84 | Math.cos(h) * c, 85 | Math.sin(h) * c, 86 | hclColor[3] 87 | ]); 88 | } 89 | 90 | module.exports = { 91 | lab: { 92 | forward: rgbToLab, 93 | reverse: labToRgb 94 | }, 95 | hcl: { 96 | forward: rgbToHcl, 97 | reverse: hclToRgb 98 | } 99 | }; 100 | -------------------------------------------------------------------------------- /client-app/src/external/mapbox-gl-style-spec/util/extend.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = function (output, ...inputs) { 3 | for (const input of inputs) { 4 | for (const k in input) { 5 | output[k] = input[k]; 6 | } 7 | } 8 | return output; 9 | }; 10 | -------------------------------------------------------------------------------- /client-app/src/external/mapbox-gl-style-spec/util/get_type.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = function getType(val) { 3 | if (val instanceof Number) { 4 | return 'number'; 5 | } else if (val instanceof String) { 6 | return 'string'; 7 | } else if (val instanceof Boolean) { 8 | return 'boolean'; 9 | } else if (Array.isArray(val)) { 10 | return 'array'; 11 | } else if (val === null) { 12 | return 'null'; 13 | } else { 14 | return typeof val; 15 | } 16 | }; 17 | -------------------------------------------------------------------------------- /client-app/src/external/mapbox-gl-style-spec/util/interpolate.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = interpolate; 3 | 4 | function interpolate(a, b, t) { 5 | return (a * (1 - t)) + (b * t); 6 | } 7 | 8 | interpolate.number = interpolate; 9 | 10 | interpolate.vec2 = function(from, to, t) { 11 | return [ 12 | interpolate(from[0], to[0], t), 13 | interpolate(from[1], to[1], t) 14 | ]; 15 | }; 16 | 17 | /* 18 | * Interpolate between two colors given as 4-element arrays. 19 | * 20 | * @param {Color} from 21 | * @param {Color} to 22 | * @param {number} t interpolation factor between 0 and 1 23 | * @returns {Color} interpolated color 24 | */ 25 | interpolate.color = function(from, to, t) { 26 | return [ 27 | interpolate(from[0], to[0], t), 28 | interpolate(from[1], to[1], t), 29 | interpolate(from[2], to[2], t), 30 | interpolate(from[3], to[3], t) 31 | ]; 32 | }; 33 | 34 | interpolate.array = function(from, to, t) { 35 | return from.map((d, i) => { 36 | return interpolate(d, to[i], t); 37 | }); 38 | }; 39 | -------------------------------------------------------------------------------- /client-app/src/external/mapbox-gl-style-spec/util/parse_color.js: -------------------------------------------------------------------------------- 1 | 2 | const parseColorString = require('csscolorparser').parseCSSColor; 3 | 4 | module.exports = function parseColor(input) { 5 | if (typeof input === 'string') { 6 | const rgba = parseColorString(input); 7 | if (!rgba) { return undefined; } 8 | 9 | // GL expects all components to be in the range [0, 1] and to be 10 | // multipled by the alpha value. 11 | return [ 12 | rgba[0] / 255 * rgba[3], 13 | rgba[1] / 255 * rgba[3], 14 | rgba[2] / 255 * rgba[3], 15 | rgba[3] 16 | ]; 17 | 18 | } else if (Array.isArray(input)) { 19 | return input; 20 | 21 | } else { 22 | return undefined; 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /client-app/src/external/mapbox-gl-style-spec/util/ref_properties.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = ['type', 'source', 'source-layer', 'minzoom', 'maxzoom', 'filter', 'layout']; 3 | -------------------------------------------------------------------------------- /client-app/src/external/mapbox-gl-style-spec/util/unbundle_jsonlint.js: -------------------------------------------------------------------------------- 1 | 2 | // Turn jsonlint-lines-primitives objects into primitive objects 3 | module.exports = function unbundle(value) { 4 | if (value instanceof Number || value instanceof String || value instanceof Boolean) { 5 | return value.valueOf(); 6 | } else { 7 | return value; 8 | } 9 | }; 10 | -------------------------------------------------------------------------------- /client-app/src/external/util.js: -------------------------------------------------------------------------------- 1 | export function deg2rad(degrees) { 2 | return degrees * Math.PI / 180; 3 | } 4 | 5 | export function getZoomForResolution(resolution, resolutions) { 6 | let i = 0; 7 | const ii = resolutions.length; 8 | for (; i < ii; ++i) { 9 | const candidate = resolutions[i]; 10 | if (candidate < resolution && i + 1 < ii) { 11 | const zoomFactor = resolutions[i] / resolutions[i + 1]; 12 | return i + Math.log(resolutions[i] / resolution) / Math.log(zoomFactor); 13 | } 14 | } 15 | return ii - 1; 16 | } 17 | -------------------------------------------------------------------------------- /client-app/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | 7 | html, body, #root { 8 | height: 100%; 9 | width: 100%; 10 | } -------------------------------------------------------------------------------- /client-app/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './containers/app/App'; 5 | import { Provider } from 'react-redux'; 6 | import store from './store'; 7 | import registerServiceWorker from './registerServiceWorker'; 8 | 9 | ReactDOM.render( 10 | 11 |
12 | 13 |
14 |
, 15 | document.getElementById('root') 16 | ); 17 | registerServiceWorker(); 18 | -------------------------------------------------------------------------------- /client-app/src/modules/index.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux'; 2 | import map from './map'; 3 | 4 | export default combineReducers({ 5 | map 6 | }); 7 | -------------------------------------------------------------------------------- /client-app/src/registerServiceWorker.js: -------------------------------------------------------------------------------- 1 | // In production, we register a service worker to serve assets from local cache. 2 | 3 | // This lets the app load faster on subsequent visits in production, and gives 4 | // it offline capabilities. However, it also means that developers (and users) 5 | // will only see deployed updates on the "N+1" visit to a page, since previously 6 | // cached resources are updated in the background. 7 | 8 | // To learn more about the benefits of this model, read https://goo.gl/KwvDNy. 9 | // This link also includes instructions on opting out of this behavior. 10 | 11 | const isLocalhost = Boolean( 12 | window.location.hostname === 'localhost' || 13 | // [::1] is the IPv6 localhost address. 14 | window.location.hostname === '[::1]' || 15 | // 127.0.0.1/8 is considered localhost for IPv4. 16 | window.location.hostname.match( 17 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ 18 | ) 19 | ); 20 | 21 | export default function register() { 22 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { 23 | // The URL constructor is available in all browsers that support SW. 24 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location); 25 | if (publicUrl.origin !== window.location.origin) { 26 | // Our service worker won't work if PUBLIC_URL is on a different origin 27 | // from what our page is served on. This might happen if a CDN is used to 28 | // serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374 29 | return; 30 | } 31 | 32 | window.addEventListener('load', () => { 33 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; 34 | 35 | if (isLocalhost) { 36 | // This is running on localhost. Lets check if a service worker still exists or not. 37 | checkValidServiceWorker(swUrl); 38 | 39 | // Add some additional logging to localhost, pointing developers to the 40 | // service worker/PWA documentation. 41 | navigator.serviceWorker.ready.then(() => { 42 | console.log( 43 | 'This web app is being served cache-first by a service ' + 44 | 'worker. To learn more, visit https://goo.gl/SC7cgQ' 45 | ); 46 | }); 47 | } else { 48 | // Is not local host. Just register service worker 49 | registerValidSW(swUrl); 50 | } 51 | }); 52 | } 53 | } 54 | 55 | function registerValidSW(swUrl) { 56 | navigator.serviceWorker 57 | .register(swUrl) 58 | .then(registration => { 59 | registration.onupdatefound = () => { 60 | const installingWorker = registration.installing; 61 | installingWorker.onstatechange = () => { 62 | if (installingWorker.state === 'installed') { 63 | if (navigator.serviceWorker.controller) { 64 | // At this point, the old content will have been purged and 65 | // the fresh content will have been added to the cache. 66 | // It's the perfect time to display a "New content is 67 | // available; please refresh." message in your web app. 68 | console.log('New content is available; please refresh.'); 69 | } else { 70 | // At this point, everything has been precached. 71 | // It's the perfect time to display a 72 | // "Content is cached for offline use." message. 73 | console.log('Content is cached for offline use.'); 74 | } 75 | } 76 | }; 77 | }; 78 | }) 79 | .catch(error => { 80 | console.error('Error during service worker registration:', error); 81 | }); 82 | } 83 | 84 | function checkValidServiceWorker(swUrl) { 85 | // Check if the service worker can be found. If it can't reload the page. 86 | fetch(swUrl) 87 | .then(response => { 88 | // Ensure service worker exists, and that we really are getting a JS file. 89 | if ( 90 | response.status === 404 || 91 | response.headers.get('content-type').indexOf('javascript') === -1 92 | ) { 93 | // No service worker found. Probably a different app. Reload the page. 94 | navigator.serviceWorker.ready.then(registration => { 95 | registration.unregister().then(() => { 96 | window.location.reload(); 97 | }); 98 | }); 99 | } else { 100 | // Service worker found. Proceed as normal. 101 | registerValidSW(swUrl); 102 | } 103 | }) 104 | .catch(() => { 105 | console.log( 106 | 'No internet connection found. App is running in offline mode.' 107 | ); 108 | }); 109 | } 110 | 111 | export function unregister() { 112 | if ('serviceWorker' in navigator) { 113 | navigator.serviceWorker.ready.then(registration => { 114 | registration.unregister(); 115 | }); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /client-app/src/store.js: -------------------------------------------------------------------------------- 1 | import { createStore, applyMiddleware, compose } from 'redux'; 2 | import thunk from 'redux-thunk'; 3 | import rootReducer from './modules'; 4 | 5 | const initialState = {}; 6 | const enhancers = []; 7 | const middleware = [thunk]; 8 | 9 | const composedEnhancers = compose(applyMiddleware(...middleware), ...enhancers); 10 | 11 | export default createStore(rootReducer, initialState, composedEnhancers); 12 | -------------------------------------------------------------------------------- /cloudformation/1-vpc-setup.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | AWSTemplateFormatVersion: '2010-09-09' 3 | Description: 'VPC for FOSS4G Oceania Workshop with public subnet and two Availability Zones' 4 | 5 | Metadata: 6 | AWS::CloudFormation::Interface: 7 | ParameterGroups: 8 | - Label: 9 | default: VPC Configuration 10 | Parameters: 11 | - ClassB 12 | ParameterLabels: 13 | ClassB: 14 | default: Class B of VPC 15 | 16 | Parameters: 17 | ClassB: 18 | Description: 'Class B of VPC CIDR (10.XXX.0.0/16)' 19 | Type: Number 20 | Default: 99 21 | ConstraintDescription: 'Must be in the range [0-255]' 22 | MinValue: 0 23 | MaxValue: 255 24 | 25 | Outputs: 26 | VPC: 27 | Description: 'VPC ID' 28 | Value: !Ref VPC 29 | Export: 30 | Name: 'foss4g-oceania-workshop-vpc' 31 | CidrBlock: 32 | Description: 'CidrBlock' 33 | Value: !GetAtt VPC.CidrBlock 34 | Export: 35 | Name: 'foss4g-oceania-workshop-cidr-block' 36 | SubnetsPublic: 37 | Description: 'Subnets public.' 38 | Value: !Join [',', [!Ref SubnetAPublic, !Ref SubnetBPublic]] 39 | Export: 40 | Name: 'foss4g-oceania-workshop-subnets-public' 41 | RouteTablePublic: 42 | Description: 'Route table public.' 43 | Value: !Ref RouteTablePublic 44 | Export: 45 | Name: 'foss4g-oceania-workshop-route-table-public' 46 | SubnetAPublic: 47 | Description: 'Subnet A public.' 48 | Value: !Ref SubnetAPublic 49 | Export: 50 | Name: 'foss4g-oceania-workshop-subnet-a-public' 51 | SubnetBPublic: 52 | Description: 'Subnet B public.' 53 | Value: !Ref SubnetBPublic 54 | Export: 55 | Name: 'foss4g-oceania-workshop-subnet-b-public' 56 | NetworkAclPublic: 57 | Description: 'Network Acl public.' 58 | Value: !Ref NetworkAclPublic 59 | Export: 60 | Name: 'foss4g-oceania-workshop-network-acl-public' 61 | 62 | Resources: 63 | # VPC Configuration 64 | VPC: 65 | Type: 'AWS::EC2::VPC' 66 | Properties: 67 | CidrBlock: !Sub '10.${ClassB}.0.0/16' 68 | EnableDnsSupport: true 69 | EnableDnsHostnames: true 70 | InstanceTenancy: default 71 | Tags: 72 | - Key: Name 73 | Value: !Sub 'foss4g-oceania-workshop-10.${ClassB}.0.0/16' 74 | InternetGateway: 75 | Type: 'AWS::EC2::InternetGateway' 76 | Properties: 77 | Tags: 78 | - Key: Name 79 | Value: !Sub 'foss4g-oceania-workshop-10.${ClassB}.0.0/16' 80 | VPCGatewayAttachment: 81 | Type: 'AWS::EC2::VPCGatewayAttachment' 82 | Properties: 83 | VpcId: !Ref VPC 84 | InternetGatewayId: !Ref InternetGateway 85 | 86 | # Subnet Configuration for Public Subnet 87 | SubnetAPublic: 88 | Type: 'AWS::EC2::Subnet' 89 | Properties: 90 | AvailabilityZone: !Select [0, !GetAZs ''] 91 | CidrBlock: !Sub '10.${ClassB}.0.0/20' 92 | MapPublicIpOnLaunch: true 93 | VpcId: !Ref VPC 94 | Tags: 95 | - Key: Name 96 | Value: 'foss4g-oceania-workshop-subnet-a-public' 97 | - Key: Reach 98 | Value: public 99 | SubnetBPublic: 100 | Type: 'AWS::EC2::Subnet' 101 | Properties: 102 | AvailabilityZone: !Select [1, !GetAZs ''] 103 | CidrBlock: !Sub '10.${ClassB}.16.0/20' 104 | MapPublicIpOnLaunch: true 105 | VpcId: !Ref VPC 106 | Tags: 107 | - Key: Name 108 | Value: 'foss4g-oceania-workshop-subnet-b-public' 109 | - Key: Reach 110 | Value: public 111 | 112 | # Route Tables 113 | RouteTablePublic: 114 | Type: 'AWS::EC2::RouteTable' 115 | Properties: 116 | VpcId: !Ref VPC 117 | Tags: 118 | - Key: Name 119 | Value: !Sub 'foss4g-oceania-workshop-route-table-public' 120 | RouteTableAssociationPublicA: 121 | Type: 'AWS::EC2::SubnetRouteTableAssociation' 122 | Properties: 123 | SubnetId: !Ref SubnetAPublic 124 | RouteTableId: !Ref RouteTablePublic 125 | RouteTableAssociationPublicB: 126 | Type: 'AWS::EC2::SubnetRouteTableAssociation' 127 | Properties: 128 | SubnetId: !Ref SubnetBPublic 129 | RouteTableId: !Ref RouteTablePublic 130 | RouteTablePublicInternetRouteIPv4: 131 | Type: 'AWS::EC2::Route' 132 | DependsOn: VPCGatewayAttachment 133 | Properties: 134 | RouteTableId: !Ref RouteTablePublic 135 | DestinationCidrBlock: '0.0.0.0/0' 136 | GatewayId: !Ref InternetGateway 137 | 138 | # Network ACLs 139 | NetworkAclPublic: 140 | Type: 'AWS::EC2::NetworkAcl' 141 | Properties: 142 | VpcId: !Ref VPC 143 | Tags: 144 | - Key: Name 145 | Value: 'foss4g-oceania-workshop-public' 146 | SubnetNetworkAclAssociationPublicA: 147 | Type: 'AWS::EC2::SubnetNetworkAclAssociation' 148 | Properties: 149 | SubnetId: !Ref SubnetAPublic 150 | NetworkAclId: !Ref NetworkAclPublic 151 | SubnetNetworkAclAssociationPublicB: 152 | Type: 'AWS::EC2::SubnetNetworkAclAssociation' 153 | Properties: 154 | SubnetId: !Ref SubnetBPublic 155 | NetworkAclId: !Ref NetworkAclPublic 156 | 157 | # Public Subnet Inbound ACL Entries 158 | NetworkAclEntryInPublicAllowSshIPv4: 159 | Type: 'AWS::EC2::NetworkAclEntry' 160 | Properties: 161 | NetworkAclId: !Ref NetworkAclPublic 162 | RuleNumber: 122 163 | Protocol: 6 # TCP 164 | RuleAction: allow 165 | Egress: false 166 | CidrBlock: '0.0.0.0/0' # Internet 167 | PortRange: 168 | From: '22' 169 | To: '22' 170 | NetworkAclEntryInPublicAllowHttpIPv4: 171 | Type: 'AWS::EC2::NetworkAclEntry' 172 | Properties: 173 | NetworkAclId: !Ref NetworkAclPublic 174 | RuleNumber: 123 175 | Protocol: 6 # TCP 176 | RuleAction: allow 177 | Egress: false 178 | CidrBlock: '0.0.0.0/0' # Internet 179 | PortRange: 180 | From: '80' 181 | To: '80' 182 | NetworkAclEntryInPublicAllowHttpsIPv4: 183 | Type: 'AWS::EC2::NetworkAclEntry' 184 | Properties: 185 | NetworkAclId: !Ref NetworkAclPublic 186 | RuleNumber: 124 187 | Protocol: 6 # TCP 188 | RuleAction: allow 189 | Egress: false 190 | CidrBlock: '0.0.0.0/0' # Internet 191 | PortRange: 192 | From: '443' 193 | To: '443' 194 | NetworkAclEntryInPublicAllowEphemeralPortsIPv4: 195 | Type: 'AWS::EC2::NetworkAclEntry' 196 | Properties: 197 | NetworkAclId: !Ref NetworkAclPublic 198 | RuleNumber: 130 199 | Protocol: 6 # TCP 200 | RuleAction: allow 201 | Egress: false 202 | CidrBlock: '0.0.0.0/0' # Internet 203 | PortRange: # Ephemeral Ports: http://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/VPC_ACLs.html#VPC_ACLs_Ephemeral_Ports 204 | From: '1024' 205 | To: '65535' 206 | 207 | # Public Subnet Outbound ACL Entries 208 | NetworkAclEntryOutPublicAllowAllIPv4: 209 | Type: 'AWS::EC2::NetworkAclEntry' 210 | Properties: 211 | NetworkAclId: !Ref NetworkAclPublic 212 | RuleNumber: 150 213 | Protocol: 6 # TCP 214 | RuleAction: allow 215 | Egress: true 216 | CidrBlock: '0.0.0.0/0' 217 | PortRange: # Allowing all outbound traffic 218 | From: 1 219 | To: 65535 220 | -------------------------------------------------------------------------------- /cloudformation/2-security-groups.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | AWSTemplateFormatVersion: '2010-09-09' 3 | Description: 'Security Groups for FOSS4G Oceania workshop' 4 | Metadata: 5 | AWS::CloudFormation::Interface: 6 | ParameterGroups: 7 | - Label: 8 | default: Security Configuration 9 | Parameters: 10 | - AllowedIPs 11 | - SSHPort 12 | - DBPort 13 | - GeoserverPort 14 | ParameterLabels: 15 | AllowedIPs: 16 | default: Allowed IPs 17 | SSHPort: 18 | default: SSH port 19 | DBPort: 20 | default: Postgres port 21 | GeoserverPort: 22 | default: GeoServer port 23 | 24 | Parameters: 25 | AllowedIPs: 26 | Description: 'Allowed IP addresses (CIDR notation)' 27 | AllowedPattern: '^\d+\.\d+\.\d+\.\d+/\d+$' 28 | Type: String 29 | SSHPort: 30 | Description: 'TCP port for SSH access' 31 | Type: Number 32 | Default: '22' 33 | DBPort: 34 | Description: 'TCP port for database access' 35 | Type: Number 36 | Default: '5432' 37 | GeoserverPort: 38 | Description: 'TCP port for GeoServer web access' 39 | Type: Number 40 | Default: '8080' 41 | 42 | Outputs: 43 | SSHSecurityGroupID: 44 | Description: SSH Security Group ID 45 | Value: 46 | Ref: SSHSecurityGroup 47 | Export: 48 | Name: 'foss4g-oceania-workshop-ssh-security-group-id' 49 | GeoserverSecurityGroupID: 50 | Description: GeoServer Security Group ID 51 | Value: 52 | Ref: GeoserverSecurityGroup 53 | Export: 54 | Name: 'foss4g-oceania-workshop-geoserver-security-group-id' 55 | GeoserverLBSecurityGroupID: 56 | Description: GeoServer Load Balancer Security Group ID 57 | Value: 58 | Ref: GeoserverLBSecurityGroup 59 | Export: 60 | Name: 'foss4g-oceania-workshop-geoserver-lb-security-group-id' 61 | GeoserverNFSSecurityGroupID: 62 | Description: GeoServer NFS Security Group ID 63 | Value: 64 | Ref: GeoserverNFSSecurityGroup 65 | Export: 66 | Name: 'foss4g-oceania-workshop-geoserver-nfs-security-group-id' 67 | DBAccessSecurityGroupID: 68 | Description: DB Access Security Group ID 69 | Value: 70 | Ref: DBAccessSecurityGroup 71 | Export: 72 | Name: 'foss4g-oceania-workshop-db-access-security-group-id' 73 | DBSecurityGroupID: 74 | Description: DB Security Group ID 75 | Value: 76 | Ref: DBSecurityGroup 77 | Export: 78 | Name: 'foss4g-oceania-workshop-db-security-group-id' 79 | 80 | Resources: 81 | GeoserverLBSecurityGroup: 82 | Type: AWS::EC2::SecurityGroup 83 | Properties: 84 | GroupDescription: GeoServer Load Balancer 85 | VpcId: !ImportValue 'foss4g-oceania-workshop-vpc' 86 | SecurityGroupIngress: 87 | - IpProtocol: tcp 88 | FromPort: '80' 89 | ToPort: '80' 90 | CidrIp: !Ref AllowedIPs 91 | Description: 'Allowed IPs' 92 | Tags: 93 | - Key: Name 94 | Value: 'foss4g-oceania-geoserver-lb' 95 | 96 | GeoserverSecurityGroup: 97 | Type: AWS::EC2::SecurityGroup 98 | Properties: 99 | GroupDescription: GeoServer 100 | VpcId: !ImportValue 'foss4g-oceania-workshop-vpc' 101 | SecurityGroupIngress: 102 | - SourceSecurityGroupId: !Ref GeoserverLBSecurityGroup 103 | IpProtocol: tcp 104 | FromPort: !Ref GeoserverPort 105 | ToPort: !Ref GeoserverPort 106 | Description: 'Geoserver load balancer' 107 | Tags: 108 | - Key: Name 109 | Value: 'foss4g-oceania-geoserver' 110 | 111 | SSHSecurityGroup: 112 | Type: AWS::EC2::SecurityGroup 113 | Properties: 114 | GroupDescription: SSH 115 | VpcId: !ImportValue 'foss4g-oceania-workshop-vpc' 116 | SecurityGroupIngress: 117 | - IpProtocol: tcp 118 | FromPort: !Ref SSHPort 119 | ToPort: !Ref SSHPort 120 | CidrIp: !Ref AllowedIPs 121 | Description: 'Allowed IPs' 122 | Tags: 123 | - Key: Name 124 | Value: 'foss4g-oceania-ssh' 125 | 126 | GeoserverNFSSecurityGroup: 127 | Type: AWS::EC2::SecurityGroup 128 | Properties: 129 | GroupDescription: GeoServer NFS 130 | VpcId: !ImportValue 'foss4g-oceania-workshop-vpc' 131 | SecurityGroupIngress: 132 | - SourceSecurityGroupId: !Ref GeoserverSecurityGroup 133 | IpProtocol: tcp 134 | FromPort: '2049' 135 | ToPort: '2049' 136 | Description: 'Allow NFS from GeoServer SG' 137 | Tags: 138 | - Key: Name 139 | Value: 'foss4g-oceania-geoserver-nfs' 140 | 141 | DBAccessSecurityGroup: 142 | Type: AWS::EC2::SecurityGroup 143 | Properties: 144 | GroupDescription: Access to the database security group 145 | VpcId: !ImportValue 'foss4g-oceania-workshop-vpc' 146 | Tags: 147 | - Key: Name 148 | Value: 'foss4g-oceania-db-access' 149 | 150 | DBSecurityGroup: 151 | Type: AWS::EC2::SecurityGroup 152 | Properties: 153 | GroupDescription: Database 154 | VpcId: !ImportValue 'foss4g-oceania-workshop-vpc' 155 | SecurityGroupIngress: 156 | - IpProtocol: tcp 157 | FromPort: !Ref DBPort 158 | ToPort: !Ref DBPort 159 | SourceSecurityGroupId: !Ref DBAccessSecurityGroup 160 | Description: 'Security Group db-access' 161 | - IpProtocol: tcp 162 | FromPort: !Ref DBPort 163 | ToPort: !Ref DBPort 164 | CidrIp: !Ref AllowedIPs 165 | Description: 'Allowed IPs' 166 | Tags: 167 | - Key: Name 168 | Value: 'foss4g-oceania-db' 169 | -------------------------------------------------------------------------------- /cloudformation/3-db-setup.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | AWSTemplateFormatVersion: '2010-09-09' 3 | Description: 'Postgres database setup for FOSS4G Oceania workshop' 4 | Metadata: 5 | AWS::CloudFormation::Interface: 6 | ParameterGroups: 7 | - Label: 8 | default: RDS instance configuration 9 | Parameters: 10 | - InstanceName 11 | - InstanceClass 12 | - AllocatedStorage 13 | - MasterUsername 14 | - MasterPassword 15 | ParameterLabels: 16 | InstanceName: 17 | default: RDS instance name 18 | InstanceClass: 19 | default: RDS instance type 20 | AllocatedStorage: 21 | default: RDS allocated storage 22 | MasterUsername: 23 | default: RDS master username 24 | MasterPassword: 25 | default: RDS master password 26 | 27 | Parameters: 28 | InstanceName: 29 | Description: 'RDS instance name' 30 | Type: String 31 | Default: foss4g 32 | AllocatedStorage: 33 | Description: 'RDS instance allocated storage (GB)' 34 | Type: Number 35 | Default: 20 36 | InstanceClass: 37 | Description: 'RDS instance type' 38 | AllowedValues: 39 | - db.t2.micro 40 | - db.t2.small 41 | - db.t2.medium 42 | - db.t2.large 43 | - db.m4.large 44 | - db.m4.xlarge 45 | Type: String 46 | Default: db.t2.micro 47 | MasterUsername: 48 | Description: 'RDS master user name' 49 | Type: String 50 | Default: postgres 51 | MasterPassword: 52 | Description: 'RDS master user password' 53 | Type: String 54 | NoEcho: true 55 | 56 | Outputs: 57 | Foss4GOceaniaWorkshopDBSubnetGroup: 58 | Description: 'DB Subnet Group' 59 | Value: !Ref Foss4GOceaniaWorkshopDBSubnetGroup 60 | Export: 61 | Name: !Sub 'foss4g-oceania-workshop-db-subnet-group' 62 | Foss4GOceaniaWorkshopDB: 63 | Description: 'Postgres RDS instance' 64 | Value: !Ref Foss4GOceaniaWorkshopDB 65 | Export: 66 | Name: !Sub 'foss4g-oceania-workshop-db' 67 | Foss4GOceaniaWorkshopDBEndpoint: 68 | Description: 'Postgres RDS instance endpoint' 69 | Value: !GetAtt Foss4GOceaniaWorkshopDB.Endpoint.Address 70 | Export: 71 | Name: !Sub 'foss4g-oceania-workshop-db-endpoint' 72 | 73 | Resources: 74 | Foss4GOceaniaWorkshopDBSubnetGroup: 75 | Type: AWS::RDS::DBSubnetGroup 76 | Properties: 77 | DBSubnetGroupDescription: Subnets used by the PostGIS DB 78 | SubnetIds: 79 | - !ImportValue 'foss4g-oceania-workshop-subnet-a-public' 80 | - !ImportValue 'foss4g-oceania-workshop-subnet-b-public' 81 | Foss4GOceaniaWorkshopDB: 82 | Type: "AWS::RDS::DBInstance" 83 | Properties: 84 | DBName: !Ref InstanceName 85 | AllocatedStorage: !Ref AllocatedStorage 86 | DBInstanceClass: !Ref InstanceClass 87 | DBSubnetGroupName: !Ref Foss4GOceaniaWorkshopDBSubnetGroup 88 | Engine: 'postgres' 89 | EngineVersion: "9.6.6" 90 | MasterUsername: !Ref MasterUsername 91 | MasterUserPassword: !Ref MasterPassword 92 | PubliclyAccessible: true 93 | VPCSecurityGroups: 94 | - !ImportValue 'foss4g-oceania-workshop-db-security-group-id' 95 | Tags: 96 | - Key: Name 97 | Value: "foss4g-oceania-workshop-db" 98 | DeletionPolicy: "Snapshot" 99 | -------------------------------------------------------------------------------- /cloudformation/4-admin.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | AWSTemplateFormatVersion: '2010-09-09' 3 | Description: 'Admin instance for FOSS4G Oceania workshop' 4 | Metadata: 5 | AWS::CloudFormation::Interface: 6 | ParameterGroups: 7 | - Label: 8 | default: EC2 instance configuration 9 | Parameters: 10 | - InstanceAMI 11 | - InstanceName 12 | - InstanceType 13 | - KeyPair 14 | ParameterLabels: 15 | InstanceAMI: 16 | default: AMI 17 | InstanceName: 18 | default: Instance name 19 | InstanceType: 20 | default: Instance type 21 | KeyPair: 22 | default: SSH key pair 23 | 24 | Parameters: 25 | InstanceAMI: 26 | Description: 'EC2 instance image' 27 | Type: String 28 | Default: ami-09b42976632b27e9b # Amazon Linux AMI 2018.03.0 (HVM), SSD Volume Type 29 | InstanceName: 30 | Description: 'EC2 instance name' 31 | Type: String 32 | Default: foss4g-oceania-workshop-admin 33 | InstanceType: 34 | Description: 'EC2 instance type' 35 | AllowedValues: 36 | - t2.nano 37 | - t2.micro 38 | - t2.small 39 | - t2.medium 40 | - m5.large 41 | Type: String 42 | Default: t2.nano 43 | KeyPair: 44 | Description: 'SSH key pair for access to instance' 45 | Type: AWS::EC2::KeyPair::KeyName 46 | 47 | Resources: 48 | AdminInstance: 49 | Type: AWS::EC2::Instance 50 | Properties: 51 | ImageId: !Ref InstanceAMI 52 | InstanceType: !Ref InstanceType 53 | KeyName: !Ref KeyPair 54 | NetworkInterfaces: 55 | - AssociatePublicIpAddress: true 56 | DeleteOnTermination: true 57 | DeviceIndex: 0 58 | GroupSet: 59 | - !ImportValue 'foss4g-oceania-workshop-ssh-security-group-id' 60 | - !ImportValue 'foss4g-oceania-workshop-db-access-security-group-id' 61 | SubnetId: !ImportValue 'foss4g-oceania-workshop-subnet-a-public' 62 | UserData: 63 | Fn::Base64: !Sub 64 | - | 65 | #!/bin/bash 66 | # Update all packages. 67 | yum -y update 68 | yum -y install postgresql96 python27-psycopg2 69 | 70 | # Put the RDS endpoint into a shell variable for convenience. 71 | echo "export DBHOST=${DBEndpoint}" >> /home/ec2-user/.bashrc 72 | 73 | # Download and extract workshop resources into ec2-user's home directory. 74 | curl -Lo /tmp/workshop-data.tar.gz https://s3-ap-southeast-2.amazonaws.com/foss4g-oceania-2018-workshop-resources/workshop-data.tar.gz 75 | mkdir -p /home/ec2-user/workshop 76 | tar -zxf /tmp/workshop-data.tar.gz -C /home/ec2-user/workshop 77 | rm /tmp/workshop-data.tar.gz 78 | 79 | - DBEndpoint: !ImportValue foss4g-oceania-workshop-db-endpoint 80 | Tags: 81 | - Key: Name 82 | Value: !Ref InstanceName 83 | -------------------------------------------------------------------------------- /cloudformation/5-geoserver.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | AWSTemplateFormatVersion: '2010-09-09' 3 | Description: 'Geoserver auto-scaling group for FOSS4G Oceania workshop' 4 | 5 | Metadata: 6 | AWS::CloudFormation::Interface: 7 | ParameterGroups: 8 | - Label: 9 | default: EC2 instance configuration 10 | Parameters: 11 | - AMI 12 | - InstanceName 13 | - InstanceType 14 | - KeyPair 15 | - InitialInstances 16 | - MinInstances 17 | - MaxInstances 18 | ParameterLabels: 19 | AMI: 20 | default: AMI 21 | InstanceName: 22 | default: Instance name 23 | InstanceType: 24 | default: Instance type 25 | KeyPair: 26 | default: SSH key pair 27 | InitialInstances: 28 | default: Initial number of instances 29 | MinInstances: 30 | default: Minimum number of instances 31 | MaxInstances: 32 | default: Maximum number of instances 33 | 34 | Parameters: 35 | AMI: 36 | Description: AMI for GeoServer instances 37 | Type: String 38 | Default: ami-00b784efcd7b2731c # foss4g-workshop-geoserver 20180919T023714Z 39 | InstanceName: 40 | Description: EC2 instance name 41 | Type: String 42 | Default: foss4g-oceania-workshop-geoserver 43 | InstanceType: 44 | AllowedValues: 45 | - t2.small 46 | - t2.medium 47 | - m5.large 48 | - m5.2xlarge 49 | Default: t2.medium 50 | Description: Instance type for GeoServer instances 51 | Type: String 52 | KeyPair: 53 | Description: SSH key pair for access to instances 54 | Type: AWS::EC2::KeyPair::KeyName 55 | InitialInstances: 56 | Default: '1' 57 | Description: Initial number of GeoServer instances 58 | Type: Number 59 | MinInstances: 60 | Default: '1' 61 | Description: Minimum number of GeoServer instances 62 | Type: Number 63 | MaxInstances: 64 | Default: '6' 65 | Description: Maximum number of GeoServer instances 66 | Type: Number 67 | 68 | Resources: 69 | GeoserverElasticFileSystem: 70 | Type: AWS::EFS::FileSystem 71 | DeletionPolicy: Delete 72 | Properties: 73 | FileSystemTags: 74 | - Key: Name 75 | Value: 'geoserver-efs' 76 | 77 | GeoserverEFSMountAZ1: 78 | Type: AWS::EFS::MountTarget 79 | Properties: 80 | FileSystemId: !Ref GeoserverElasticFileSystem 81 | SubnetId: !ImportValue 'foss4g-oceania-workshop-subnet-a-public' 82 | SecurityGroups: 83 | - !ImportValue 'foss4g-oceania-workshop-geoserver-nfs-security-group-id' 84 | 85 | GeoserverEFSMountAZ2: 86 | Type: AWS::EFS::MountTarget 87 | Properties: 88 | FileSystemId: !Ref GeoserverElasticFileSystem 89 | SubnetId: !ImportValue 'foss4g-oceania-workshop-subnet-b-public' 90 | SecurityGroups: 91 | - !ImportValue 'foss4g-oceania-workshop-geoserver-nfs-security-group-id' 92 | 93 | GeoserverLB: 94 | Type: AWS::ElasticLoadBalancingV2::LoadBalancer 95 | Properties: 96 | Subnets: 97 | - !ImportValue 'foss4g-oceania-workshop-subnet-a-public' 98 | - !ImportValue 'foss4g-oceania-workshop-subnet-b-public' 99 | SecurityGroups: 100 | - !ImportValue 'foss4g-oceania-workshop-geoserver-lb-security-group-id' 101 | Tags: 102 | - Key: Name 103 | Value: 'foss4g-oceania-workshop-geoserver' 104 | 105 | GeoserverTargetGroup: 106 | Type: AWS::ElasticLoadBalancingV2::TargetGroup 107 | Properties: 108 | Name: geoserver 109 | Protocol: HTTP 110 | Port: 8080 111 | VpcId: !ImportValue 'foss4g-oceania-workshop-vpc' 112 | HealthCheckPath: /geoserver/index.html 113 | TargetGroupAttributes: 114 | - Key: stickiness.enabled 115 | Value: true 116 | 117 | GeoserverListener: 118 | Type: AWS::ElasticLoadBalancingV2::Listener 119 | Properties: 120 | LoadBalancerArn: !Ref GeoserverLB 121 | Protocol: HTTP 122 | Port: 80 123 | DefaultActions: 124 | - TargetGroupArn: !Ref GeoserverTargetGroup 125 | Type: forward 126 | 127 | GeoserverLaunchTemplate: 128 | Type: AWS::EC2::LaunchTemplate 129 | DependsOn: 130 | - GeoserverEFSMountAZ1 131 | - GeoserverEFSMountAZ2 132 | Metadata: 133 | AWS::CloudFormation::Init: 134 | configSets: 135 | default: 136 | - setup_geoserver 137 | setup_geoserver: 138 | commands: 139 | 010_make_mount_point: 140 | command: "mkdir -p /efs" 141 | 020_add_nfs_mount: 142 | command: !Sub 'echo "${GeoserverElasticFileSystem}.efs.${AWS::Region}.amazonaws.com:/ /efs nfs4 nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2 0 0" >>/etc/fstab' 143 | 030_mount_all: 144 | command: "mount -a" 145 | 040_make_shared_home_dir: 146 | command: "mkdir -p /efs/geoserver/data" 147 | 050_change_ownership_shared_home_dir: 148 | command: "chown -R tomcat:tomcat /efs/geoserver/data/" 149 | 060_restart_geoserver: 150 | command: "service tomcat restart" 151 | services: 152 | sysvinit: 153 | rpcbind: 154 | enabled: true 155 | ensureRunning: true 156 | packages: 157 | yum: 158 | nfs-utils: [] 159 | Properties: 160 | LaunchTemplateName: foss4g-oceania-workshop-geoserver 161 | LaunchTemplateData: 162 | ImageId: !Ref AMI 163 | InstanceType: !Ref InstanceType 164 | KeyName: !Ref KeyPair 165 | SecurityGroupIds: 166 | - !ImportValue 'foss4g-oceania-workshop-geoserver-security-group-id' 167 | - !ImportValue 'foss4g-oceania-workshop-db-access-security-group-id' 168 | TagSpecifications: 169 | - ResourceType: instance 170 | Tags: 171 | - Key: Name 172 | Value: !Ref InstanceName 173 | UserData: 174 | Fn::Base64: !Sub | 175 | #!/bin/bash -x 176 | 177 | # Process the default configset from the CloudFormation::Init metadata 178 | /opt/aws/bin/cfn-init -v \ 179 | --region ${AWS::Region} \ 180 | --stack ${AWS::StackName} \ 181 | --resource GeoserverLaunchTemplate \ 182 | --configsets default 183 | 184 | # Signal GeoserverScalingGroup with the cfn-init exit status 185 | /opt/aws/bin/cfn-signal -e $? \ 186 | --region ${AWS::Region} \ 187 | --stack ${AWS::StackName} \ 188 | --resource GeoserverScalingGroup 189 | 190 | GeoserverScalingGroup: 191 | Type: AWS::AutoScaling::AutoScalingGroup 192 | Properties: 193 | DesiredCapacity: !Ref InitialInstances 194 | LaunchTemplate: 195 | LaunchTemplateId: !Ref GeoserverLaunchTemplate 196 | Version: !GetAtt GeoserverLaunchTemplate.LatestVersionNumber 197 | TargetGroupARNs: 198 | - !Ref GeoserverTargetGroup 199 | VPCZoneIdentifier: 200 | - !ImportValue 'foss4g-oceania-workshop-subnet-a-public' 201 | - !ImportValue 'foss4g-oceania-workshop-subnet-b-public' 202 | MaxSize: !Ref MaxInstances 203 | MinSize: !Ref MinInstances 204 | Tags: 205 | - Key: Name 206 | PropagateAtLaunch: true 207 | Value: !Ref InstanceName 208 | UpdatePolicy: 209 | AutoScalingRollingUpdate: 210 | MaxBatchSize: 1 211 | PauseTime: PT10M 212 | MinInstancesInService: 1 213 | WaitOnResourceSignals: true 214 | SuspendProcesses: [ReplaceUnhealthy, AZRebalance, AlarmNotification, ScheduledActions] 215 | CreationPolicy: 216 | ResourceSignal: 217 | Count: !Ref InitialInstances 218 | Timeout: PT10M 219 | -------------------------------------------------------------------------------- /images/client_end_state.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geoplex/foss4g-oceania-2018-workshop/ca3bb5d73533802f0e665903e2de2530fa2345ca/images/client_end_state.png -------------------------------------------------------------------------------- /images/client_select_statistic_control.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geoplex/foss4g-oceania-2018-workshop/ca3bb5d73533802f0e665903e2de2530fa2345ca/images/client_select_statistic_control.png -------------------------------------------------------------------------------- /images/client_stage_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geoplex/foss4g-oceania-2018-workshop/ca3bb5d73533802f0e665903e2de2530fa2345ca/images/client_stage_1.png -------------------------------------------------------------------------------- /images/client_stage_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geoplex/foss4g-oceania-2018-workshop/ca3bb5d73533802f0e665903e2de2530fa2345ca/images/client_stage_2.png -------------------------------------------------------------------------------- /images/cloudformation_admin_params.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geoplex/foss4g-oceania-2018-workshop/ca3bb5d73533802f0e665903e2de2530fa2345ca/images/cloudformation_admin_params.png -------------------------------------------------------------------------------- /images/cloudformation_db_params.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geoplex/foss4g-oceania-2018-workshop/ca3bb5d73533802f0e665903e2de2530fa2345ca/images/cloudformation_db_params.png -------------------------------------------------------------------------------- /images/cloudformation_geoserver_params.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geoplex/foss4g-oceania-2018-workshop/ca3bb5d73533802f0e665903e2de2530fa2345ca/images/cloudformation_geoserver_params.png -------------------------------------------------------------------------------- /images/cloudformation_sg_params.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geoplex/foss4g-oceania-2018-workshop/ca3bb5d73533802f0e665903e2de2530fa2345ca/images/cloudformation_sg_params.png -------------------------------------------------------------------------------- /images/cloudformation_vpc_params.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geoplex/foss4g-oceania-2018-workshop/ca3bb5d73533802f0e665903e2de2530fa2345ca/images/cloudformation_vpc_params.png -------------------------------------------------------------------------------- /images/color_brewer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geoplex/foss4g-oceania-2018-workshop/ca3bb5d73533802f0e665903e2de2530fa2345ca/images/color_brewer.png -------------------------------------------------------------------------------- /images/db_schema.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geoplex/foss4g-oceania-2018-workshop/ca3bb5d73533802f0e665903e2de2530fa2345ca/images/db_schema.png -------------------------------------------------------------------------------- /images/geoserver_add_postgis_store.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geoplex/foss4g-oceania-2018-workshop/ca3bb5d73533802f0e665903e2de2530fa2345ca/images/geoserver_add_postgis_store.png -------------------------------------------------------------------------------- /images/geoserver_add_workspace.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geoplex/foss4g-oceania-2018-workshop/ca3bb5d73533802f0e665903e2de2530fa2345ca/images/geoserver_add_workspace.png -------------------------------------------------------------------------------- /images/geoserver_bounding_boxes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geoplex/foss4g-oceania-2018-workshop/ca3bb5d73533802f0e665903e2de2530fa2345ca/images/geoserver_bounding_boxes.png -------------------------------------------------------------------------------- /images/geoserver_chain_settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geoplex/foss4g-oceania-2018-workshop/ca3bb5d73533802f0e665903e2de2530fa2345ca/images/geoserver_chain_settings.png -------------------------------------------------------------------------------- /images/geoserver_enable_mvt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geoplex/foss4g-oceania-2018-workshop/ca3bb5d73533802f0e665903e2de2530fa2345ca/images/geoserver_enable_mvt.png -------------------------------------------------------------------------------- /images/geoserver_filter_chains.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geoplex/foss4g-oceania-2018-workshop/ca3bb5d73533802f0e665903e2de2530fa2345ca/images/geoserver_filter_chains.png -------------------------------------------------------------------------------- /images/geoserver_layer_preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geoplex/foss4g-oceania-2018-workshop/ca3bb5d73533802f0e665903e2de2530fa2345ca/images/geoserver_layer_preview.png -------------------------------------------------------------------------------- /images/geoserver_new_layer_table.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geoplex/foss4g-oceania-2018-workshop/ca3bb5d73533802f0e665903e2de2530fa2345ca/images/geoserver_new_layer_table.png -------------------------------------------------------------------------------- /images/geoserver_styles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geoplex/foss4g-oceania-2018-workshop/ca3bb5d73533802f0e665903e2de2530fa2345ca/images/geoserver_styles.png -------------------------------------------------------------------------------- /images/maputnik_add_filter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geoplex/foss4g-oceania-2018-workshop/ca3bb5d73533802f0e665903e2de2530fa2345ca/images/maputnik_add_filter.png -------------------------------------------------------------------------------- /images/maputnik_add_layer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geoplex/foss4g-oceania-2018-workshop/ca3bb5d73533802f0e665903e2de2530fa2345ca/images/maputnik_add_layer.png -------------------------------------------------------------------------------- /images/maputnik_add_source.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geoplex/foss4g-oceania-2018-workshop/ca3bb5d73533802f0e665903e2de2530fa2345ca/images/maputnik_add_source.png -------------------------------------------------------------------------------- /images/maputnik_duplicate_layer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geoplex/foss4g-oceania-2018-workshop/ca3bb5d73533802f0e665903e2de2530fa2345ca/images/maputnik_duplicate_layer.png -------------------------------------------------------------------------------- /images/putty_pk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geoplex/foss4g-oceania-2018-workshop/ca3bb5d73533802f0e665903e2de2530fa2345ca/images/putty_pk.png -------------------------------------------------------------------------------- /images/putty_session.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geoplex/foss4g-oceania-2018-workshop/ca3bb5d73533802f0e665903e2de2530fa2345ca/images/putty_session.png -------------------------------------------------------------------------------- /images/putty_username.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geoplex/foss4g-oceania-2018-workshop/ca3bb5d73533802f0e665903e2de2530fa2345ca/images/putty_username.png -------------------------------------------------------------------------------- /images/puttygen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geoplex/foss4g-oceania-2018-workshop/ca3bb5d73533802f0e665903e2de2530fa2345ca/images/puttygen.png -------------------------------------------------------------------------------- /images/qgis_import_layer_file_button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geoplex/foss4g-oceania-2018-workshop/ca3bb5d73533802f0e665903e2de2530fa2345ca/images/qgis_import_layer_file_button.png -------------------------------------------------------------------------------- /images/qgis_import_vector_layer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geoplex/foss4g-oceania-2018-workshop/ca3bb5d73533802f0e665903e2de2530fa2345ca/images/qgis_import_vector_layer.png -------------------------------------------------------------------------------- /images/qgis_postgis_connection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geoplex/foss4g-oceania-2018-workshop/ca3bb5d73533802f0e665903e2de2530fa2345ca/images/qgis_postgis_connection.png -------------------------------------------------------------------------------- /images/qgis_select_features.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geoplex/foss4g-oceania-2018-workshop/ca3bb5d73533802f0e665903e2de2530fa2345ca/images/qgis_select_features.png -------------------------------------------------------------------------------- /images/qgis_select_features_dialog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geoplex/foss4g-oceania-2018-workshop/ca3bb5d73533802f0e665903e2de2530fa2345ca/images/qgis_select_features_dialog.png -------------------------------------------------------------------------------- /images/react_redux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geoplex/foss4g-oceania-2018-workshop/ca3bb5d73533802f0e665903e2de2530fa2345ca/images/react_redux.png -------------------------------------------------------------------------------- /images/workshop-architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geoplex/foss4g-oceania-2018-workshop/ca3bb5d73533802f0e665903e2de2530fa2345ca/images/workshop-architecture.png -------------------------------------------------------------------------------- /packer/geoserver.json: -------------------------------------------------------------------------------- 1 | { 2 | "variables": { 3 | "aws_access_key": "", 4 | "aws_secret_key": "", 5 | "associate_public_ip_address": "true", 6 | "ssh_keypair_name": "foss4g-workshop", 7 | "ssh_private_key_file": "YOUR_KEY_FILE", 8 | "vpc_id": "YOUR_VPC_ID", 9 | "subnet_id": "YOUR_SUBNET_ID", 10 | "base_ami": "ami-09b42976632b27e9b", 11 | "aws_region": "ap-southeast-2", 12 | "aws_instance_type": "t2.medium", 13 | "name": "foss4g-workshop-geoserver", 14 | "description": "Geoserver for FOSS4G Workshop", 15 | "ssh_username": "ec2-user" 16 | }, 17 | "builders": [ 18 | { 19 | "type": "amazon-ebs", 20 | "access_key": "{{user `aws_access_key`}}", 21 | "secret_key": "{{user `aws_secret_key`}}", 22 | "token": "{{user `aws_session_token`}}", 23 | "name": "{{user `name`}}", 24 | "region": "{{user `aws_region`}}", 25 | "instance_type": "{{user `aws_instance_type`}}", 26 | "ssh_username": "{{user `ssh_username`}}", 27 | "ssh_timeout": "10m", 28 | "source_ami": "{{user `base_ami`}}", 29 | "ami_name": "{{user `name`}} {{isotime \"20060102T150405Z\"}}", 30 | "ami_description": "{{user `description`}}", 31 | "tags": 32 | { 33 | "Name": "{{user `name`}} {{isotime \"20060102T150405Z\"}}" 34 | }, 35 | "run_tags": { 36 | "Name": "Packer Builder - {{user `name`}}" 37 | }, 38 | "run_volume_tags": { 39 | "Name": "Packer Builder - {{user `name`}}" 40 | }, 41 | "associate_public_ip_address": "{{user `associate_public_ip_address`}}", 42 | "ssh_keypair_name": "{{user `ssh_keypair_name`}}", 43 | "ssh_private_key_file": "{{user `ssh_private_key_file`}}", 44 | "vpc_id": "{{user `vpc_id`}}", 45 | "subnet_id": "{{user `subnet_id`}}" 46 | } 47 | ], 48 | "provisioners": [ 49 | { 50 | "type": "ansible", 51 | "playbook_file": "../ansible/geoserver.yml" 52 | } 53 | ], 54 | "post-processors": [ 55 | { 56 | "type": "manifest", 57 | "output": "manifest.json", 58 | "strip_path": true 59 | } 60 | ] 61 | } 62 | -------------------------------------------------------------------------------- /python/abs_loader.py: -------------------------------------------------------------------------------- 1 | import psycopg2 2 | import os 3 | import re 4 | 5 | 6 | class AbsLoader: 7 | def __init__(self, db_name, db_user, db_password, db_host, abs_data_path, state, area_types, load_schema, table_codes): 8 | self.db_name = db_name 9 | self.db_user = db_user 10 | self.db_password = db_password 11 | self.db_host = db_host 12 | self.abs_data_path = abs_data_path 13 | self.state = state 14 | self.area_types = area_types 15 | self.load_schema = load_schema 16 | self.table_codes = table_codes 17 | 18 | # Create DB Connection 19 | self.conn = self.create_connection() 20 | self.cur = self.conn.cursor() 21 | 22 | STRING_FIELDS = ['sa1_7digitcode_2016', 'sa2_maincode_2016', 'sa3_code_2016', 'sa4_code_2016'] 23 | 24 | def create_connection(self): 25 | return psycopg2.connect( 26 | "dbname='{db_name}' user='{user}' password='{password}' host='{host}'".format( 27 | db_name=self.db_name, 28 | user=self.db_user, 29 | password=self.db_password, 30 | host=self.db_host 31 | ) 32 | ) 33 | 34 | def get_column_def(self, column_name): 35 | if column_name in self.STRING_FIELDS: 36 | return '{} character varying'.format(column_name) 37 | else: 38 | return '{} double precision'.format(column_name) 39 | 40 | def process_csv_file(self, csv_path): 41 | with open(csv_path) as csv_contents: 42 | header = csv_contents.readline() 43 | column_names = [x.lower() for x in header.replace('\r\n','').split(',')] 44 | table_name = 'abs_{}'.format(os.path.basename(csv_path).rsplit('.', 1)[0].lower()) 45 | create_table_sql = \ 46 | 'DROP TABLE IF EXISTS {load_schema}.{table_name}; ' \ 47 | 'CREATE TABLE {load_schema}.{table_name} ({columns});'.format( 48 | load_schema=self.load_schema, 49 | table_name=table_name, 50 | columns=', '.join([self.get_column_def(x) for x in column_names]) 51 | ) 52 | self.cur.execute(create_table_sql) 53 | 54 | create_index_sql = \ 55 | 'CREATE INDEX {table_name}_{column_name}_idx ON {load_schema}.{table_name} ' \ 56 | 'USING btree ({column_name} ASC NULLS LAST) TABLESPACE pg_default;'.format( 57 | column_name=column_names[0], 58 | load_schema=self.load_schema, 59 | table_name=table_name 60 | ) 61 | self.cur.execute(create_index_sql) 62 | 63 | copy_csv_sql = 'COPY {load_schema}.{table_name} FROM STDIN DELIMITER \',\' CSV HEADER;'.format( 64 | load_schema=self.load_schema, 65 | table_name=table_name 66 | ) 67 | with open(csv_path) as f: 68 | self.cur.copy_expert(copy_csv_sql, f) 69 | self.conn.commit() 70 | 71 | def process_area_type(self, area_type, path): 72 | csv_files = [ 73 | x for x in os.listdir(path) 74 | if re.match( 75 | '2016Census_({codes})_{state}_{area_type}.csv'.format( 76 | state=self.state, 77 | codes='|'.join(self.table_codes), 78 | area_type=area_type 79 | ), 80 | x 81 | ) 82 | ] 83 | 84 | count = 1 85 | for csv_file in csv_files: 86 | print 'Processing CSV File {} ({}/{})'.format(csv_file, count, len(csv_files)) 87 | csv_path = os.path.join(path, csv_file) 88 | self.process_csv_file(csv_path) 89 | count += 1 90 | 91 | def process_abs_data(self): 92 | for area_type in self.area_types: 93 | print 'Processing Area Type {}'.format(area_type) 94 | path = os.path.join(self.abs_data_path, area_type, self.state) 95 | self.process_area_type(area_type, path) 96 | 97 | 98 | if __name__ == '__main__': 99 | abs_loader = AbsLoader( 100 | db_name='foss4g', 101 | db_user='workshop', 102 | db_password='workshop', 103 | db_host='ABC.XYZ.ap-southeast-2.rds.amazonaws.com', 104 | abs_data_path='../data/abs/2016_GCP_ALL_for_Vic_short-header/2016 Census GCP All Geographies for VIC', 105 | state='VIC', 106 | area_types=['SA1', 'SA2', 'SA3', 'SA4'], 107 | load_schema='abs', 108 | table_codes=['G01', 'G02'] 109 | ) 110 | abs_loader.process_abs_data() 111 | -------------------------------------------------------------------------------- /python/generate_abs_mb_style.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | 4 | 5 | class AbsMbStyleGenerator: 6 | def __init__(self, geoserver_base_url, config_path): 7 | self.geoserver_base_url = geoserver_base_url 8 | with open(config_path, 'r') as config_file: 9 | config = json.loads(config_file.read()) 10 | self.style_name = config["styleName"] 11 | self.out_file_path = config["outFilePath"] 12 | self.layer_names = config["layerNames"] 13 | self.projection = config["projection"] 14 | self.style_type = config["styleType"] 15 | self.area_types = config["areaTypes"] 16 | self.breaks = config["breaks"] 17 | 18 | def create_vector_tile_wmts_get_tile_url(self, layer_name): 19 | params = { 20 | "REQUEST": "GetTile", 21 | "SERVICE": "WMTS", 22 | "VERSION": "1.0.0", 23 | "LAYER": "{}:{}".format(layer_name['workspace'], layer_name['name']), 24 | "TILEMATRIX": "{projection}:{{z}}".format(projection=self.projection), 25 | "TILEMATRIXSET":self.projection, 26 | "FORMAT": "application/x-protobuf;type=mapbox-vector", 27 | "TILECOL": "{x}", 28 | "TILEROW": "{y}" 29 | } 30 | url = "{base_url}/gwc/service/wmts?{params}".format( 31 | base_url=self.geoserver_base_url, 32 | params='&'.join(['{}={}'.format(x, params[x]) for x in params.keys()]) 33 | ) 34 | return url 35 | 36 | def create_geoserver_vector_tile_xyz_sources(self): 37 | source_dict = {} 38 | for layer_name in self.layer_names: 39 | source_dict[layer_name['name']] = { 40 | "type": "vector", 41 | "tiles": [ 42 | self.create_vector_tile_wmts_get_tile_url(layer_name) 43 | ], 44 | "minZoom": 0, 45 | "maxZoom": 22 46 | } 47 | 48 | return source_dict 49 | 50 | def create_stops(self, stopValueKey): 51 | stops = [] 52 | for break_value in self.breaks['values']: 53 | stop = [{ 54 | "zoom": 0, 55 | "value": break_value['value'] 56 | }, break_value[stopValueKey]] 57 | stops.append(stop) 58 | 59 | return stops 60 | 61 | def create_layer_style(self, area_type, layer_name): 62 | layer_style = { 63 | "id": "{}_{}".format(layer_name['name'], area_type['value']), 64 | "source": layer_name['name'], 65 | "source-layer": layer_name['name'], 66 | "minzoom": area_type['zoomRange'][1], 67 | "maxzoom": area_type['zoomRange'][0], 68 | "filter": [ 69 | "all", 70 | [ 71 | "==", 72 | "area_type", 73 | area_type['value'] 74 | ] 75 | ], 76 | "layout": { 77 | "visibility": "visible" 78 | } 79 | } 80 | 81 | if self.style_type == 'fill': 82 | layer_style["type"] = "fill" 83 | layer_style["paint"] = { 84 | "fill-outline-color": { 85 | "property": self.breaks['field'], 86 | "type": "categorical", 87 | "stops": self.create_stops('outlineColor') 88 | }, 89 | "fill-antialias": True, 90 | "fill-color": { 91 | "property": self.breaks['field'], 92 | "type": "categorical", 93 | "stops": self.create_stops('color') 94 | }, 95 | "fill-opacity": { 96 | "property": self.breaks['field'], 97 | "type": "categorical", 98 | "stops": self.create_stops('opacity') 99 | } 100 | } 101 | 102 | if self.style_type == 'circle': 103 | layer_style["type"] = "circle" 104 | layer_style["paint"] = { 105 | "circle-color": { 106 | "property": self.breaks['field'], 107 | "type": "categorical", 108 | "stops": self.create_stops("color") 109 | }, 110 | "circle-radius": { 111 | "property": self.breaks['field'], 112 | "type": "categorical", 113 | "stops": self.create_stops("size") 114 | }, 115 | "circle-blur": 0, 116 | "circle-stroke-color": { 117 | "property": self.breaks['field'], 118 | "type": "categorical", 119 | "stops": self.create_stops("outlineColor") 120 | }, 121 | "circle-stroke-width": 1, 122 | "circle-opacity": { 123 | "property": self.breaks['field'], 124 | "type": "categorical", 125 | "stops": self.create_stops("opacity") 126 | }, 127 | } 128 | 129 | return layer_style 130 | 131 | def create_mb_style_layers(self): 132 | layers = [] 133 | for layer_name in self.layer_names: 134 | for area_type in self.area_types['values']: 135 | layers.append(self.create_layer_style(area_type, layer_name)) 136 | 137 | return layers 138 | 139 | def create_mb_style(self): 140 | mb_style = { 141 | "id": self.style_name, 142 | "name": self.style_name, 143 | "version": 8, 144 | "metadata": { 145 | "mapbox:autocomposite": False, 146 | "mapbox:type": "template", 147 | "maputnik:renderer": "mbgljs", 148 | "openmaptiles:version": "3.x" 149 | }, 150 | "sources": self.create_geoserver_vector_tile_xyz_sources(), 151 | "glyphs": "https://demo.tileserver.org/fonts/{fontstack}/{range}.pbf", 152 | "sprites": "https://demo.tileserver.org/fonts/{fontstack}/{range}.pbf", 153 | "layers": self.create_mb_style_layers() 154 | } 155 | 156 | with open(self.out_file_path, "w") as out_file: 157 | out_file.write(json.dumps(mb_style)) 158 | 159 | 160 | if __name__ == '__main__': 161 | style_config_dir = 'style_generator_config' 162 | geoserver_base_url = '' 163 | style_config_files = os.listdir(style_config_dir) 164 | 165 | for style_config_file in style_config_files: 166 | print 'Generating MB Style for config {}'.format(style_config_file) 167 | abs_mb_style_generator = AbsMbStyleGenerator( 168 | geoserver_base_url=geoserver_base_url, 169 | config_path='{}/{}'.format(style_config_dir, style_config_file) 170 | ) 171 | abs_mb_style_generator.create_mb_style() 172 | -------------------------------------------------------------------------------- /python/style_generator_config/point_blue.json: -------------------------------------------------------------------------------- 1 | { 2 | "styleName": "point_blue", 3 | "outFilePath": "../styles/point_blue.json", 4 | "styleType": "circle", 5 | "projection": "EPSG:900913", 6 | "layerNames": [ 7 | { 8 | "workspace": "workshop", 9 | "name": "abs_stats_point" 10 | } 11 | ], 12 | "areaTypes": { 13 | "field": "area_type", 14 | "values": [ 15 | { 16 | "value": "SA1", 17 | "zoomRange": [ 18 | 24, 19 | 13 20 | ] 21 | }, 22 | { 23 | "value": "SA2", 24 | "zoomRange": [ 25 | 13, 26 | 9 27 | ] 28 | }, 29 | { 30 | "value": "SA3", 31 | "zoomRange": [ 32 | 9, 33 | 6 34 | ] 35 | }, 36 | { 37 | "value": "SA4", 38 | "zoomRange": [ 39 | 6, 40 | 0 41 | ] 42 | } 43 | ] 44 | }, 45 | "breaks": { 46 | "field": "_cl", 47 | "values": [ 48 | { 49 | "value": 1, 50 | "color": "rgba(241, 238, 246, 1)", 51 | "outlineColor": "rgba(0, 0, 0, 1)", 52 | "opacity": 0.8, 53 | "size": 3 54 | }, 55 | { 56 | "value": 2, 57 | "color": "rgba(189, 201, 225, 1)", 58 | "outlineColor": "rgba(0, 0, 0, 1)", 59 | "opacity": 0.8, 60 | "size": 5 61 | }, 62 | { 63 | "value": 3, 64 | "color": "rgba(116, 169, 207, 1)", 65 | "outlineColor": "rgba(0, 0, 0, 1)", 66 | "opacity": 0.8, 67 | "size": 8 68 | }, 69 | { 70 | "value": 4, 71 | "color": "rgba(43, 140, 190, 1)", 72 | "outlineColor": "rgba(0, 0, 0, 1)", 73 | "opacity": 0.8, 74 | "size": 13 75 | }, 76 | { 77 | "value": 5, 78 | "color": "rgba(4, 90, 141, 1)", 79 | "outlineColor": "rgba(0, 0, 0, 1)", 80 | "opacity": 0.8, 81 | "size": 21 82 | } 83 | ] 84 | } 85 | } -------------------------------------------------------------------------------- /python/style_generator_config/point_green.json: -------------------------------------------------------------------------------- 1 | { 2 | "styleName": "point_green", 3 | "outFilePath": "../styles/point_green.json", 4 | "styleType": "circle", 5 | "projection": "EPSG:900913", 6 | "layerNames": [ 7 | { 8 | "workspace": "workshop", 9 | "name": "abs_stats_point" 10 | } 11 | ], 12 | "areaTypes": { 13 | "field": "area_type", 14 | "values": [ 15 | { 16 | "value": "SA1", 17 | "zoomRange": [ 18 | 24, 19 | 13 20 | ] 21 | }, 22 | { 23 | "value": "SA2", 24 | "zoomRange": [ 25 | 13, 26 | 9 27 | ] 28 | }, 29 | { 30 | "value": "SA3", 31 | "zoomRange": [ 32 | 9, 33 | 6 34 | ] 35 | }, 36 | { 37 | "value": "SA4", 38 | "zoomRange": [ 39 | 6, 40 | 0 41 | ] 42 | } 43 | ] 44 | }, 45 | "breaks": { 46 | "field": "_cl", 47 | "values": [ 48 | { 49 | "value": 1, 50 | "color": "rgba(240, 249, 232, 1)", 51 | "outlineColor": "rgba(0, 0, 0, 1)", 52 | "opacity": 0.8, 53 | "size": 3 54 | }, 55 | { 56 | "value": 2, 57 | "color": "rgba(186, 228, 188, 1)", 58 | "outlineColor": "rgba(0, 0, 0, 1)", 59 | "opacity": 0.8, 60 | "size": 5 61 | }, 62 | { 63 | "value": 3, 64 | "color": "rgba(123, 204, 196, 1)", 65 | "outlineColor": "rgba(0, 0, 0, 1)", 66 | "opacity": 0.8, 67 | "size": 8 68 | }, 69 | { 70 | "value": 4, 71 | "color": "rgba(67, 162, 202, 1)", 72 | "outlineColor": "rgba(0, 0, 0, 1)", 73 | "opacity": 0.8, 74 | "size": 13 75 | }, 76 | { 77 | "value": 5, 78 | "color": "rgba(8, 104, 172, 1)", 79 | "outlineColor": "rgba(0, 0, 0, 1)", 80 | "opacity": 0.8, 81 | "size": 21 82 | } 83 | ] 84 | } 85 | } -------------------------------------------------------------------------------- /python/style_generator_config/point_orange.json: -------------------------------------------------------------------------------- 1 | { 2 | "styleName": "point_orange", 3 | "outFilePath": "../styles/point_orange.json", 4 | "styleType": "circle", 5 | "projection": "EPSG:900913", 6 | "layerNames": [ 7 | { 8 | "workspace": "workshop", 9 | "name": "abs_stats_point" 10 | } 11 | ], 12 | "areaTypes": { 13 | "field": "area_type", 14 | "values": [ 15 | { 16 | "value": "SA1", 17 | "zoomRange": [ 18 | 24, 19 | 13 20 | ] 21 | }, 22 | { 23 | "value": "SA2", 24 | "zoomRange": [ 25 | 13, 26 | 9 27 | ] 28 | }, 29 | { 30 | "value": "SA3", 31 | "zoomRange": [ 32 | 9, 33 | 6 34 | ] 35 | }, 36 | { 37 | "value": "SA4", 38 | "zoomRange": [ 39 | 6, 40 | 0 41 | ] 42 | } 43 | ] 44 | }, 45 | "breaks": { 46 | "field": "_cl", 47 | "values": [ 48 | { 49 | "value": 1, 50 | "color": "rgba(255, 255, 212, 1)", 51 | "outlineColor": "rgba(0, 0, 0, 1)", 52 | "opacity": 0.8, 53 | "size": 3 54 | }, 55 | { 56 | "value": 2, 57 | "color": "rgba(254, 217, 142, 1)", 58 | "outlineColor": "rgba(0, 0, 0, 1)", 59 | "opacity": 0.8, 60 | "size": 5 61 | }, 62 | { 63 | "value": 3, 64 | "color": "rgba(254, 153, 41, 1)", 65 | "outlineColor": "rgba(0, 0, 0, 1)", 66 | "opacity": 0.8, 67 | "size": 8 68 | }, 69 | { 70 | "value": 4, 71 | "color": "rgba(217, 95, 14, 1)", 72 | "outlineColor": "rgba(0, 0, 0, 1)", 73 | "opacity": 0.8, 74 | "size": 13 75 | }, 76 | { 77 | "value": 5, 78 | "color": "rgba(153, 52, 4, 1)", 79 | "outlineColor": "rgba(0, 0, 0, 1)", 80 | "opacity": 0.8, 81 | "size": 21 82 | } 83 | ] 84 | } 85 | } -------------------------------------------------------------------------------- /python/style_generator_config/point_pink.json: -------------------------------------------------------------------------------- 1 | { 2 | "styleName": "point_pink", 3 | "outFilePath": "../styles/point_pink.json", 4 | "styleType": "circle", 5 | "projection": "EPSG:900913", 6 | "layerNames": [ 7 | { 8 | "workspace": "workshop", 9 | "name": "abs_stats_point" 10 | } 11 | ], 12 | "areaTypes": { 13 | "field": "area_type", 14 | "values": [ 15 | { 16 | "value": "SA1", 17 | "zoomRange": [ 18 | 24, 19 | 13 20 | ] 21 | }, 22 | { 23 | "value": "SA2", 24 | "zoomRange": [ 25 | 13, 26 | 9 27 | ] 28 | }, 29 | { 30 | "value": "SA3", 31 | "zoomRange": [ 32 | 9, 33 | 6 34 | ] 35 | }, 36 | { 37 | "value": "SA4", 38 | "zoomRange": [ 39 | 6, 40 | 0 41 | ] 42 | } 43 | ] 44 | }, 45 | "breaks": { 46 | "field": "_cl", 47 | "values": [ 48 | { 49 | "value": 1, 50 | "color": "rgba(254, 235, 226, 1)", 51 | "outlineColor": "rgba(0, 0, 0, 1)", 52 | "opacity": 0.8, 53 | "size": 3 54 | }, 55 | { 56 | "value": 2, 57 | "color": "rgba(251, 180, 185, 1)", 58 | "outlineColor": "rgba(0, 0, 0, 1)", 59 | "opacity": 0.8, 60 | "size": 5 61 | }, 62 | { 63 | "value": 3, 64 | "color": "rgba(247, 104, 161, 1)", 65 | "outlineColor": "rgba(0, 0, 0, 1)", 66 | "opacity": 0.8, 67 | "size": 8 68 | }, 69 | { 70 | "value": 4, 71 | "color": "rgba(197, 27, 138, 1)", 72 | "outlineColor": "rgba(0, 0, 0, 1)", 73 | "opacity": 0.8, 74 | "size": 13 75 | }, 76 | { 77 | "value": 5, 78 | "color": "rgba(122, 1, 119, 1)", 79 | "outlineColor": "rgba(0, 0, 0, 1)", 80 | "opacity": 0.8, 81 | "size": 21 82 | } 83 | ] 84 | } 85 | } -------------------------------------------------------------------------------- /python/style_generator_config/point_purple.json: -------------------------------------------------------------------------------- 1 | { 2 | "styleName": "point_purple", 3 | "outFilePath": "../styles/point_purple.json", 4 | "styleType": "circle", 5 | "projection": "EPSG:900913", 6 | "layerNames": [ 7 | { 8 | "workspace": "workshop", 9 | "name": "abs_stats_point" 10 | } 11 | ], 12 | "areaTypes": { 13 | "field": "area_type", 14 | "values": [ 15 | { 16 | "value": "SA1", 17 | "zoomRange": [ 18 | 24, 19 | 13 20 | ] 21 | }, 22 | { 23 | "value": "SA2", 24 | "zoomRange": [ 25 | 13, 26 | 9 27 | ] 28 | }, 29 | { 30 | "value": "SA3", 31 | "zoomRange": [ 32 | 9, 33 | 6 34 | ] 35 | }, 36 | { 37 | "value": "SA4", 38 | "zoomRange": [ 39 | 6, 40 | 0 41 | ] 42 | } 43 | ] 44 | }, 45 | "breaks": { 46 | "field": "_cl", 47 | "values": [ 48 | { 49 | "value": 1, 50 | "color": "rgba(237, 248, 251, 1)", 51 | "outlineColor": "rgba(0, 0, 0, 1)", 52 | "opacity": 0.8, 53 | "size": 3 54 | }, 55 | { 56 | "value": 2, 57 | "color": "rgba(179, 205, 227, 1)", 58 | "outlineColor": "rgba(0, 0, 0, 1)", 59 | "opacity": 0.8, 60 | "size": 5 61 | }, 62 | { 63 | "value": 3, 64 | "color": "rgba(140, 150, 198, 1)", 65 | "outlineColor": "rgba(0, 0, 0, 1)", 66 | "opacity": 0.8, 67 | "size": 8 68 | }, 69 | { 70 | "value": 4, 71 | "color": "rgba(136, 86, 167, 1)", 72 | "outlineColor": "rgba(0, 0, 0, 1)", 73 | "opacity": 0.8, 74 | "size": 13 75 | }, 76 | { 77 | "value": 5, 78 | "color": "rgba(129, 15, 124, 1)", 79 | "outlineColor": "rgba(0, 0, 0, 1)", 80 | "opacity": 0.8, 81 | "size": 21 82 | } 83 | ] 84 | } 85 | } -------------------------------------------------------------------------------- /python/style_generator_config/poly_blue.json: -------------------------------------------------------------------------------- 1 | { 2 | "styleName": "poly_blue", 3 | "outFilePath": "../styles/poly_blue.json", 4 | "styleType": "fill", 5 | "projection": "EPSG:900913", 6 | "layerNames": [ 7 | { 8 | "workspace": "workshop", 9 | "name": "abs_stats_poly" 10 | } 11 | ], 12 | "areaTypes": { 13 | "field": "area_type", 14 | "values": [ 15 | { 16 | "value": "SA1", 17 | "zoomRange": [ 18 | 24, 19 | 13 20 | ] 21 | }, 22 | { 23 | "value": "SA2", 24 | "zoomRange": [ 25 | 13, 26 | 9 27 | ] 28 | }, 29 | { 30 | "value": "SA3", 31 | "zoomRange": [ 32 | 9, 33 | 6 34 | ] 35 | }, 36 | { 37 | "value": "SA4", 38 | "zoomRange": [ 39 | 6, 40 | 0 41 | ] 42 | } 43 | ] 44 | }, 45 | "breaks": { 46 | "field": "_cl", 47 | "values": [ 48 | { 49 | "value": 1, 50 | "color": "rgba(241, 238, 246, 1)", 51 | "outlineColor": "rgba(0, 0, 0, 1)", 52 | "opacity": 0.8 53 | }, 54 | { 55 | "value": 2, 56 | "color": "rgba(189, 201, 225, 1)", 57 | "outlineColor": "rgba(0, 0, 0, 1)", 58 | "opacity": 0.8 59 | }, 60 | { 61 | "value": 3, 62 | "color": "rgba(116, 169, 207, 1)", 63 | "outlineColor": "rgba(0, 0, 0, 1)", 64 | "opacity": 0.8 65 | }, 66 | { 67 | "value": 4, 68 | "color": "rgba(43, 140, 190, 1)", 69 | "outlineColor": "rgba(0, 0, 0, 1)", 70 | "opacity": 0.8 71 | }, 72 | { 73 | "value": 5, 74 | "color": "rgba(4, 90, 141, 1)", 75 | "outlineColor": "rgba(0, 0, 0, 1)", 76 | "opacity": 0.8 77 | } 78 | ] 79 | } 80 | } -------------------------------------------------------------------------------- /python/style_generator_config/poly_green.json: -------------------------------------------------------------------------------- 1 | { 2 | "styleName": "poly_green", 3 | "outFilePath": "../styles/poly_green.json", 4 | "styleType": "fill", 5 | "projection": "EPSG:900913", 6 | "layerNames": [ 7 | { 8 | "workspace": "workshop", 9 | "name": "abs_stats_poly" 10 | } 11 | ], 12 | "areaTypes": { 13 | "field": "area_type", 14 | "values": [ 15 | { 16 | "value": "SA1", 17 | "zoomRange": [ 18 | 24, 19 | 13 20 | ] 21 | }, 22 | { 23 | "value": "SA2", 24 | "zoomRange": [ 25 | 13, 26 | 9 27 | ] 28 | }, 29 | { 30 | "value": "SA3", 31 | "zoomRange": [ 32 | 9, 33 | 6 34 | ] 35 | }, 36 | { 37 | "value": "SA4", 38 | "zoomRange": [ 39 | 6, 40 | 0 41 | ] 42 | } 43 | ] 44 | }, 45 | "breaks": { 46 | "field": "_cl", 47 | "values": [ 48 | { 49 | "value": 1, 50 | "color": "rgba(240, 249, 232, 1)", 51 | "outlineColor": "rgba(0, 0, 0, 1)", 52 | "opacity": 0.8 53 | }, 54 | { 55 | "value": 2, 56 | "color": "rgba(186, 228, 188, 1)", 57 | "outlineColor": "rgba(0, 0, 0, 1)", 58 | "opacity": 0.8 59 | }, 60 | { 61 | "value": 3, 62 | "color": "rgba(123, 204, 196, 1)", 63 | "outlineColor": "rgba(0, 0, 0, 1)", 64 | "opacity": 0.8 65 | }, 66 | { 67 | "value": 4, 68 | "color": "rgba(67, 162, 202, 1)", 69 | "outlineColor": "rgba(0, 0, 0, 1)", 70 | "opacity": 0.8 71 | }, 72 | { 73 | "value": 5, 74 | "color": "rgba(8, 104, 172, 1)", 75 | "outlineColor": "rgba(0, 0, 0, 1)", 76 | "opacity": 0.8 77 | } 78 | ] 79 | } 80 | } -------------------------------------------------------------------------------- /python/style_generator_config/poly_orange.json: -------------------------------------------------------------------------------- 1 | { 2 | "styleName": "poly_orange", 3 | "outFilePath": "../styles/poly_orange.json", 4 | "styleType": "fill", 5 | "projection": "EPSG:900913", 6 | "layerNames": [ 7 | { 8 | "workspace": "workshop", 9 | "name": "abs_stats_poly" 10 | } 11 | ], 12 | "areaTypes": { 13 | "field": "area_type", 14 | "values": [ 15 | { 16 | "value": "SA1", 17 | "zoomRange": [ 18 | 24, 19 | 13 20 | ] 21 | }, 22 | { 23 | "value": "SA2", 24 | "zoomRange": [ 25 | 13, 26 | 9 27 | ] 28 | }, 29 | { 30 | "value": "SA3", 31 | "zoomRange": [ 32 | 9, 33 | 6 34 | ] 35 | }, 36 | { 37 | "value": "SA4", 38 | "zoomRange": [ 39 | 6, 40 | 0 41 | ] 42 | } 43 | ] 44 | }, 45 | "breaks": { 46 | "field": "_cl", 47 | "values": [ 48 | { 49 | "value": 1, 50 | "color": "rgba(255, 255, 212, 1)", 51 | "outlineColor": "rgba(0, 0, 0, 1)", 52 | "opacity": 0.8 53 | }, 54 | { 55 | "value": 2, 56 | "color": "rgba(254, 217, 142, 1)", 57 | "outlineColor": "rgba(0, 0, 0, 1)", 58 | "opacity": 0.8 59 | }, 60 | { 61 | "value": 3, 62 | "color": "rgba(254, 153, 41, 1)", 63 | "outlineColor": "rgba(0, 0, 0, 1)", 64 | "opacity": 0.8 65 | }, 66 | { 67 | "value": 4, 68 | "color": "rgba(217, 95, 14, 1)", 69 | "outlineColor": "rgba(0, 0, 0, 1)", 70 | "opacity": 0.8 71 | }, 72 | { 73 | "value": 5, 74 | "color": "rgba(153, 52, 4, 1)", 75 | "outlineColor": "rgba(0, 0, 0, 1)", 76 | "opacity": 0.8 77 | } 78 | ] 79 | } 80 | } -------------------------------------------------------------------------------- /python/style_generator_config/poly_pink.json: -------------------------------------------------------------------------------- 1 | { 2 | "styleName": "poly_pink", 3 | "outFilePath": "../styles/poly_pink.json", 4 | "styleType": "fill", 5 | "projection": "EPSG:900913", 6 | "layerNames": [ 7 | { 8 | "workspace": "workshop", 9 | "name": "abs_stats_poly" 10 | } 11 | ], 12 | "areaTypes": { 13 | "field": "area_type", 14 | "values": [ 15 | { 16 | "value": "SA1", 17 | "zoomRange": [ 18 | 24, 19 | 13 20 | ] 21 | }, 22 | { 23 | "value": "SA2", 24 | "zoomRange": [ 25 | 13, 26 | 9 27 | ] 28 | }, 29 | { 30 | "value": "SA3", 31 | "zoomRange": [ 32 | 9, 33 | 6 34 | ] 35 | }, 36 | { 37 | "value": "SA4", 38 | "zoomRange": [ 39 | 6, 40 | 0 41 | ] 42 | } 43 | ] 44 | }, 45 | "breaks": { 46 | "field": "_cl", 47 | "values": [ 48 | { 49 | "value": 1, 50 | "color": "rgba(254, 235, 226, 1)", 51 | "outlineColor": "rgba(0, 0, 0, 1)", 52 | "opacity": 0.8 53 | }, 54 | { 55 | "value": 2, 56 | "color": "rgba(251, 180, 185, 1)", 57 | "outlineColor": "rgba(0, 0, 0, 1)", 58 | "opacity": 0.8 59 | }, 60 | { 61 | "value": 3, 62 | "color": "rgba(247, 104, 161, 1)", 63 | "outlineColor": "rgba(0, 0, 0, 1)", 64 | "opacity": 0.8 65 | }, 66 | { 67 | "value": 4, 68 | "color": "rgba(197, 27, 138, 1)", 69 | "outlineColor": "rgba(0, 0, 0, 1)", 70 | "opacity": 0.8 71 | }, 72 | { 73 | "value": 5, 74 | "color": "rgba(122, 1, 119, 1)", 75 | "outlineColor": "rgba(0, 0, 0, 1)", 76 | "opacity": 0.8 77 | } 78 | ] 79 | } 80 | } -------------------------------------------------------------------------------- /python/style_generator_config/poly_purple.json: -------------------------------------------------------------------------------- 1 | { 2 | "styleName": "poly_purple", 3 | "outFilePath": "../styles/poly_purple.json", 4 | "styleType": "fill", 5 | "projection": "EPSG:900913", 6 | "layerNames": [ 7 | { 8 | "workspace": "workshop", 9 | "name": "abs_stats_poly" 10 | } 11 | ], 12 | "areaTypes": { 13 | "field": "area_type", 14 | "values": [ 15 | { 16 | "value": "SA1", 17 | "zoomRange": [ 18 | 24, 19 | 13 20 | ] 21 | }, 22 | { 23 | "value": "SA2", 24 | "zoomRange": [ 25 | 13, 26 | 9 27 | ] 28 | }, 29 | { 30 | "value": "SA3", 31 | "zoomRange": [ 32 | 9, 33 | 6 34 | ] 35 | }, 36 | { 37 | "value": "SA4", 38 | "zoomRange": [ 39 | 6, 40 | 0 41 | ] 42 | } 43 | ] 44 | }, 45 | "breaks": { 46 | "field": "_cl", 47 | "values": [ 48 | { 49 | "value": 1, 50 | "color": "rgba(237, 248, 251, 1)", 51 | "outlineColor": "rgba(0, 0, 0, 1)", 52 | "opacity": 0.8 53 | }, 54 | { 55 | "value": 2, 56 | "color": "rgba(179, 205, 227, 1)", 57 | "outlineColor": "rgba(0, 0, 0, 1)", 58 | "opacity": 0.8 59 | }, 60 | { 61 | "value": 3, 62 | "color": "rgba(140, 150, 198, 1)", 63 | "outlineColor": "rgba(0, 0, 0, 1)", 64 | "opacity": 0.8 65 | }, 66 | { 67 | "value": 4, 68 | "color": "rgba(136, 86, 167, 1)", 69 | "outlineColor": "rgba(0, 0, 0, 1)", 70 | "opacity": 0.8 71 | }, 72 | { 73 | "value": 5, 74 | "color": "rgba(129, 15, 124, 1)", 75 | "outlineColor": "rgba(0, 0, 0, 1)", 76 | "opacity": 0.8 77 | } 78 | ] 79 | } 80 | } -------------------------------------------------------------------------------- /section-1-setup.md: -------------------------------------------------------------------------------- 1 | # 0 to 100 on AWS – Building a full stack web mapping application with PostGIS, GeoServer, OpenLayers and ReactJS 2 | 3 | #### FOSS4G SotM Oceania - Afternoon Workshop #1 - 20th November 2018 4 | 5 | ## 1. Prerequistes and preliminary setup 6 | 7 | ### AWS account 8 | 9 | To complete the workshop, you will need access to an Amazon Web Services 10 | account. Please verify that you are able to sign in to your account prior to 11 | attending the workshop. 12 | 13 | We recommend using your own account with root credentials. If you wish to 14 | access an account as an IAM user, please ensure that your user has broad 15 | permissions within the account to deploy CloudFormation stacks, create 16 | VPCs/subnets, launch EC2/RDS instances, et cetera. 17 | 18 | ### Required software 19 | 20 | Software and libraries listed in the table below are required to complete the workshop. 21 | 22 | | Software / Library | Version Required | Installation | 23 | | --- | --- | --- | 24 | | An Amazon Web Services account | | [Create an AWS account](https://portal.aws.amazon.com/billing/signup#/start) | 25 | | QGIS | >= 2.12.0 | [Download and install](https://www.qgis.org/en/site/forusers/download.html) 26 | | Node.js / NPM | Node >= 6 / NPM >= 5.2 | [Download and install](https://nodejs.org/) 27 | | SSH client | | Mac/Linux have this as standard, Windows users please [download and install the PuTTY MSI](https://www.chiark.greenend.org.uk/~sgtatham/putty/latest.html) 28 | | Preferred Code Editor | | Throughout the workshop we'll be using [Visual Studio Code](https://code.visualstudio.com/) 29 | 30 | ### Workshop files 31 | 32 | Please download and extract the following data to your local machine prior to attending the workshop: 33 | 34 | [foss4g-oceania-workshop.zip](https://s3-ap-southeast-2.amazonaws.com/foss4g-oceania-2018-workshop-resources/foss4g-oceania-workshop.zip) 35 | 36 | ### Statistical data 37 | 38 | Please download and extract the following data to your local machine prior to attending the workshop: 39 | 40 | * [ABS Statistical Areas Level 4 Shapefile](http://www.abs.gov.au/AUSSTATS/subscriber.nsf/log?openagent&1270055001_sa4_2016_aust_shape.zip&1270.0.55.001&Data%20Cubes&C65BC89E549D1CA3CA257FED0013E074&0&July%202016&12.07.2016&Latest) 41 | 42 | *** 43 | 44 | **Up**: [Index](README.md) | **Next**: [2. Deploy AWS resources](section-2-aws.md) 45 | -------------------------------------------------------------------------------- /section-4-geoserver.md: -------------------------------------------------------------------------------- 1 | # 0 to 100 on AWS – Building a full stack web mapping application with PostGIS, GeoServer, OpenLayers and ReactJS 2 | 3 | #### FOSS4G SotM Oceania - Afternoon Workshop #1 - 20th November 2018 4 | 5 | ## 4. Deploy GeoServer instance 6 | 7 | We will now deploy a CloudFormation stack to create an auto-scaling group and 8 | load balancer for our GeoServer instances. We will be using a pre-prepared AMI 9 | with Tomcat and GeoServer already installed. 10 | 11 | 1. In the AWS CloudFormation console, select ***Create Stack***. 12 | 2. Under ***Choose a template***, select ***Browse...***, navigate to the 13 | workshop files and under **cloudformation** choose the template 14 | `5-geoserver.yaml`. Select ***Next***. 15 | 3. Enter a name for the stack. We suggest `foss4g-oceania-workshop-geoserver`. 16 | 4. Under ***KeyPair***, choose the key pair which you previously created. 17 | 5. Leave the rest of the parameters at their defaults and select ***Next***. 18 | 6. Leave all settings under ***Options*** at their default values and select 19 | ***Next***. 20 | 7. Under ***Review***, select ***Create***. 21 | 8. Monitor the progress of your stack in the CloudFormation console until the 22 | stack status reads `CREATE_COMPLETE`. 23 | 24 | ![CloudFormation parameters for GeoServer stack](images/cloudformation_geoserver_params.png) 25 | 26 | The pre-prepared GeoServer AMI was produced using 27 | **[Packer](https://packer.io/)** and **[Ansible](https://www.ansible.com/)**. 28 | 29 | **Packer** automates the process of creating an AMI -- it launches a new EC2 30 | instance, connects to it over SSH, runs a "provisioner" to configure the 31 | instance, then finally shuts the instance down and creates a new AMI from the 32 | instance volume. 33 | 34 | **Ansible** acts as the provisioner for Packer -- it provides a structured 35 | format for a repeatable set of tasks. In our case, we use an Ansible playbook 36 | to download, install and configure Oracle Java, Apache Tomcat and GeoServer. 37 | 38 | If you're interested in learning more about the AMI build, or customising it to 39 | create your own AMIs, see the `packer` and `ansible` directories within the 40 | workshop repository. 41 | 42 | *** 43 | 44 | **Previous**: [3. Import data into PostGIS](section-3-data.md) | **Up**: [Index](README.md) | **Next**: [5. Publish vector tiles](section-5-publish.md) 45 | -------------------------------------------------------------------------------- /section-5-publish.md: -------------------------------------------------------------------------------- 1 | # 0 to 100 on AWS – Building a full stack web mapping application with PostGIS, GeoServer, OpenLayers and ReactJS 2 | 3 | #### FOSS4G SotM Oceania - Afternoon Workshop #1 - 20th November 2018 4 | 5 | ## 5. Publish Vector Tiles in GeoServer 6 | 7 | With GeoServer deployed in an autoscaling group and the database deployed and populated with ABS data, we can now add the service layer into the stack by publishing some OGC web services in Mapbox Vector Tile (MVT) format. 8 | 9 | The GeoServer AMI has several plugins already installed, including the [Vector Tiles core extension](http://docs.geoserver.org/stable/en/user/extensions/index.html#extensions) and the [Mapbox Styles community module](http://docs.geoserver.org/stable/en/user/community/index.html). 10 | 11 | **1.** In the AWS console, select ***Services > EC2***. 12 | 13 | **2.** Under ***Load Balancers***, select the load balancer that was deployed in the previous section (it should have a name like ***foss4-Geose-XXXXXXXXXXXXX***), and under ***Description > Basic Configuration***, find and copy the DNS name to the clipboard. 14 | 15 | **3.** Open a new web browser window or tab, and go to the URL `http:///geoserver/web` 16 | 17 | **4.** You should be presented with the GeoServer login page. Login to the GeoServer web admin interface with user ***admin*** and password ***geoserver***. For this workshop, we'll just leave the password for the admin user at its default. You can change the password under ***Users, Groups, Roles > Users / Groups*** 18 | 19 | **5.** First we'll add a workspace to organise the workshop GeoServer resources. Open the ***Data > Workspaces*** page and click on ***Add new workspace***. Add a new workspace with name ***workshop*** and Namespace URI ***workshop*** and select the ***Default Workspace*** checkbox. 20 | 21 | ![GeoServer add workspace](images/geoserver_add_workspace.png) 22 | 23 | **6.** Now we'll add a store so that we can publish tables and views from PostGIS as services. Open the ***Stores*** page and click on ***Add new store***. 24 | 25 | **7.** When prompted to select the data source, select ***Vector Data Sources > PostGIS***. 26 | 27 | **8.** Create the store using the values in the table below. Note: to get the database host value, open the AWS console and locate the RDS instance you deployed earlier in the ***RDS > Instances*** and copy the ***Endpoint***. 28 | 29 | | Parameter | Value | 30 | | --- | --- | 31 | | Workspace | `workshop` | 32 | | Data Source Name | `workshop-postgis` | 33 | | Description | `FOSS4G Oceania Workshop PostGIS instance` | 34 | | host | `` | 35 | | port | `5432` | 36 | | database | `foss4g` | 37 | | schema | `geoserver` | 38 | | user | `workshop` | 39 | | password | `` | 40 | 41 | ![GeoServer add PostGIS store](images/geoserver_add_postgis_store.png) 42 | 43 | **9.** Leave the remaining parameters with default values and then press ***Save***. 44 | 45 | **10.** You should then be presented with the ***New Layer*** page, from which you can publish the tables/views in the `geoserver` schema as services. 46 | 47 | ![GeoServer new layer table](images/geoserver_new_layer_table.png) 48 | 49 | **11.** First we'll publish the points layer. From the table, locate the `abs_stats_point` layer and click ***Publish***. 50 | 51 | **12.** The ***Edit Layer*** page should be displayed. Locate the ***Bounding Boxes*** section and for the ***Native Bounding Box*** click ***Compute from data***. For the ***Lat/Lon Bounding Box*** click ***Compute from native bounds***. 52 | 53 | ![GeoServer bounding boxes](images/geoserver_bounding_boxes.png) 54 | 55 | **13.** Leave the rest of the properties with the default values and click ***Save***. 56 | 57 | **14.** Now we'll publish the polygon layer. From the ***Layers*** page, click ***Add a new layer*** and select the ***workshop:workshop-postgis*** store. Follow the process outlined above to publish the `abs_stats_poly` layer. 58 | 59 | **15.** The layers are now published and you can preview them in a variety of formats on the ***Data > Layer Preview*** page (i.e. click on ***OpenLayers*** to preview as a simple web map). 60 | 61 | ![GeoServer layer preview](images/geoserver_layer_preview.png) 62 | 63 | **16.** The final step is to enable the Mapbox Vector Tile format on the published layers. Open the ***Tile Caching > Tile Layers*** page. 64 | 65 | **17.** Select the `workshop:abs_stats_point` layer from the table and then on the ***Edit Layer*** page, select the ***Tile Caching*** tab and activate the ***application/x-protobuf;type=mapbox-vector*** checkbox under the ***Tile Image Formats***. Click ***Save***. 66 | 67 | ![GeoServer enable MVT](images/geoserver_enable_mvt.png) 68 | 69 | **18.** Repeat the process to enable Mapbox Vector Tiles on the `workshop:abs_stats_poly` layer. 70 | 71 | *** 72 | 73 | **Previous**: [4. Deploy Geoserver](section-4-geoserver.md) | **Up**: [Index](README.md) | **Next**: [6. Style vector tiles](section-6-style.md) 74 | -------------------------------------------------------------------------------- /section-7-4-publish-app.md: -------------------------------------------------------------------------------- 1 | # 0 to 100 on AWS – Building a full stack web mapping application with PostGIS, GeoServer, OpenLayers and ReactJS 2 | 3 | #### FOSS4G SotM Oceania - Afternoon Workshop #1 - 20th November 2018 4 | 5 | ### Section 7 Stage 4 - Build and upload to S3 6 | 7 | We can now package our application for deployment, and use S3 to serve it as a 8 | static website. 9 | 10 | From the top-level directory of your `foss4g-client` app, run the following command. 11 | 12 | ``` 13 | npm run build 14 | ``` 15 | 16 | This will compile and minify our application down to a handful of HTML, JS and 17 | CSS files. Once the build process completes, look in the `build` directory to 18 | see what was produced. You should see the following directory structure: 19 | 20 | ``` 21 | |-- build 22 | | |-- static 23 | | | |-- css 24 | | | |-- js 25 | ``` 26 | 27 | Now, we can set up an S3 bucket to serve the website: 28 | 29 | 1. Log in to the AWS console 30 | 2. Go to the S3 service 31 | 3. Select ***Create bucket*** 32 | 4. Enter a name for the bucket and select ***Next***. An S3 bucket name must 33 | be a valid DNS segment and must be globally unique. 34 | 5. Add tags to the bucket if you wish, and select ***Next***. 35 | 6. Under ***Manage public permissions***, select "Grant public read access to this bucket" from the drop list and then ***Next***. 36 | 7. Select ***Create bucket***. 37 | 8. Select your new bucket from the list. 38 | 9. Choose the ***Properties*** tab. 39 | 10. Click on ***Static website hosting***. 40 | 11. Select "Use this bucket to host a website". 41 | 12. Under ***Index document***, enter `index.html` and ***Save***. 42 | 43 | We now have a publically viewable S3 bucket which can serve a static website. 44 | Next, we'll upload our build files to the bucket. If you're comfortable using 45 | the AWS CLI, you can use a `aws s3 sync` command to recursively upload the 46 | contents of the build directory to the bucket. To upload the files manually 47 | via the web console, proceed as follows: 48 | 49 | 1. Return to the ***Overview*** tab of your S3 bucket. 50 | 2. Select ***Upload*** and drag and drop the contents of the build directory into the Upload dialog and then click ***Next***. 51 | 3. In the ***Set permissions*** dialog under ***Manage public permissions*** select "Grant public read access to this object(s). 52 | 4. Click ***Upload*** 53 | 54 | Our web application should now be ready to use. Return to the bucket root 55 | folder, select the ***Properties*** tab and click on ***Static website 56 | hosting***. Click on the endpoint link to visit your new website. 57 | 58 | Congratulations, you've just gone from 0 to 100 on AWS and built a full-stack 59 | web mapping application! 60 | 61 | *** 62 | 63 | **Previous**: [7.3. Finish app](section-7-3-finish-app.md) | **Up**: [Index](README.md) | **Next**: [8. Cleanup](section-8-clean.md) 64 | -------------------------------------------------------------------------------- /section-7.md: -------------------------------------------------------------------------------- 1 | # 0 to 100 on AWS – Building a full stack web mapping application with PostGIS, GeoServer, OpenLayers and ReactJS 2 | 3 | #### FOSS4G SotM Oceania - Afternoon Workshop #1 - 20th November 2018 4 | 5 | ## 7. Create a ReactJS and OpenLayers Web Mapping Application 6 | 7 | We now have all of the server-side components of our stack deployed and we're 8 | ready to start building our web application. 9 | 10 | The concept is an interactive app that lets you visualise, query and compare 11 | different ABS statistics across Victoria. We'll build it using the following 12 | key technologies. 13 | 14 | | Technology / Library | Description | 15 | | --- | --- | 16 | | [ReactJS](https://reactjs.org/) | Provides the JavaScript framework for building a single page application | 17 | | [Redux](https://Redux.js.org/) and [ImmutableJS](http://facebook.github.io/immutable-js/) | Provides the framework for managing application state | 18 | | [Material UI]() | Provides a set of React UI components based on Google's Material Design | 19 | | [OpenLayers](https://openlayers.org/) | Provides the mapping API | 20 | | [OL Mapbox Style](https://github.com/boundlessgeo/ol-mapbox-style) | Style vector tile layers in OpenLayers | 21 | 22 | Each stage of the application build is detailed in its own section: 23 | 24 | * [7.1. Skeleton React app](section-7-1-skeleton-app.md) 25 | * [7.2. Redux and UI control](section-7-2-ui.md) 26 | * [7.3. Finish the app](section-7-3-finish-app.md) 27 | * [7.4. Publish the app to S3](section-7-4-publish-app.md) 28 | 29 | *** 30 | 31 | **Previous**: [6. Style vector tiles](section-6-style.md) | **Up**: [Index](README.md) | **Next**: [7.1. Skeleton React app](section-7-1-skeleton-app.md) 32 | -------------------------------------------------------------------------------- /section-8-clean.md: -------------------------------------------------------------------------------- 1 | # 0 to 100 on AWS – Building a full stack web mapping application with PostGIS, GeoServer, OpenLayers and ReactJS 2 | 3 | #### FOSS4G SotM Oceania - Afternoon Workshop #1 - 20th November 2018 4 | 5 | ## 8. (Optional) Remove AWS resources 6 | 7 | If you wish to remove the resources created in this workshop, the fastest 8 | method is to delete the CloudFormation stacks. 9 | 10 | Please be aware that if you do not delete or stop these resources, you will 11 | incur AWS charges over time. 12 | 13 | To delete all resources created during this workshop, you can delete the 14 | CloudFormation stacks in reverse order: 15 | 16 | 1. Log in to the AWS console 17 | 2. Go to the CloudFormation service 18 | 3. Select the *foss4g-oceania-workshop-admin* stack 19 | 4. Select ***Actions > Delete*** Stack 20 | 5. Wait for the Delete Stack operation to indicate success 21 | 6. Repeat steps 3 - 5 for each of the geoserver, db, security-groups, and finally vpc stacks. 22 | 23 | Alternatively if you wish to access these resources after the workshop, but 24 | don't want to pay for them to be running 24x7, you can stop the resources as 25 | follows: 26 | 27 | 1. Log in to the AWS console 28 | 2. Go to the EC2 service 29 | 3. Select Instances 30 | 4. Choose the foss4g-oceania-workshop-geoserver instance and the foss4g-oceania-workshop-admin instance. 31 | 5. Select ***Actions > Instance State > Stop*** 32 | 6. Confirm that you want to stop the instances 33 | 7. Go to the RDS service 34 | 8. Select Instances 35 | 9. Choose the foss4g-oceania-workshop instance 36 | 10. Select ***Instance actions > Stop*** 37 | 38 | Please note that if you choose to stop the RDS instance, AWS may automatically 39 | restart the instance after 7 days. If you wish to retain the data in the 40 | database for later use, but avoid incurring RDS running costs, you can create a 41 | database snapshot and then terminate the RDS instance. You can then create a 42 | new RDS instance from the snapshot at any time. 43 | 44 | You may also wish to delete the static website S3 bucket, or disable public 45 | read access to it. 46 | 47 | *** 48 | 49 | **Previous**: [7.4. Publish app](section-7-4-publish-app.md) | **Up**: [Index](README.md) 50 | -------------------------------------------------------------------------------- /sql/geoserver-stats.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS geoserver.abs_stats CASCADE; 2 | CREATE TABLE geoserver.abs_stats AS 3 | SELECT a.area_id, 4 | a.area_type, 5 | b.tot_p_p, 6 | b.tot_p_p / (ST_Area(ST_Transform(a.geom, 3111)) / 100000.0) AS tot_p_p_sq_km, 7 | b.tot_p_f, 8 | b.tot_p_f / (ST_Area(ST_Transform(a.geom, 3111)) / 100000.0) AS tot_p_f_sq_km, 9 | b.tot_p_m, 10 | b.tot_p_m / (ST_Area(ST_Transform(a.geom, 3111)) / 100000.0) AS tot_p_m_sq_km, 11 | b.indigenous_p_tot_p, 12 | b.indigenous_p_tot_p / (ST_Area(ST_Transform(a.geom, 3111)) / 100000.0) AS indigenous_p_tot_p_sq_km, 13 | c.median_age_persons, 14 | c.median_rent_weekly, 15 | c.median_tot_prsnl_inc_weekly, 16 | c.average_household_size, 17 | ST_Area(ST_Transform(a.geom, 3111)) / 100000.0 AS area_sq_km, 18 | a.geom, 19 | ST_Centroid(a.geom) as centroid_geom 20 | FROM geoserver.abs_areas a 21 | LEFT JOIN geoserver.abs_g01 b ON a.area_id = b.area_id 22 | LEFT JOIN geoserver.abs_g02 c ON a.area_id = c.area_id; 23 | 24 | CREATE INDEX sidx_abs_stats_geom ON geoserver.abs_stats USING gist (geom); 25 | 26 | CREATE INDEX sidx_abs_stats_centroid_geom ON geoserver.abs_stats USING gist (centroid_geom); 27 | 28 | ALTER TABLE geoserver.abs_stats ADD COLUMN tot_p_p_sq_km_cl int; 29 | WITH jenks_bins AS 30 | (SELECT CDB_JenksBins( 31 | (SELECT array_agg(tot_p_p_sq_km)::numeric[] FROM geoserver.abs_stats WHERE tot_p_p_sq_km IS NOT NULL), 32 | 4 33 | ) AS breaks) 34 | UPDATE geoserver.abs_stats a 35 | SET tot_p_p_sq_km_cl = jenks_break_class(b.breaks, a.tot_p_p_sq_km::numeric) 36 | FROM jenks_bins b; 37 | 38 | CREATE INDEX idx_tot_p_p_sq_km_cl ON geoserver.abs_stats (tot_p_p_sq_km_cl); 39 | 40 | ALTER TABLE geoserver.abs_stats ADD COLUMN tot_p_f_sq_km_cl int; 41 | WITH jenks_bins AS 42 | (SELECT CDB_JenksBins( 43 | (SELECT array_agg(tot_p_f_sq_km)::numeric[] FROM geoserver.abs_stats WHERE tot_p_f_sq_km IS NOT NULL), 44 | 4 45 | ) AS breaks) 46 | UPDATE geoserver.abs_stats a 47 | SET tot_p_f_sq_km_cl = jenks_break_class(b.breaks, a.tot_p_f_sq_km::numeric) 48 | FROM jenks_bins b; 49 | 50 | CREATE INDEX idx_tot_p_f_sq_km_cl ON geoserver.abs_stats (tot_p_f_sq_km_cl); 51 | 52 | ALTER TABLE geoserver.abs_stats ADD COLUMN tot_p_m_sq_km_cl int; 53 | WITH jenks_bins AS 54 | (SELECT CDB_JenksBins( 55 | (SELECT array_agg(tot_p_m_sq_km)::numeric[] FROM geoserver.abs_stats WHERE tot_p_m_sq_km IS NOT NULL), 56 | 4 57 | ) AS breaks) 58 | UPDATE geoserver.abs_stats a 59 | SET tot_p_m_sq_km_cl = jenks_break_class(b.breaks, a.tot_p_m_sq_km::numeric) 60 | FROM jenks_bins b; 61 | 62 | CREATE INDEX idx_tot_p_m_sq_km_cl ON geoserver.abs_stats (tot_p_m_sq_km_cl); 63 | 64 | ALTER TABLE geoserver.abs_stats ADD COLUMN indigenous_p_tot_p_sq_km_cl int; 65 | WITH jenks_bins AS 66 | (SELECT CDB_JenksBins( 67 | (SELECT array_agg(indigenous_p_tot_p_sq_km)::numeric[] FROM geoserver.abs_stats WHERE indigenous_p_tot_p_sq_km IS NOT NULL), 68 | 4 69 | ) AS breaks) 70 | UPDATE geoserver.abs_stats a 71 | SET indigenous_p_tot_p_sq_km_cl = jenks_break_class(b.breaks, a.indigenous_p_tot_p_sq_km::numeric) 72 | FROM jenks_bins b; 73 | 74 | CREATE INDEX idx_indigenous_p_tot_p_sq_km_cl ON geoserver.abs_stats (indigenous_p_tot_p_sq_km_cl); 75 | 76 | ALTER TABLE geoserver.abs_stats ADD COLUMN median_age_persons_cl int; 77 | WITH jenks_bins AS 78 | (SELECT CDB_JenksBins( 79 | (SELECT array_agg(median_age_persons)::numeric[] FROM geoserver.abs_stats WHERE median_age_persons IS NOT NULL), 80 | 4 81 | ) AS breaks) 82 | UPDATE geoserver.abs_stats a 83 | SET median_age_persons_cl = jenks_break_class(b.breaks, a.median_age_persons::numeric) 84 | FROM jenks_bins b; 85 | 86 | CREATE INDEX idx_median_age_persons_cl ON geoserver.abs_stats (median_age_persons_cl); 87 | 88 | ALTER TABLE geoserver.abs_stats ADD COLUMN median_rent_weekly_cl int; 89 | WITH jenks_bins AS 90 | (SELECT CDB_JenksBins( 91 | (SELECT array_agg(median_rent_weekly)::numeric[] FROM geoserver.abs_stats WHERE median_rent_weekly IS NOT NULL), 92 | 4 93 | ) AS breaks) 94 | UPDATE geoserver.abs_stats a 95 | SET median_rent_weekly_cl = jenks_break_class(b.breaks, a.median_rent_weekly::numeric) 96 | FROM jenks_bins b; 97 | 98 | CREATE INDEX idx_median_rent_weekly_cl ON geoserver.abs_stats (median_rent_weekly_cl); 99 | 100 | ALTER TABLE geoserver.abs_stats ADD COLUMN median_tot_prsnl_inc_weekly_cl int; 101 | WITH jenks_bins AS 102 | (SELECT CDB_JenksBins( 103 | (SELECT array_agg(median_tot_prsnl_inc_weekly)::numeric[] FROM geoserver.abs_stats WHERE median_tot_prsnl_inc_weekly IS NOT NULL), 104 | 4 105 | ) AS breaks) 106 | UPDATE geoserver.abs_stats a 107 | SET median_tot_prsnl_inc_weekly_cl = jenks_break_class(b.breaks, a.median_tot_prsnl_inc_weekly::numeric) 108 | FROM jenks_bins b; 109 | 110 | CREATE INDEX idx_median_tot_prsnl_inc_weekly_cl ON geoserver.abs_stats (median_tot_prsnl_inc_weekly_cl); 111 | 112 | ALTER TABLE geoserver.abs_stats ADD COLUMN average_household_size_cl int; 113 | WITH jenks_bins AS 114 | (SELECT CDB_JenksBins( 115 | (SELECT array_agg(average_household_size)::numeric[] FROM geoserver.abs_stats WHERE average_household_size IS NOT NULL), 116 | 4 117 | ) AS breaks) 118 | UPDATE geoserver.abs_stats a 119 | SET average_household_size_cl = jenks_break_class(b.breaks, a.average_household_size::numeric) 120 | FROM jenks_bins b; 121 | 122 | CREATE INDEX idx_average_household_size_cl ON geoserver.abs_stats (average_household_size_cl); 123 | -------------------------------------------------------------------------------- /sql/geoserver-views.sql: -------------------------------------------------------------------------------- 1 | CREATE OR REPLACE VIEW geoserver.abs_stats_poly AS 2 | SELECT area_id, 3 | area_type, 4 | tot_p_p, 5 | tot_p_p_sq_km, 6 | tot_p_p_sq_km_cl, 7 | tot_p_f, 8 | tot_p_f_sq_km, 9 | tot_p_f_sq_km_cl, 10 | tot_p_m, 11 | tot_p_m_sq_km, 12 | tot_p_m_sq_km_cl, 13 | indigenous_p_tot_p, 14 | indigenous_p_tot_p_sq_km, 15 | indigenous_p_tot_p_sq_km_cl, 16 | median_age_persons, 17 | median_age_persons_cl, 18 | median_rent_weekly, 19 | median_rent_weekly_cl, 20 | median_tot_prsnl_inc_weekly, 21 | median_tot_prsnl_inc_weekly_cl, 22 | average_household_size, 23 | average_household_size_cl, 24 | geom 25 | FROM geoserver.abs_stats; 26 | 27 | CREATE OR REPLACE VIEW geoserver.abs_stats_point AS 28 | SELECT area_id, 29 | area_type, 30 | tot_p_p, 31 | tot_p_p_sq_km, 32 | tot_p_p_sq_km_cl, 33 | tot_p_f, 34 | tot_p_f_sq_km, 35 | tot_p_f_sq_km_cl, 36 | tot_p_m, 37 | tot_p_m_sq_km, 38 | tot_p_m_sq_km_cl, 39 | indigenous_p_tot_p, 40 | indigenous_p_tot_p_sq_km, 41 | indigenous_p_tot_p_sq_km_cl, 42 | median_age_persons, 43 | median_age_persons_cl, 44 | median_rent_weekly, 45 | median_rent_weekly_cl, 46 | median_tot_prsnl_inc_weekly, 47 | median_tot_prsnl_inc_weekly_cl, 48 | average_household_size, 49 | average_household_size_cl, 50 | centroid_geom as geom 51 | FROM geoserver.abs_stats; 52 | -------------------------------------------------------------------------------- /sql/jenks.sql: -------------------------------------------------------------------------------- 1 | CREATE OR REPLACE FUNCTION jenks_break_class(jenks_breaks numeric[], val numeric) RETURNS int AS $$ 2 | BEGIN 3 | IF val <= jenks_breaks[1] THEN 4 | RETURN 1; 5 | END IF; 6 | 7 | FOR i IN array_lower(jenks_breaks, 1) .. array_upper(jenks_breaks, 1) - 1 8 | LOOP 9 | IF val > jenks_breaks[i] AND val <= jenks_breaks[i + 1] THEN 10 | RETURN i + 1; 11 | END IF; 12 | END LOOP; 13 | 14 | RETURN 0; 15 | END; 16 | $$ LANGUAGE plpgsql; 17 | -------------------------------------------------------------------------------- /styles/point_blue.json: -------------------------------------------------------------------------------- 1 | {"layers": [{"layout": {"visibility": "visible"}, "paint": {"circle-stroke-width": 1, "circle-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(241, 238, 246, 1)"], [{"zoom": 0, "value": 2}, "rgba(189, 201, 225, 1)"], [{"zoom": 0, "value": 3}, "rgba(116, 169, 207, 1)"], [{"zoom": 0, "value": 4}, "rgba(43, 140, 190, 1)"], [{"zoom": 0, "value": 5}, "rgba(4, 90, 141, 1)"]]}, "circle-blur": 0, "circle-stroke-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 2}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 3}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 4}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 5}, "rgba(0, 0, 0, 1)"]]}, "circle-radius": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, 3], [{"zoom": 0, "value": 2}, 5], [{"zoom": 0, "value": 3}, 8], [{"zoom": 0, "value": 4}, 13], [{"zoom": 0, "value": 5}, 21]]}, "circle-opacity": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, 0.8], [{"zoom": 0, "value": 2}, 0.8], [{"zoom": 0, "value": 3}, 0.8], [{"zoom": 0, "value": 4}, 0.8], [{"zoom": 0, "value": 5}, 0.8]]}}, "filter": ["all", ["==", "area_type", "SA1"]], "source": "abs_stats_point", "minzoom": 13, "source-layer": "abs_stats_point", "maxzoom": 24, "type": "circle", "id": "abs_stats_point_SA1"}, {"layout": {"visibility": "visible"}, "paint": {"circle-stroke-width": 1, "circle-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(241, 238, 246, 1)"], [{"zoom": 0, "value": 2}, "rgba(189, 201, 225, 1)"], [{"zoom": 0, "value": 3}, "rgba(116, 169, 207, 1)"], [{"zoom": 0, "value": 4}, "rgba(43, 140, 190, 1)"], [{"zoom": 0, "value": 5}, "rgba(4, 90, 141, 1)"]]}, "circle-blur": 0, "circle-stroke-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 2}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 3}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 4}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 5}, "rgba(0, 0, 0, 1)"]]}, "circle-radius": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, 3], [{"zoom": 0, "value": 2}, 5], [{"zoom": 0, "value": 3}, 8], [{"zoom": 0, "value": 4}, 13], [{"zoom": 0, "value": 5}, 21]]}, "circle-opacity": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, 0.8], [{"zoom": 0, "value": 2}, 0.8], [{"zoom": 0, "value": 3}, 0.8], [{"zoom": 0, "value": 4}, 0.8], [{"zoom": 0, "value": 5}, 0.8]]}}, "filter": ["all", ["==", "area_type", "SA2"]], "source": "abs_stats_point", "minzoom": 9, "source-layer": "abs_stats_point", "maxzoom": 13, "type": "circle", "id": "abs_stats_point_SA2"}, {"layout": {"visibility": "visible"}, "paint": {"circle-stroke-width": 1, "circle-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(241, 238, 246, 1)"], [{"zoom": 0, "value": 2}, "rgba(189, 201, 225, 1)"], [{"zoom": 0, "value": 3}, "rgba(116, 169, 207, 1)"], [{"zoom": 0, "value": 4}, "rgba(43, 140, 190, 1)"], [{"zoom": 0, "value": 5}, "rgba(4, 90, 141, 1)"]]}, "circle-blur": 0, "circle-stroke-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 2}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 3}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 4}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 5}, "rgba(0, 0, 0, 1)"]]}, "circle-radius": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, 3], [{"zoom": 0, "value": 2}, 5], [{"zoom": 0, "value": 3}, 8], [{"zoom": 0, "value": 4}, 13], [{"zoom": 0, "value": 5}, 21]]}, "circle-opacity": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, 0.8], [{"zoom": 0, "value": 2}, 0.8], [{"zoom": 0, "value": 3}, 0.8], [{"zoom": 0, "value": 4}, 0.8], [{"zoom": 0, "value": 5}, 0.8]]}}, "filter": ["all", ["==", "area_type", "SA3"]], "source": "abs_stats_point", "minzoom": 6, "source-layer": "abs_stats_point", "maxzoom": 9, "type": "circle", "id": "abs_stats_point_SA3"}, {"layout": {"visibility": "visible"}, "paint": {"circle-stroke-width": 1, "circle-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(241, 238, 246, 1)"], [{"zoom": 0, "value": 2}, "rgba(189, 201, 225, 1)"], [{"zoom": 0, "value": 3}, "rgba(116, 169, 207, 1)"], [{"zoom": 0, "value": 4}, "rgba(43, 140, 190, 1)"], [{"zoom": 0, "value": 5}, "rgba(4, 90, 141, 1)"]]}, "circle-blur": 0, "circle-stroke-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 2}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 3}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 4}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 5}, "rgba(0, 0, 0, 1)"]]}, "circle-radius": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, 3], [{"zoom": 0, "value": 2}, 5], [{"zoom": 0, "value": 3}, 8], [{"zoom": 0, "value": 4}, 13], [{"zoom": 0, "value": 5}, 21]]}, "circle-opacity": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, 0.8], [{"zoom": 0, "value": 2}, 0.8], [{"zoom": 0, "value": 3}, 0.8], [{"zoom": 0, "value": 4}, 0.8], [{"zoom": 0, "value": 5}, 0.8]]}}, "filter": ["all", ["==", "area_type", "SA4"]], "source": "abs_stats_point", "minzoom": 0, "source-layer": "abs_stats_point", "maxzoom": 6, "type": "circle", "id": "abs_stats_point_SA4"}], "sources": {"abs_stats_point": {"maxZoom": 22, "tiles": ["/gwc/service/wmts?LAYER=workshop:abs_stats_point&VERSION=1.0.0&TILEMATRIXSET=EPSG:900913&TILEMATRIX=EPSG:900913:{z}&SERVICE=WMTS&FORMAT=application/x-protobuf;type=mapbox-vector&TILEROW={y}&REQUEST=GetTile&TILECOL={x}"], "type": "vector", "minZoom": 0}}, "version": 8, "name": "point_blue", "sprites": "https://demo.tileserver.org/fonts/{fontstack}/{range}.pbf", "glyphs": "https://demo.tileserver.org/fonts/{fontstack}/{range}.pbf", "id": "point_blue", "metadata": {"maputnik:renderer": "mbgljs", "mapbox:type": "template", "openmaptiles:version": "3.x", "mapbox:autocomposite": false}} -------------------------------------------------------------------------------- /styles/point_green.json: -------------------------------------------------------------------------------- 1 | {"layers": [{"layout": {"visibility": "visible"}, "paint": {"circle-stroke-width": 1, "circle-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(240, 249, 232, 1)"], [{"zoom": 0, "value": 2}, "rgba(186, 228, 188, 1)"], [{"zoom": 0, "value": 3}, "rgba(123, 204, 196, 1)"], [{"zoom": 0, "value": 4}, "rgba(67, 162, 202, 1)"], [{"zoom": 0, "value": 5}, "rgba(8, 104, 172, 1)"]]}, "circle-blur": 0, "circle-stroke-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 2}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 3}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 4}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 5}, "rgba(0, 0, 0, 1)"]]}, "circle-radius": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, 3], [{"zoom": 0, "value": 2}, 5], [{"zoom": 0, "value": 3}, 8], [{"zoom": 0, "value": 4}, 13], [{"zoom": 0, "value": 5}, 21]]}, "circle-opacity": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, 0.8], [{"zoom": 0, "value": 2}, 0.8], [{"zoom": 0, "value": 3}, 0.8], [{"zoom": 0, "value": 4}, 0.8], [{"zoom": 0, "value": 5}, 0.8]]}}, "filter": ["all", ["==", "area_type", "SA1"]], "source": "abs_stats_point", "minzoom": 13, "source-layer": "abs_stats_point", "maxzoom": 24, "type": "circle", "id": "abs_stats_point_SA1"}, {"layout": {"visibility": "visible"}, "paint": {"circle-stroke-width": 1, "circle-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(240, 249, 232, 1)"], [{"zoom": 0, "value": 2}, "rgba(186, 228, 188, 1)"], [{"zoom": 0, "value": 3}, "rgba(123, 204, 196, 1)"], [{"zoom": 0, "value": 4}, "rgba(67, 162, 202, 1)"], [{"zoom": 0, "value": 5}, "rgba(8, 104, 172, 1)"]]}, "circle-blur": 0, "circle-stroke-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 2}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 3}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 4}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 5}, "rgba(0, 0, 0, 1)"]]}, "circle-radius": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, 3], [{"zoom": 0, "value": 2}, 5], [{"zoom": 0, "value": 3}, 8], [{"zoom": 0, "value": 4}, 13], [{"zoom": 0, "value": 5}, 21]]}, "circle-opacity": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, 0.8], [{"zoom": 0, "value": 2}, 0.8], [{"zoom": 0, "value": 3}, 0.8], [{"zoom": 0, "value": 4}, 0.8], [{"zoom": 0, "value": 5}, 0.8]]}}, "filter": ["all", ["==", "area_type", "SA2"]], "source": "abs_stats_point", "minzoom": 9, "source-layer": "abs_stats_point", "maxzoom": 13, "type": "circle", "id": "abs_stats_point_SA2"}, {"layout": {"visibility": "visible"}, "paint": {"circle-stroke-width": 1, "circle-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(240, 249, 232, 1)"], [{"zoom": 0, "value": 2}, "rgba(186, 228, 188, 1)"], [{"zoom": 0, "value": 3}, "rgba(123, 204, 196, 1)"], [{"zoom": 0, "value": 4}, "rgba(67, 162, 202, 1)"], [{"zoom": 0, "value": 5}, "rgba(8, 104, 172, 1)"]]}, "circle-blur": 0, "circle-stroke-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 2}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 3}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 4}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 5}, "rgba(0, 0, 0, 1)"]]}, "circle-radius": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, 3], [{"zoom": 0, "value": 2}, 5], [{"zoom": 0, "value": 3}, 8], [{"zoom": 0, "value": 4}, 13], [{"zoom": 0, "value": 5}, 21]]}, "circle-opacity": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, 0.8], [{"zoom": 0, "value": 2}, 0.8], [{"zoom": 0, "value": 3}, 0.8], [{"zoom": 0, "value": 4}, 0.8], [{"zoom": 0, "value": 5}, 0.8]]}}, "filter": ["all", ["==", "area_type", "SA3"]], "source": "abs_stats_point", "minzoom": 6, "source-layer": "abs_stats_point", "maxzoom": 9, "type": "circle", "id": "abs_stats_point_SA3"}, {"layout": {"visibility": "visible"}, "paint": {"circle-stroke-width": 1, "circle-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(240, 249, 232, 1)"], [{"zoom": 0, "value": 2}, "rgba(186, 228, 188, 1)"], [{"zoom": 0, "value": 3}, "rgba(123, 204, 196, 1)"], [{"zoom": 0, "value": 4}, "rgba(67, 162, 202, 1)"], [{"zoom": 0, "value": 5}, "rgba(8, 104, 172, 1)"]]}, "circle-blur": 0, "circle-stroke-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 2}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 3}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 4}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 5}, "rgba(0, 0, 0, 1)"]]}, "circle-radius": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, 3], [{"zoom": 0, "value": 2}, 5], [{"zoom": 0, "value": 3}, 8], [{"zoom": 0, "value": 4}, 13], [{"zoom": 0, "value": 5}, 21]]}, "circle-opacity": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, 0.8], [{"zoom": 0, "value": 2}, 0.8], [{"zoom": 0, "value": 3}, 0.8], [{"zoom": 0, "value": 4}, 0.8], [{"zoom": 0, "value": 5}, 0.8]]}}, "filter": ["all", ["==", "area_type", "SA4"]], "source": "abs_stats_point", "minzoom": 0, "source-layer": "abs_stats_point", "maxzoom": 6, "type": "circle", "id": "abs_stats_point_SA4"}], "sources": {"abs_stats_point": {"maxZoom": 22, "tiles": ["/gwc/service/wmts?LAYER=workshop:abs_stats_point&VERSION=1.0.0&TILEMATRIXSET=EPSG:900913&TILEMATRIX=EPSG:900913:{z}&SERVICE=WMTS&FORMAT=application/x-protobuf;type=mapbox-vector&TILEROW={y}&REQUEST=GetTile&TILECOL={x}"], "type": "vector", "minZoom": 0}}, "version": 8, "name": "point_green", "sprites": "https://demo.tileserver.org/fonts/{fontstack}/{range}.pbf", "glyphs": "https://demo.tileserver.org/fonts/{fontstack}/{range}.pbf", "id": "point_green", "metadata": {"maputnik:renderer": "mbgljs", "mapbox:type": "template", "openmaptiles:version": "3.x", "mapbox:autocomposite": false}} -------------------------------------------------------------------------------- /styles/point_orange.json: -------------------------------------------------------------------------------- 1 | {"layers": [{"layout": {"visibility": "visible"}, "paint": {"circle-stroke-width": 1, "circle-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(255, 255, 212, 1)"], [{"zoom": 0, "value": 2}, "rgba(254, 217, 142, 1)"], [{"zoom": 0, "value": 3}, "rgba(254, 153, 41, 1)"], [{"zoom": 0, "value": 4}, "rgba(217, 95, 14, 1)"], [{"zoom": 0, "value": 5}, "rgba(153, 52, 4, 1)"]]}, "circle-blur": 0, "circle-stroke-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 2}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 3}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 4}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 5}, "rgba(0, 0, 0, 1)"]]}, "circle-radius": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, 3], [{"zoom": 0, "value": 2}, 5], [{"zoom": 0, "value": 3}, 8], [{"zoom": 0, "value": 4}, 13], [{"zoom": 0, "value": 5}, 21]]}, "circle-opacity": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, 0.8], [{"zoom": 0, "value": 2}, 0.8], [{"zoom": 0, "value": 3}, 0.8], [{"zoom": 0, "value": 4}, 0.8], [{"zoom": 0, "value": 5}, 0.8]]}}, "filter": ["all", ["==", "area_type", "SA1"]], "source": "abs_stats_point", "minzoom": 13, "source-layer": "abs_stats_point", "maxzoom": 24, "type": "circle", "id": "abs_stats_point_SA1"}, {"layout": {"visibility": "visible"}, "paint": {"circle-stroke-width": 1, "circle-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(255, 255, 212, 1)"], [{"zoom": 0, "value": 2}, "rgba(254, 217, 142, 1)"], [{"zoom": 0, "value": 3}, "rgba(254, 153, 41, 1)"], [{"zoom": 0, "value": 4}, "rgba(217, 95, 14, 1)"], [{"zoom": 0, "value": 5}, "rgba(153, 52, 4, 1)"]]}, "circle-blur": 0, "circle-stroke-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 2}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 3}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 4}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 5}, "rgba(0, 0, 0, 1)"]]}, "circle-radius": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, 3], [{"zoom": 0, "value": 2}, 5], [{"zoom": 0, "value": 3}, 8], [{"zoom": 0, "value": 4}, 13], [{"zoom": 0, "value": 5}, 21]]}, "circle-opacity": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, 0.8], [{"zoom": 0, "value": 2}, 0.8], [{"zoom": 0, "value": 3}, 0.8], [{"zoom": 0, "value": 4}, 0.8], [{"zoom": 0, "value": 5}, 0.8]]}}, "filter": ["all", ["==", "area_type", "SA2"]], "source": "abs_stats_point", "minzoom": 9, "source-layer": "abs_stats_point", "maxzoom": 13, "type": "circle", "id": "abs_stats_point_SA2"}, {"layout": {"visibility": "visible"}, "paint": {"circle-stroke-width": 1, "circle-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(255, 255, 212, 1)"], [{"zoom": 0, "value": 2}, "rgba(254, 217, 142, 1)"], [{"zoom": 0, "value": 3}, "rgba(254, 153, 41, 1)"], [{"zoom": 0, "value": 4}, "rgba(217, 95, 14, 1)"], [{"zoom": 0, "value": 5}, "rgba(153, 52, 4, 1)"]]}, "circle-blur": 0, "circle-stroke-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 2}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 3}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 4}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 5}, "rgba(0, 0, 0, 1)"]]}, "circle-radius": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, 3], [{"zoom": 0, "value": 2}, 5], [{"zoom": 0, "value": 3}, 8], [{"zoom": 0, "value": 4}, 13], [{"zoom": 0, "value": 5}, 21]]}, "circle-opacity": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, 0.8], [{"zoom": 0, "value": 2}, 0.8], [{"zoom": 0, "value": 3}, 0.8], [{"zoom": 0, "value": 4}, 0.8], [{"zoom": 0, "value": 5}, 0.8]]}}, "filter": ["all", ["==", "area_type", "SA3"]], "source": "abs_stats_point", "minzoom": 6, "source-layer": "abs_stats_point", "maxzoom": 9, "type": "circle", "id": "abs_stats_point_SA3"}, {"layout": {"visibility": "visible"}, "paint": {"circle-stroke-width": 1, "circle-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(255, 255, 212, 1)"], [{"zoom": 0, "value": 2}, "rgba(254, 217, 142, 1)"], [{"zoom": 0, "value": 3}, "rgba(254, 153, 41, 1)"], [{"zoom": 0, "value": 4}, "rgba(217, 95, 14, 1)"], [{"zoom": 0, "value": 5}, "rgba(153, 52, 4, 1)"]]}, "circle-blur": 0, "circle-stroke-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 2}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 3}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 4}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 5}, "rgba(0, 0, 0, 1)"]]}, "circle-radius": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, 3], [{"zoom": 0, "value": 2}, 5], [{"zoom": 0, "value": 3}, 8], [{"zoom": 0, "value": 4}, 13], [{"zoom": 0, "value": 5}, 21]]}, "circle-opacity": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, 0.8], [{"zoom": 0, "value": 2}, 0.8], [{"zoom": 0, "value": 3}, 0.8], [{"zoom": 0, "value": 4}, 0.8], [{"zoom": 0, "value": 5}, 0.8]]}}, "filter": ["all", ["==", "area_type", "SA4"]], "source": "abs_stats_point", "minzoom": 0, "source-layer": "abs_stats_point", "maxzoom": 6, "type": "circle", "id": "abs_stats_point_SA4"}], "sources": {"abs_stats_point": {"maxZoom": 22, "tiles": ["/gwc/service/wmts?LAYER=workshop:abs_stats_point&VERSION=1.0.0&TILEMATRIXSET=EPSG:900913&TILEMATRIX=EPSG:900913:{z}&SERVICE=WMTS&FORMAT=application/x-protobuf;type=mapbox-vector&TILEROW={y}&REQUEST=GetTile&TILECOL={x}"], "type": "vector", "minZoom": 0}}, "version": 8, "name": "point_orange", "sprites": "https://demo.tileserver.org/fonts/{fontstack}/{range}.pbf", "glyphs": "https://demo.tileserver.org/fonts/{fontstack}/{range}.pbf", "id": "point_orange", "metadata": {"maputnik:renderer": "mbgljs", "mapbox:type": "template", "openmaptiles:version": "3.x", "mapbox:autocomposite": false}} -------------------------------------------------------------------------------- /styles/point_pink.json: -------------------------------------------------------------------------------- 1 | {"layers": [{"layout": {"visibility": "visible"}, "paint": {"circle-stroke-width": 1, "circle-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(254, 235, 226, 1)"], [{"zoom": 0, "value": 2}, "rgba(251, 180, 185, 1)"], [{"zoom": 0, "value": 3}, "rgba(247, 104, 161, 1)"], [{"zoom": 0, "value": 4}, "rgba(197, 27, 138, 1)"], [{"zoom": 0, "value": 5}, "rgba(122, 1, 119, 1)"]]}, "circle-blur": 0, "circle-stroke-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 2}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 3}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 4}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 5}, "rgba(0, 0, 0, 1)"]]}, "circle-radius": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, 3], [{"zoom": 0, "value": 2}, 5], [{"zoom": 0, "value": 3}, 8], [{"zoom": 0, "value": 4}, 13], [{"zoom": 0, "value": 5}, 21]]}, "circle-opacity": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, 0.8], [{"zoom": 0, "value": 2}, 0.8], [{"zoom": 0, "value": 3}, 0.8], [{"zoom": 0, "value": 4}, 0.8], [{"zoom": 0, "value": 5}, 0.8]]}}, "filter": ["all", ["==", "area_type", "SA1"]], "source": "abs_stats_point", "minzoom": 13, "source-layer": "abs_stats_point", "maxzoom": 24, "type": "circle", "id": "abs_stats_point_SA1"}, {"layout": {"visibility": "visible"}, "paint": {"circle-stroke-width": 1, "circle-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(254, 235, 226, 1)"], [{"zoom": 0, "value": 2}, "rgba(251, 180, 185, 1)"], [{"zoom": 0, "value": 3}, "rgba(247, 104, 161, 1)"], [{"zoom": 0, "value": 4}, "rgba(197, 27, 138, 1)"], [{"zoom": 0, "value": 5}, "rgba(122, 1, 119, 1)"]]}, "circle-blur": 0, "circle-stroke-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 2}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 3}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 4}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 5}, "rgba(0, 0, 0, 1)"]]}, "circle-radius": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, 3], [{"zoom": 0, "value": 2}, 5], [{"zoom": 0, "value": 3}, 8], [{"zoom": 0, "value": 4}, 13], [{"zoom": 0, "value": 5}, 21]]}, "circle-opacity": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, 0.8], [{"zoom": 0, "value": 2}, 0.8], [{"zoom": 0, "value": 3}, 0.8], [{"zoom": 0, "value": 4}, 0.8], [{"zoom": 0, "value": 5}, 0.8]]}}, "filter": ["all", ["==", "area_type", "SA2"]], "source": "abs_stats_point", "minzoom": 9, "source-layer": "abs_stats_point", "maxzoom": 13, "type": "circle", "id": "abs_stats_point_SA2"}, {"layout": {"visibility": "visible"}, "paint": {"circle-stroke-width": 1, "circle-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(254, 235, 226, 1)"], [{"zoom": 0, "value": 2}, "rgba(251, 180, 185, 1)"], [{"zoom": 0, "value": 3}, "rgba(247, 104, 161, 1)"], [{"zoom": 0, "value": 4}, "rgba(197, 27, 138, 1)"], [{"zoom": 0, "value": 5}, "rgba(122, 1, 119, 1)"]]}, "circle-blur": 0, "circle-stroke-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 2}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 3}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 4}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 5}, "rgba(0, 0, 0, 1)"]]}, "circle-radius": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, 3], [{"zoom": 0, "value": 2}, 5], [{"zoom": 0, "value": 3}, 8], [{"zoom": 0, "value": 4}, 13], [{"zoom": 0, "value": 5}, 21]]}, "circle-opacity": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, 0.8], [{"zoom": 0, "value": 2}, 0.8], [{"zoom": 0, "value": 3}, 0.8], [{"zoom": 0, "value": 4}, 0.8], [{"zoom": 0, "value": 5}, 0.8]]}}, "filter": ["all", ["==", "area_type", "SA3"]], "source": "abs_stats_point", "minzoom": 6, "source-layer": "abs_stats_point", "maxzoom": 9, "type": "circle", "id": "abs_stats_point_SA3"}, {"layout": {"visibility": "visible"}, "paint": {"circle-stroke-width": 1, "circle-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(254, 235, 226, 1)"], [{"zoom": 0, "value": 2}, "rgba(251, 180, 185, 1)"], [{"zoom": 0, "value": 3}, "rgba(247, 104, 161, 1)"], [{"zoom": 0, "value": 4}, "rgba(197, 27, 138, 1)"], [{"zoom": 0, "value": 5}, "rgba(122, 1, 119, 1)"]]}, "circle-blur": 0, "circle-stroke-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 2}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 3}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 4}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 5}, "rgba(0, 0, 0, 1)"]]}, "circle-radius": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, 3], [{"zoom": 0, "value": 2}, 5], [{"zoom": 0, "value": 3}, 8], [{"zoom": 0, "value": 4}, 13], [{"zoom": 0, "value": 5}, 21]]}, "circle-opacity": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, 0.8], [{"zoom": 0, "value": 2}, 0.8], [{"zoom": 0, "value": 3}, 0.8], [{"zoom": 0, "value": 4}, 0.8], [{"zoom": 0, "value": 5}, 0.8]]}}, "filter": ["all", ["==", "area_type", "SA4"]], "source": "abs_stats_point", "minzoom": 0, "source-layer": "abs_stats_point", "maxzoom": 6, "type": "circle", "id": "abs_stats_point_SA4"}], "sources": {"abs_stats_point": {"maxZoom": 22, "tiles": ["/gwc/service/wmts?LAYER=workshop:abs_stats_point&VERSION=1.0.0&TILEMATRIXSET=EPSG:900913&TILEMATRIX=EPSG:900913:{z}&SERVICE=WMTS&FORMAT=application/x-protobuf;type=mapbox-vector&TILEROW={y}&REQUEST=GetTile&TILECOL={x}"], "type": "vector", "minZoom": 0}}, "version": 8, "name": "point_pink", "sprites": "https://demo.tileserver.org/fonts/{fontstack}/{range}.pbf", "glyphs": "https://demo.tileserver.org/fonts/{fontstack}/{range}.pbf", "id": "point_pink", "metadata": {"maputnik:renderer": "mbgljs", "mapbox:type": "template", "openmaptiles:version": "3.x", "mapbox:autocomposite": false}} -------------------------------------------------------------------------------- /styles/point_purple.json: -------------------------------------------------------------------------------- 1 | {"layers": [{"layout": {"visibility": "visible"}, "paint": {"circle-stroke-width": 1, "circle-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(237, 248, 251, 1)"], [{"zoom": 0, "value": 2}, "rgba(179, 205, 227, 1)"], [{"zoom": 0, "value": 3}, "rgba(140, 150, 198, 1)"], [{"zoom": 0, "value": 4}, "rgba(136, 86, 167, 1)"], [{"zoom": 0, "value": 5}, "rgba(129, 15, 124, 1)"]]}, "circle-blur": 0, "circle-stroke-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 2}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 3}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 4}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 5}, "rgba(0, 0, 0, 1)"]]}, "circle-radius": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, 3], [{"zoom": 0, "value": 2}, 5], [{"zoom": 0, "value": 3}, 8], [{"zoom": 0, "value": 4}, 13], [{"zoom": 0, "value": 5}, 21]]}, "circle-opacity": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, 0.8], [{"zoom": 0, "value": 2}, 0.8], [{"zoom": 0, "value": 3}, 0.8], [{"zoom": 0, "value": 4}, 0.8], [{"zoom": 0, "value": 5}, 0.8]]}}, "filter": ["all", ["==", "area_type", "SA1"]], "source": "abs_stats_point", "minzoom": 13, "source-layer": "abs_stats_point", "maxzoom": 24, "type": "circle", "id": "abs_stats_point_SA1"}, {"layout": {"visibility": "visible"}, "paint": {"circle-stroke-width": 1, "circle-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(237, 248, 251, 1)"], [{"zoom": 0, "value": 2}, "rgba(179, 205, 227, 1)"], [{"zoom": 0, "value": 3}, "rgba(140, 150, 198, 1)"], [{"zoom": 0, "value": 4}, "rgba(136, 86, 167, 1)"], [{"zoom": 0, "value": 5}, "rgba(129, 15, 124, 1)"]]}, "circle-blur": 0, "circle-stroke-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 2}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 3}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 4}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 5}, "rgba(0, 0, 0, 1)"]]}, "circle-radius": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, 3], [{"zoom": 0, "value": 2}, 5], [{"zoom": 0, "value": 3}, 8], [{"zoom": 0, "value": 4}, 13], [{"zoom": 0, "value": 5}, 21]]}, "circle-opacity": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, 0.8], [{"zoom": 0, "value": 2}, 0.8], [{"zoom": 0, "value": 3}, 0.8], [{"zoom": 0, "value": 4}, 0.8], [{"zoom": 0, "value": 5}, 0.8]]}}, "filter": ["all", ["==", "area_type", "SA2"]], "source": "abs_stats_point", "minzoom": 9, "source-layer": "abs_stats_point", "maxzoom": 13, "type": "circle", "id": "abs_stats_point_SA2"}, {"layout": {"visibility": "visible"}, "paint": {"circle-stroke-width": 1, "circle-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(237, 248, 251, 1)"], [{"zoom": 0, "value": 2}, "rgba(179, 205, 227, 1)"], [{"zoom": 0, "value": 3}, "rgba(140, 150, 198, 1)"], [{"zoom": 0, "value": 4}, "rgba(136, 86, 167, 1)"], [{"zoom": 0, "value": 5}, "rgba(129, 15, 124, 1)"]]}, "circle-blur": 0, "circle-stroke-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 2}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 3}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 4}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 5}, "rgba(0, 0, 0, 1)"]]}, "circle-radius": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, 3], [{"zoom": 0, "value": 2}, 5], [{"zoom": 0, "value": 3}, 8], [{"zoom": 0, "value": 4}, 13], [{"zoom": 0, "value": 5}, 21]]}, "circle-opacity": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, 0.8], [{"zoom": 0, "value": 2}, 0.8], [{"zoom": 0, "value": 3}, 0.8], [{"zoom": 0, "value": 4}, 0.8], [{"zoom": 0, "value": 5}, 0.8]]}}, "filter": ["all", ["==", "area_type", "SA3"]], "source": "abs_stats_point", "minzoom": 6, "source-layer": "abs_stats_point", "maxzoom": 9, "type": "circle", "id": "abs_stats_point_SA3"}, {"layout": {"visibility": "visible"}, "paint": {"circle-stroke-width": 1, "circle-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(237, 248, 251, 1)"], [{"zoom": 0, "value": 2}, "rgba(179, 205, 227, 1)"], [{"zoom": 0, "value": 3}, "rgba(140, 150, 198, 1)"], [{"zoom": 0, "value": 4}, "rgba(136, 86, 167, 1)"], [{"zoom": 0, "value": 5}, "rgba(129, 15, 124, 1)"]]}, "circle-blur": 0, "circle-stroke-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 2}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 3}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 4}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 5}, "rgba(0, 0, 0, 1)"]]}, "circle-radius": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, 3], [{"zoom": 0, "value": 2}, 5], [{"zoom": 0, "value": 3}, 8], [{"zoom": 0, "value": 4}, 13], [{"zoom": 0, "value": 5}, 21]]}, "circle-opacity": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, 0.8], [{"zoom": 0, "value": 2}, 0.8], [{"zoom": 0, "value": 3}, 0.8], [{"zoom": 0, "value": 4}, 0.8], [{"zoom": 0, "value": 5}, 0.8]]}}, "filter": ["all", ["==", "area_type", "SA4"]], "source": "abs_stats_point", "minzoom": 0, "source-layer": "abs_stats_point", "maxzoom": 6, "type": "circle", "id": "abs_stats_point_SA4"}], "sources": {"abs_stats_point": {"maxZoom": 22, "tiles": ["/gwc/service/wmts?LAYER=workshop:abs_stats_point&VERSION=1.0.0&TILEMATRIXSET=EPSG:900913&TILEMATRIX=EPSG:900913:{z}&SERVICE=WMTS&FORMAT=application/x-protobuf;type=mapbox-vector&TILEROW={y}&REQUEST=GetTile&TILECOL={x}"], "type": "vector", "minZoom": 0}}, "version": 8, "name": "point_purple", "sprites": "https://demo.tileserver.org/fonts/{fontstack}/{range}.pbf", "glyphs": "https://demo.tileserver.org/fonts/{fontstack}/{range}.pbf", "id": "point_purple", "metadata": {"maputnik:renderer": "mbgljs", "mapbox:type": "template", "openmaptiles:version": "3.x", "mapbox:autocomposite": false}} -------------------------------------------------------------------------------- /styles/poly_blue.json: -------------------------------------------------------------------------------- 1 | {"layers": [{"layout": {"visibility": "visible"}, "paint": {"fill-outline-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 2}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 3}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 4}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 5}, "rgba(0, 0, 0, 1)"]]}, "fill-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(241, 238, 246, 1)"], [{"zoom": 0, "value": 2}, "rgba(189, 201, 225, 1)"], [{"zoom": 0, "value": 3}, "rgba(116, 169, 207, 1)"], [{"zoom": 0, "value": 4}, "rgba(43, 140, 190, 1)"], [{"zoom": 0, "value": 5}, "rgba(4, 90, 141, 1)"]]}, "fill-antialias": true, "fill-opacity": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, 0.8], [{"zoom": 0, "value": 2}, 0.8], [{"zoom": 0, "value": 3}, 0.8], [{"zoom": 0, "value": 4}, 0.8], [{"zoom": 0, "value": 5}, 0.8]]}}, "filter": ["all", ["==", "area_type", "SA1"]], "source": "abs_stats_poly", "minzoom": 13, "source-layer": "abs_stats_poly", "maxzoom": 24, "type": "fill", "id": "abs_stats_poly_SA1"}, {"layout": {"visibility": "visible"}, "paint": {"fill-outline-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 2}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 3}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 4}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 5}, "rgba(0, 0, 0, 1)"]]}, "fill-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(241, 238, 246, 1)"], [{"zoom": 0, "value": 2}, "rgba(189, 201, 225, 1)"], [{"zoom": 0, "value": 3}, "rgba(116, 169, 207, 1)"], [{"zoom": 0, "value": 4}, "rgba(43, 140, 190, 1)"], [{"zoom": 0, "value": 5}, "rgba(4, 90, 141, 1)"]]}, "fill-antialias": true, "fill-opacity": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, 0.8], [{"zoom": 0, "value": 2}, 0.8], [{"zoom": 0, "value": 3}, 0.8], [{"zoom": 0, "value": 4}, 0.8], [{"zoom": 0, "value": 5}, 0.8]]}}, "filter": ["all", ["==", "area_type", "SA2"]], "source": "abs_stats_poly", "minzoom": 9, "source-layer": "abs_stats_poly", "maxzoom": 13, "type": "fill", "id": "abs_stats_poly_SA2"}, {"layout": {"visibility": "visible"}, "paint": {"fill-outline-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 2}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 3}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 4}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 5}, "rgba(0, 0, 0, 1)"]]}, "fill-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(241, 238, 246, 1)"], [{"zoom": 0, "value": 2}, "rgba(189, 201, 225, 1)"], [{"zoom": 0, "value": 3}, "rgba(116, 169, 207, 1)"], [{"zoom": 0, "value": 4}, "rgba(43, 140, 190, 1)"], [{"zoom": 0, "value": 5}, "rgba(4, 90, 141, 1)"]]}, "fill-antialias": true, "fill-opacity": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, 0.8], [{"zoom": 0, "value": 2}, 0.8], [{"zoom": 0, "value": 3}, 0.8], [{"zoom": 0, "value": 4}, 0.8], [{"zoom": 0, "value": 5}, 0.8]]}}, "filter": ["all", ["==", "area_type", "SA3"]], "source": "abs_stats_poly", "minzoom": 6, "source-layer": "abs_stats_poly", "maxzoom": 9, "type": "fill", "id": "abs_stats_poly_SA3"}, {"layout": {"visibility": "visible"}, "paint": {"fill-outline-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 2}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 3}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 4}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 5}, "rgba(0, 0, 0, 1)"]]}, "fill-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(241, 238, 246, 1)"], [{"zoom": 0, "value": 2}, "rgba(189, 201, 225, 1)"], [{"zoom": 0, "value": 3}, "rgba(116, 169, 207, 1)"], [{"zoom": 0, "value": 4}, "rgba(43, 140, 190, 1)"], [{"zoom": 0, "value": 5}, "rgba(4, 90, 141, 1)"]]}, "fill-antialias": true, "fill-opacity": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, 0.8], [{"zoom": 0, "value": 2}, 0.8], [{"zoom": 0, "value": 3}, 0.8], [{"zoom": 0, "value": 4}, 0.8], [{"zoom": 0, "value": 5}, 0.8]]}}, "filter": ["all", ["==", "area_type", "SA4"]], "source": "abs_stats_poly", "minzoom": 0, "source-layer": "abs_stats_poly", "maxzoom": 6, "type": "fill", "id": "abs_stats_poly_SA4"}], "sources": {"abs_stats_poly": {"maxZoom": 22, "tiles": ["/gwc/service/wmts?LAYER=workshop:abs_stats_poly&VERSION=1.0.0&TILEMATRIXSET=EPSG:900913&TILEMATRIX=EPSG:900913:{z}&SERVICE=WMTS&FORMAT=application/x-protobuf;type=mapbox-vector&TILEROW={y}&REQUEST=GetTile&TILECOL={x}"], "type": "vector", "minZoom": 0}}, "version": 8, "name": "poly_blue", "sprites": "https://demo.tileserver.org/fonts/{fontstack}/{range}.pbf", "glyphs": "https://demo.tileserver.org/fonts/{fontstack}/{range}.pbf", "id": "poly_blue", "metadata": {"maputnik:renderer": "mbgljs", "mapbox:type": "template", "openmaptiles:version": "3.x", "mapbox:autocomposite": false}} -------------------------------------------------------------------------------- /styles/poly_green.json: -------------------------------------------------------------------------------- 1 | {"layers": [{"layout": {"visibility": "visible"}, "paint": {"fill-outline-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 2}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 3}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 4}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 5}, "rgba(0, 0, 0, 1)"]]}, "fill-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(240, 249, 232, 1)"], [{"zoom": 0, "value": 2}, "rgba(186, 228, 188, 1)"], [{"zoom": 0, "value": 3}, "rgba(123, 204, 196, 1)"], [{"zoom": 0, "value": 4}, "rgba(67, 162, 202, 1)"], [{"zoom": 0, "value": 5}, "rgba(8, 104, 172, 1)"]]}, "fill-antialias": true, "fill-opacity": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, 0.8], [{"zoom": 0, "value": 2}, 0.8], [{"zoom": 0, "value": 3}, 0.8], [{"zoom": 0, "value": 4}, 0.8], [{"zoom": 0, "value": 5}, 0.8]]}}, "filter": ["all", ["==", "area_type", "SA1"]], "source": "abs_stats_poly", "minzoom": 13, "source-layer": "abs_stats_poly", "maxzoom": 24, "type": "fill", "id": "abs_stats_poly_SA1"}, {"layout": {"visibility": "visible"}, "paint": {"fill-outline-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 2}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 3}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 4}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 5}, "rgba(0, 0, 0, 1)"]]}, "fill-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(240, 249, 232, 1)"], [{"zoom": 0, "value": 2}, "rgba(186, 228, 188, 1)"], [{"zoom": 0, "value": 3}, "rgba(123, 204, 196, 1)"], [{"zoom": 0, "value": 4}, "rgba(67, 162, 202, 1)"], [{"zoom": 0, "value": 5}, "rgba(8, 104, 172, 1)"]]}, "fill-antialias": true, "fill-opacity": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, 0.8], [{"zoom": 0, "value": 2}, 0.8], [{"zoom": 0, "value": 3}, 0.8], [{"zoom": 0, "value": 4}, 0.8], [{"zoom": 0, "value": 5}, 0.8]]}}, "filter": ["all", ["==", "area_type", "SA2"]], "source": "abs_stats_poly", "minzoom": 9, "source-layer": "abs_stats_poly", "maxzoom": 13, "type": "fill", "id": "abs_stats_poly_SA2"}, {"layout": {"visibility": "visible"}, "paint": {"fill-outline-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 2}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 3}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 4}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 5}, "rgba(0, 0, 0, 1)"]]}, "fill-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(240, 249, 232, 1)"], [{"zoom": 0, "value": 2}, "rgba(186, 228, 188, 1)"], [{"zoom": 0, "value": 3}, "rgba(123, 204, 196, 1)"], [{"zoom": 0, "value": 4}, "rgba(67, 162, 202, 1)"], [{"zoom": 0, "value": 5}, "rgba(8, 104, 172, 1)"]]}, "fill-antialias": true, "fill-opacity": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, 0.8], [{"zoom": 0, "value": 2}, 0.8], [{"zoom": 0, "value": 3}, 0.8], [{"zoom": 0, "value": 4}, 0.8], [{"zoom": 0, "value": 5}, 0.8]]}}, "filter": ["all", ["==", "area_type", "SA3"]], "source": "abs_stats_poly", "minzoom": 6, "source-layer": "abs_stats_poly", "maxzoom": 9, "type": "fill", "id": "abs_stats_poly_SA3"}, {"layout": {"visibility": "visible"}, "paint": {"fill-outline-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 2}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 3}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 4}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 5}, "rgba(0, 0, 0, 1)"]]}, "fill-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(240, 249, 232, 1)"], [{"zoom": 0, "value": 2}, "rgba(186, 228, 188, 1)"], [{"zoom": 0, "value": 3}, "rgba(123, 204, 196, 1)"], [{"zoom": 0, "value": 4}, "rgba(67, 162, 202, 1)"], [{"zoom": 0, "value": 5}, "rgba(8, 104, 172, 1)"]]}, "fill-antialias": true, "fill-opacity": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, 0.8], [{"zoom": 0, "value": 2}, 0.8], [{"zoom": 0, "value": 3}, 0.8], [{"zoom": 0, "value": 4}, 0.8], [{"zoom": 0, "value": 5}, 0.8]]}}, "filter": ["all", ["==", "area_type", "SA4"]], "source": "abs_stats_poly", "minzoom": 0, "source-layer": "abs_stats_poly", "maxzoom": 6, "type": "fill", "id": "abs_stats_poly_SA4"}], "sources": {"abs_stats_poly": {"maxZoom": 22, "tiles": ["/gwc/service/wmts?LAYER=workshop:abs_stats_poly&VERSION=1.0.0&TILEMATRIXSET=EPSG:900913&TILEMATRIX=EPSG:900913:{z}&SERVICE=WMTS&FORMAT=application/x-protobuf;type=mapbox-vector&TILEROW={y}&REQUEST=GetTile&TILECOL={x}"], "type": "vector", "minZoom": 0}}, "version": 8, "name": "poly_green", "sprites": "https://demo.tileserver.org/fonts/{fontstack}/{range}.pbf", "glyphs": "https://demo.tileserver.org/fonts/{fontstack}/{range}.pbf", "id": "poly_green", "metadata": {"maputnik:renderer": "mbgljs", "mapbox:type": "template", "openmaptiles:version": "3.x", "mapbox:autocomposite": false}} -------------------------------------------------------------------------------- /styles/poly_orange.json: -------------------------------------------------------------------------------- 1 | {"layers": [{"layout": {"visibility": "visible"}, "paint": {"fill-outline-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 2}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 3}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 4}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 5}, "rgba(0, 0, 0, 1)"]]}, "fill-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(255, 255, 212, 1)"], [{"zoom": 0, "value": 2}, "rgba(254, 217, 142, 1)"], [{"zoom": 0, "value": 3}, "rgba(254, 153, 41, 1)"], [{"zoom": 0, "value": 4}, "rgba(217, 95, 14, 1)"], [{"zoom": 0, "value": 5}, "rgba(153, 52, 4, 1)"]]}, "fill-antialias": true, "fill-opacity": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, 0.8], [{"zoom": 0, "value": 2}, 0.8], [{"zoom": 0, "value": 3}, 0.8], [{"zoom": 0, "value": 4}, 0.8], [{"zoom": 0, "value": 5}, 0.8]]}}, "filter": ["all", ["==", "area_type", "SA1"]], "source": "abs_stats_poly", "minzoom": 13, "source-layer": "abs_stats_poly", "maxzoom": 24, "type": "fill", "id": "abs_stats_poly_SA1"}, {"layout": {"visibility": "visible"}, "paint": {"fill-outline-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 2}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 3}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 4}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 5}, "rgba(0, 0, 0, 1)"]]}, "fill-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(255, 255, 212, 1)"], [{"zoom": 0, "value": 2}, "rgba(254, 217, 142, 1)"], [{"zoom": 0, "value": 3}, "rgba(254, 153, 41, 1)"], [{"zoom": 0, "value": 4}, "rgba(217, 95, 14, 1)"], [{"zoom": 0, "value": 5}, "rgba(153, 52, 4, 1)"]]}, "fill-antialias": true, "fill-opacity": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, 0.8], [{"zoom": 0, "value": 2}, 0.8], [{"zoom": 0, "value": 3}, 0.8], [{"zoom": 0, "value": 4}, 0.8], [{"zoom": 0, "value": 5}, 0.8]]}}, "filter": ["all", ["==", "area_type", "SA2"]], "source": "abs_stats_poly", "minzoom": 9, "source-layer": "abs_stats_poly", "maxzoom": 13, "type": "fill", "id": "abs_stats_poly_SA2"}, {"layout": {"visibility": "visible"}, "paint": {"fill-outline-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 2}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 3}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 4}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 5}, "rgba(0, 0, 0, 1)"]]}, "fill-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(255, 255, 212, 1)"], [{"zoom": 0, "value": 2}, "rgba(254, 217, 142, 1)"], [{"zoom": 0, "value": 3}, "rgba(254, 153, 41, 1)"], [{"zoom": 0, "value": 4}, "rgba(217, 95, 14, 1)"], [{"zoom": 0, "value": 5}, "rgba(153, 52, 4, 1)"]]}, "fill-antialias": true, "fill-opacity": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, 0.8], [{"zoom": 0, "value": 2}, 0.8], [{"zoom": 0, "value": 3}, 0.8], [{"zoom": 0, "value": 4}, 0.8], [{"zoom": 0, "value": 5}, 0.8]]}}, "filter": ["all", ["==", "area_type", "SA3"]], "source": "abs_stats_poly", "minzoom": 6, "source-layer": "abs_stats_poly", "maxzoom": 9, "type": "fill", "id": "abs_stats_poly_SA3"}, {"layout": {"visibility": "visible"}, "paint": {"fill-outline-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 2}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 3}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 4}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 5}, "rgba(0, 0, 0, 1)"]]}, "fill-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(255, 255, 212, 1)"], [{"zoom": 0, "value": 2}, "rgba(254, 217, 142, 1)"], [{"zoom": 0, "value": 3}, "rgba(254, 153, 41, 1)"], [{"zoom": 0, "value": 4}, "rgba(217, 95, 14, 1)"], [{"zoom": 0, "value": 5}, "rgba(153, 52, 4, 1)"]]}, "fill-antialias": true, "fill-opacity": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, 0.8], [{"zoom": 0, "value": 2}, 0.8], [{"zoom": 0, "value": 3}, 0.8], [{"zoom": 0, "value": 4}, 0.8], [{"zoom": 0, "value": 5}, 0.8]]}}, "filter": ["all", ["==", "area_type", "SA4"]], "source": "abs_stats_poly", "minzoom": 0, "source-layer": "abs_stats_poly", "maxzoom": 6, "type": "fill", "id": "abs_stats_poly_SA4"}], "sources": {"abs_stats_poly": {"maxZoom": 22, "tiles": ["/gwc/service/wmts?LAYER=workshop:abs_stats_poly&VERSION=1.0.0&TILEMATRIXSET=EPSG:900913&TILEMATRIX=EPSG:900913:{z}&SERVICE=WMTS&FORMAT=application/x-protobuf;type=mapbox-vector&TILEROW={y}&REQUEST=GetTile&TILECOL={x}"], "type": "vector", "minZoom": 0}}, "version": 8, "name": "poly_orange", "sprites": "https://demo.tileserver.org/fonts/{fontstack}/{range}.pbf", "glyphs": "https://demo.tileserver.org/fonts/{fontstack}/{range}.pbf", "id": "poly_orange", "metadata": {"maputnik:renderer": "mbgljs", "mapbox:type": "template", "openmaptiles:version": "3.x", "mapbox:autocomposite": false}} -------------------------------------------------------------------------------- /styles/poly_pink.json: -------------------------------------------------------------------------------- 1 | {"layers": [{"layout": {"visibility": "visible"}, "paint": {"fill-outline-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 2}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 3}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 4}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 5}, "rgba(0, 0, 0, 1)"]]}, "fill-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(254, 235, 226, 1)"], [{"zoom": 0, "value": 2}, "rgba(251, 180, 185, 1)"], [{"zoom": 0, "value": 3}, "rgba(247, 104, 161, 1)"], [{"zoom": 0, "value": 4}, "rgba(197, 27, 138, 1)"], [{"zoom": 0, "value": 5}, "rgba(122, 1, 119, 1)"]]}, "fill-antialias": true, "fill-opacity": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, 0.8], [{"zoom": 0, "value": 2}, 0.8], [{"zoom": 0, "value": 3}, 0.8], [{"zoom": 0, "value": 4}, 0.8], [{"zoom": 0, "value": 5}, 0.8]]}}, "filter": ["all", ["==", "area_type", "SA1"]], "source": "abs_stats_poly", "minzoom": 13, "source-layer": "abs_stats_poly", "maxzoom": 24, "type": "fill", "id": "abs_stats_poly_SA1"}, {"layout": {"visibility": "visible"}, "paint": {"fill-outline-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 2}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 3}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 4}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 5}, "rgba(0, 0, 0, 1)"]]}, "fill-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(254, 235, 226, 1)"], [{"zoom": 0, "value": 2}, "rgba(251, 180, 185, 1)"], [{"zoom": 0, "value": 3}, "rgba(247, 104, 161, 1)"], [{"zoom": 0, "value": 4}, "rgba(197, 27, 138, 1)"], [{"zoom": 0, "value": 5}, "rgba(122, 1, 119, 1)"]]}, "fill-antialias": true, "fill-opacity": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, 0.8], [{"zoom": 0, "value": 2}, 0.8], [{"zoom": 0, "value": 3}, 0.8], [{"zoom": 0, "value": 4}, 0.8], [{"zoom": 0, "value": 5}, 0.8]]}}, "filter": ["all", ["==", "area_type", "SA2"]], "source": "abs_stats_poly", "minzoom": 9, "source-layer": "abs_stats_poly", "maxzoom": 13, "type": "fill", "id": "abs_stats_poly_SA2"}, {"layout": {"visibility": "visible"}, "paint": {"fill-outline-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 2}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 3}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 4}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 5}, "rgba(0, 0, 0, 1)"]]}, "fill-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(254, 235, 226, 1)"], [{"zoom": 0, "value": 2}, "rgba(251, 180, 185, 1)"], [{"zoom": 0, "value": 3}, "rgba(247, 104, 161, 1)"], [{"zoom": 0, "value": 4}, "rgba(197, 27, 138, 1)"], [{"zoom": 0, "value": 5}, "rgba(122, 1, 119, 1)"]]}, "fill-antialias": true, "fill-opacity": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, 0.8], [{"zoom": 0, "value": 2}, 0.8], [{"zoom": 0, "value": 3}, 0.8], [{"zoom": 0, "value": 4}, 0.8], [{"zoom": 0, "value": 5}, 0.8]]}}, "filter": ["all", ["==", "area_type", "SA3"]], "source": "abs_stats_poly", "minzoom": 6, "source-layer": "abs_stats_poly", "maxzoom": 9, "type": "fill", "id": "abs_stats_poly_SA3"}, {"layout": {"visibility": "visible"}, "paint": {"fill-outline-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 2}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 3}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 4}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 5}, "rgba(0, 0, 0, 1)"]]}, "fill-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(254, 235, 226, 1)"], [{"zoom": 0, "value": 2}, "rgba(251, 180, 185, 1)"], [{"zoom": 0, "value": 3}, "rgba(247, 104, 161, 1)"], [{"zoom": 0, "value": 4}, "rgba(197, 27, 138, 1)"], [{"zoom": 0, "value": 5}, "rgba(122, 1, 119, 1)"]]}, "fill-antialias": true, "fill-opacity": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, 0.8], [{"zoom": 0, "value": 2}, 0.8], [{"zoom": 0, "value": 3}, 0.8], [{"zoom": 0, "value": 4}, 0.8], [{"zoom": 0, "value": 5}, 0.8]]}}, "filter": ["all", ["==", "area_type", "SA4"]], "source": "abs_stats_poly", "minzoom": 0, "source-layer": "abs_stats_poly", "maxzoom": 6, "type": "fill", "id": "abs_stats_poly_SA4"}], "sources": {"abs_stats_poly": {"maxZoom": 22, "tiles": ["/gwc/service/wmts?LAYER=workshop:abs_stats_poly&VERSION=1.0.0&TILEMATRIXSET=EPSG:900913&TILEMATRIX=EPSG:900913:{z}&SERVICE=WMTS&FORMAT=application/x-protobuf;type=mapbox-vector&TILEROW={y}&REQUEST=GetTile&TILECOL={x}"], "type": "vector", "minZoom": 0}}, "version": 8, "name": "poly_pink", "sprites": "https://demo.tileserver.org/fonts/{fontstack}/{range}.pbf", "glyphs": "https://demo.tileserver.org/fonts/{fontstack}/{range}.pbf", "id": "poly_pink", "metadata": {"maputnik:renderer": "mbgljs", "mapbox:type": "template", "openmaptiles:version": "3.x", "mapbox:autocomposite": false}} -------------------------------------------------------------------------------- /styles/poly_purple.json: -------------------------------------------------------------------------------- 1 | {"layers": [{"layout": {"visibility": "visible"}, "paint": {"fill-outline-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 2}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 3}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 4}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 5}, "rgba(0, 0, 0, 1)"]]}, "fill-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(237, 248, 251, 1)"], [{"zoom": 0, "value": 2}, "rgba(179, 205, 227, 1)"], [{"zoom": 0, "value": 3}, "rgba(140, 150, 198, 1)"], [{"zoom": 0, "value": 4}, "rgba(136, 86, 167, 1)"], [{"zoom": 0, "value": 5}, "rgba(129, 15, 124, 1)"]]}, "fill-antialias": true, "fill-opacity": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, 0.8], [{"zoom": 0, "value": 2}, 0.8], [{"zoom": 0, "value": 3}, 0.8], [{"zoom": 0, "value": 4}, 0.8], [{"zoom": 0, "value": 5}, 0.8]]}}, "filter": ["all", ["==", "area_type", "SA1"]], "source": "abs_stats_poly", "minzoom": 13, "source-layer": "abs_stats_poly", "maxzoom": 24, "type": "fill", "id": "abs_stats_poly_SA1"}, {"layout": {"visibility": "visible"}, "paint": {"fill-outline-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 2}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 3}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 4}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 5}, "rgba(0, 0, 0, 1)"]]}, "fill-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(237, 248, 251, 1)"], [{"zoom": 0, "value": 2}, "rgba(179, 205, 227, 1)"], [{"zoom": 0, "value": 3}, "rgba(140, 150, 198, 1)"], [{"zoom": 0, "value": 4}, "rgba(136, 86, 167, 1)"], [{"zoom": 0, "value": 5}, "rgba(129, 15, 124, 1)"]]}, "fill-antialias": true, "fill-opacity": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, 0.8], [{"zoom": 0, "value": 2}, 0.8], [{"zoom": 0, "value": 3}, 0.8], [{"zoom": 0, "value": 4}, 0.8], [{"zoom": 0, "value": 5}, 0.8]]}}, "filter": ["all", ["==", "area_type", "SA2"]], "source": "abs_stats_poly", "minzoom": 9, "source-layer": "abs_stats_poly", "maxzoom": 13, "type": "fill", "id": "abs_stats_poly_SA2"}, {"layout": {"visibility": "visible"}, "paint": {"fill-outline-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 2}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 3}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 4}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 5}, "rgba(0, 0, 0, 1)"]]}, "fill-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(237, 248, 251, 1)"], [{"zoom": 0, "value": 2}, "rgba(179, 205, 227, 1)"], [{"zoom": 0, "value": 3}, "rgba(140, 150, 198, 1)"], [{"zoom": 0, "value": 4}, "rgba(136, 86, 167, 1)"], [{"zoom": 0, "value": 5}, "rgba(129, 15, 124, 1)"]]}, "fill-antialias": true, "fill-opacity": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, 0.8], [{"zoom": 0, "value": 2}, 0.8], [{"zoom": 0, "value": 3}, 0.8], [{"zoom": 0, "value": 4}, 0.8], [{"zoom": 0, "value": 5}, 0.8]]}}, "filter": ["all", ["==", "area_type", "SA3"]], "source": "abs_stats_poly", "minzoom": 6, "source-layer": "abs_stats_poly", "maxzoom": 9, "type": "fill", "id": "abs_stats_poly_SA3"}, {"layout": {"visibility": "visible"}, "paint": {"fill-outline-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 2}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 3}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 4}, "rgba(0, 0, 0, 1)"], [{"zoom": 0, "value": 5}, "rgba(0, 0, 0, 1)"]]}, "fill-color": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, "rgba(237, 248, 251, 1)"], [{"zoom": 0, "value": 2}, "rgba(179, 205, 227, 1)"], [{"zoom": 0, "value": 3}, "rgba(140, 150, 198, 1)"], [{"zoom": 0, "value": 4}, "rgba(136, 86, 167, 1)"], [{"zoom": 0, "value": 5}, "rgba(129, 15, 124, 1)"]]}, "fill-antialias": true, "fill-opacity": {"property": "_cl", "type": "categorical", "stops": [[{"zoom": 0, "value": 1}, 0.8], [{"zoom": 0, "value": 2}, 0.8], [{"zoom": 0, "value": 3}, 0.8], [{"zoom": 0, "value": 4}, 0.8], [{"zoom": 0, "value": 5}, 0.8]]}}, "filter": ["all", ["==", "area_type", "SA4"]], "source": "abs_stats_poly", "minzoom": 0, "source-layer": "abs_stats_poly", "maxzoom": 6, "type": "fill", "id": "abs_stats_poly_SA4"}], "sources": {"abs_stats_poly": {"maxZoom": 22, "tiles": ["/gwc/service/wmts?LAYER=workshop:abs_stats_poly&VERSION=1.0.0&TILEMATRIXSET=EPSG:900913&TILEMATRIX=EPSG:900913:{z}&SERVICE=WMTS&FORMAT=application/x-protobuf;type=mapbox-vector&TILEROW={y}&REQUEST=GetTile&TILECOL={x}"], "type": "vector", "minZoom": 0}}, "version": 8, "name": "poly_purple", "sprites": "https://demo.tileserver.org/fonts/{fontstack}/{range}.pbf", "glyphs": "https://demo.tileserver.org/fonts/{fontstack}/{range}.pbf", "id": "poly_purple", "metadata": {"maputnik:renderer": "mbgljs", "mapbox:type": "template", "openmaptiles:version": "3.x", "mapbox:autocomposite": false}} --------------------------------------------------------------------------------