Live » History » Version 11
Philippe May, 06/05/2019 17:46
1 | 1 | Philippe May | h1. Live |
---|---|---|---|
2 | 1 | Philippe May | |
3 | 1 | Philippe May | While the primary intention is use a database for all layers, Gisaf has the capability to display layers directly from GeoPandas GeodataFrames. |
4 | 1 | Philippe May | |
5 | 1 | Philippe May | In this case, they can also be updated dynamically, adding animation capabilities to the maps. |
6 | 1 | Philippe May | |
7 | 1 | Philippe May | This can be used for, eg: |
8 | 1 | Philippe May | |
9 | 1 | Philippe May | * displaying text (eg. temperatures, well levels) |
10 | 1 | Philippe May | * moving elements |
11 | 1 | Philippe May | * results of computations and analysis... |
12 | 1 | Philippe May | |
13 | 1 | Philippe May | h2. Using live directly from a Python script on the Gisaf server |
14 | 1 | Philippe May | |
15 | 1 | Philippe May | Eg: |
16 | 1 | Philippe May | |
17 | 1 | Philippe May | <pre><code class="python"> |
18 | 1 | Philippe May | #!/usr/bin/env python |
19 | 1 | Philippe May | from asyncio import run |
20 | 1 | Philippe May | |
21 | 1 | Philippe May | import geopandas as gpd |
22 | 1 | Philippe May | |
23 | 1 | Philippe May | from shapely.geometry import Point |
24 | 1 | Philippe May | |
25 | 1 | Philippe May | from gisaf.live import live_server |
26 | 1 | Philippe May | |
27 | 1 | Philippe May | async def run(gs): |
28 | 1 | Philippe May | gdf = gpd.GeoDataFrame( |
29 | 1 | Philippe May | data={ |
30 | 1 | Philippe May | 'geometry': [ |
31 | 1 | Philippe May | Point(12.01, 79.81) |
32 | 1 | Philippe May | ] |
33 | 1 | Philippe May | }, |
34 | 1 | Philippe May | crs='epsg:4326' |
35 | 1 | Philippe May | ) |
36 | 1 | Philippe May | |
37 | 1 | Philippe May | await live_server.publish_gdf('FooLayer', gdf) |
38 | 1 | Philippe May | |
39 | 1 | Philippe May | |
40 | 1 | Philippe May | async def main(): |
41 | 1 | Philippe May | await live_server.create_connections() |
42 | 1 | Philippe May | await run(gs) |
43 | 1 | Philippe May | |
44 | 1 | Philippe May | if __name__ == '__main__': |
45 | 1 | Philippe May | run(main()) |
46 | 1 | Philippe May | </code></pre> |
47 | 2 | Philippe May | |
48 | 2 | Philippe May | Explanations: |
49 | 2 | Philippe May | |
50 | 2 | Philippe May | 1. Initialize the connection with @live_server.create_connections()@. |
51 | 2 | Philippe May | |
52 | 2 | Philippe May | 2. Publish a geo dataframe with @live_server.publish_gdf('name of the layer', gdf)@ |
53 | 2 | Philippe May | |
54 | 8 | Philippe May | |
55 | 9 | Philippe May | This mode of operation is well adapted for live updates, when the script can be controlled by @systemd@ or similar OS service control tool. |
56 | 8 | Philippe May | |
57 | 2 | Philippe May | h2. From Jupyter notebooks |
58 | 1 | Philippe May | |
59 | 6 | Philippe May | Quite similarly to the case above, jupyter notebooks (running on a different machine) can be used to publish and control live layers through an HTTP POST API (at http:///api/live/my_channel_name), which is multipart (the layer definition in the first part, the data in the second). |
60 | 6 | Philippe May | |
61 | 6 | Philippe May | <pre> |
62 | 6 | Philippe May | from gisaf.ipynb_tools import Gisaf |
63 | 6 | Philippe May | gs = Gisaf() |
64 | 6 | Philippe May | async_run(gs.to_live_layer(my_channel_name, my_gdf)) |
65 | 6 | Philippe May | </pre> |
66 | 6 | Philippe May | |
67 | 7 | Philippe May | In other words, from the example above using directly Gisaf code, the only difference is the replacement of @await live_server.publish_gdf(...@ by @async_run(gs.to_live_layer(...@. |
68 | 7 | Philippe May | |
69 | 6 | Philippe May | See the examples in @Templates/gisaf_live_templates@ of the avgs jupyter notebooks. |
70 | 2 | Philippe May | |
71 | 11 | Philippe May | h2. Styling |
72 | 11 | Philippe May | |
73 | 11 | Philippe May | The live layers can be styled with Mapbox (see https://www.mapbox.com/mapbox-gl-js/style-spec#types-layout). |
74 | 11 | Philippe May | |
75 | 11 | Philippe May | Eg: |
76 | 11 | Philippe May | <pre><code class="python"> |
77 | 11 | Philippe May | await live_server.publish_gdf( |
78 | 11 | Philippe May | 'FooLayer', |
79 | 11 | Philippe May | gdf, |
80 | 11 | Philippe May | mapbox_layout={ |
81 | 11 | Philippe May | 'text-line-height': 1, |
82 | 11 | Philippe May | 'text-padding': 0, |
83 | 11 | Philippe May | 'text-allow-overlap': True, |
84 | 11 | Philippe May | 'text-field': '\ue005', |
85 | 11 | Philippe May | 'icon-optional': True, |
86 | 11 | Philippe May | 'text-font': ['GisafSymbols'], |
87 | 11 | Philippe May | 'text-size': 32, |
88 | 11 | Philippe May | }, |
89 | 11 | Philippe May | mapbox_paint={ |
90 | 11 | Philippe May | 'text-color': 'green' |
91 | 11 | Philippe May | } |
92 | 11 | Philippe May | ) |
93 | 11 | Philippe May | </code></pre> |
94 | 11 | Philippe May | |
95 | 11 | Philippe May | h3. Data driven styling |
96 | 11 | Philippe May | |
97 | 11 | Philippe May | One can leverage the power of Mapbox's data driven styling (using properties for each feature): |
98 | 11 | Philippe May | |
99 | 11 | Philippe May | 1. Define one or more columns in the dataframe with a property to be used for styling (eg. color, text, etc) |
100 | 11 | Philippe May | |
101 | 11 | Philippe May | 2. Pass the list of property columns to be given to mapbox with the @properties@ parameter of @publish_gdf@. |
102 | 11 | Philippe May | |
103 | 11 | Philippe May | Eg: |
104 | 11 | Philippe May | |
105 | 11 | Philippe May | <pre><code class="python"> |
106 | 11 | Philippe May | await live_server.publish_gdf( |
107 | 11 | Philippe May | gdf, |
108 | 11 | Philippe May | 'Text', |
109 | 11 | Philippe May | mapbox_layout={ |
110 | 11 | Philippe May | "text-field": "{text}", |
111 | 11 | Philippe May | "text-font": ["Noto Sans Regular"], |
112 | 11 | Philippe May | "text-offset": [0, 0], |
113 | 11 | Philippe May | "text-anchor": "center", |
114 | 11 | Philippe May | "text-size": size, |
115 | 11 | Philippe May | 'text-rotate': angle, |
116 | 11 | Philippe May | }, |
117 | 11 | Philippe May | mapbox_paint={ |
118 | 11 | Philippe May | 'text-color': color |
119 | 11 | Philippe May | }, |
120 | 11 | Philippe May | properties=['text'] |
121 | 11 | Philippe May | )) |
122 | 11 | Philippe May | </code></pre> |
123 | 11 | Philippe May | |
124 | 2 | Philippe May | h2. Architecture |
125 | 2 | Philippe May | |
126 | 2 | Philippe May | Gisaf live layers use a redis data store for: |
127 | 2 | Philippe May | |
128 | 2 | Philippe May | 1. Storage of the live layers |
129 | 2 | Philippe May | |
130 | 2 | Philippe May | 2. Publish/subscribe for live updates. |
131 | 2 | Philippe May | |
132 | 2 | Philippe May | The live updates are sent through a websocket, initiated by the clients (web browsers). |
133 | 2 | Philippe May | |
134 | 1 | Philippe May | Moreover, Gisaf exposes an HTTP API for external control of the live layers, eg. by Jupyter notebooks running on another server. |
135 | 3 | Philippe May | |
136 | 10 | Philippe May | This mode of operation is well adapted for experimenting with GeoPandas and publishing the results directly in the context, with other layers coming from the database. |
137 | 10 | Philippe May | |
138 | 5 | Philippe May | p=. !Live_arch.png! |