├── .gitignore ├── LICENSE ├── README.md ├── data ├── countries-aggregated.csv ├── countries.geojson ├── reference.csv └── us_confirmed.csv ├── docker ├── .env ├── Makefile └── docker-compose.yml ├── docs ├── Makefile ├── make.bat ├── requirements.txt └── source │ ├── conf.py │ ├── images │ ├── dashboard.gif │ ├── geoloop.png │ ├── grafana_login.png │ ├── influx.png │ ├── postgres.png │ ├── preview.gif │ ├── us.png │ └── worldmap.png │ └── index.rst └── scripts ├── delete_tables.sql ├── schema.sql └── write_points.py /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | *.pyc 3 | scripts/__pychache__/ 4 | docs/build/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Aleksey Piskun 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # docker-postgres-influxdb-grafana 2 | 3 | This tutorial provides a quick guide of how to install a dashboard environment 4 | from Grafana, Postgres and InfluxDB with docker-compose, create map overlays with Worldmap Panel plugin and 5 | build animated maps using GeoLoop Panel plugin. 6 | 7 | To illustrate the process of building the animated maps with GeoLoop, 8 | we will use time series data tracking the number of people affected by COVID-19 worldwide, 9 | including confirmed cases of Coronavirus infection, the number of people died while 10 | sick with Coronavirus, and the number of people recovered from it. 11 | 12 | The data is borrowed from “covid-19” [dataset][1] 13 | and stored as csv files in [data](https://github.com/viktorsapozhok/docker-postgres-influxdb-grafana/tree/master/data) 14 | directory. 15 | 16 | [Read the tutorial][2] for more. 17 | 18 | 19 | 20 | ## License 21 | 22 | MIT License (see [LICENSE](LICENSE)). 23 | 24 | [1]: https://github.com/datasets/covid-19 25 | [2]: https://viktorsapozhok.github.io/docker-postgres-grafana/ "Docker with postgres, influxdb and grafana" 26 | -------------------------------------------------------------------------------- /data/countries.geojson: -------------------------------------------------------------------------------- 1 | geo( 2 | { 3 | "type": "FeatureCollection", 4 | "features": [ 5 | {"type": "Feature", "id": 0, "properties": {"name": "Afghanistan"}, "geometry": {"type": "Point", "coordinates": [67.70995, 33.93911]}}, 6 | {"type": "Feature", "id": 1, "properties": {"name": "Albania"}, "geometry": {"type": "Point", "coordinates": [20.1683, 41.1533]}}, 7 | {"type": "Feature", "id": 2, "properties": {"name": "Algeria"}, "geometry": {"type": "Point", "coordinates": [1.6596, 28.0339]}}, 8 | {"type": "Feature", "id": 3, "properties": {"name": "Andorra"}, "geometry": {"type": "Point", "coordinates": [1.5218, 42.5063]}}, 9 | {"type": "Feature", "id": 4, "properties": {"name": "Angola"}, "geometry": {"type": "Point", "coordinates": [17.8739, -11.2027]}}, 10 | {"type": "Feature", "id": 5, "properties": {"name": "Antigua and Barbuda"}, "geometry": {"type": "Point", "coordinates": [-61.7964, 17.0608]}}, 11 | {"type": "Feature", "id": 6, "properties": {"name": "Argentina"}, "geometry": {"type": "Point", "coordinates": [-63.6167, -38.4161]}}, 12 | {"type": "Feature", "id": 7, "properties": {"name": "Armenia"}, "geometry": {"type": "Point", "coordinates": [45.0382, 40.0691]}}, 13 | {"type": "Feature", "id": 8, "properties": {"name": "Austria"}, "geometry": {"type": "Point", "coordinates": [14.5501, 47.5162]}}, 14 | {"type": "Feature", "id": 9, "properties": {"name": "Azerbaijan"}, "geometry": {"type": "Point", "coordinates": [47.5769, 40.1431]}}, 15 | {"type": "Feature", "id": 10, "properties": {"name": "Bahamas"}, "geometry": {"type": "Point", "coordinates": [-78.03589, 25.025885]}}, 16 | {"type": "Feature", "id": 11, "properties": {"name": "Bahrain"}, "geometry": {"type": "Point", "coordinates": [50.55, 26.0275]}}, 17 | {"type": "Feature", "id": 12, "properties": {"name": "Bangladesh"}, "geometry": {"type": "Point", "coordinates": [90.3563, 23.685]}}, 18 | {"type": "Feature", "id": 13, "properties": {"name": "Barbados"}, "geometry": {"type": "Point", "coordinates": [-59.5432, 13.1939]}}, 19 | {"type": "Feature", "id": 14, "properties": {"name": "Belarus"}, "geometry": {"type": "Point", "coordinates": [27.9534, 53.7098]}}, 20 | {"type": "Feature", "id": 15, "properties": {"name": "Belgium"}, "geometry": {"type": "Point", "coordinates": [4.469936, 50.8333]}}, 21 | {"type": "Feature", "id": 16, "properties": {"name": "Belize"}, "geometry": {"type": "Point", "coordinates": [-88.4976, 17.1899]}}, 22 | {"type": "Feature", "id": 17, "properties": {"name": "Benin"}, "geometry": {"type": "Point", "coordinates": [2.3158, 9.3077]}}, 23 | {"type": "Feature", "id": 18, "properties": {"name": "Bhutan"}, "geometry": {"type": "Point", "coordinates": [90.4336, 27.5142]}}, 24 | {"type": "Feature", "id": 19, "properties": {"name": "Bolivia"}, "geometry": {"type": "Point", "coordinates": [-63.5887, -16.2902]}}, 25 | {"type": "Feature", "id": 20, "properties": {"name": "Bosnia and Herzegovina"}, "geometry": {"type": "Point", "coordinates": [17.6791, 43.9159]}}, 26 | {"type": "Feature", "id": 21, "properties": {"name": "Botswana"}, "geometry": {"type": "Point", "coordinates": [24.6849, -22.3285]}}, 27 | {"type": "Feature", "id": 22, "properties": {"name": "Brazil"}, "geometry": {"type": "Point", "coordinates": [-51.9253, -14.235]}}, 28 | {"type": "Feature", "id": 23, "properties": {"name": "Brunei"}, "geometry": {"type": "Point", "coordinates": [114.7277, 4.5353]}}, 29 | {"type": "Feature", "id": 24, "properties": {"name": "Bulgaria"}, "geometry": {"type": "Point", "coordinates": [25.4858, 42.7339]}}, 30 | {"type": "Feature", "id": 25, "properties": {"name": "Burkina Faso"}, "geometry": {"type": "Point", "coordinates": [-1.5616, 12.2383]}}, 31 | {"type": "Feature", "id": 26, "properties": {"name": "Burma"}, "geometry": {"type": "Point", "coordinates": [95.956, 21.9162]}}, 32 | {"type": "Feature", "id": 27, "properties": {"name": "Burundi"}, "geometry": {"type": "Point", "coordinates": [29.9189, -3.3731]}}, 33 | {"type": "Feature", "id": 28, "properties": {"name": "Cabo Verde"}, "geometry": {"type": "Point", "coordinates": [-23.0418, 16.5388]}}, 34 | {"type": "Feature", "id": 29, "properties": {"name": "Cambodia"}, "geometry": {"type": "Point", "coordinates": [104.9167, 11.55]}}, 35 | {"type": "Feature", "id": 30, "properties": {"name": "Cameroon"}, "geometry": {"type": "Point", "coordinates": [11.5021, 3.848]}}, 36 | {"type": "Feature", "id": 31, "properties": {"name": "Central African Republic"}, "geometry": {"type": "Point", "coordinates": [20.9394, 6.6111]}}, 37 | {"type": "Feature", "id": 32, "properties": {"name": "Chad"}, "geometry": {"type": "Point", "coordinates": [18.7322, 15.4542]}}, 38 | {"type": "Feature", "id": 33, "properties": {"name": "Chile"}, "geometry": {"type": "Point", "coordinates": [-71.543, -35.6751]}}, 39 | {"type": "Feature", "id": 34, "properties": {"name": "Colombia"}, "geometry": {"type": "Point", "coordinates": [-74.2973, 4.5709]}}, 40 | {"type": "Feature", "id": 35, "properties": {"name": "Congo (Brazzaville)"}, "geometry": {"type": "Point", "coordinates": [15.8277, -0.228]}}, 41 | {"type": "Feature", "id": 36, "properties": {"name": "Congo (Kinshasa)"}, "geometry": {"type": "Point", "coordinates": [21.7587, -4.0383]}}, 42 | {"type": "Feature", "id": 37, "properties": {"name": "Comoros"}, "geometry": {"type": "Point", "coordinates": [43.3333, -11.6455]}}, 43 | {"type": "Feature", "id": 38, "properties": {"name": "Costa Rica"}, "geometry": {"type": "Point", "coordinates": [-83.7534, 9.7489]}}, 44 | {"type": "Feature", "id": 39, "properties": {"name": "Cote d'Ivoire"}, "geometry": {"type": "Point", "coordinates": [-5.5471, 7.54]}}, 45 | {"type": "Feature", "id": 40, "properties": {"name": "Croatia"}, "geometry": {"type": "Point", "coordinates": [15.2, 45.1]}}, 46 | {"type": "Feature", "id": 41, "properties": {"name": "Cuba"}, "geometry": {"type": "Point", "coordinates": [-77.781166, 21.521757]}}, 47 | {"type": "Feature", "id": 42, "properties": {"name": "Cyprus"}, "geometry": {"type": "Point", "coordinates": [33.4299, 35.1264]}}, 48 | {"type": "Feature", "id": 43, "properties": {"name": "Czechia"}, "geometry": {"type": "Point", "coordinates": [15.473, 49.8175]}}, 49 | {"type": "Feature", "id": 44, "properties": {"name": "Denmark"}, "geometry": {"type": "Point", "coordinates": [9.5018, 56.2639]}}, 50 | {"type": "Feature", "id": 45, "properties": {"name": "Djibouti"}, "geometry": {"type": "Point", "coordinates": [42.5903, 11.8251]}}, 51 | {"type": "Feature", "id": 46, "properties": {"name": "Dominica"}, "geometry": {"type": "Point", "coordinates": [-61.371, 15.415]}}, 52 | {"type": "Feature", "id": 47, "properties": {"name": "Dominican Republic"}, "geometry": {"type": "Point", "coordinates": [-70.1627, 18.7357]}}, 53 | {"type": "Feature", "id": 48, "properties": {"name": "Ecuador"}, "geometry": {"type": "Point", "coordinates": [-78.1834, -1.8312]}}, 54 | {"type": "Feature", "id": 49, "properties": {"name": "Egypt"}, "geometry": {"type": "Point", "coordinates": [30.802498, 26.820553]}}, 55 | {"type": "Feature", "id": 50, "properties": {"name": "El Salvador"}, "geometry": {"type": "Point", "coordinates": [-88.8965, 13.7942]}}, 56 | {"type": "Feature", "id": 51, "properties": {"name": "Equatorial Guinea"}, "geometry": {"type": "Point", "coordinates": [10.2679, 1.6508]}}, 57 | {"type": "Feature", "id": 52, "properties": {"name": "Eritrea"}, "geometry": {"type": "Point", "coordinates": [39.7823, 15.1794]}}, 58 | {"type": "Feature", "id": 53, "properties": {"name": "Estonia"}, "geometry": {"type": "Point", "coordinates": [25.0136, 58.5953]}}, 59 | {"type": "Feature", "id": 54, "properties": {"name": "Eswatini"}, "geometry": {"type": "Point", "coordinates": [31.4659, -26.5225]}}, 60 | {"type": "Feature", "id": 55, "properties": {"name": "Ethiopia"}, "geometry": {"type": "Point", "coordinates": [40.4897, 9.145]}}, 61 | {"type": "Feature", "id": 56, "properties": {"name": "Fiji"}, "geometry": {"type": "Point", "coordinates": [178.065, -17.7134]}}, 62 | {"type": "Feature", "id": 57, "properties": {"name": "Finland"}, "geometry": {"type": "Point", "coordinates": [25.748152, 61.92411]}}, 63 | {"type": "Feature", "id": 58, "properties": {"name": "France"}, "geometry": {"type": "Point", "coordinates": [2.2137, 46.2276]}}, 64 | {"type": "Feature", "id": 59, "properties": {"name": "Gabon"}, "geometry": {"type": "Point", "coordinates": [11.6094, -0.8037]}}, 65 | {"type": "Feature", "id": 60, "properties": {"name": "Gambia"}, "geometry": {"type": "Point", "coordinates": [-15.3101, 13.4432]}}, 66 | {"type": "Feature", "id": 61, "properties": {"name": "Georgia"}, "geometry": {"type": "Point", "coordinates": [43.3569, 42.3154]}}, 67 | {"type": "Feature", "id": 62, "properties": {"name": "Germany"}, "geometry": {"type": "Point", "coordinates": [10.451526, 51.16569]}}, 68 | {"type": "Feature", "id": 63, "properties": {"name": "Ghana"}, "geometry": {"type": "Point", "coordinates": [-1.0232, 7.9465]}}, 69 | {"type": "Feature", "id": 64, "properties": {"name": "Greece"}, "geometry": {"type": "Point", "coordinates": [21.8243, 39.0742]}}, 70 | {"type": "Feature", "id": 65, "properties": {"name": "Grenada"}, "geometry": {"type": "Point", "coordinates": [-61.679, 12.1165]}}, 71 | {"type": "Feature", "id": 66, "properties": {"name": "Guatemala"}, "geometry": {"type": "Point", "coordinates": [-90.2308, 15.7835]}}, 72 | {"type": "Feature", "id": 67, "properties": {"name": "Guinea"}, "geometry": {"type": "Point", "coordinates": [-9.6966, 9.9456]}}, 73 | {"type": "Feature", "id": 68, "properties": {"name": "Guinea-Bissau"}, "geometry": {"type": "Point", "coordinates": [-15.1804, 11.8037]}}, 74 | {"type": "Feature", "id": 69, "properties": {"name": "Guyana"}, "geometry": {"type": "Point", "coordinates": [-58.93018, 4.860416]}}, 75 | {"type": "Feature", "id": 70, "properties": {"name": "Haiti"}, "geometry": {"type": "Point", "coordinates": [-72.2852, 18.9712]}}, 76 | {"type": "Feature", "id": 71, "properties": {"name": "Holy See"}, "geometry": {"type": "Point", "coordinates": [12.4534, 41.9029]}}, 77 | {"type": "Feature", "id": 72, "properties": {"name": "Honduras"}, "geometry": {"type": "Point", "coordinates": [-86.2419, 15.2]}}, 78 | {"type": "Feature", "id": 73, "properties": {"name": "Hungary"}, "geometry": {"type": "Point", "coordinates": [19.5033, 47.1625]}}, 79 | {"type": "Feature", "id": 74, "properties": {"name": "Iceland"}, "geometry": {"type": "Point", "coordinates": [-19.0208, 64.9631]}}, 80 | {"type": "Feature", "id": 75, "properties": {"name": "India"}, "geometry": {"type": "Point", "coordinates": [78.96288, 20.593683]}}, 81 | {"type": "Feature", "id": 76, "properties": {"name": "Indonesia"}, "geometry": {"type": "Point", "coordinates": [113.9213, -0.7893]}}, 82 | {"type": "Feature", "id": 77, "properties": {"name": "Iran"}, "geometry": {"type": "Point", "coordinates": [53.688046, 32.42791]}}, 83 | {"type": "Feature", "id": 78, "properties": {"name": "Iraq"}, "geometry": {"type": "Point", "coordinates": [43.67929, 33.22319]}}, 84 | {"type": "Feature", "id": 79, "properties": {"name": "Ireland"}, "geometry": {"type": "Point", "coordinates": [-7.6921, 53.1424]}}, 85 | {"type": "Feature", "id": 80, "properties": {"name": "Israel"}, "geometry": {"type": "Point", "coordinates": [34.851612, 31.046051]}}, 86 | {"type": "Feature", "id": 81, "properties": {"name": "Italy"}, "geometry": {"type": "Point", "coordinates": [12.56738, 41.87194]}}, 87 | {"type": "Feature", "id": 82, "properties": {"name": "Jamaica"}, "geometry": {"type": "Point", "coordinates": [-77.2975, 18.1096]}}, 88 | {"type": "Feature", "id": 83, "properties": {"name": "Japan"}, "geometry": {"type": "Point", "coordinates": [138.25293, 36.204823]}}, 89 | {"type": "Feature", "id": 84, "properties": {"name": "Jordan"}, "geometry": {"type": "Point", "coordinates": [36.51, 31.24]}}, 90 | {"type": "Feature", "id": 85, "properties": {"name": "Kazakhstan"}, "geometry": {"type": "Point", "coordinates": [66.9237, 48.0196]}}, 91 | {"type": "Feature", "id": 86, "properties": {"name": "Kenya"}, "geometry": {"type": "Point", "coordinates": [37.9062, -0.0236]}}, 92 | {"type": "Feature", "id": 87, "properties": {"name": "Korea South"}, "geometry": {"type": "Point", "coordinates": [127.76692, 35.907757]}}, 93 | {"type": "Feature", "id": 88, "properties": {"name": "Kosovo"}, "geometry": {"type": "Point", "coordinates": [20.902977, 42.602634]}}, 94 | {"type": "Feature", "id": 89, "properties": {"name": "Kuwait"}, "geometry": {"type": "Point", "coordinates": [47.481766, 29.31166]}}, 95 | {"type": "Feature", "id": 90, "properties": {"name": "Kyrgyzstan"}, "geometry": {"type": "Point", "coordinates": [74.7661, 41.20438]}}, 96 | {"type": "Feature", "id": 91, "properties": {"name": "Laos"}, "geometry": {"type": "Point", "coordinates": [102.4955, 19.85627]}}, 97 | {"type": "Feature", "id": 92, "properties": {"name": "Latvia"}, "geometry": {"type": "Point", "coordinates": [24.6032, 56.8796]}}, 98 | {"type": "Feature", "id": 93, "properties": {"name": "Lebanon"}, "geometry": {"type": "Point", "coordinates": [35.8623, 33.8547]}}, 99 | {"type": "Feature", "id": 94, "properties": {"name": "Lesotho"}, "geometry": {"type": "Point", "coordinates": [28.2336, -29.61]}}, 100 | {"type": "Feature", "id": 95, "properties": {"name": "Liberia"}, "geometry": {"type": "Point", "coordinates": [-9.429499, 6.428055]}}, 101 | {"type": "Feature", "id": 96, "properties": {"name": "Libya"}, "geometry": {"type": "Point", "coordinates": [17.22833, 26.3351]}}, 102 | {"type": "Feature", "id": 97, "properties": {"name": "Liechtenstein"}, "geometry": {"type": "Point", "coordinates": [9.55, 47.14]}}, 103 | {"type": "Feature", "id": 98, "properties": {"name": "Lithuania"}, "geometry": {"type": "Point", "coordinates": [23.8813, 55.1694]}}, 104 | {"type": "Feature", "id": 99, "properties": {"name": "Luxembourg"}, "geometry": {"type": "Point", "coordinates": [6.1296, 49.8153]}}, 105 | {"type": "Feature", "id": 100, "properties": {"name": "Madagascar"}, "geometry": {"type": "Point", "coordinates": [46.869106, -18.766947]}}, 106 | {"type": "Feature", "id": 101, "properties": {"name": "Malawi"}, "geometry": {"type": "Point", "coordinates": [34.3015, -13.2543]}}, 107 | {"type": "Feature", "id": 102, "properties": {"name": "Malaysia"}, "geometry": {"type": "Point", "coordinates": [101.97577, 4.210484]}}, 108 | {"type": "Feature", "id": 103, "properties": {"name": "Maldives"}, "geometry": {"type": "Point", "coordinates": [73.2207, 3.2028]}}, 109 | {"type": "Feature", "id": 104, "properties": {"name": "Mali"}, "geometry": {"type": "Point", "coordinates": [-3.996166, 17.570692]}}, 110 | {"type": "Feature", "id": 105, "properties": {"name": "Malta"}, "geometry": {"type": "Point", "coordinates": [14.3754, 35.9375]}}, 111 | {"type": "Feature", "id": 106, "properties": {"name": "Mauritania"}, "geometry": {"type": "Point", "coordinates": [-10.9408, 21.0079]}}, 112 | {"type": "Feature", "id": 107, "properties": {"name": "Mauritius"}, "geometry": {"type": "Point", "coordinates": [57.55215, -20.348404]}}, 113 | {"type": "Feature", "id": 108, "properties": {"name": "Mexico"}, "geometry": {"type": "Point", "coordinates": [-102.5528, 23.6345]}}, 114 | {"type": "Feature", "id": 109, "properties": {"name": "Moldova"}, "geometry": {"type": "Point", "coordinates": [28.3699, 47.4116]}}, 115 | {"type": "Feature", "id": 110, "properties": {"name": "Monaco"}, "geometry": {"type": "Point", "coordinates": [7.4167, 43.7333]}}, 116 | {"type": "Feature", "id": 111, "properties": {"name": "Mongolia"}, "geometry": {"type": "Point", "coordinates": [103.8467, 46.8625]}}, 117 | {"type": "Feature", "id": 112, "properties": {"name": "Montenegro"}, "geometry": {"type": "Point", "coordinates": [19.37439, 42.70868]}}, 118 | {"type": "Feature", "id": 113, "properties": {"name": "Morocco"}, "geometry": {"type": "Point", "coordinates": [-7.0926, 31.7917]}}, 119 | {"type": "Feature", "id": 114, "properties": {"name": "Mozambique"}, "geometry": {"type": "Point", "coordinates": [35.529564, -18.665695]}}, 120 | {"type": "Feature", "id": 115, "properties": {"name": "Namibia"}, "geometry": {"type": "Point", "coordinates": [18.4904, -22.9576]}}, 121 | {"type": "Feature", "id": 116, "properties": {"name": "Nepal"}, "geometry": {"type": "Point", "coordinates": [84.25, 28.1667]}}, 122 | {"type": "Feature", "id": 117, "properties": {"name": "Netherlands"}, "geometry": {"type": "Point", "coordinates": [5.2913, 52.1326]}}, 123 | {"type": "Feature", "id": 118, "properties": {"name": "New Zealand"}, "geometry": {"type": "Point", "coordinates": [174.886, -40.9006]}}, 124 | {"type": "Feature", "id": 119, "properties": {"name": "Nicaragua"}, "geometry": {"type": "Point", "coordinates": [-85.20723, 12.865416]}}, 125 | {"type": "Feature", "id": 120, "properties": {"name": "Niger"}, "geometry": {"type": "Point", "coordinates": [8.081666, 17.607788]}}, 126 | {"type": "Feature", "id": 121, "properties": {"name": "Nigeria"}, "geometry": {"type": "Point", "coordinates": [8.6753, 9.082]}}, 127 | {"type": "Feature", "id": 122, "properties": {"name": "North Macedonia"}, "geometry": {"type": "Point", "coordinates": [21.7453, 41.6086]}}, 128 | {"type": "Feature", "id": 123, "properties": {"name": "Norway"}, "geometry": {"type": "Point", "coordinates": [8.4689, 60.472]}}, 129 | {"type": "Feature", "id": 124, "properties": {"name": "Oman"}, "geometry": {"type": "Point", "coordinates": [55.923256, 21.512583]}}, 130 | {"type": "Feature", "id": 125, "properties": {"name": "Pakistan"}, "geometry": {"type": "Point", "coordinates": [69.3451, 30.3753]}}, 131 | {"type": "Feature", "id": 126, "properties": {"name": "Panama"}, "geometry": {"type": "Point", "coordinates": [-80.7821, 8.538]}}, 132 | {"type": "Feature", "id": 127, "properties": {"name": "Papua New Guinea"}, "geometry": {"type": "Point", "coordinates": [143.95555, -6.314993]}}, 133 | {"type": "Feature", "id": 128, "properties": {"name": "Paraguay"}, "geometry": {"type": "Point", "coordinates": [-58.4438, -23.4425]}}, 134 | {"type": "Feature", "id": 129, "properties": {"name": "Peru"}, "geometry": {"type": "Point", "coordinates": [-75.0152, -9.19]}}, 135 | {"type": "Feature", "id": 130, "properties": {"name": "Philippines"}, "geometry": {"type": "Point", "coordinates": [121.77402, 12.879721]}}, 136 | {"type": "Feature", "id": 131, "properties": {"name": "Poland"}, "geometry": {"type": "Point", "coordinates": [19.1451, 51.9194]}}, 137 | {"type": "Feature", "id": 132, "properties": {"name": "Portugal"}, "geometry": {"type": "Point", "coordinates": [-8.2245, 39.3999]}}, 138 | {"type": "Feature", "id": 133, "properties": {"name": "Qatar"}, "geometry": {"type": "Point", "coordinates": [51.1839, 25.3548]}}, 139 | {"type": "Feature", "id": 134, "properties": {"name": "Romania"}, "geometry": {"type": "Point", "coordinates": [24.9668, 45.9432]}}, 140 | {"type": "Feature", "id": 135, "properties": {"name": "Russia"}, "geometry": {"type": "Point", "coordinates": [105.318756, 61.52401]}}, 141 | {"type": "Feature", "id": 136, "properties": {"name": "Rwanda"}, "geometry": {"type": "Point", "coordinates": [29.8739, -1.9403]}}, 142 | {"type": "Feature", "id": 137, "properties": {"name": "Saint Kitts and Nevis"}, "geometry": {"type": "Point", "coordinates": [-62.782997, 17.357822]}}, 143 | {"type": "Feature", "id": 138, "properties": {"name": "Saint Lucia"}, "geometry": {"type": "Point", "coordinates": [-60.9789, 13.9094]}}, 144 | {"type": "Feature", "id": 139, "properties": {"name": "Saint Vincent and the Grenadines"}, "geometry": {"type": "Point", "coordinates": [-61.2872, 12.9843]}}, 145 | {"type": "Feature", "id": 140, "properties": {"name": "San Marino"}, "geometry": {"type": "Point", "coordinates": [12.4578, 43.9424]}}, 146 | {"type": "Feature", "id": 141, "properties": {"name": "Sao Tome and Principe"}, "geometry": {"type": "Point", "coordinates": [6.6131, 0.1864]}}, 147 | {"type": "Feature", "id": 142, "properties": {"name": "Saudi Arabia"}, "geometry": {"type": "Point", "coordinates": [45.079163, 23.885942]}}, 148 | {"type": "Feature", "id": 143, "properties": {"name": "Senegal"}, "geometry": {"type": "Point", "coordinates": [-14.4524, 14.4974]}}, 149 | {"type": "Feature", "id": 144, "properties": {"name": "Serbia"}, "geometry": {"type": "Point", "coordinates": [21.0059, 44.0165]}}, 150 | {"type": "Feature", "id": 145, "properties": {"name": "Seychelles"}, "geometry": {"type": "Point", "coordinates": [55.492, -4.6796]}}, 151 | {"type": "Feature", "id": 146, "properties": {"name": "Sierra Leone"}, "geometry": {"type": "Point", "coordinates": [-11.779889, 8.460555]}}, 152 | {"type": "Feature", "id": 147, "properties": {"name": "Singapore"}, "geometry": {"type": "Point", "coordinates": [103.8333, 1.2833]}}, 153 | {"type": "Feature", "id": 148, "properties": {"name": "Slovakia"}, "geometry": {"type": "Point", "coordinates": [19.699, 48.669]}}, 154 | {"type": "Feature", "id": 149, "properties": {"name": "Slovenia"}, "geometry": {"type": "Point", "coordinates": [14.9955, 46.1512]}}, 155 | {"type": "Feature", "id": 150, "properties": {"name": "Somalia"}, "geometry": {"type": "Point", "coordinates": [46.199615, 5.152149]}}, 156 | {"type": "Feature", "id": 151, "properties": {"name": "South Africa"}, "geometry": {"type": "Point", "coordinates": [22.9375, -30.5595]}}, 157 | {"type": "Feature", "id": 152, "properties": {"name": "South Sudan"}, "geometry": {"type": "Point", "coordinates": [31.307, 6.877]}}, 158 | {"type": "Feature", "id": 153, "properties": {"name": "Spain"}, "geometry": {"type": "Point", "coordinates": [-3.74922, 40.46367]}}, 159 | {"type": "Feature", "id": 154, "properties": {"name": "Sri Lanka"}, "geometry": {"type": "Point", "coordinates": [80.7718, 7.873054]}}, 160 | {"type": "Feature", "id": 155, "properties": {"name": "Sudan"}, "geometry": {"type": "Point", "coordinates": [30.2176, 12.8628]}}, 161 | {"type": "Feature", "id": 156, "properties": {"name": "Suriname"}, "geometry": {"type": "Point", "coordinates": [-56.0278, 3.9193]}}, 162 | {"type": "Feature", "id": 157, "properties": {"name": "Sweden"}, "geometry": {"type": "Point", "coordinates": [18.643501, 60.128162]}}, 163 | {"type": "Feature", "id": 158, "properties": {"name": "Switzerland"}, "geometry": {"type": "Point", "coordinates": [8.2275, 46.8182]}}, 164 | {"type": "Feature", "id": 159, "properties": {"name": "Syria"}, "geometry": {"type": "Point", "coordinates": [38.996815, 34.802074]}}, 165 | {"type": "Feature", "id": 160, "properties": {"name": "Taiwan*"}, "geometry": {"type": "Point", "coordinates": [121.0, 23.7]}}, 166 | {"type": "Feature", "id": 161, "properties": {"name": "Tajikistan"}, "geometry": {"type": "Point", "coordinates": [71.2761, 38.861]}}, 167 | {"type": "Feature", "id": 162, "properties": {"name": "Tanzania"}, "geometry": {"type": "Point", "coordinates": [34.88882, -6.369028]}}, 168 | {"type": "Feature", "id": 163, "properties": {"name": "Thailand"}, "geometry": {"type": "Point", "coordinates": [100.99254, 15.870032]}}, 169 | {"type": "Feature", "id": 164, "properties": {"name": "Timor-Leste"}, "geometry": {"type": "Point", "coordinates": [125.72754, -8.874217]}}, 170 | {"type": "Feature", "id": 165, "properties": {"name": "Togo"}, "geometry": {"type": "Point", "coordinates": [0.8248, 8.6195]}}, 171 | {"type": "Feature", "id": 166, "properties": {"name": "Trinidad and Tobago"}, "geometry": {"type": "Point", "coordinates": [-61.2225, 10.6918]}}, 172 | {"type": "Feature", "id": 167, "properties": {"name": "Tunisia"}, "geometry": {"type": "Point", "coordinates": [9.537499, 33.886917]}}, 173 | {"type": "Feature", "id": 168, "properties": {"name": "Turkey"}, "geometry": {"type": "Point", "coordinates": [35.2433, 38.9637]}}, 174 | {"type": "Feature", "id": 169, "properties": {"name": "Uganda"}, "geometry": {"type": "Point", "coordinates": [32.290276, 1.373333]}}, 175 | {"type": "Feature", "id": 170, "properties": {"name": "Ukraine"}, "geometry": {"type": "Point", "coordinates": [31.1656, 48.3794]}}, 176 | {"type": "Feature", "id": 171, "properties": {"name": "United Arab Emirates"}, "geometry": {"type": "Point", "coordinates": [53.847816, 23.424076]}}, 177 | {"type": "Feature", "id": 172, "properties": {"name": "United Kingdom"}, "geometry": {"type": "Point", "coordinates": [-3.436, 55.3781]}}, 178 | {"type": "Feature", "id": 173, "properties": {"name": "Uruguay"}, "geometry": {"type": "Point", "coordinates": [-55.7658, -32.5228]}}, 179 | {"type": "Feature", "id": 174, "properties": {"name": "Uzbekistan"}, "geometry": {"type": "Point", "coordinates": [64.58526, 41.37749]}}, 180 | {"type": "Feature", "id": 175, "properties": {"name": "Venezuela"}, "geometry": {"type": "Point", "coordinates": [-66.5897, 6.4238]}}, 181 | {"type": "Feature", "id": 176, "properties": {"name": "Vietnam"}, "geometry": {"type": "Point", "coordinates": [108.2772, 14.058324]}}, 182 | {"type": "Feature", "id": 177, "properties": {"name": "West Bank and Gaza"}, "geometry": {"type": "Point", "coordinates": [35.2332, 31.9522]}}, 183 | {"type": "Feature", "id": 178, "properties": {"name": "Western Sahara"}, "geometry": {"type": "Point", "coordinates": [-12.8858, 24.2155]}}, 184 | {"type": "Feature", "id": 179, "properties": {"name": "Yemen"}, "geometry": {"type": "Point", "coordinates": [48.516388, 15.552727]}}, 185 | {"type": "Feature", "id": 180, "properties": {"name": "Zambia"}, "geometry": {"type": "Point", "coordinates": [27.849333, -13.133897]}}, 186 | {"type": "Feature", "id": 181, "properties": {"name": "Zimbabwe"}, "geometry": {"type": "Point", "coordinates": [29.154858, -19.015438]}}, 187 | {"type": "Feature", "id": 182, "properties": {"name": "Australia"}, "geometry": {"type": "Point", "coordinates": [133.0, -25.0]}}, 188 | {"type": "Feature", "id": 183, "properties": {"name": "Canada"}, "geometry": {"type": "Point", "coordinates": [-95.0, 60.0]}}, 189 | {"type": "Feature", "id": 184, "properties": {"name": "China"}, "geometry": {"type": "Point", "coordinates": [114.3055, 30.5928]}}, 190 | {"type": "Feature", "id": 185, "properties": {"name": "US"}, "geometry": {"type": "Point", "coordinates": [-100.0, 40.0]}} 191 | ] 192 | } 193 | ) -------------------------------------------------------------------------------- /docker/.env: -------------------------------------------------------------------------------- 1 | POSTGRES_CONTAINER=postgres 2 | POSTGRES_HOST=localhost 3 | POSTGRES_PORT=5433 4 | POSTGRES_USER=postgres 5 | POSTGRES_PASSWORD=password 6 | POSTGRES_DBNAME=grafana 7 | 8 | INFLUX_CONTAINER=influx 9 | INFLUX_HOST=localhost 10 | INFLUX_PORT=8086 11 | INFLUX_USER=influx 12 | INFLUX_PASSWORD=password 13 | INFLUX_DBNAME=grafana 14 | 15 | GRAFANA_CONTAINER=grafana 16 | GRAFANA_HOST=localhost 17 | GRAFANA_PORT=3000 18 | GRAFANA_ADMIN_USER=admin 19 | GRAFANA_ADMIN_PASSWORD=password -------------------------------------------------------------------------------- /docker/Makefile: -------------------------------------------------------------------------------- 1 | # Usage: make COMMAND 2 | # 3 | # Commands 4 | # help Show help message. 5 | # postgres-console Login to postgres interactive terminal. 6 | # postgres-create-db Create postgres database. 7 | # postgres-init-schema Initialize postgres schema. 8 | # postgres-copy-data Write data to postgres tables. 9 | # postgres-delete-tables Delete postgres tables. 10 | # influx-console Login to InfluxDB CLI. 11 | # influx-copy-data Write data to InfluxDB. 12 | # build Build application. 13 | # service Start up application. 14 | # data-server Serve data directory. 15 | # stop Stop running application. 16 | # clean Stop app and remove containers. 17 | # clean-all Remove app, remove containers and volumes. 18 | # 19 | include .env 20 | 21 | PSQL = PGPASSWORD=$(POSTGRES_PASSWORD) \ 22 | docker exec -i $(POSTGRES_CONTAINER) \ 23 | psql -h $(POSTGRES_HOST) -p 5432 -U $(POSTGRES_USER) 24 | 25 | help: 26 | @head -18 Makefile 27 | 28 | postgres-console: 29 | PGPASSWORD=$(POSTGRES_PASSWORD) \ 30 | docker exec -it $(POSTGRES_CONTAINER) \ 31 | psql -h $(POSTGRES_HOST) -p 5432 -U $(POSTGRES_USER) -d $(POSTGRES_DBNAME) 32 | 33 | postgres-create-db: 34 | $(PSQL) -c "CREATE DATABASE $(POSTGRES_DBNAME)" 35 | 36 | postgres-init-schema: 37 | cat ../scripts/schema.sql | $(PSQL) -d $(POSTGRES_DBNAME) 38 | 39 | postgres-copy-data: 40 | cat ../data/reference.csv | $(PSQL) -d $(POSTGRES_DBNAME) \ 41 | -c "COPY covid.countries_ref FROM STDIN CSV HEADER" 42 | 43 | cat ../data/us_confirmed.csv | $(PSQL) -d $(POSTGRES_DBNAME) \ 44 | -c "COPY covid.us_confirmed FROM STDIN CSV HEADER" 45 | 46 | cat ../data/countries-aggregated.csv | $(PSQL) -d $(POSTGRES_DBNAME) \ 47 | -c "COPY covid.countries_aggregated FROM STDIN CSV HEADER" 48 | 49 | postgres-delete-tables: 50 | cat ../scripts/delete_tables.sql | $(PSQL) -d $(POSTGRES_DBNAME) 51 | 52 | influx-console: 53 | docker exec -it $(INFLUX_CONTAINER) influx -database '$(INFLUX_DBNAME)' 54 | 55 | influx-copy-data: 56 | python ../scripts/write_points.py 57 | 58 | build: 59 | docker-compose up -d 60 | make postgres-create-database 61 | make postgres-init-schema 62 | 63 | service: 64 | docker-compose up -d 65 | 66 | data-server: 67 | python -m http.server --directory ../data/ 68 | 69 | stop: 70 | docker-compose down 71 | 72 | clean: 73 | make stop 74 | docker system prune -af 75 | 76 | clean-all: 77 | make clean 78 | docker volume prune 79 | -------------------------------------------------------------------------------- /docker/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | influx: 5 | image: influxdb:latest 6 | container_name: ${INFLUX_CONTAINER} 7 | restart: always 8 | ports: 9 | - "${INFLUX_PORT}:8086" 10 | volumes: 11 | - influx:/var/lib/influxdb 12 | environment: 13 | - INFLUXDB_HOST=${INFLUX_HOST} 14 | - INFLUXDB_DB=${INFLUX_DBNAME} 15 | - INFLUXDB_ADMIN_USER=${INFLUX_USER} 16 | - INFLUXDB_ADMIN_PASSWORD=${INFLUX_PASSWORD} 17 | networks: 18 | - influx 19 | 20 | postgres: 21 | image: library/postgres:latest 22 | container_name: ${POSTGRES_CONTAINER} 23 | restart: always 24 | ports: 25 | - "${POSTGRES_PORT}:5432" 26 | environment: 27 | POSTGRES_HOST: ${POSTGRES_HOST} 28 | POSTGRES_USER: ${POSTGRES_USER} 29 | POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} 30 | volumes: 31 | - postgres:/var/lib/postgresql/data 32 | networks: 33 | - postgres 34 | 35 | grafana: 36 | image: grafana/grafana:latest 37 | container_name: ${GRAFANA_CONTAINER} 38 | restart: always 39 | ports: 40 | - "${GRAFANA_PORT}:3000" 41 | environment: 42 | GF_SECURITY_ADMIN_USER: ${GRAFANA_ADMIN_USER} 43 | GF_SECURITY_ADMIN_PASSWORD: ${GRAFANA_ADMIN_PASSWORD} 44 | GF_SERVER_ROOT_URL: http://${GRAFANA_HOST}:${GRAFANA_PORT}/grafana/ 45 | GF_SERVER_SERVE_FROM_SUB_PATH: "true" 46 | GF_USERS_DEFAULT_THEME: "dark" 47 | GF_INSTALL_PLUGINS: grafana-worldmap-panel, citilogics-geoloop-panel 48 | volumes: 49 | - grafana:/var/lib/grafana:rw 50 | depends_on: 51 | - influx 52 | - postgres 53 | networks: 54 | - influx 55 | - postgres 56 | 57 | networks: 58 | influx: 59 | driver: bridge 60 | postgres: 61 | driver: bridge 62 | 63 | volumes: 64 | influx: 65 | postgres: 66 | grafana: 67 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = source 9 | BUILDDIR = build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=source 11 | set BUILDDIR=build 12 | 13 | if "%1" == "" goto help 14 | 15 | %SPHINXBUILD% >NUL 2>NUL 16 | if errorlevel 9009 ( 17 | echo. 18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 19 | echo.installed, then set the SPHINXBUILD environment variable to point 20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 21 | echo.may add the Sphinx directory to PATH. 22 | echo. 23 | echo.If you don't have Sphinx installed, grab it from 24 | echo.http://sphinx-doc.org/ 25 | exit /b 1 26 | ) 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | sphinx 2 | sphinx_rtd_theme -------------------------------------------------------------------------------- /docs/source/conf.py: -------------------------------------------------------------------------------- 1 | #import os 2 | #import sys 3 | 4 | #sys.path.insert(0, os.path.abspath('../..')) 5 | 6 | #import sphinx_rtd_theme 7 | 8 | project = 'docker-postgres-influxdb-grafana' 9 | copyright = '2020, viktorsapozhok' 10 | author = 'viktorsapozhok' 11 | user = 'viktorsapozhok' 12 | 13 | # Add any Sphinx extension module names here, as strings. They can be 14 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 15 | # ones. 16 | extensions = [ 17 | 'sphinx.ext.napoleon', 18 | 'sphinx.ext.viewcode', 19 | ] 20 | 21 | # The suffix of source filenames. 22 | source_suffix = '.rst' 23 | 24 | # The master toctree document. 25 | master_doc = 'index' 26 | 27 | # The name of the Pygments (syntax highlighting) style to use. 28 | pygments_style = 'sphinx' 29 | 30 | language = "en" 31 | 32 | # The theme to use for HTML and HTML Help pages. See the documentation for 33 | # a list of builtin themes. 34 | html_theme = 'sphinx_rtd_theme' 35 | 36 | html_context = { 37 | 'display_github': True, 38 | 'github_user': user, 39 | 'github_repo': project, 40 | 'github_version': 'master', 41 | 'conf_py_path': '/docs/source/', 42 | } 43 | 44 | # Add any paths that contain custom static files (such as style sheets) here, 45 | # relative to this directory. They are copied after the builtin static files, 46 | # so a file named "default.css" will overwrite the builtin "default.css". 47 | html_static_path = [] 48 | -------------------------------------------------------------------------------- /docs/source/images/dashboard.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/viktorsapozhok/docker-postgres-influxdb-grafana/0d106dd0cf46a775d542d0990f6524f1366ff199/docs/source/images/dashboard.gif -------------------------------------------------------------------------------- /docs/source/images/geoloop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/viktorsapozhok/docker-postgres-influxdb-grafana/0d106dd0cf46a775d542d0990f6524f1366ff199/docs/source/images/geoloop.png -------------------------------------------------------------------------------- /docs/source/images/grafana_login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/viktorsapozhok/docker-postgres-influxdb-grafana/0d106dd0cf46a775d542d0990f6524f1366ff199/docs/source/images/grafana_login.png -------------------------------------------------------------------------------- /docs/source/images/influx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/viktorsapozhok/docker-postgres-influxdb-grafana/0d106dd0cf46a775d542d0990f6524f1366ff199/docs/source/images/influx.png -------------------------------------------------------------------------------- /docs/source/images/postgres.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/viktorsapozhok/docker-postgres-influxdb-grafana/0d106dd0cf46a775d542d0990f6524f1366ff199/docs/source/images/postgres.png -------------------------------------------------------------------------------- /docs/source/images/preview.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/viktorsapozhok/docker-postgres-influxdb-grafana/0d106dd0cf46a775d542d0990f6524f1366ff199/docs/source/images/preview.gif -------------------------------------------------------------------------------- /docs/source/images/us.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/viktorsapozhok/docker-postgres-influxdb-grafana/0d106dd0cf46a775d542d0990f6524f1366ff199/docs/source/images/us.png -------------------------------------------------------------------------------- /docs/source/images/worldmap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/viktorsapozhok/docker-postgres-influxdb-grafana/0d106dd0cf46a775d542d0990f6524f1366ff199/docs/source/images/worldmap.png -------------------------------------------------------------------------------- /docs/source/index.rst: -------------------------------------------------------------------------------- 1 | Building animated maps with Grafana 2 | =================================== 3 | 4 | .. meta:: 5 | :description lang=en: 6 | How to install Grafana, PostgreSQL and InfluxDB with docker-compose, 7 | create map overlays with Worldmap Panel, and build animated maps 8 | using GeoLoop Panel plugin. 9 | :keywords: postgresql, influxdb, grafana, geoloop panel, worldmap panel, animated map, docker-compose 10 | :google-site-verification: wkNtHJvBxvApbXh93yaO6fiT-TMEjcK6IFExr0vQ-Ng 11 | 12 | This tutorial provides a quick guide of how to install a dashboard environment 13 | from Grafana, Postgres and InfluxDB with docker-compose, create map overlays with Worldmap Panel plugin, and 14 | build animated maps using GeoLoop Panel plugin. 15 | 16 | To illustrate the process of building the animated maps with GeoLoop, 17 | we will use time series data tracking the number of people affected by COVID-19 worldwide, 18 | including confirmed cases of Coronavirus infection, the number of people died while 19 | sick with Coronavirus, and the number of people recovered from it. 20 | 21 | The data is borrowed from `“covid-19” dataset `__ 22 | and stored as csv files in `data `__ directory. 23 | 24 | .. image:: /images/dashboard.gif 25 | :align: center 26 | :alt: Grafana dashboard example used Worldmap and GeoLoop Panel plugins to visualize "covid-19" data 27 | 28 | Quick Start 29 | ----------- 30 | 31 | To start the application, install 32 | `docker-compose `__ 33 | on the host, clone this repo and run docker-compose from the 34 | `docker `__ 35 | directory. 36 | 37 | .. code-block:: bash 38 | 39 | $ cd docker && docker-compose up -d 40 | 41 | Starting influx ... done 42 | Starting postgres ... done 43 | Starting grafana ... done 44 | 45 | $ docker ps 46 | 47 | CONTAINER ID IMAGE PORTS NAMES 48 | e00c0bd0d4a5 grafana/grafana:latest 0.0.0.0:3000->3000/tcp grafana 49 | 2b33999d97b3 influxdb:latest 0.0.0.0:8086->8086/tcp influx 50 | c4453cac69eb postgres:latest 0.0.0.0:5433->5432/tcp postgres 51 | 52 | Three containers have been created and started. For the app services we expose the following ports: 53 | 3000 (grafana), 8086 (influx), 5432 (postgres). Note that we use ``HOST:CONTAINER`` mapping when specifying 54 | postgres ports in docker-compose file. Inside a docker container, postgres is running on port ``5432``, 55 | whereas the publicly exposed port outside the container is ``5433``. 56 | 57 | .. code-block:: bash 58 | 59 | version: '3' 60 | 61 | services: 62 | postgres: 63 | ports: 64 | - "5433:5432" 65 | 66 | Before we login to Grafana UI, we need to create PostgreSQL database. Note that we have already 67 | created InfluxDB database specifying ``INFLUXDB_DB`` environment variable in docker-compose file. 68 | 69 | To create postgres database we use 70 | `psql `__, postgres terminal, inside the docker container. See 71 | `Makefile `__ 72 | for more details. 73 | 74 | .. code-block:: bash 75 | 76 | $ make -C docker/ postgres-create-db 77 | $ make -C docker/ postgres-init-schema 78 | 79 | Now we can login to the Grafana web UI in browser (http://localhost:3000/grafana/) with the login ``admin`` and 80 | password ``password`` and initialize data sources. 81 | 82 | .. image:: /images/grafana_login.png 83 | :align: center 84 | :alt: Grafana login 85 | 86 | Data Sources 87 | ------------ 88 | 89 | Before we create a dashboard, we need to add InfluxDB and PostgreSQL data sources. Follow 90 | `this guide `__ 91 | to find out how to do this. 92 | 93 | Here is the configuration parameters we use to add InfluxDB data source. 94 | 95 | .. image:: /images/influx.png 96 | :align: center 97 | :alt: Configuration parameters used to add InfluxDB data source 98 | 99 | And this is the configuration parameters we use to add PostgreSQL data source. 100 | 101 | .. image:: /images/postgres.png 102 | :align: center 103 | :alt: Configuration parameters used to add PostgreSQL data source 104 | 105 | The valid password for both data sources is ``password``. You can change the credentials in 106 | `docker/.env `__ 107 | file before starting the service via ``docker-compose up``. 108 | 109 | Populating the Database 110 | ----------------------- 111 | 112 | To illustrate the process of building the animated map with GeoLoop Panel plugin, we will use time series data 113 | tracking the number of people affected by COVID-19 worldwide, including confirmed cases of Coronavirus infection, 114 | the number of people died while sick with Coronavirus, the number of people recovered from it. 115 | 116 | We borrowed data from `"covid-19" dataset `__ 117 | and store it as csv files in `data `__ directory: 118 | 119 | * `countries-aggregated.csv `__ - cumulative cases (confirmed, recovered, deaths) across the globe. 120 | * `us_confirmed.csv `__ - cumulative confirmed cases for US regions. 121 | * `reference.csv `__ - regions metadata, location, names. 122 | 123 | For writing this data to postgres tables we use ``COPY FROM`` command available via postgres console. 124 | See `Makefile `__ 125 | for more details. 126 | 127 | .. code-block:: bash 128 | 129 | $ make -C docker/ postgres-copy-data 130 | 131 | After we have written data to the tables, we can login to terminal and view schema contents. 132 | 133 | .. code-block:: bash 134 | 135 | $ make -C docker/ postgres-console 136 | 137 | psql (12.3 (Debian 12.3-1.pgdg100+1)) 138 | Type "help" for help. 139 | 140 | grafana=# \dt+ covid.* 141 | List of relations 142 | Schema | Name | Type | Owner | Size | Description 143 | --------+----------------------+-------+----------+---------+------------- 144 | covid | countries_aggregated | table | postgres | 1936 kB | 145 | covid | countries_ref | table | postgres | 496 kB | 146 | covid | us_confirmed | table | postgres | 74 MB | 147 | (3 rows) 148 | 149 | Now we calculate logarithm of the number of active cases and write it to InfluxDB database (measurement "covid"). 150 | We can also login to influx database from console and view the database contents. 151 | 152 | .. code-block:: bash 153 | 154 | $ make -C docker/ influx-console 155 | 156 | Connected to http://localhost:8086 version 1.8.1 157 | InfluxDB shell version: 1.8.1 158 | 159 | > SHOW MEASUREMENTS 160 | name: measurements 161 | name 162 | ---- 163 | covid 164 | 165 | > SHOW SERIES FROM covid LIMIT 5 166 | key 167 | --- 168 | covid,Country=Afghanistan 169 | covid,Country=Albania 170 | covid,Country=Algeria 171 | covid,Country=Andorra 172 | covid,Country=Angola 173 | 174 | Worldmap Panel 175 | -------------- 176 | 177 | Let's visualize the number of confirmed cases across the US regions using Worldmap panel. 178 | This panel is a tile map that can be overlaid with circles representing data points from a query. 179 | It needs two sources of data: a location (latitude and longitude) and data that has link to a location. 180 | 181 | The screenshot below shows query and configuration settings we used. 182 | 183 | .. image:: /images/worldmap.png 184 | :align: center 185 | :alt: Configuring Worldmap Panel 186 | 187 | And as the result we obtain the following map. 188 | 189 | .. image:: /images/us.png 190 | :align: center 191 | :alt: Worldmap Panel example 192 | 193 | See Worldmap Panel plugin `documentation `__ 194 | for more details. 195 | 196 | GeoLoop Panel 197 | ------------- 198 | 199 | Now everything is ready to configure the GeoLoop panel and visualize Covid-19 growth rates. 200 | Following `this tutorial `__, 201 | we create a `GeoJSON `__ 202 | with countries coordinates and wrap it up in a callback: 203 | 204 | .. code-block:: bash 205 | 206 | geo({ "type": "FeatureCollection", ... }); 207 | 208 | To access geojson from grafana, we need to put it on a server somewhere. In this tutorial, 209 | we will confine ourselves to serving the local directory where geojson is stored 210 | (however, this approach is not recommended for production). 211 | 212 | .. code-block:: bash 213 | 214 | $ make -C docker/ data-server 215 | 216 | The GeoJSON URL: ``http://0.0.0.0:8000/countries.geojson`` 217 | 218 | A further step is to obtain a free `MapBox API Key `__, 219 | the only thing is you need to create a mapbox account. 220 | 221 | Here is the panel configuration settings. 222 | 223 | .. image:: /images/geoloop.png 224 | :align: center 225 | :alt: Configuring GeoLoop Panel 226 | 227 | And that's how the panel looks like. 228 | 229 | .. image:: /images/preview.gif 230 | :align: center 231 | :alt: GeoLoop Panel 232 | 233 | -------------------------------------------------------------------------------- /scripts/delete_tables.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE covid.countries_aggregated; 2 | DROP TABLE covid.countries_ref; 3 | DROP TABLE covid.us_confirmed; -------------------------------------------------------------------------------- /scripts/schema.sql: -------------------------------------------------------------------------------- 1 | CREATE SCHEMA IF NOT EXISTS covid; 2 | 3 | CREATE TABLE covid.countries_ref( 4 | uid INTEGER NOT NULL PRIMARY key, 5 | iso2 VARCHAR(2), 6 | iso3 VARCHAR(3), 7 | code3 INTEGER, 8 | fips INTEGER, 9 | admin2 VARCHAR(41), 10 | province_state VARCHAR(40), 11 | country_region VARCHAR(32), 12 | lat NUMERIC(11,8), 13 | long_ NUMERIC(13,9), 14 | combined_key VARCHAR(55), 15 | population INTEGER 16 | ); 17 | 18 | CREATE TABLE covid.countries_aggregated( 19 | date DATE NOT NULL, 20 | country VARCHAR(32) NOT NULL, 21 | confirmed INTEGER, 22 | recovered INTEGER, 23 | deaths INTEGER, 24 | PRIMARY KEY(date, country) 25 | ); 26 | 27 | CREATE TABLE covid.us_confirmed( 28 | uid INTEGER NOT NULL, 29 | iso2 VARCHAR(2), 30 | iso3 VARCHAR(3), 31 | code3 INTEGER, 32 | fips NUMERIC(7,1), 33 | admin2 VARCHAR(41), 34 | lat NUMERIC(19,15), 35 | combined_key VARCHAR(55), 36 | date DATE NOT NULL, 37 | confirmed INTEGER, 38 | long_ NUMERIC(18,14), 39 | country_region VARCHAR(32), 40 | province_state VARCHAR(40), 41 | PRIMARY KEY (uid, date) 42 | ); -------------------------------------------------------------------------------- /scripts/write_points.py: -------------------------------------------------------------------------------- 1 | from configparser import ConfigParser 2 | import os 3 | 4 | from influxdb import DataFrameClient 5 | import numpy as np 6 | import pandas as pd 7 | 8 | root_dir = os.path.dirname(os.path.abspath(os.path.join(__file__, '..'))) 9 | 10 | # get connection parameters 11 | config = ConfigParser() 12 | with open(os.path.join(root_dir, 'docker', '.env')) as file: 13 | config.read_string('[top]\n' + file.read()) 14 | config = {s: dict(config.items(s)) for s in config.sections()}['top'] 15 | 16 | # init influx client 17 | client = DataFrameClient( 18 | host=config['influx_host'], port=config['influx_port'], 19 | username=config['influx_user'], password=config['influx_password'], 20 | database=config['influx_dbname']) 21 | 22 | # read data 23 | df = pd.read_csv(os.path.join(root_dir, 'data', 'countries-aggregated.csv')) 24 | df['date'] = pd.to_datetime(df['Date'], format='%Y-%m-%d', utc=True) 25 | df['active'] = df['Confirmed'] - df['Recovered'] - df['Deaths'] 26 | df['active_log'] = np.log(1 + 0.0001 * df['active']) 27 | 28 | # import to influx 29 | client.write_points( 30 | dataframe=df.set_index('date'), measurement='covid', 31 | tag_columns=['Country'], field_columns=['active_log'], protocol='line') 32 | --------------------------------------------------------------------------------