├── .gitignore
├── README.md
├── TimedependentPolygonmapTBWidget
├── Readme.md
├── argos_maps.json
├── maputils.json
├── polyimagemap.js
├── pubsubGeoJSON.py
├── pubsubGeoJSONmulti.1.py
├── pubsubGeoJSONmulti.py
└── pubsubWind.py
├── js
├── PolygonMap.js
└── PolygonMap_orig.js
└── widgets
├── PolygonMap.js
├── TimeseriesPolygonImageMap.js
├── TimeseriesPolygonMap.js
└── newWidget.js
/.gitignore:
--------------------------------------------------------------------------------
1 | TimedependentPolygonmapTBWidget/.env/
2 | *.pyc
3 | *.pem
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Thingsboard-widgets
2 | Additional widgets for the Argos project
3 |
4 | ## Time dependent polygon map widget
5 | -------------------------------------
6 |
7 | This widget displays polygons in different time slices.
8 | For details see [here](https://github.com/argosp/argos/wiki/A-TB-widget-to-display-a-dispersion-model-results)
9 |
10 | See original [TbMapWidgetV2](https://github.com/thingsboard/thingsboard/blob/master/ui/src/app/widget/lib/map-widget2.js) and [image-map](https://github.com/thingsboard/thingsboard/blob/master/ui/src/app/widget/lib/image-map.js)
11 |
--------------------------------------------------------------------------------
/TimedependentPolygonmapTBWidget/Readme.md:
--------------------------------------------------------------------------------
1 | # Timedependent Polygon map TBWidget
2 | ------------------------------------
3 |
4 | A first version of the Timedependent Polygon map TBWidget.
5 |
6 | The widget assumes that the attribute of the device contain a single geoJSON.
7 |
8 | To setup the example:
9 |
10 | 1. Install anaconda3, paho package.
11 | 1. Create a device in Thingsboard and change its token to A1_TEST_TOKEN. (The string in the pubsubJSON.py, line 88).
12 | 2. Change the IP of the server in pubsubJSON.py to be that of the server.
13 | 3. Create a new dashboard and define an alias of a single device. Choose the device.
14 | 4. Add 3 attributes A,B and C (they do not exist until you have published with the pubsub code, so either publish a new
15 | few messages or click on the create new attribute and change the name to A,B or C.
16 |
17 | 5. The pubsubJSON.py publishes the subtraction of the polygons from each other to maintain the colors.
18 |
19 | Alternately to anaconda:
20 |
21 | ```
22 | cd TimedependentPolygonmapTBWidget
23 | python3 -m venv .env
24 | source .env/bin/activate
25 | pip install paho-mqtt numpy geopandas
26 | python -m pubsubGeoJSONmulti.py
27 | ```
28 |
--------------------------------------------------------------------------------
/TimedependentPolygonmapTBWidget/argos_maps.json:
--------------------------------------------------------------------------------
1 | {
2 | "widgetsBundle": {
3 | "alias": "eran",
4 | "title": "Argos-maps",
5 | "image": null
6 | },
7 | "widgetTypes": [
8 | {
9 | "alias": "number",
10 | "name": "TimeseriesPolygonMap",
11 | "descriptor": {
12 | "type": "latest",
13 | "sizeX": 7.5,
14 | "sizeY": 5.5,
15 | "resources": [
16 | {
17 | "url": ""
18 | }
19 | ],
20 | "templateHtml": "
\n\n\n",
21 | "templateCss": "#container {\n overflow: auto;\n}\n\n.tbDatasource-container {\n margin: 5px;\n padding: 8px;\n}\n\n.tbDatasource-title {\n font-size: 1.200rem;\n font-weight: 500;\n padding-bottom: 10px;\n}\n\n.tbDatasource-table {\n width: 100%;\n box-shadow: 0 0 10px #ccc;\n border-collapse: collapse;\n white-space: nowrap;\n font-size: 1.000rem;\n color: #757575;\n}\n\n.tbDatasource-table td {\n position: relative;\n border-top: 1px solid rgba(0, 0, 0, 0.12);\n border-bottom: 1px solid rgba(0, 0, 0, 0.12);\n padding: 0px 18px;\n box-sizing: border-box;\n}",
22 | "controllerScript": "self.onInit = function() {\n var map = $('#TimeseriesPolygonMap');\n self.ctx.map = new TbMapWidgetV2('openstreet-map',\n false, self.ctx, undefined, map);\n self.mapleaflet = self.ctx.map.map.map;\n window.TimeseriesPolygonMapSelf = self;\n //self.mapleaflet.panBy([200, 300]); \n self.mapleaflet.setView([32.7, 34.95], 9);\n // self.resizeMap();\n // console.log(self);\n}\n\nself.resizeMap = function() {\n self.mapleaflet._container.style.height = (self.ctx\n .height - 30) + \"px\";\n}\n\nself.clearPolygons = function() {\n if (!self.mapleaflet._layers) return;\n for (var i in self.mapleaflet._layers) {\n if (self.mapleaflet._layers[i]._path !==\n undefined) {\n try {\n self.mapleaflet.removeLayer(self.mapleaflet\n ._layers[i]);\n } catch (e) {\n console.log(\"problem with \" + e + self.mapleaflet\n ._layers[i]);\n }\n\n }\n }\n\n}\n\nfunction parseJson(json) {\n var value = String(json).replace(/'/g, '\\\"');\n return JSON.parse(value);\n}\nself.showPolygons = function(parsed, color) {\n try {\n geojsonFeature = parsed.features;\n } catch (err) {\n return;\n // geojsonFeature = null\n }\n var myStyle = {\n \"color\": color,\n \"opacity\": 0.8\n };\n try {\n L.geoJSON(geojsonFeature, {\n style: myStyle\n }).addTo(self.mapleaflet);\n } catch (e) {\n console.log(e);\n }\n\n};\n\nself.updateTimeRange = function(parsed) {\n var maxRange = 0;\n for (i = 0; i < parsed.length; i++) {\n // var prop = self.ctx.data[i].data[0][1];\n // console.log(prop);\n for (j = 0; j < parsed[i].length; j++) {\n if (parsed[i][j].index > maxRange) {\n maxRange = parsed[i][j].index;\n }\n }\n }\n $('#TimeFrame')[0].max = maxRange;\n};\n\nfunction findCurrFrame(frames, index) {\n if (frames instanceof Array) {\n for (var i = 0; i < frames.length; ++i) {\n if (parseInt(frames[i].index) === index) {\n return frames[i];\n }\n }\n }\n return undefined;\n}\n\nself.getCurrIndex = function() {\n return parseInt($('#TimeFrame')[0].value);\n}\nself.setCurrIndex = function(val) {\n // console.log('setCurrIndex', val);\n $('#TimeFrame')[0].value = val;\n self.onDataUpdated();\n}\nself.onDataUpdated = function() {\n var parsed = self.ctx.data.map(prop => parseJson(\n prop.data[0][1]));\n self.updateTimeRange(parsed);\n var index = self.getCurrIndex();\n self.clearPolygons();\n // console.log(self.ctx.data);\n var valueName = undefined;\n for (i = 0; i < self.ctx.data.length; i++) {\n var poly = findCurrFrame(parsed[i], index);\n if (!poly) continue;\n valueName = poly.name;\n var color = self.ctx.data[i].dataKey.color;\n self.showPolygons(poly.value, color);\n }\n $('#TimeFrameLabel')[0].value = valueName ?\n valueName : '';\n self.ctx.map.update();\n}\n\nself.setNext = function() {\n var maxRange = parseInt($('#TimeFrame')[0].max);\n if (maxRange >= 1) {\n self.setCurrIndex((self.getCurrIndex() + 1) % (\n maxRange + 1));\n } else {\n self.setCurrIndex(0);\n }\n}\n\nself.Play = function() {\n self.playing = !self.playing\n $('#PlayButton').text(self.playing ? 'Stop' :\n 'Play');\n clearInterval(self.playInterval);\n if (self.playing) {\n var delay = self.ctx.settings.playDelay || 500;\n self.playInterval = setInterval((function() {\n self.setNext()\n }).bind(this), delay);\n }\n}\n\nself.onResize = function() {\n self.ctx.map.resize();\n // self.resizeMap();\n}\n\nself.getSettingsSchema = function() {\n var tbScheme = TbMapWidgetV2.settingsSchema(\n 'openstreet-map');\n return tbScheme;\n}\nself.getDataKeySettingsSchema = function() {\n return TbMapWidgetV2.dataKeySettingsSchema(\n 'openstreet-map');\n}\nself.actionSources = function() {\n return TbMapWidgetV2.actionSources();\n}\nself.onDestroy = function() {}",
23 | "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"playDelay\": {\n \"title\": \"Play Delay\",\n \"type\": \"number\",\n \"default\": 500\n }\n }\n },\n \"form\": [\n \"playDelay\"\n ]\n}",
24 | "dataKeySettingsSchema": "{}\n",
25 | "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"TimeseriesPolygonMap\"}"
26 | }
27 | },
28 | {
29 | "alias": "polygonmap",
30 | "name": "PolygonMap",
31 | "descriptor": {
32 | "type": "latest",
33 | "sizeX": 7.5,
34 | "sizeY": 3,
35 | "resources": [],
36 | "templateHtml": "",
37 | "templateCss": "#container {\n overflow: auto;\n}\n\n.tbDatasource-container {\n margin: 5px;\n padding: 8px;\n}\n\n.tbDatasource-title {\n font-size: 1.200rem;\n font-weight: 500;\n padding-bottom: 10px;\n}\n\n.tbDatasource-table {\n width: 100%;\n box-shadow: 0 0 10px #ccc;\n border-collapse: collapse;\n white-space: nowrap;\n font-size: 1.000rem;\n color: #757575;\n}\n\n.tbDatasource-table td {\n position: relative;\n border-top: 1px solid rgba(0, 0, 0, 0.12);\n border-bottom: 1px solid rgba(0, 0, 0, 0.12);\n padding: 0px 18px;\n box-sizing: border-box;\n}",
38 | "controllerScript": "self.onInit = function() {\n self.ctx.map = new TbMapWidgetV2('openstreet-map', false, self.ctx);\n self.mapleaflet = self.ctx.map.map.map;\n //self.mapleaflet.panBy([200, 300]); \n self.mapleaflet.setView([32, 34.95], 9);\n}\n\nself.onDataUpdated = function() {\n \n for (var i in self.mapleaflet._layers) {\n if (self.mapleaflet._layers[i]._path !==\n undefined) {\n try {\n self.mapleaflet.removeLayer(self.mapleaflet\n ._layers[i]);\n } catch (e) {\n console.log(\"problem with \" + e + self.mapleaflet._layers[i]);\n }\n\n }\n }\n \n for (i = 0; i < self.ctx.data.length; i++) {\n var dataitem = self.ctx.data[i];\n var value = String(dataitem.data[0][1]).replace(/'/g, '\\\"');\n var parsed = JSON.parse(value);\n try {\n geojsonFeature = parsed.features;\n\n } catch (err) {\n geojsonFeature = null\n }\n var myStyle = {\n \"color\": dataitem.dataKey.color, \n \"opacity\": 0.8 \n };\n try {\n L.geoJSON(geojsonFeature, {\n style: myStyle\n }).addTo(self.mapleaflet);\n } catch (e) {\n console.log(e);\n }\n self.ctx.map.update(); \n \n }\n}\nself.onResize = function() {\n self.ctx.map.resize();\n}\nself.getSettingsSchema = function() {\n var tbScheme = TbMapWidgetV2.settingsSchema(\n 'openstreet-map');\n return tbScheme;\n}\nself.getDataKeySettingsSchema = function() {\n return TbMapWidgetV2.dataKeySettingsSchema('openstreet-map');\n}\nself.actionSources = function() {\n return TbMapWidgetV2.actionSources();\n}\nself.onDestroy = function() {}\n",
39 | "settingsSchema": "{}",
40 | "dataKeySettingsSchema": "{}\n",
41 | "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"PolygonMap\"}"
42 | }
43 | },
44 | {
45 | "alias": "image_map",
46 | "name": "TimeseriesPolygonImageMap",
47 | "descriptor": {
48 | "type": "latest",
49 | "sizeX": 9,
50 | "sizeY": 9,
51 | "resources": [
52 | {
53 | "url": ""
54 | }
55 | ],
56 | "templateHtml": "\n",
57 | "templateCss": ".leaflet-zoom-box {\n\tz-index: 9;\n}\n\n.leaflet-pane { z-index: 4; }\n\n.leaflet-tile-pane { z-index: 2; }\n.leaflet-overlay-pane { z-index: 4; }\n.leaflet-shadow-pane { z-index: 5; }\n.leaflet-marker-pane { z-index: 6; }\n.leaflet-tooltip-pane { z-index: 7; }\n.leaflet-popup-pane { z-index: 8; }\n\n.leaflet-map-pane canvas { z-index: 1; }\n.leaflet-map-pane svg { z-index: 2; }\n\n.leaflet-control {\n\tz-index: 9;\n}\n.leaflet-top,\n.leaflet-bottom {\n\tz-index: 11;\n}\n\n.tb-marker-label {\n border: none;\n background: none;\n box-shadow: none;\n}\n\n.tb-marker-label:before {\n border: none;\n background: none;\n}\n",
58 | "controllerScript": "self.onInit = function () {\n var elemap = $('#TimeseriesPolygonImageMap');\n self.ctx.map = new TbMapWidgetV2('image-map', false, self.ctx, undefined, elemap);\n self.posFunc = self.ctx.map.map.posFunction;\n setTimeout(() => {\n self.mapleaflet = self.ctx.map.map.map;\n window.TimeseriesPolygonImageMapSelf = self;\n self.onResize();\n self.onDataUpdated();\n\n console.log(self);\n }, 10);\n};\n\nself.resizeMap = function () {\n if (!self.mapleaflet || !self.mapleaflet._container) {\n return;\n }\n self.mapleaflet._container.style.height = (self.ctx.height - 30) + \"px\";\n self.mapleaflet.invalidateSize();\n};\n\nself.clearPolygons = function () {\n if (!self.mapleaflet || !self.mapleaflet._layers) {\n return;\n }\n for (var i in self.mapleaflet._layers) {\n if (self.mapleaflet._layers[i]._path !== undefined) {\n try {\n self.mapleaflet.removeLayer(self.mapleaflet._layers[i]);\n } catch (e) {\n console.log(\"problem with \" + e + self.mapleaflet._layers[i]);\n }\n }\n }\n};\n\nfunction parseJson(json) {\n var value = String(json).replace(/'/g, '\\\"');\n return JSON.parse(value);\n}\n\nfunction lerp(a, b, f) {\n return a + f * (b - a);\n}\n\nself.posOnMap = function (v) {\n const bounds = self.ctx.map.map.imageOverlay._bounds;\n var ne = bounds._northEast,\n sw = bounds._southWest;\n var pos = self.posFunc(v[0], v[1]);\n pos.y = lerp(ne.lat, sw.lat, pos.y);\n pos.x = lerp(sw.lng, ne.lng, pos.x);\n return [pos.x, pos.y];\n};\n\nself.posFuncGeoJson = function (geojson) {\n geojson.forEach((g) => {\n g.geometry.coordinates = g.geometry.coordinates.map(shape => shape.map(self.posOnMap));\n });\n return geojson;\n};\n\nself.showPolygons = function (parsed, color) {\n try {\n var geojsonFeature = self.posFuncGeoJson(parsed.features);\n L.geoJSON(geojsonFeature, {\n style: { \"color\": color, \"opacity\": 0.8 }\n }).addTo(self.mapleaflet);\n } catch (e) {\n console.log(e);\n }\n};\n\nself.getCurrIndex = function () {\n return parseInt($('#TimeFrame')[0].value);\n};\n\nself.setCurrIndex = function (val) {\n $('#TimeFrame')[0].value = val;\n self.onDataUpdated();\n};\n\nself.getPolygonFrames = function () {\n const jsons = self.ctx.data.filter(prop => prop.data.length > 0 && typeof prop.data[0][1] === \"string\");\n var maxRange = 0;\n const polygonFrames = jsons.map(prop => {\n const frames = parseJson(prop.data[0][1]);\n maxRange = Math.max(Math.max.apply(this, frames.map(b => b.index)));\n return { frames: frames, color: prop.dataKey.color };\n });\n return [polygonFrames, maxRange];\n};\n\nself.showPolygonFrame = function (polygonFrames, index) {\n let valueName = '';\n self.clearPolygons();\n polygonFrames.forEach(polys => {\n const poly = polys.frames.find(frame => parseInt(frame.index) === index);\n if (poly) {\n valueName = poly.name;\n self.showPolygons(poly.value, polys.color);\n }\n });\n self.ctx.map.update();\n return valueName ? valueName : '';\n};\n\nself.getDirectionTelemetry = function () {\n // console.log(self.ctx.data);\n const nonJsons = self.ctx.data.filter(prop => prop.data.length > 0 && typeof prop.data[0][1] !== \"string\");\n let ret = {};\n nonJsons.forEach(prop => {\n const t = prop.datasource.name;\n ret[t] = ret[t] || {};\n const kind = prop.dataKey.name;\n ret[t][kind] = prop.data[0][1];\n });\n return Object.values(ret);\n};\n\nself.showDirectionMarker = function (s) {\n if (s.latitude === undefined || s.longitude === undefined) return undefined;\n let dir = s[self.ctx.settings.keyNameDir];\n if (dir === undefined) return undefined;\n let power = s[self.ctx.settings.keyNamePower];\n if (power === undefined) power = 4;\n const powerSize = parseFloat(power) * self.ctx.settings.ArrowLenAt4 / 4;\n if (/(Win32|Win64)/i.test(navigator.platform)) dir = (dir + 180) % 360;\n\n const pos = self.posOnMap([s.latitude, s.longitude]);\n var marker = L.marker([pos[1], pos[0]], {\n icon: L.icon({\n iconUrl: self.ctx.settings.arrowImageUrl,\n iconSize: [powerSize, parseFloat(self.ctx.settings.ArrowWidth)]\n }),\n rotationAngle: 360 - dir % 360, // counter clockwise\n rotationOrigin: 'center'\n }).addTo(self.mapleaflet);\n\n return marker;\n};\n\nself.onDataUpdated = function () {\n try {\n const index = self.getCurrIndex();\n [polygonFrames, maxRange] = self.getPolygonFrames();\n $('#TimeFrame')[0].max = maxRange;\n $('#TimeFrameLabel')[0].value = self.showPolygonFrame(polygonFrames, index);\n } catch (err) {\n // console.log(\"JSON error:\", self.ctx.data, err);\n // return;\n }\n\n (self.windMarkers || []).forEach(m => m.removeFrom(self.mapleaflet));\n const dirs = self.getDirectionTelemetry();\n self.windMarkers = dirs.map(self.showDirectionMarker);\n self.windMarkers = self.windMarkers.filter(a => a);\n};\n\nself.setNext = function () {\n var maxRange = parseInt($('#TimeFrame')[0].max);\n if (maxRange >= 1) {\n self.setCurrIndex((self.getCurrIndex() + 1) % (maxRange + 1));\n } else {\n self.setCurrIndex(0);\n }\n};\n\nself.Play = function () {\n self.playing = !self.playing;\n $('#PlayButton').text(self.playing ? 'Stop' : 'Play');\n if (self.playing) {\n var cont = () => {\n self.setNext();\n if (self.playing) {\n setTimeout(cont, self.ctx.settings.playDelay);\n }\n };\n cont();\n }\n};\n\nself.onResize = function () {\n self.ctx.map.resize();\n self.resizeMap();\n};\n\nself.getSettingsSchema = function () {\n var tbScheme = JSON.parse(JSON.stringify(TbMapWidgetV2.settingsSchema('image-map')));\n // console.log(tbScheme);\n tbScheme.form.unshift(\n \"playDelay\",\n \"keyNameDir\",\n \"keyNamePower\",\n \"ArrowLenAt4\",\n \"ArrowWidth\",\n {\n \"key\": \"arrowImageUrl\",\n \"type\": \"image\"\n }\n );\n Object.assign(tbScheme.schema.properties,\n {\n \"playDelay\": {\n \"title\": \"Play delay\",\n \"type\": \"number\",\n \"default\": 500\n },\n \"keyNameDir\": {\n \"title\": \"Direction key name\",\n \"type\": \"string\",\n \"default\": \"wind_dir\"\n },\n \"keyNamePower\": {\n \"title\": \"Power key name\",\n \"type\": \"string\",\n \"default\": \"wind_speed\"\n },\n \"ArrowLenAt4\": {\n \"title\": \"Arrow length in pixels when power/speed is 4\",\n \"type\": \"number\",\n \"default\": 40\n },\n \"ArrowWidth\": {\n \"title\": \"Arrow width in pixels\",\n \"type\": \"number\",\n \"default\": 40\n },\n \"arrowImageUrl\": {\n \"title\": \"Arrow image\",\n \"type\": \"string\",\n \"default\": \"data:image/png;base64,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\"\n }\n });\n return tbScheme;\n};\nself.getDataKeySettingsSchema = function () {\n return TbMapWidgetV2.dataKeySettingsSchema('image-map');\n};\n\nself.actionSources = function () {\n return TbMapWidgetV2.actionSources();\n};\n\nself.onDestroy = function () { };\n\n(function () {\n // save these original methods before they are overwritten\n var proto_initIcon = L.Marker.prototype._initIcon;\n var proto_setPos = L.Marker.prototype._setPos;\n\n var oldIE = (L.DomUtil.TRANSFORM === 'msTransform');\n\n L.Marker.addInitHook(function () {\n var iconOptions = this.options.icon && this.options.icon.options;\n var iconAnchor = iconOptions && this.options.icon.options.iconAnchor;\n if (iconAnchor) {\n iconAnchor = (iconAnchor[0] + 'px ' + iconAnchor[1] + 'px');\n }\n this.options.rotationOrigin = this.options.rotationOrigin || iconAnchor || 'center bottom';\n this.options.rotationAngle = this.options.rotationAngle || 0;\n\n // Ensure marker keeps rotated during dragging\n this.on('drag', function (e) { e.target._applyRotation(); });\n });\n\n L.Marker.include({\n _initIcon: function () {\n proto_initIcon.call(this);\n },\n\n _setPos: function (pos) {\n proto_setPos.call(this, pos);\n this._applyRotation();\n },\n\n _applyRotation: function () {\n if (this.options.rotationAngle) {\n this._icon.style[L.DomUtil.TRANSFORM + 'Origin'] = this.options.rotationOrigin;\n\n // console.log(this.options.rotationAngle);\n // if(oldIE) {\n // // for IE 9, use the 2D rotation\n // this._icon.style[L.DomUtil.TRANSFORM] = 'rotate(' + this.options.rotationAngle + 'deg)';\n // } else {\n // // for modern browsers, prefer the 3D accelerated version\n this._icon.style[L.DomUtil.TRANSFORM] =\n this._icon.style[L.DomUtil.TRANSFORM].replace(/ rotateZ\\(\\d+deg\\)/g, '')\n + ' rotateZ(' + this.options.rotationAngle + 'deg)';\n // }\n }\n },\n\n setRotationAngle: function (angle) {\n this.options.rotationAngle = angle;\n this.update();\n return this;\n },\n\n setRotationOrigin: function (origin) {\n this.options.rotationOrigin = origin;\n this.update();\n return this;\n }\n });\n})();\n",
59 | "settingsSchema": "{}",
60 | "dataKeySettingsSchema": "{}\n",
61 | "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"First point\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"xPos\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.05427416942713381,\"funcBody\":\"var value = prevValue || 0.2;\\nif (time % 5000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"yPos\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.680594833308841,\"funcBody\":\"var value = prevValue || 0.3;\\nif (time % 5000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"temperature\",\"color\":\"#9c27b0\",\"settings\":{},\"_hash\":0.9430343126300238,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"type\",\"color\":\"#8bc34a\",\"settings\":{},\"_hash\":0.1784452363910778,\"funcBody\":\"return \\\"colorpin\\\";\"}]},{\"type\":\"function\",\"name\":\"Second point\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"xPos\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.05012157428742059,\"funcBody\":\"var value = prevValue || 0.6;\\nif (time % 4000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"yPos\",\"color\":\"#ffc107\",\"settings\":{},\"_hash\":0.6742359401617628,\"funcBody\":\"var value = prevValue || 0.7;\\nif (time % 4000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"temperature\",\"color\":\"#8bc34a\",\"settings\":{},\"_hash\":0.773875863339494,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"type\",\"color\":\"#3f51b5\",\"settings\":{},\"_hash\":0.405822538899673,\"funcBody\":\"return \\\"thermomether\\\";\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"showLabel\":true,\"label\":\"${entityName}\",\"tooltipPattern\":\"${entityName}
X Pos: ${xPos:2}
Y Pos: ${yPos:2}
Temperature: ${temperature} °C
See advanced settings for details\",\"markerImageSize\":34,\"useColorFunction\":true,\"markerImages\":[\"data:image/png;base64,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\",\"data:image/png;base64,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\",\"data:image/png;base64,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\",\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAB/CAYAAAD4mHJdAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAACWAAAAlgB7MGOJQAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAxNSURBVGiB7Zp7kFTllcB/5/a93dMz3T0PemYIDgoCPhZ5iaD4wNkFjQjRRMlLTNbSlKlyzZpobSVbFRPUbNVWSRCWuKvlxqybtbIrukp4SATZCAgospEBgeElj4EZ5t3d0+++37d/9O2ZnqEHQZzZSlXfqlMz/c253+875zvfOefeHuH8L6u83P+AwH0lJZ4pbrenEiCVSnYmEsndGl4NhSKvAJkLmPPcV0VFxZjKivKPv77wXr274WN9uvm0PnHihD5y9IhuPNioN216Vy+Yf6eurAj8b2Vl5aXnM6d8loLf7w9apvHhyy//29jZ9fW0t7fpdWtWN7Wdao4qpaiqDpbdXF9fV1paKpu3bGbxk08eSWXU9ZFIpOPirC33v7xs+TIdiUT0Pz239NjeaTOTHXXjdb4cuP6W5DOLFx/7aNdH+oknfqQryv0vXZTFfr8/GKyqaN7XeMhc//ba6NSfPFXqS6fESJ29jdGAX69+9KHY9OnTyxbec08mHInWhsPhzsHmNs4FNgxdf+NNN5sAh3/7n40dCxeKedUsOr6x8CzdsnBEQu9sPABwzTWTTMNQ9eec+1x/FDEuGTduHABXtreOKutJYyiFqq4tqD+5O3wJQF1dHSij7nODtdZuj9cLgMfGOpcuQInSFoDldqNFez43eCivIrgILoKL4CK4CC6Ci+AiuAgugovgIrgILoKL4CK4CC6Ci+A/B7B5vor6Mz4PNnbRYAAtoCQLUMMFVobuBWOALWdjVIGxiwbbZC3WkrXWLqAzJBZrR5T0LWTgdSHfdF1YcIlG57t8oM5nfov1OcCKPmDW1Rfi2IsA5yI5F9WFXF0o0i8arARwggsBu4BbhwaM6g0ujXY+9b+GLqrzLR5E5wsH2ziB5QRXoW8lCy3mosH553iwlDlEe9znai2DpMyhAJ+PxUNTJMhZm51+WM9xvsWFXD2kx0nl9rjQ4oYC3C+4BoEMnasl39Vn6wxRdcqbXApXpwupWBcEVgLKGLw6DU1w5bkaCjcChcYuHozuLYtqEFfroXC1TZ67GcbjlEuZWjSIHr6ozjZ7/y/VSWOLdgJIF9zjQl3JFwDOXn1lsYDOULm6X+YaROcLB6s8+LC2tzqvoc+Wx0L2nT/6wlIm5y6LQ9bs5TLXsO5x7jG192lxuJq9bCOg0aIRGcYEkt9lCsPp6lxlMsBlFE4ghcYuGoxznHKFYNjKYq7Zy5XFYW32lMtCBGzbLlwWLwB83m/2NNC44R0iFaP503+8jO1UqHz5wiwW0aNzvysgdPJTQr/7dFD9fHD+vecN9vl8NaYpv546ZeqCBx98CMhGbPXEqZRfcTWmyySTjuO2TMora/B4Sji+832OnWoGYMGCBez88IMfHD50eExG6Yd6enraBjJcAwf8fv+Vbsv1Pz9f/NT1y1esQCnNPz6zeGuy6WBN+MRRrwp1YMR6MOIJMqEuOj49xNFd2zh5aD9SVpr44PCJXVOmXXvpHfPm4fP7rtz98Z/usSz3+lQq1e/fnvuFSHl5+VjTNLb96lfPj6yv/0t2bN/eufJnj+37Uql1c/1Xv8WM279CaZn/rJcBGoj1hNm+7k22rF5JcyK1edp3Hps0bfq0yj0Ne/jFL55pVopZ3d3dx88C19bWlqVS8Z2Lf/7U1XNvu51Vb72x7/irz9fUBEcEv/03PyFYPRJDgZHt9XpvzG8QlAFnWppY+S9LaOnsaPPOWdhxx7z5V320cydLl/7yE2+pb+bp06dj/VxtWbJ03h13zr/r7rtZu2bNwVP/9cKYMiHwtW8+QNAbwOiOIN09SCiChCKQL+EIKhxBhcN4EGpGjuJww66yxNH9gePac+zGm26sikQiNY379/kSydT63uCqrCybXB6oeuS+RYvYv29f/OTKFz1+dIlXXFQrCznRjNhkRfdJzmIMEAExsqbUmh68holWGXf43deMg6NHJ+5btKjkgw8//IFh8lJnZ88nBoBWxpPf+e53DYC1Ly5bVSb6Mo8WSrQgx5uRY6cHSDMcz0q/vx/PSTNeJXi04EOPfe93L70JcP/99xu2bfwUwPT5fNU+n++rM2fO5P3332+uS3V9y9KCG8FSmtjRo3iN0uz+qqylemDnLhpDQDsFJGrHMG2F2xAyGi5Nhr65Y8f21unTZ9T4yrz3KqVHGC4X91x33XUmwN7N775nApbuk90nD5BpbUbaWqG9Dd3eju5o6y/t7dDehrS1kmltYffJ/ViA25nDBcbeLZs2AUyaNNkyDL5minDL7Nm3opSiNtQ0yUQwESydlXg6xc70Sf5CewliYSD9TqHu/anpIMUnJIiLjSVCGjAFTA21odNTlFLMunEWO7bvuMUUjKkTrriCvXv3RDyiJxpacGVXSc56W2uO6DhtKkmFFsocHchmtKhoukURNrJPG5YDdAEuDYaAV/TVjY0HesaNG+8Tw5hmuC1zFEBLS0urkQ3QPtFgILgQTC0IkAZSgEJQCClnTBwdF4KBOPf2iQBnzrS2GYaBZblGmWK4/ADxWCzqoS85iDOZDFiMS2ddV5Kz2EkGhgwECYLOzqOzxy0W7YkAiBgBw7btFIC3tMw/2JsrnS9OI5B2pPdt0AC9gdVZZxkBANu2k0Ymk2kDCI6oqsw1c/nNu8rVW8l+2ZFCkxRNzMhKUjQpNBlnv23nXfbAeTRQHayudMBtBlod6OrqZNz4CeVprcKqd4KsZBxgGk1KNEmBmGiijsScsZRo0s4CMnn3284CMqJCY8aOCXR2dqK0PmBokQ3r1q7D7/dLq7tyY8axMCOatDNZFqhJiCbuWNsLNrJjCUcnt4C0ZOew0WTQnDYr3/X5fLJx4wZE5B1DKVm1a9dHAIyYesPYjEBa+vYwJZAUSAgkHAtjookaWcl9Togm4eim8u5PS9YDNVNmXg7QsLsBreX3RjgcPtzW1rarubmZ+QvumtahXJvzrUzmWRvrZ61yxNnvPKuTA6xvt13bvjxv/tSW5mY6Ozt3hkKhoy4Ar6ek6dChg4vm3nY7oZJAJnG4oUIQESdD5Ud0v30XSBlZC1OGdjyTA/darwK3LcxcPm585ZJnl9ATinwvnkweNgC6wuF1x44d27R3714WfOWucZGrb3g7kee+eJ6LewPLcXU0bzwuuf2G3P3NoyevnzP3tsv3NOylqenkHzvD4fWQ197aikeW/nJJd1dnJ4//9On57V+a8Hoib7K4kQeUAWL0D7RcsJ2oqHv9wUcfu7Orq5MVK5Z3KS0P53j96lsgEPjyiKqKtW/891uu2tpalvzDMxsTW96s9yhMC8HUOCkxm07JO/fZk5A9dkmDTOSqWe/99fcfmRPtifHY3z6a6Q5F7gyFQhsKggFGjKh4wFviffG11153T59xHVu3bg3968/+7g9V3ae+0Zv0kX49l3ISjA2ccpe/NXvR9+uvnX5tRdOpJv7+xz9OxnpiD3d0d/97PqcXrLWeBcwGLnv11d96n3j88QVPPf108KHvPUwmk+HttWu71q96Y0dozzajJBUfXyqMA4gpfShmeY54JkzX19/6VzfMmDmjMpPOsOqtVbzyym9alz23fM23Fy1KACeAP4rIBwCitb4MeAQY5SxEt7a2qIaGBn70wx+OTKXTc5Y+t8w1d85cdN5KtdbYSqGVImPbJOIxotEo6/+wniXPPmsH/L4Ny5etaJk46Rqprq7JPTgooBn4Z9FaPw9UAHR1dSnbTsuZMy1GMpnItLZ2GFu3bq5d/fvVc0ZUjZB7F36d2fW3MmHCFZguF0pr0uk0Bxsb2bL5PV5fuZLuUEjfdffdG2+66ebW6mCVLvP5qa4OAoYEg8Gcg7tNIAIEADHdJnbcxmNZ6UQ05nK7TT1x4sRYRVV1/FTTqdLVa9bywgsvEImESKfSAFiWhT9QzqhL6rh25g3UjbokPnJkTaKkxFRaa8NtGbaIy+Up8eS2VgEx0VpXO66+HKfdbW9vV93d7RKNJl3xeNQOd4d1Mp0i3B3yRCKRsmgiYSVTaa9orS23lfR5vany8vKYLxCIeyxLKqoqtddbKh6PSVVVtQ4Gg5IHPQI8nx9ck4CbgSuBarJnvARsiUai4XBPmGQyqbWGRCxh2VrZAKYYLtNjZUyXSxsuU6oqyg1fwO91nhUSzvQdwB5gm4h8UvA4OYsoByYDY4EaoBLwAN7sYiDvZ4LsqUo60uNIK3AY2CMioYGM/wPREY0iGUY58wAAAABJRU5ErkJggg==\"],\"useMarkerImageFunction\":true,\"colorFunction\":\"var type = dsData[dsIndex]['type'];\\nif (type == 'colorpin') {\\n\\tvar temperature = dsData[dsIndex]['temperature'];\\n\\tif (typeof temperature !== undefined) {\\n\\t var percent = (temperature + 60)/120 * 100;\\n\\t return tinycolor.mix('blue', 'red', amount = percent).toHexString();\\n\\t}\\n\\treturn 'blue';\\n}\\n\",\"markerImageFunction\":\"var type = dsData[dsIndex]['type'];\\nif (type == 'thermomether') {\\n\\tvar res = {\\n\\t url: images[0],\\n\\t size: 40\\n\\t}\\n\\tvar temperature = dsData[dsIndex]['temperature'];\\n\\tif (typeof temperature !== undefined) {\\n\\t var percent = (temperature + 60)/120;\\n\\t var index = Math.min(3, Math.floor(4 * percent));\\n\\t res.url = images[index];\\n\\t}\\n\\treturn res;\\n}\",\"color\":\"#fe7569\",\"mapImageUrl\":\"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgoKPHN2ZwogICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiCiAgIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIKICAgeG1sbnM6c3ZnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxuczpzb2RpcG9kaT0iaHR0cDovL3NvZGlwb2RpLnNvdXJjZWZvcmdlLm5ldC9EVEQvc29kaXBvZGktMC5kdGQiCiAgIHhtbG5zOmlua3NjYXBlPSJodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy9uYW1lc3BhY2VzL2lua3NjYXBlIgogICB3aWR0aD0iMTEzNC41MTgzIgogICBoZWlnaHQ9Ijc2Mi43ODI0MSIKICAgaWQ9InN2ZzIiCiAgIHZlcnNpb249IjEuMSIKICAgaW5rc2NhcGU6dmVyc2lvbj0iMC40OC41IHIxMDA0MCIKICAgc29kaXBvZGk6ZG9jbmFtZT0id2ljaGl0YW1hcC1ub2xpYi5zdmciPgogIDxkZWZzCiAgICAgaWQ9ImRlZnM0IiAvPgogIDxzb2RpcG9kaTpuYW1lZHZpZXcKICAgICBpZD0iYmFzZSIKICAgICBwYWdlY29sb3I9IiNmZmZmZmYiCiAgICAgYm9yZGVyY29sb3I9IiM2NjY2NjYiCiAgICAgYm9yZGVyb3BhY2l0eT0iMS4wIgogICAgIGlua3NjYXBlOnBhZ2VvcGFjaXR5PSIwLjAiCiAgICAgaW5rc2NhcGU6cGFnZXNoYWRvdz0iMiIKICAgICBpbmtzY2FwZTp6b29tPSIwLjM1IgogICAgIGlua3NjYXBlOmN4PSI4OS45MDc4NTciCiAgICAgaW5rc2NhcGU6Y3k9IjQ1My43ODI0MSIKICAgICBpbmtzY2FwZTpkb2N1bWVudC11bml0cz0icHgiCiAgICAgaW5rc2NhcGU6Y3VycmVudC1sYXllcj0ibGF5ZXIxIgogICAgIHNob3dncmlkPSJmYWxzZSIKICAgICBpbmtzY2FwZTp3aW5kb3ctd2lkdGg9IjEzNjYiCiAgICAgaW5rc2NhcGU6d2luZG93LWhlaWdodD0iNzIxIgogICAgIGlua3NjYXBlOndpbmRvdy14PSItNCIKICAgICBpbmtzY2FwZTp3aW5kb3cteT0iLTQiCiAgICAgaW5rc2NhcGU6d2luZG93LW1heGltaXplZD0iMSIKICAgICBpbmtzY2FwZTpvYmplY3QtcGF0aHM9InRydWUiCiAgICAgaW5rc2NhcGU6c25hcC1nbG9iYWw9ImZhbHNlIgogICAgIHNob3dndWlkZXM9InRydWUiCiAgICAgaW5rc2NhcGU6Z3VpZGUtYmJveD0idHJ1ZSIKICAgICBmaXQtbWFyZ2luLXRvcD0iMCIKICAgICBmaXQtbWFyZ2luLWxlZnQ9IjAiCiAgICAgZml0LW1hcmdpbi1yaWdodD0iMCIKICAgICBmaXQtbWFyZ2luLWJvdHRvbT0iMCIgLz4KICA8bWV0YWRhdGEKICAgICBpZD0ibWV0YWRhdGE3Ij4KICAgIDxyZGY6UkRGPgogICAgICA8Y2M6V29yawogICAgICAgICByZGY6YWJvdXQ9IiI+CiAgICAgICAgPGRjOmZvcm1hdD5pbWFnZS9zdmcreG1sPC9kYzpmb3JtYXQ+CiAgICAgICAgPGRjOnR5cGUKICAgICAgICAgICByZGY6cmVzb3VyY2U9Imh0dHA6Ly9wdXJsLm9yZy9kYy9kY21pdHlwZS9TdGlsbEltYWdlIiAvPgogICAgICAgIDxkYzp0aXRsZT48L2RjOnRpdGxlPgogICAgICA8L2NjOldvcms+CiAgICA8L3JkZjpSREY+CiAgPC9tZXRhZGF0YT4KICA8ZwogICAgIGlua3NjYXBlOmxhYmVsPSJMYXllciAxIgogICAgIGlua3NjYXBlOmdyb3VwbW9kZT0ibGF5ZXIiCiAgICAgaWQ9ImxheWVyMSIKICAgICB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMjcuMDcxNDI4LC0zMDcuOTAyOTkpIj4KICAgIDxwYXRoCiAgICAgICBpZD0icGF0aDM3ODciCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMzY0ZTU5O3N0cm9rZS13aWR0aDoyLjk5OTk5OTc2O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLW9wYWNpdHk6MTtzdHJva2UtZGFzaGFycmF5Om5vbmUiCiAgICAgICBkPSJtIDkwNi4wMzMxNSw3MDYuMTMzNjcgMy40MjkyLDE3Ljc5NTUyIE0gMjguNTcxNDI4LDc2NS4wNTA2NyBjIDE1MC40MzUyMDIsNi44MzM0MiAxNDYuMzkyMzIyLC0yNi4zMzQxNSAxNjYuNDM0NTQyLC0yOS4zMjAwOSAzNi4xNDM3NSwtNS4zODQ3NiAxMTQuMjg2NzYsLTYuNTI1NCAxNDguMzI1MDgsLTguNjIzNTQgNDMuMzc4MDgsLTIuNjczODUgMTQxLjc2MjIxLC0xMS4yMzA5OSAxODguODU1NzgsLTE5LjgzNDE4IDM5LjgxMTM4LC03LjI3Mjg0IDIyMS4zNjk5MSwtMC44NjIzNSAzMTkuMDcxNDEsLTAuODYyMzUgNzAuODI3MzUsMCAxNDYuOTE4NjcsLTEuNzI0NyAyMTguMTc1ODYsLTEuNzI0NyAtMzEuNjE5NywwIDExNy44NTUyLC0yLjU4NzA3IDg2LjIzNTUsLTIuNTg3MDcgbSAtMjUuMDkwNywtNjguMTI2MDYgYyAtNTIuNzk5NiwzNC43ODQ4NCAtNjUuODk1MSw1MS43NDg2NSAtOTUuNjM5LDgxLjQ5MjU4IC0yNC45MzEzLDI0LjkzMTI3IC0xNDAuMzk2NTMsLTE5LjEzOTIgLTE3OC45Mzg3MSwzNi42NTAwNyAtMTIuMjgxNCwxNy43NzcxNSAtNDcuMDAyNTcsNDYuNTQ2NTMgLTY1LjEwNzgzLDU5LjA3MTMzIC0yMC4xMDUsMTMuOTA4MTggLTU2LjAzNjcyLDQ0Ljk1NjY0IC02Ny43Njg4NSw3My4wNzgyNyAtNC44MDE0NywxMS41MDkwMiAtMTMuMzgwNDYsMzUuOTkyOTggLTIzLjQ0OTQ5LDQ2LjA2MjAxIC0xMC40OTY5OSwxMC40OTY5OSAtMzguMzc3MzMsNi4zODU2OSAtNDQuMDIzNDUsMTcuNjQ3NjQgLTE5LjAwNTAyLDM3LjkwODEyIC0yNS40NjUzLDEwMC45MjM1MiAtNjcuNjE3ODksMTAyLjA1MTAyIG0gMTkuMjgxNTEsLTYyNC4wMTQ2NCBjIDM0LjY1OTM0LC0xLjg3MzgyIDg0LjAyNzMzLDcuMzkxMzEgMTA5LjkwMDcxLC00LjI4NTQ1IDEzLjI4MTcyLC01Ljk5NDA4IDQxLjQwNzIxLC0yLjQ2MTM1IDY2LjgyODY2LC0yLjMyMDQ2IDM1LjMyMjM4LDAuMTk1NzggNjQuMzgyNDksMC42MzQ3NyAxMDEuOTE2Nyw1LjAyMzIgMjUuMDMwMzYsMi45MjY1IDQ0LjY2MjczLDM0LjI4NzIyIDU4LjUyNjk4LDUwLjY0MzkgMTcuMDk4NzgsMjAuMTcyNjggNjIuNzYzODYsLTEuNzE0NjcgNjYuMzA1NjYsMzIuMTM0MzMgNS4xMDI3LDQ4Ljc2NTg3IC02LjMyODQsNzguNjM3MjUgNi4xNDExLDk3LjM0MTUgMTkuOTY5MiwyOS45NTM3OSA1MC40ODY0LDE3Ljg1NTc5IDQ0LjYxOTMsODMuOTcxMTkgTSA1ODkuMTAyMjcsMzA5LjcyNzE1IGMgNC42NDM0NiwyMy43MjkyMyAxNS4wNjkwNCw3Mi43NzU3NSAxOS4wNjEyOCwxMzAuNjQyODggMC44NzIwNiwxMi42NDA0OCA1LjQ0NzE4LDI0Ljk5MjUzIDQuMjIyMzEsNDUuMjc3NTcgLTIuNTE3MjEsNDEuNjg3NSAtMTUuNzE3MDYsNDMuNjc3MjcgLTE1LjA5MTIyLDYwLjM2NDg2IDEuNDMxOTUsMzguMTgyMjQgMzAuNjEzNjEsOTMuODM3MTkgMzAuNjEzNjEsMTM5LjcwMTU0IDAsMjQuMTgwOCAtMi42Njk2NCwxMTUuMzkwNDUgNy4zMzAwMSwxMzUuMzg5NzYgMC4xNTkxMSwwLjMxODIxIDEwLjA2NDc2LDM1Ljg4MzMyIDEwLjc3OTQ1LDQ5LjE1NDI0IDAuOTQzNzgsMTcuNTI0NjkgLTI0LjQ3OCwzOS40NzAwOCAtMjguMDI2NTUsNDYuNTY3MTYgLTUuNDc3NywxMC45NTUzOSAtMzYuOTczMjQsMTAuODgxOTcgLTQwLjA5OTUsMjQuMTQ1OTUgLTMuODY4ODQsMTYuNDE0NTEgLTMuODY2Myw0My43OTczNSA0LjA0NjQ3LDU5LjQ0MTI5IG0gOTcuMzM3MzQsLTY5MS4wMDk0MSBjIC01LjAxMzMyLDM1LjUxNTk1IC00My42NTkwMSwxMS4zMTY1MiAtNTguNTM4NjEsMjMuNzgxMzEgLTIxLjMzMDE5LDE3Ljg2ODUyIC02Mi40OTk2NCwzMS40MzIxMiAtNzAuMTI0MzcsMzUuMzY3MDggLTM1LjA4NzYzLDE4LjEwNzkzIC0xMTAuNDcyMTUsLTE1LjE0MTk2IC0xMjUuNjE0MSw0LjI2ODQzIC0xNS45NTA2MywyMC40NDcwMyAtMC4wNzM1LDYxLjQ2NjQ4IC05LjE0NjY2LDg0LjE0OTI0IC02LjAzNTcsMTUuMDg5MjYgLTE4Ljg3NjcsMjMuMDE3MzQgLTI3LjQzOTk3LDMyLjkyNzk4IC0xOS43NDgyOSwyMi44NTU1NSAtNjkuOTc0MjgsNjkuODI0MTkgLTg0Ljc1OTA0LDEwMC4wMDM0NiAtNy40OTc0MSwxNS4zMDQwNCAtMy4yODQyNiw0NC40MjA0MSAtMy40NzA1Myw2My4zNDI4NCAtMC4xMjc5MywxMi45OTQxNCAtMC44MTAxNSwyMy4xMDM4NSAyLjQwMzQzLDI4LjI3NjE4IDQuOTYxNTgsNy45ODU4MSAyMy43MjA1LDI4LjExMjA3IDI0LjIzODY1LDUwLjYxMTQ5IDAuMjk0MTEsMTIuNzcxNDYgMC4wMTMzLDc4LjU5MTAxIDMuMDQ4ODgsODcuNjU1NDkgMi4zMTI1Niw2LjkwNTQ2IDQuMjIwMDQsMjYuNTY0OTcgMTAuMjEzNzcsMzYuNTg2NjIgMTEuMzU0MDEsMTguOTg0MTUgNC4zODczNyw0MC4xNTY2MiAyNy44OTczLDUzLjUwNzk1IDE5LjA1MDEyLDEwLjgxODU5IDQ2Ljg3NzgxLDEyLjIxODYyIDgxLjkyNjE4LDE0LjQ2MDU0IDMzLjcwMzQ1LDIuMTU1ODkgNjEuNTEyMTcsLTEuNDMwMzUgNzYuOTIwNzcsNi4xNDExIDExLjU4NTA4LDUuNjkyNjYgOC41ODE1MSwxNy45MzM0NCAxNC4yOTU0MSwyOS4zNjEyMyA1LjY0MDQyLDExLjI4MDg1IDMxLjUwMjYzLDExLjE1NjI3IDQxLjgwNDA5LDQzLjQ1NDg3IDcuNjA1OSwyMy44NDcxIDMuMDg1OTMsNDQuMTU2OSA2LjcwNzU1LDY1Ljg4NjYiCiAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjY2Nzc3NzY2Njc3Nzc3NzY2Nzc3Nzc3NjY3Nzc3Njc3NzY2Nzc3Nzc3Nzc3Nzc3Nzc3NzYyIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMzMzMzY2O3N0cm9rZS13aWR0aDoxcHg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Im0gNDMuMjc3ODgxLDUxNy45NDY3OSBjIDAsMCAyMzAuODQ4Mjg5LC0zLjYzODA1IDI1MC4wMDg2MzksLTMuNjU4NjcgNy40ODIyMiwtMC4wMDggOC42MTk1NCw1LjE1MTk0IDE0LjAyMDksMTEuNDU4NjkgMjQuNTk2MDgsMjguNzE4OTMgOTMuOTA5NjYsMTEyLjkzNTg1IDkzLjkwOTY2LDExMi45MzU4NSIKICAgICAgIGlkPSJwYXRoMzc4OSIKICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNzc2MiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzMzMzM2NjtzdHJva2Utd2lkdGg6MXB4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICBkPSJtIDM1Ljk2MDU1NSw1NzcuNzA0OTQgYyAwLDAgMTY1LjUyNDU2NSwtMS42ODQ1NCAyNDguNzc5NTY1LC0xLjY4NDU0IDQuOTQ3NDksMCA3LjcyOTkzLC0yLjg4MzMgMTAuNTM3NzEsLTUuNzI5NzcgOS42NjEwNywtOS43OTQxNiAyNS42MzE5OSwtMjguNTg5OTUgMjUuNjMxOTksLTI4LjU4OTk1IgogICAgICAgaWQ9InBhdGgzNzkxIgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY3NzYyIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtmaWxsOiMzMzMzNjY7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOiMzMzMzNjY7c3Ryb2tlLXdpZHRoOjFweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7bWFya2VyOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7ZW5hYmxlLWJhY2tncm91bmQ6YWNjdW11bGF0ZSIKICAgICAgIGQ9Ik0gMzguMzk5NjYzLDY0MS43MzE1NSA0MzEuNzA1OTMsNjM3LjQ2MzExIgogICAgICAgaWQ9InBhdGgzNzk1IgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtmaWxsOiMzMzMzNjY7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOiMzMzMzNjY7c3Ryb2tlLXdpZHRoOjFweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7bWFya2VyOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7ZW5hYmxlLWJhY2tncm91bmQ6YWNjdW11bGF0ZSIKICAgICAgIGQ9Ik0gMzkuMDA5NDQyLDcwNC41Mzg1OSA1MjMuMTcyNTMsNjk3LjgzMTA0IgogICAgICAgaWQ9InBhdGgzNzk3IgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtmaWxsOm5vbmU7c3Ryb2tlOiMzMzMzNjY7c3Ryb2tlLXdpZHRoOjFweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7bWFya2VyOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7ZW5hYmxlLWJhY2tncm91bmQ6YWNjdW11bGF0ZSIKICAgICAgIGQ9Im0gMzAzLjk1NzYyLDY4Mi41ODY2MSAxNDYuNzk1NDIsMS44MjkzMyBjIDEwLjUzNDAzLDAuMTMxMjcgMTQuMzQzNzQsLTIuNjM3MzkgMjUuNDg3MTUsLTYuMzcyOCAxMC40MTIxMiwtMy40OTAyNyAzMS40MjQxNSwtMi42OTg5NiA0MS4zODUzOCwtMi43NzM4NSBsIDQwNS41NjA3OSwtMy4wNDg5IgogICAgICAgaWQ9InBhdGgzNzk5IgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY3Nzc2MiIC8+CiAgICA8cGF0aAogICAgICAgaWQ9InBhdGgzODA0IgogICAgICAgc3R5bGU9ImNvbG9yOiMwMDAwMDA7ZmlsbDpub25lO3N0cm9rZTojMzMzMzY2O3N0cm9rZS13aWR0aDoxcHg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2Utb3BhY2l0eToxO3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2UtZGFzaG9mZnNldDowO21hcmtlcjpub25lO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICBkPSJtIDQyNi4yMTc5NCwzMTQuODkwOTggYyAyLjA2NzU0LDkuMDUyNzMgMS44NDE3Nyw1MS43Mjc3NyA2LjUwNzk0LDc0LjgzNDY2IDEuNjc0NzUsOC4yOTMzNiA4LjY3NTA4LDE0LjA2NTk4IDEwLjA1NTQxLDE0Ljg1ODYyIDQuOTAxNDcsMi44MTQ2MyAxMC44MTQ3OSw4LjE0OTgyIDEzLjA0NTc5LDE2LjA4ODMxIDYuNzU3NzksMjQuMDQ1OTEgMC44Nzk3Miw2OC40NTIxMiAwLjg3OTcyLDExMC42ODkzIDAsNi4wOTc4MiAxLjY2MDEsMzAuMTQ2NiAtMi4xNTU4OCwzMy45NjI1OSAtMi41NDA4NSwyLjU0MDgzIC0wLjI4MTYzLDEyLjk5MDY5IC0zLjQzNjc1LDE2LjE0Mzc3IGwgLTkuODQ5NDQsOS44NDMxMSBjIC0xMC4zNjcxNSwxMC4zNjA0NyAtMTEuNTkwMTcsNi41MjYxNCAtMTcuNzM4NDgsMTguODIyNzYgLTMuNTY3NzIsNy4xMzU0MyA1LjQwMjM1LDIwLjY3MjEgNy4zNTQzMiwyNC41NzYwMiAxLjkzMjE0LDMuODY0MyAtMS44NDIxNiw0Ljc3NzczIC0xLjc5MjM1LDcuNDQ2MjYgMC4yNTI4NiwxMy41NDQ4MyAyLjI5NzUsMzczLjkyNzEyIDIuMjk3NSwzNzMuOTI3MTIiCiAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjc3Nzc3Nzc3Nzc2MiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImNvbG9yOiMwMDAwMDA7ZmlsbDpub25lO3N0cm9rZTojMzMzMzY2O3N0cm9rZS13aWR0aDoxcHg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2Utb3BhY2l0eToxO3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2UtZGFzaG9mZnNldDowO21hcmtlcjpub25lO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICBkPSJtIDM2NS4yNDAyMiw1MTkuNzc2MTIgNC4xMTU5OSw1MDIuMTUxNTgiCiAgICAgICBpZD0icGF0aDM4MDYiCiAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJjb2xvcjojMDAwMDAwO2ZpbGw6bm9uZTtzdHJva2U6IzMzMzM2NjtzdHJva2Utd2lkdGg6MXB4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLW9wYWNpdHk6MTtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLWRhc2hvZmZzZXQ6MDttYXJrZXI6bm9uZTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIgogICAgICAgZD0ibSAxMTYuNTMxNjUsNTA0LjE4Njk5IDMuODgwNTksMzEwLjk2NDM2IgogICAgICAgaWQ9InBhdGgzODMxIgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIgLz4KICAgIDxwYXRoCiAgICAgICBpZD0icGF0aDM4ODkiCiAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtmaWxsOm5vbmU7c3Ryb2tlOiMzMzMzNjY7c3Ryb2tlLXdpZHRoOjFweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7bWFya2VyOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7ZW5hYmxlLWJhY2tncm91bmQ6YWNjdW11bGF0ZSIKICAgICAgIGQ9Im0gMzE3LjY3NzYsNTc2LjQ4NTM5IDEzMC4xODc0MiwxLjUyNDQ0IGMgNC41MTA3OSwzLjI0MTY5IDIwLjM0NDcxLDcuOTY4NTMgMjcuNzQ0ODYsNC4yNjg0NCAzLjE1NTQ2LC0xLjU3NzcyIDkuNDE5LC01LjM4ODE3IDE0LjAyNDg5LC0zLjk2MzU1IDQuMjY2OTgsMS4zMTk4MSA2LjAxNjg5LDMuMTE2MzIgMTAuMzY2MjEsMy4wNDg4OSAxMC4zMDQwMywtMC4xNTk3NSAyMC4yMTE3LDAuMzg3NDEgMzAuNDg4ODYsMC4zMDQ4OSAxNzcuODkwOCwtMS40MjgyNyAzNTYuNTkwMzUsLTIuMTMyNDcgNTM0Ljc3NDU2LC0zLjA0ODg4IgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2Nzc3NzYyIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtmaWxsOm5vbmU7c3Ryb2tlOiMzMzMzNjY7c3Ryb2tlLXdpZHRoOjFweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7bWFya2VyOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7ZW5hYmxlLWJhY2tncm91bmQ6YWNjdW11bGF0ZSIKICAgICAgIGQ9Im0gNDc1LjMwNTAxLDU4Mi44ODgwNSBjIC0zLjQ0NDE4LDExLjM1MDY2IC0yLjEwMzQzLDEyLjQzMzczIDMuNjU4NjUsMjEuMDM3MzEgMy43OTQ0NSw1LjY2NTY0IDUwLjg2MjYxLDEzLjAzODQ1IDQxLjQ2NDg1LDI3LjEzNTA5IC0xMC41MzY5NywxNS44MDU0NyAtMjIuODk3NDUsLTUuNDc3NzIgLTMzLjg0MjYzLC0xLjgyOTMzIC01LjQ1MjM2LDEuODE3NDUgLTcuMzQ5MDEsNS40NTYzMSAtMy42NTg2Niw5LjE0NjY1IDIuODA2ODMsMi44MDY4NCA0LjA0OCwxLjgwMzk2IDYuNTIwMzQsNS4xMDA0MSIKICAgICAgIGlkPSJwYXRoMzkxMCIKICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNzc3NzYyIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtmaWxsOm5vbmU7c3Ryb2tlOiMzMzMzNjY7c3Ryb2tlLXdpZHRoOjFweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7bWFya2VyOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7ZW5hYmxlLWJhY2tncm91bmQ6YWNjdW11bGF0ZSIKICAgICAgIGQ9Im0gNDMyLjAxMDgyLDYzNi44NTMzMyBjIDguMzE4OTksMTMuMTEwMTYgMTguODQ2MjEsMTQuNjM0NjUgMzUuNjcxOTYsMTQuNjM0NjUgMi45Mzg2NSwwIDcuODY5OTgsLTAuOTMzNzEgMTAuNjcxMTEsMCAxMS4zNTkxNywzLjc4NjM5IDI3LjE5Mzk4LDEwLjI3NTc3IDM2LjIwMTkzLDIxLjEyOTQ4IDguMjgwMDIsOS45NzY2MSAxMC4yNTI3OCwyMy44ODMwOCA3LjcwMjAyLDM3LjEwNDI0IC02LjE2OTg5LDMxLjk3OTk4IC0xNi43MTQzMSw1Ni45ODg1MyAtMTkuMDQzNTUsODYuNTY5MDUgLTEuMzQ3OTgsMTcuMTE4OCA0LjUwOTU3LDIyLjUzNTIyIDExLjA3MTQzLDMzLjkyODU3IDEwLjY3MDIzLDE4LjUyNjcyIDguNzI0NTMsMTQuMTk5NTUgOC41NzE0MywzNC4yODU3MiAtMC4xMzk2MywxOC4zMTk0NCAwLDYwLjI2Mzg1IDAsODAuNzE0MjkiCiAgICAgICBpZD0icGF0aDM5MTIiCiAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjc3Nzc3Nzc2MiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImNvbG9yOiMwMDAwMDA7ZmlsbDpub25lO3N0cm9rZTojMzMzMzY2O3N0cm9rZS13aWR0aDoxcHg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2Utb3BhY2l0eToxO3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2UtZGFzaG9mZnNldDowO21hcmtlcjpub25lO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICBkPSJtIDUyOC41MDgwNiw2NTguOTU3NzYgYyAtMTAuNjgxMjMsMC45MDQ1NCAtNy4xMDgwNCwtNS42MDI1NSAtMTAuODIzNTQsLTguMDc5NTYgLTQuNzg0NTQsLTMuMTg5NjkgLTEyLjIyNzA0LC0xLjI1MTA0IC0xNi43Njg4OCwtNS43OTI4OCAtMC42NjYxMiwtMC42NjYxMiAtOC44MDk2OSwtNC4xMDg3NyAtMTAuMTc0NDcsLTIuNzQzOTkgLTguMzY0NTksOC4zNjQ1OSAtMy4wNDg4OCwyMC41NTE4OCAtMy4wNDg4OCwzMy41Mzc3NCBsIDMuMDIyLDMzOS42OTc0MyIKICAgICAgIGlkPSJwYXRoMzkxNCIKICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNzc3NjIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJjb2xvcjojMDAwMDAwO2ZpbGw6bm9uZTtzdHJva2U6IzMzMzM2NjtzdHJva2Utd2lkdGg6MXB4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLW9wYWNpdHk6MTtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLWRhc2hvZmZzZXQ6MDttYXJrZXI6bm9uZTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIgogICAgICAgZD0ibSA1MTcuOTg5NDEsNjUxLjAzMDY1IGMgLTAuMjIxNzEsLTIuNzAxODQgMS45MDM0NiwtNS41NjIxMyAzLjM1Mzc3LC03LjAxMjQ1IDEuNzk5NDMsLTEuNzk5NDIgNi45MjI5NCwxLjAwNDE5IDguODQxNzgsLTAuOTE0NjYgMC4yODc2NSwtMC4yODc2NiAwLjg0MzI5LC0xMS4xNjQxIDAuMjI4NjYsLTEzLjU2NzUzIC0yLjA2NDgzLC04LjA3NDE2IC0yLjA1ODAxLC0yOC42NTY1OCAtMi4wNTgwMSwtMzguNzIwODYgbCAwLC03My4xNzMyNiIKICAgICAgIGlkPSJwYXRoMzkxNiIKICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNzY3NzYyIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtmaWxsOm5vbmU7c3Ryb2tlOiMzMzMzNjY7c3Ryb2tlLXdpZHRoOjFweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7bWFya2VyOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7ZW5hYmxlLWJhY2tncm91bmQ6YWNjdW11bGF0ZSIKICAgICAgIGQ9Im0gNTI4LjY2MDUsNjc1LjQyMTczIC0wLjQ1NzMzLC0zMS41NTU5NiIKICAgICAgIGlkPSJwYXRoMzk3NCIKICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImNvbG9yOiMwMDAwMDA7ZmlsbDpub25lO3N0cm9rZTojMzMzMzY2O3N0cm9rZS13aWR0aDoxcHg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2Utb3BhY2l0eToxO3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2UtZGFzaG9mZnNldDowO21hcmtlcjpub25lO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICBkPSJtIDc2Ni4zMTYyNSw1NzkuNjQ0MzEgMC40MzExOCwxMy43OTc2OCBjIDMuMTM2NDMsNC42NjkxNSAzLjAxODI0LDkuNjAwNjggMy4wMTgyNCwxNi4zODQ3NSBsIDAsMTU3LjM3OTgxIgogICAgICAgaWQ9InBhdGgzOTgyIgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtmaWxsOm5vbmU7c3Ryb2tlOiMzMzMzNjY7c3Ryb2tlLXdpZHRoOjFweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7bWFya2VyOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7ZW5hYmxlLWJhY2tncm91bmQ6YWNjdW11bGF0ZSIKICAgICAgIGQ9Im0gMTEyMi45MDAxLDc2NS45MTMwMyBjIC0yMDIuMzA2NjksNC42OTA1IC00MDMuNzQ0MDUsLTEuMTEzODEgLTYwNS45NTQ1NCwzLjM1MzkgLTEwLjg2MzYyLDAuMjQwMDIgLTMuMzYxNDcsLTguNTg2MyAtMjguNTM2OCwtOC41ODYzIgogICAgICAgaWQ9InBhdGgzOTg0IgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY3NjIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJjb2xvcjojMDAwMDAwO2ZpbGw6bm9uZTtzdHJva2U6IzMzMzM2NjtzdHJva2Utd2lkdGg6MXB4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLW9wYWNpdHk6MTtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLWRhc2hvZmZzZXQ6MDttYXJrZXI6bm9uZTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIgogICAgICAgZD0ibSA4NjAuMDA4MDUsNzM3LjA2NjUxIGMgMCwwIC05Ny40NDc1LDAuODU4MDYgLTE0Ny41Njg5MiwwLjg1ODA2IC01LjI2ODYxLDAgLTQuNTE1NDYsLTguMzI5ODYgLTcuMzAwODksLTguMzI5ODYgLTMuOTc0MzUsMCAtOC42MjkyNSwwLjAyMDEgLTEwLjUwOTQ4LDAuMDM1OSAtMi4zMzQ3NywwLjAxOTcgLTEuODEwOTQsOC4zNjU5NyAtNC4xNDU4LDguMzY2OTIgLTQ2LjE2ODk5LDAuMDE4OCAtMTY3LjQwNzY3LC0xLjMwNzk5IC0xNzUuMDUyNjMsLTEuMzA3OTkgLTQuNDI5NTUsMCAtOC41NzYyNywtNi40Mzk3MiAtMTMuMTMxOTgsLTYuNDM5NzIgLTEuMzYxMTUsMCAtNi4yMzg3MywwIC0xNC4zOTQ2NywwIgogICAgICAgaWQ9InBhdGgzOTg2IgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY3Nzc3Nzc2MiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImNvbG9yOiMwMDAwMDA7ZmlsbDpub25lO3N0cm9rZTojMzMzMzY2O3N0cm9rZS13aWR0aDoxcHg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2Utb3BhY2l0eToxO3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2UtZGFzaG9mZnNldDowO21hcmtlcjpub25lO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICBkPSJNIDY3NS4wMDcwMyw4MzEuMTc0MDIgNjc0LjM5NzI1LDMwOS40MDI5OSIKICAgICAgIGlkPSJwYXRoMzk4OCIKICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImNvbG9yOiMwMDAwMDA7ZmlsbDpub25lO3N0cm9rZTojMzMzMzY2O3N0cm9rZS13aWR0aDoxcHg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2Utb3BhY2l0eToxO3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2UtZGFzaG9mZnNldDowO21hcmtlcjpub25lO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICBkPSJtIDc5OS40MDE1NywzMTMuMDYxNjUgMS4yMTk1NSw0OTUuODY2NTMiCiAgICAgICBpZD0icGF0aDM5OTAiCiAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJjb2xvcjojMDAwMDAwO2ZpbGw6bm9uZTtzdHJva2U6IzMzMzM2NjtzdHJva2Utd2lkdGg6MXB4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLW9wYWNpdHk6MTtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLWRhc2hvZmZzZXQ6MDttYXJrZXI6bm9uZTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIgogICAgICAgZD0ibSA3MzYuNTk0NTIsMzEyLjQ1MTg4IC0xLjIxOTU1LDcxNi40ODgyMiIKICAgICAgIGlkPSJwYXRoMzk5MiIKICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImNvbG9yOiMwMDAwMDA7ZmlsbDpub25lO3N0cm9rZTojMzMzMzY2O3N0cm9rZS13aWR0aDoxcHg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2Utb3BhY2l0eToxO3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2UtZGFzaG9mZnNldDowO21hcmtlcjpub25lO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICBkPSJtIDUzMC4wMzA5NCw2NDMuNDU4NTkgMzkyLjM3MTU5LC0zLjAxODI1IgogICAgICAgaWQ9InBhdGg0MDQ4IgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtmaWxsOm5vbmU7c3Ryb2tlOiMzMzMzNjY7c3Ryb2tlLXdpZHRoOjFweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7bWFya2VyOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7ZW5hYmxlLWJhY2tncm91bmQ6YWNjdW11bGF0ZSIKICAgICAgIGQ9Im0gODU5LjQ1MDYsMzE0LjkwMTI4IDEuMjkzNTQsNTA3Ljk4MDU4IgogICAgICAgaWQ9InBhdGg0MDUwIgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtmaWxsOm5vbmU7c3Ryb2tlOiMzMzMzNjY7c3Ryb2tlLXdpZHRoOjAuOTk5OTk5OTRweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7bWFya2VyOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7ZW5hYmxlLWJhY2tncm91bmQ6YWNjdW11bGF0ZSIKICAgICAgIGQ9Im0gOTIxLjU0MDE3LDMxMC41ODk0OSAxLjcyNDcxLDUzMS43NTIyNyIKICAgICAgIGlkPSJwYXRoNDA1MiIKICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImNvbG9yOiMwMDAwMDA7ZmlsbDpub25lO3N0cm9rZTojMzMzMzY2O3N0cm9rZS13aWR0aDoxcHg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2Utb3BhY2l0eToxO3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2UtZGFzaG9mZnNldDowO21hcmtlcjpub25lO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICBkPSJtIDczNi4yODk2Myw0NTMuMzEwNCAxODUuNjc3MTUsLTAuMzA0ODkiCiAgICAgICBpZD0icGF0aDQxODciCiAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJjb2xvcjojMDAwMDAwO2ZpbGw6bm9uZTtzdHJva2U6IzMzMzM2NjtzdHJva2Utd2lkdGg6MXB4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLW9wYWNpdHk6MTtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLWRhc2hvZmZzZXQ6MDttYXJrZXI6bm9uZTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIgogICAgICAgZD0ibSAxMDYwLjgxMDUsNTE0Ljk2NzY3IGMgMCwwIC0zNjMuMjgxMjYsLTUuNjI2MTggLTU0NC42NTA0MiwyLjUyMTc4IC00LjE3Nzc2LDAuMTg3NjkgLTEyLjUwMDQ0LDEuMDY3MTEgLTEyLjUwMDQ0LDEuMDY3MTEgLTEuNTcwOTUsMC4xMzQxIC0yLjAwMDkzLC0yLjMyNDk1IC0yLjU5MTU1LC0zLjUwNjIzIC0wLjA5NjcsLTAuMTkzNDMgLTcuMDYwODEsLTEuOTMzNCAtNy42MjIyMSwtMS4zNzE5OSAtMi44OTMxNCwyLjg5MzE0IC03LjYzMTY3LDQuMjQ4NjkgLTEyLjE5NTU1LDQuMTE2IEwgMzY5LjIwMTcsNTE0LjUzNjUiCiAgICAgICBpZD0icGF0aDQyNjEiCiAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjc3Nzc3NjIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJjb2xvcjojMDAwMDAwO2ZpbGw6bm9uZTtzdHJva2U6IzMzMzM2NjtzdHJva2Utd2lkdGg6MXB4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLW9wYWNpdHk6MTtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLWRhc2hvZmZzZXQ6MDttYXJrZXI6bm9uZTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIgogICAgICAgZD0ibSAzOTkuODE1MzEsNDc5LjYxMTEyIDExLjY0MTgsNS42MDUzIGMgMi45ODQxMiwxLjQzNjc5IDYuNTI4NzgsLTAuNDc3MTIgOS45MTcwOCwtMC40MzExOCBsIDEyNy4xOTczOSwxLjcyNDcxIgogICAgICAgaWQ9InBhdGg0MjYzIgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY3NzYyIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtmaWxsOm5vbmU7c3Ryb2tlOiMzMzMzNjY7c3Ryb2tlLXdpZHRoOjFweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7bWFya2VyOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7ZW5hYmxlLWJhY2tncm91bmQ6YWNjdW11bGF0ZSIKICAgICAgIGQ9Ik0gNTE5LjI1MTUxLDUxNy4xMjM1NyA1MTguODIwMzIsMzA4LjQzMzYyIgogICAgICAgaWQ9InBhdGg0MjY1IgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtmaWxsOm5vbmU7c3Ryb2tlOiMzMzMzNjY7c3Ryb2tlLXdpZHRoOjFweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7bWFya2VyOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7ZW5hYmxlLWJhY2tncm91bmQ6YWNjdW11bGF0ZSIKICAgICAgIGQ9Im0gNDMyLjkyNTQ5LDM4OS43MTQ5OCBjIDExLjA0NDk2LDAgMzUuNTMzMDcsMC42MTkyNyA0Mi41Nzk3OCwtMS4wMDM5NyA4LjQwNTIyLC0xLjkzNjE4IDcuMDY2LC02Ljk1Mzc4IDE0LjE5NzEyLC02Ljk1Mzc4IDcuODA5NSwwIDYuNTQyOTEsOC4wNjIzNyAyMC4xNDE3LDguMDYyMzcgMTMuOTkwNjgsMCA0NC45NzY4OSwwLjM3ODg2IDYzLjkzOTkyLDAuMzc4ODYgMTIuMDgzOTUsMCA4Mi4wMDI2NiwwLjMwNDg5IDkzLjYwMDgxLDAuMzA0ODkgOC43NjA0NywwIDEzLjE1OTcsLTIuMjg4MjcgMjEuMzQyMTksLTcuMDEyNDMgNy4xOTUxNSwtNC4xNTQxMyAyLjA1NDU5LC05LjQ5MTM3IDIwLjQyNzU0LC04Ljg0MTc3IDIzLjE0NTQsMC44MTgzMyAxMi42NDMzNCwxNC4wMjQ4NyAzMi4zMTgxOSwxNC4wMjQ4NyAyNS4zNTk1NCwwIDEzMC45OTkwMiwwIDE1MC45MTk4NSwwIDE0LjMzMjQ0LDAgLTQuMTE5MTEsLTEzLjExMDIxIDI5LjI2OTMsLTEzLjQxNTEiCiAgICAgICBpZD0icGF0aDQyNjkiCiAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjc3Nzc3Nzc3NzYyIgLz4KICAgIDx0ZXh0CiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIgogICAgICAgc3R5bGU9ImZvbnQtc2l6ZTo5LjY1ODM3NzY1cHg7Zm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zdHJldGNoOm5vcm1hbDtsaW5lLWhlaWdodDoxMjUlO2xldHRlci1zcGFjaW5nOjBweDt3b3JkLXNwYWNpbmc6MHB4O2ZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtmb250LWZhbWlseTpWZXJkYW5hOy1pbmtzY2FwZS1mb250LXNwZWNpZmljYXRpb246VmVyZGFuYSIKICAgICAgIHg9IjU4OC42Nzk1NyIKICAgICAgIHk9IjczNS44MDQ2MyIKICAgICAgIGlkPSJ0ZXh0NDMxMCIKICAgICAgIHNvZGlwb2RpOmxpbmVzcGFjaW5nPSIxMjUlIj48dHNwYW4KICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSIKICAgICAgICAgaWQ9InRzcGFuNDMxMiIKICAgICAgICAgeD0iNTg4LjY3OTU3IgogICAgICAgICB5PSI3MzUuODA0NjMiPkxpbmNvbG48L3RzcGFuPjwvdGV4dD4KICAgIDx0ZXh0CiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIgogICAgICAgc3R5bGU9ImZvbnQtc2l6ZTo5LjY1ODM3NzY1cHg7Zm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zdHJldGNoOm5vcm1hbDtsaW5lLWhlaWdodDoxMjUlO2xldHRlci1zcGFjaW5nOjBweDt3b3JkLXNwYWNpbmc6MHB4O2ZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtmb250LWZhbWlseTpWZXJkYW5hOy1pbmtzY2FwZS1mb250LXNwZWNpZmljYXRpb246VmVyZGFuYSIKICAgICAgIHg9IjY4Ni4zOTg1IgogICAgICAgeT0iNzY1LjYyODQyIgogICAgICAgaWQ9InRleHQ0MzEwLTciCiAgICAgICBzb2RpcG9kaTpsaW5lc3BhY2luZz0iMTI1JSI+PHRzcGFuCiAgICAgICAgIHNvZGlwb2RpOnJvbGU9ImxpbmUiCiAgICAgICAgIGlkPSJ0c3BhbjQzMTItNiIKICAgICAgICAgeD0iNjg2LjM5ODUiCiAgICAgICAgIHk9Ijc2NS42Mjg0MiI+SGFycnk8L3RzcGFuPjwvdGV4dD4KICAgIDx0ZXh0CiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIgogICAgICAgc3R5bGU9ImZvbnQtc2l6ZTo5LjY1ODM3NzY1cHg7Zm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zdHJldGNoOm5vcm1hbDtsaW5lLWhlaWdodDoxMjUlO2xldHRlci1zcGFjaW5nOjBweDt3b3JkLXNwYWNpbmc6MHB4O2ZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtmb250LWZhbWlseTpWZXJkYW5hOy1pbmtzY2FwZS1mb250LXNwZWNpZmljYXRpb246VmVyZGFuYSIKICAgICAgIHg9IjcwOS44NzE4MyIKICAgICAgIHk9Ii04MDIuMzc3MzgiCiAgICAgICBpZD0idGV4dDQzMTAtNy0xIgogICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiCiAgICAgICB0cmFuc2Zvcm09Im1hdHJpeCgwLDEsLTEsMCwwLDApIj48dHNwYW4KICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSIKICAgICAgICAgaWQ9InRzcGFuNDMxMi02LTgiCiAgICAgICAgIHg9IjcwOS44NzE4MyIKICAgICAgICAgeT0iLTgwMi4zNzczOCI+V29vZGxhd248L3RzcGFuPjwvdGV4dD4KICAgIDx0ZXh0CiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIgogICAgICAgc3R5bGU9ImZvbnQtc2l6ZTo5LjY1ODM3NzY1cHg7Zm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zdHJldGNoOm5vcm1hbDtsaW5lLWhlaWdodDoxMjUlO2xldHRlci1zcGFjaW5nOjBweDt3b3JkLXNwYWNpbmc6MHB4O2ZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtmb250LWZhbWlseTpWZXJkYW5hOy1pbmtzY2FwZS1mb250LXNwZWNpZmljYXRpb246VmVyZGFuYSIKICAgICAgIHg9IjU2Mi4xMTkyNiIKICAgICAgIHk9Ii03NzEuOTY4MTQiCiAgICAgICBpZD0idGV4dDQzMTAtNy0xLTkiCiAgICAgICBzb2RpcG9kaTpsaW5lc3BhY2luZz0iMTI1JSIKICAgICAgIHRyYW5zZm9ybT0ibWF0cml4KDAsMSwtMSwwLDAsMCkiPjx0c3BhbgogICAgICAgICBzb2RpcG9kaTpyb2xlPSJsaW5lIgogICAgICAgICBpZD0idHNwYW40MzEyLTYtOC0yIgogICAgICAgICB4PSI1NjIuMTE5MjYiCiAgICAgICAgIHk9Ii03NzEuOTY4MTQiPkVkZ2Vtb29yPC90c3Bhbj48L3RleHQ+CiAgICA8dGV4dAogICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIKICAgICAgIHN0eWxlPSJmb250LXNpemU6OS42NTgzNzc2NXB4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7bGluZS1oZWlnaHQ6MTI1JTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7Zm9udC1mYW1pbHk6VmVyZGFuYTstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlZlcmRhbmEiCiAgICAgICB4PSI1OTguMzA0ODciCiAgICAgICB5PSItNzM4LjM2NjQ2IgogICAgICAgaWQ9InRleHQ0MzEwLTctMS05LTciCiAgICAgICBzb2RpcG9kaTpsaW5lc3BhY2luZz0iMTI1JSIKICAgICAgIHRyYW5zZm9ybT0ibWF0cml4KDAsMSwtMSwwLDAsMCkiPjx0c3BhbgogICAgICAgICBzb2RpcG9kaTpyb2xlPSJsaW5lIgogICAgICAgICBpZD0idHNwYW40MzEyLTYtOC0yLTkiCiAgICAgICAgIHg9IjU5OC4zMDQ4NyIKICAgICAgICAgeT0iLTczOC4zNjY0NiI+T2xpdmVyPC90c3Bhbj48L3RleHQ+CiAgICA8dGV4dAogICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIKICAgICAgIHN0eWxlPSJmb250LXNpemU6OS42NTgzNzc2NXB4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7bGluZS1oZWlnaHQ6MTI1JTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7Zm9udC1mYW1pbHk6VmVyZGFuYTstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlZlcmRhbmEiCiAgICAgICB4PSI1OTIuMTIyODYiCiAgICAgICB5PSItNjc3LjIwMzk4IgogICAgICAgaWQ9InRleHQ0MzEwLTctMS05LTctNSIKICAgICAgIHNvZGlwb2RpOmxpbmVzcGFjaW5nPSIxMjUlIgogICAgICAgdHJhbnNmb3JtPSJtYXRyaXgoMCwxLC0xLDAsMCwwKSI+PHRzcGFuCiAgICAgICAgIHNvZGlwb2RpOnJvbGU9ImxpbmUiCiAgICAgICAgIGlkPSJ0c3BhbjQzMTItNi04LTItOS00IgogICAgICAgICB4PSI1OTIuMTIyODYiCiAgICAgICAgIHk9Ii02NzcuMjAzOTgiPkhpbGxzaWRlPC90c3Bhbj48L3RleHQ+CiAgICA8dGV4dAogICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIKICAgICAgIHN0eWxlPSJmb250LXNpemU6OS42NTgzNzc2NXB4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7bGluZS1oZWlnaHQ6MTI1JTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7Zm9udC1mYW1pbHk6VmVyZGFuYTstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlZlcmRhbmEiCiAgICAgICB4PSI1OTcuMzI3MDkiCiAgICAgICB5PSItODYyLjYxNDA3IgogICAgICAgaWQ9InRleHQ0MzEwLTctMS05LTctNS0zIgogICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiCiAgICAgICB0cmFuc2Zvcm09Im1hdHJpeCgwLDEsLTEsMCwwLDApIj48dHNwYW4KICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSIKICAgICAgICAgaWQ9InRzcGFuNDMxMi02LTgtMi05LTQtMSIKICAgICAgICAgeD0iNTk3LjMyNzA5IgogICAgICAgICB5PSItODYyLjYxNDA3Ij5Sb2NrPC90c3Bhbj48L3RleHQ+CiAgICA8dGV4dAogICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIKICAgICAgIHN0eWxlPSJmb250LXNpemU6OS42NTgzNzc2NXB4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7bGluZS1oZWlnaHQ6MTI1JTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7Zm9udC1mYW1pbHk6VmVyZGFuYTstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlZlcmRhbmEiCiAgICAgICB4PSI1ODcuMzcwMTgiCiAgICAgICB5PSItOTI2LjEzNjYiCiAgICAgICBpZD0idGV4dDQzMTAtNy0xLTktNy01LTMtMiIKICAgICAgIHNvZGlwb2RpOmxpbmVzcGFjaW5nPSIxMjUlIgogICAgICAgdHJhbnNmb3JtPSJtYXRyaXgoMCwxLC0xLDAsMCwwKSI+PHRzcGFuCiAgICAgICAgIHNvZGlwb2RpOnJvbGU9ImxpbmUiCiAgICAgICAgIGlkPSJ0c3BhbjQzMTItNi04LTItOS00LTEtMyIKICAgICAgICAgeD0iNTg3LjM3MDE4IgogICAgICAgICB5PSItOTI2LjEzNjYiPldlYmI8L3RzcGFuPjwvdGV4dD4KICAgIDx0ZXh0CiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIgogICAgICAgc3R5bGU9ImZvbnQtc2l6ZTo5LjY1ODM3NzY1cHg7Zm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zdHJldGNoOm5vcm1hbDtsaW5lLWhlaWdodDoxMjUlO2xldHRlci1zcGFjaW5nOjBweDt3b3JkLXNwYWNpbmc6MHB4O2ZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtmb250LWZhbWlseTpWZXJkYW5hOy1pbmtzY2FwZS1mb250LXNwZWNpZmljYXRpb246VmVyZGFuYSIKICAgICAgIHg9Ijg3MS4xNjEwMSIKICAgICAgIHk9IjYzNy41NzUyIgogICAgICAgaWQ9InRleHQ0NDY1IgogICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiPjx0c3BhbgogICAgICAgICBzb2RpcG9kaTpyb2xlPSJsaW5lIgogICAgICAgICBpZD0idHNwYW40NDY3IgogICAgICAgICB4PSI4NzEuMTYxMDEiCiAgICAgICAgIHk9IjYzNy41NzUyIj5DZW50cmFsPC90c3Bhbj48L3RleHQ+CiAgICA8dGV4dAogICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIKICAgICAgIHN0eWxlPSJmb250LXNpemU6OS42NTgzNzc2NXB4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7bGluZS1oZWlnaHQ6MTI1JTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7Zm9udC1mYW1pbHk6VmVyZGFuYTstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlZlcmRhbmEiCiAgICAgICB4PSI4NzMuODMyMjgiCiAgICAgICB5PSI1NzcuMDMyNDciCiAgICAgICBpZD0idGV4dDQ0NjUtMyIKICAgICAgIHNvZGlwb2RpOmxpbmVzcGFjaW5nPSIxMjUlIj48dHNwYW4KICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSIKICAgICAgICAgaWQ9InRzcGFuNDQ2Ny00IgogICAgICAgICB4PSI4NzMuODMyMjgiCiAgICAgICAgIHk9IjU3Ny4wMzI0NyI+MTN0aDwvdHNwYW4+PC90ZXh0PgogICAgPHRleHQKICAgICAgIHNvZGlwb2RpOmxpbmVzcGFjaW5nPSIxMjUlIgogICAgICAgaWQ9InRleHQ0NDkwIgogICAgICAgeT0iNTEwLjI2MTgxIgogICAgICAgeD0iODc1Ljk2NjQ5IgogICAgICAgc3R5bGU9ImZvbnQtc2l6ZTo5LjY1ODM3NzY1cHg7Zm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zdHJldGNoOm5vcm1hbDtsaW5lLWhlaWdodDoxMjUlO2xldHRlci1zcGFjaW5nOjBweDt3b3JkLXNwYWNpbmc6MHB4O2ZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtmb250LWZhbWlseTpWZXJkYW5hOy1pbmtzY2FwZS1mb250LXNwZWNpZmljYXRpb246VmVyZGFuYSIKICAgICAgIHhtbDpzcGFjZT0icHJlc2VydmUiPjx0c3BhbgogICAgICAgICB5PSI1MTAuMjYxODEiCiAgICAgICAgIHg9Ijg3NS45NjY0OSIKICAgICAgICAgaWQ9InRzcGFuNDQ5MiIKICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSI+MjFzdDwvdHNwYW4+PC90ZXh0PgogICAgPHRleHQKICAgICAgIHhtbDpzcGFjZT0icHJlc2VydmUiCiAgICAgICBzdHlsZT0iZm9udC1zaXplOjkuNjU4Mzc3NjVweDtmb250LXN0eWxlOm5vcm1hbDtmb250LXZhcmlhbnQ6bm9ybWFsO2ZvbnQtd2VpZ2h0Om5vcm1hbDtmb250LXN0cmV0Y2g6bm9ybWFsO2xpbmUtaGVpZ2h0OjEyNSU7bGV0dGVyLXNwYWNpbmc6MHB4O3dvcmQtc3BhY2luZzowcHg7ZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lO2ZvbnQtZmFtaWx5OlZlcmRhbmE7LWlua3NjYXBlLWZvbnQtc3BlY2lmaWNhdGlvbjpWZXJkYW5hIgogICAgICAgeD0iODgxLjMxNjU5IgogICAgICAgeT0iNDUwLjE5ODc2IgogICAgICAgaWQ9InRleHQ0NDk0IgogICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiPjx0c3BhbgogICAgICAgICBzb2RpcG9kaTpyb2xlPSJsaW5lIgogICAgICAgICBpZD0idHNwYW40NDk2IgogICAgICAgICB4PSI4ODEuMzE2NTkiCiAgICAgICAgIHk9IjQ1MC4xOTg3NiI+Mjl0aDwvdHNwYW4+PC90ZXh0PgogICAgPHRleHQKICAgICAgIHhtbDpzcGFjZT0icHJlc2VydmUiCiAgICAgICBzdHlsZT0iZm9udC1zaXplOjkuNjU4Mzc3NjVweDtmb250LXN0eWxlOm5vcm1hbDtmb250LXZhcmlhbnQ6bm9ybWFsO2ZvbnQtd2VpZ2h0Om5vcm1hbDtmb250LXN0cmV0Y2g6bm9ybWFsO2xpbmUtaGVpZ2h0OjEyNSU7bGV0dGVyLXNwYWNpbmc6MHB4O3dvcmQtc3BhY2luZzowcHg7ZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lO2ZvbnQtZmFtaWx5OlZlcmRhbmE7LWlua3NjYXBlLWZvbnQtc3BlY2lmaWNhdGlvbjpWZXJkYW5hIgogICAgICAgeD0iNjE1Ljc5MjQ4IgogICAgICAgeT0iMzg3Ljc0NzE2IgogICAgICAgaWQ9InRleHQ0NDY1LTMtMSIKICAgICAgIHNvZGlwb2RpOmxpbmVzcGFjaW5nPSIxMjUlIj48dHNwYW4KICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSIKICAgICAgICAgaWQ9InRzcGFuNDQ2Ny00LTEiCiAgICAgICAgIHg9IjYxNS43OTI0OCIKICAgICAgICAgeT0iMzg3Ljc0NzE2Ij4zN3RoPC90c3Bhbj48L3RleHQ+CiAgICA8dGV4dAogICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiCiAgICAgICBpZD0idGV4dDQ1MTkiCiAgICAgICB5PSI0ODEuNjUyODYiCiAgICAgICB4PSI0ODQuNjkwMzciCiAgICAgICBzdHlsZT0iZm9udC1zaXplOjkuNjU4Mzc3NjVweDtmb250LXN0eWxlOm5vcm1hbDtmb250LXZhcmlhbnQ6bm9ybWFsO2ZvbnQtd2VpZ2h0Om5vcm1hbDtmb250LXN0cmV0Y2g6bm9ybWFsO2xpbmUtaGVpZ2h0OjEyNSU7bGV0dGVyLXNwYWNpbmc6MHB4O3dvcmQtc3BhY2luZzowcHg7ZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lO2ZvbnQtZmFtaWx5OlZlcmRhbmE7LWlua3NjYXBlLWZvbnQtc3BlY2lmaWNhdGlvbjpWZXJkYW5hIgogICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuCiAgICAgICAgIHk9IjQ4MS42NTI4NiIKICAgICAgICAgeD0iNDg0LjY5MDM3IgogICAgICAgICBpZD0idHNwYW40NTIxIgogICAgICAgICBzb2RpcG9kaTpyb2xlPSJsaW5lIj4yNXRoPC90c3Bhbj48L3RleHQ+CiAgICA8dGV4dAogICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIKICAgICAgIHN0eWxlPSJmb250LXNpemU6OS42NTgzNzc2NXB4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7bGluZS1oZWlnaHQ6MTI1JTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7Zm9udC1mYW1pbHk6VmVyZGFuYTstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlZlcmRhbmEiCiAgICAgICB4PSI1NjMuMDQ2NzUiCiAgICAgICB5PSI1MTMuMzYxMzMiCiAgICAgICBpZD0idGV4dDQ1MjMiCiAgICAgICBzb2RpcG9kaTpsaW5lc3BhY2luZz0iMTI1JSI+PHRzcGFuCiAgICAgICAgIHNvZGlwb2RpOnJvbGU9ImxpbmUiCiAgICAgICAgIGlkPSJ0c3BhbjQ1MjUiCiAgICAgICAgIHg9IjU2My4wNDY3NSIKICAgICAgICAgeT0iNTEzLjM2MTMzIj4yMXN0PC90c3Bhbj48L3RleHQ+CiAgICA8dGV4dAogICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiCiAgICAgICBpZD0idGV4dDQ1MjciCiAgICAgICB5PSI1NzcuODk0ODQiCiAgICAgICB4PSI1NjUuOTcxNSIKICAgICAgIHN0eWxlPSJmb250LXNpemU6OS42NTgzNzc2NXB4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7bGluZS1oZWlnaHQ6MTI1JTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7Zm9udC1mYW1pbHk6VmVyZGFuYTstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlZlcmRhbmEiCiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIj48dHNwYW4KICAgICAgICAgeT0iNTc3Ljg5NDg0IgogICAgICAgICB4PSI1NjUuOTcxNSIKICAgICAgICAgaWQ9InRzcGFuNDUyOSIKICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSI+MTN0aDwvdHNwYW4+PC90ZXh0PgogICAgPHRleHQKICAgICAgIHRyYW5zZm9ybT0ibWF0cml4KDAsMSwtMSwwLDAsMCkiCiAgICAgICBzb2RpcG9kaTpsaW5lc3BhY2luZz0iMTI1JSIKICAgICAgIGlkPSJ0ZXh0NDUzMSIKICAgICAgIHk9Ii00NjAuNzMzMTIiCiAgICAgICB4PSI0MzMuNTgwNzUiCiAgICAgICBzdHlsZT0iZm9udC1zaXplOjkuNjU4Mzc3NjVweDtmb250LXN0eWxlOm5vcm1hbDtmb250LXZhcmlhbnQ6bm9ybWFsO2ZvbnQtd2VpZ2h0Om5vcm1hbDtmb250LXN0cmV0Y2g6bm9ybWFsO2xpbmUtaGVpZ2h0OjEyNSU7bGV0dGVyLXNwYWNpbmc6MHB4O3dvcmQtc3BhY2luZzowcHg7ZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lO2ZvbnQtZmFtaWx5OlZlcmRhbmE7LWlua3NjYXBlLWZvbnQtc3BlY2lmaWNhdGlvbjpWZXJkYW5hIgogICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuCiAgICAgICAgIHk9Ii00NjAuNzMzMTIiCiAgICAgICAgIHg9IjQzMy41ODA3NSIKICAgICAgICAgaWQ9InRzcGFuNDUzMyIKICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSI+QW1pZG9uPC90c3Bhbj48L3RleHQ+CiAgICA8dGV4dAogICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIKICAgICAgIHN0eWxlPSJmb250LXNpemU6OS42NTgzNzc2NXB4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7bGluZS1oZWlnaHQ6MTI1JTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7Zm9udC1mYW1pbHk6VmVyZGFuYTstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlZlcmRhbmEiCiAgICAgICB4PSI0MDUuNTMwOTgiCiAgICAgICB5PSItNTIzLjU0MDE2IgogICAgICAgaWQ9InRleHQ0NTM1IgogICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiCiAgICAgICB0cmFuc2Zvcm09Im1hdHJpeCgwLDEsLTEsMCwwLDApIj48dHNwYW4KICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSIKICAgICAgICAgaWQ9InRzcGFuNDUzNyIKICAgICAgICAgeD0iNDA1LjUzMDk4IgogICAgICAgICB5PSItNTIzLjU0MDE2Ij5BcmthbnNhczwvdHNwYW4+PC90ZXh0PgogICAgPHRleHQKICAgICAgIHRyYW5zZm9ybT0ibWF0cml4KDAsMSwtMSwwLDAsMCkiCiAgICAgICBzb2RpcG9kaTpsaW5lc3BhY2luZz0iMTI1JSIKICAgICAgIGlkPSJ0ZXh0NDUzOSIKICAgICAgIHk9Ii0zNzIuNTg1OTQiCiAgICAgICB4PSI3NDUuNDg0NjIiCiAgICAgICBzdHlsZT0iZm9udC1zaXplOjkuNjU4Mzc3NjVweDtmb250LXN0eWxlOm5vcm1hbDtmb250LXZhcmlhbnQ6bm9ybWFsO2ZvbnQtd2VpZ2h0Om5vcm1hbDtmb250LXN0cmV0Y2g6bm9ybWFsO2xpbmUtaGVpZ2h0OjEyNSU7bGV0dGVyLXNwYWNpbmc6MHB4O3dvcmQtc3BhY2luZzowcHg7ZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lO2ZvbnQtZmFtaWx5OlZlcmRhbmE7LWlua3NjYXBlLWZvbnQtc3BlY2lmaWNhdGlvbjpWZXJkYW5hIgogICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuCiAgICAgICAgIHk9Ii0zNzIuNTg1OTQiCiAgICAgICAgIHg9Ijc0NS40ODQ2MiIKICAgICAgICAgaWQ9InRzcGFuNDU0MSIKICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSI+V2VzdDwvdHNwYW4+PC90ZXh0PgogICAgPHRleHQKICAgICAgIHhtbDpzcGFjZT0icHJlc2VydmUiCiAgICAgICBzdHlsZT0iZm9udC1zaXplOjkuNjU4Mzc3NjVweDtmb250LXN0eWxlOm5vcm1hbDtmb250LXZhcmlhbnQ6bm9ybWFsO2ZvbnQtd2VpZ2h0Om5vcm1hbDtmb250LXN0cmV0Y2g6bm9ybWFsO2xpbmUtaGVpZ2h0OjEyNSU7bGV0dGVyLXNwYWNpbmc6MHB4O3dvcmQtc3BhY2luZzowcHg7ZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lO2ZvbnQtZmFtaWx5OlZlcmRhbmE7LWlua3NjYXBlLWZvbnQtc3BlY2lmaWNhdGlvbjpWZXJkYW5hIgogICAgICAgeD0iNTk2LjcyODMzIgogICAgICAgeT0iLTUzMS4yNTkyOCIKICAgICAgIGlkPSJ0ZXh0NDU0MyIKICAgICAgIHNvZGlwb2RpOmxpbmVzcGFjaW5nPSIxMjUlIgogICAgICAgdHJhbnNmb3JtPSJtYXRyaXgoMCwxLC0xLDAsMCwwKSI+PHRzcGFuCiAgICAgICAgIHNvZGlwb2RpOnJvbGU9ImxpbmUiCiAgICAgICAgIGlkPSJ0c3BhbjQ1NDUiCiAgICAgICAgIHg9IjU5Ni43MjgzMyIKICAgICAgICAgeT0iLTUzMS4yNTkyOCI+V2FjbzwvdHNwYW4+PC90ZXh0PgogICAgPHRleHQKICAgICAgIHRyYW5zZm9ybT0ibWF0cml4KDAsMSwtMSwwLDAsMCkiCiAgICAgICBzb2RpcG9kaTpsaW5lc3BhY2luZz0iMTI1JSIKICAgICAgIGlkPSJ0ZXh0NDU1NSIKICAgICAgIHk9Ii0xMjIuNTAyOTUiCiAgICAgICB4PSI1OTUuNDM0ODEiCiAgICAgICBzdHlsZT0iZm9udC1zaXplOjkuNjU4Mzc3NjVweDtmb250LXN0eWxlOm5vcm1hbDtmb250LXZhcmlhbnQ6bm9ybWFsO2ZvbnQtd2VpZ2h0Om5vcm1hbDtmb250LXN0cmV0Y2g6bm9ybWFsO2xpbmUtaGVpZ2h0OjEyNSU7bGV0dGVyLXNwYWNpbmc6MHB4O3dvcmQtc3BhY2luZzowcHg7ZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lO2ZvbnQtZmFtaWx5OlZlcmRhbmE7LWlua3NjYXBlLWZvbnQtc3BlY2lmaWNhdGlvbjpWZXJkYW5hIgogICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuCiAgICAgICAgIHk9Ii0xMjIuNTAyOTUiCiAgICAgICAgIHg9IjU5NS40MzQ4MSIKICAgICAgICAgaWQ9InRzcGFuNDU1NyIKICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSI+TWF6aWU8L3RzcGFuPjwvdGV4dD4KICAgIDx0ZXh0CiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIgogICAgICAgc3R5bGU9ImZvbnQtc2l6ZTo5LjY1ODM3NzY1cHg7Zm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zdHJldGNoOm5vcm1hbDtsaW5lLWhlaWdodDoxMjUlO2xldHRlci1zcGFjaW5nOjBweDt3b3JkLXNwYWNpbmc6MHB4O2ZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtmb250LWZhbWlseTpWZXJkYW5hOy1pbmtzY2FwZS1mb250LXNwZWNpZmljYXRpb246VmVyZGFuYSIKICAgICAgIHg9IjY5NS43NzI5NSIKICAgICAgIHk9IjE2Mi4wNjg3NyIKICAgICAgIGlkPSJ0ZXh0NDU1OSIKICAgICAgIHNvZGlwb2RpOmxpbmVzcGFjaW5nPSIxMjUlIgogICAgICAgdHJhbnNmb3JtPSJtYXRyaXgoMC43MDcxMDY3OCwwLjcwNzEwNjc4LC0wLjcwNzEwNjc4LDAuNzA3MTA2NzgsMCwwKSI+PHRzcGFuCiAgICAgICAgIHNvZGlwb2RpOnJvbGU9ImxpbmUiCiAgICAgICAgIGlkPSJ0c3BhbjQ1NjEiCiAgICAgICAgIHg9IjY5NS43NzI5NSIKICAgICAgICAgeT0iMTYyLjA2ODc3Ij5ab288L3RzcGFuPjwvdGV4dD4KICAgIDx0ZXh0CiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIgogICAgICAgc3R5bGU9ImZvbnQtc2l6ZTo5LjY1ODM3NzY1cHg7Zm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zdHJldGNoOm5vcm1hbDtsaW5lLWhlaWdodDoxMjUlO2xldHRlci1zcGFjaW5nOjBweDt3b3JkLXNwYWNpbmc6MHB4O2ZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtmb250LWZhbWlseTpWZXJkYW5hOy1pbmtzY2FwZS1mb250LXNwZWNpZmljYXRpb246VmVyZGFuYSIKICAgICAgIHg9IjI0MC41ODk5NyIKICAgICAgIHk9IjU3NC40NDU0MyIKICAgICAgIGlkPSJ0ZXh0NDU2MyIKICAgICAgIHNvZGlwb2RpOmxpbmVzcGFjaW5nPSIxMjUlIj48dHNwYW4KICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSIKICAgICAgICAgaWQ9InRzcGFuNDU2NSIKICAgICAgICAgeD0iMjQwLjU4OTk3IgogICAgICAgICB5PSI1NzQuNDQ1NDMiPjEzdGg8L3RzcGFuPjwvdGV4dD4KICAgIDx0ZXh0CiAgICAgICBzb2RpcG9kaTpsaW5lc3BhY2luZz0iMTI1JSIKICAgICAgIGlkPSJ0ZXh0NDU2NyIKICAgICAgIHk9IjUxMS42MzY2MyIKICAgICAgIHg9IjIwNi4wMzE3NSIKICAgICAgIHN0eWxlPSJmb250LXNpemU6OS42NTgzNzc2NXB4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7bGluZS1oZWlnaHQ6MTI1JTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7Zm9udC1mYW1pbHk6VmVyZGFuYTstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlZlcmRhbmEiCiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIj48dHNwYW4KICAgICAgICAgeT0iNTExLjYzNjYzIgogICAgICAgICB4PSIyMDYuMDMxNzUiCiAgICAgICAgIGlkPSJ0c3BhbjQ1NjkiCiAgICAgICAgIHNvZGlwb2RpOnJvbGU9ImxpbmUiPjIxc3Q8L3RzcGFuPjwvdGV4dD4KICAgIDx0ZXh0CiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIgogICAgICAgc3R5bGU9ImZvbnQtc2l6ZTo5LjY1ODM3NzY1cHg7Zm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zdHJldGNoOm5vcm1hbDtsaW5lLWhlaWdodDoxMjUlO2xldHRlci1zcGFjaW5nOjBweDt3b3JkLXNwYWNpbmc6MHB4O2ZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtmb250LWZhbWlseTpWZXJkYW5hOy1pbmtzY2FwZS1mb250LXNwZWNpZmljYXRpb246VmVyZGFuYSIKICAgICAgIHg9IjYyMC40NDMxMiIKICAgICAgIHk9Ii01MDYuNjgyMTkiCiAgICAgICBpZD0idGV4dDQ1NzEiCiAgICAgICBzb2RpcG9kaTpsaW5lc3BhY2luZz0iMTI1JSIKICAgICAgIHRyYW5zZm9ybT0ibWF0cml4KDAsMSwtMSwwLDAsMCkiPjx0c3BhbgogICAgICAgICBzb2RpcG9kaTpyb2xlPSJsaW5lIgogICAgICAgICBpZD0idHNwYW40NTczIgogICAgICAgICB4PSI2MjAuNDQzMTIiCiAgICAgICAgIHk9Ii01MDYuNjgyMTkiPk5pbXM8L3RzcGFuPjwvdGV4dD4KICAgIDx0ZXh0CiAgICAgICBzb2RpcG9kaTpsaW5lc3BhY2luZz0iMTI1JSIKICAgICAgIGlkPSJ0ZXh0NDU4MyIKICAgICAgIHk9IjY5OC44NDAwOSIKICAgICAgIHg9IjM3MC4yMTY4NiIKICAgICAgIHN0eWxlPSJmb250LXNpemU6OS42NTgzNzc2NXB4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7bGluZS1oZWlnaHQ6MTI1JTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7Zm9udC1mYW1pbHk6VmVyZGFuYTstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlZlcmRhbmEiCiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIj48dHNwYW4KICAgICAgICAgeT0iNjk4Ljg0MDA5IgogICAgICAgICB4PSIzNzAuMjE2ODYiCiAgICAgICAgIGlkPSJ0c3BhbjQ1ODUiCiAgICAgICAgIHNvZGlwb2RpOnJvbGU9ImxpbmUiPk1hcGxlPC90c3Bhbj48L3RleHQ+CiAgICA8dGV4dAogICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIKICAgICAgIHN0eWxlPSJmb250LXNpemU6OS42NTgzNzc2NXB4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7bGluZS1oZWlnaHQ6MTI1JTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7Zm9udC1mYW1pbHk6VmVyZGFuYTstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlZlcmRhbmEiCiAgICAgICB4PSIzODQuMDg0MiIKICAgICAgIHk9IjY4MC44NTEzOCIKICAgICAgIGlkPSJ0ZXh0NDU5OSIKICAgICAgIHNvZGlwb2RpOmxpbmVzcGFjaW5nPSIxMjUlIj48dHNwYW4KICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSIKICAgICAgICAgaWQ9InRzcGFuNDYwMSIKICAgICAgICAgeD0iMzg0LjA4NDIiCiAgICAgICAgIHk9IjY4MC44NTEzOCI+RG91Z2xhczwvdHNwYW4+PC90ZXh0PgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJjb2xvcjojMDAwMDAwO2ZpbGw6bm9uZTtzdHJva2U6IzMzMzM2NjtzdHJva2Utd2lkdGg6MXB4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLW9wYWNpdHk6MTtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLWRhc2hvZmZzZXQ6MDttYXJrZXI6bm9uZTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIgogICAgICAgZD0ibSAzNjcuOTA4MTcsMTAwOS45NTk2IDI2My4wMTgzMywwIgogICAgICAgaWQ9InBhdGg0NjA1IgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIgLz4KICAgIDx0ZXh0CiAgICAgICB0cmFuc2Zvcm09Im1hdHJpeCgwLDEsLTEsMCwwLDApIgogICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiCiAgICAgICBpZD0idGV4dDQ2MDciCiAgICAgICB5PSItNDMzLjEzNzc2IgogICAgICAgeD0iNzM2LjI2NzQ2IgogICAgICAgc3R5bGU9ImZvbnQtc2l6ZTo5LjY1ODM3NzY1cHg7Zm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zdHJldGNoOm5vcm1hbDtsaW5lLWhlaWdodDoxMjUlO2xldHRlci1zcGFjaW5nOjBweDt3b3JkLXNwYWNpbmc6MHB4O2ZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtmb250LWZhbWlseTpWZXJkYW5hOy1pbmtzY2FwZS1mb250LXNwZWNpZmljYXRpb246VmVyZGFuYSIKICAgICAgIHhtbDpzcGFjZT0icHJlc2VydmUiPjx0c3BhbgogICAgICAgICB5PSItNDMzLjEzNzc2IgogICAgICAgICB4PSI3MzYuMjY3NDYiCiAgICAgICAgIGlkPSJ0c3BhbjQ2MDkiCiAgICAgICAgIHNvZGlwb2RpOnJvbGU9ImxpbmUiPk1lcmlkaWFuPC90c3Bhbj48L3RleHQ+CiAgICA8dGV4dAogICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiCiAgICAgICBpZD0idGV4dDQ5NzkiCiAgICAgICB5PSI2NDAuMjA1MjYiCiAgICAgICB4PSI1NzIuODMyMTUiCiAgICAgICBzdHlsZT0iZm9udC1zaXplOjkuNjU4Mzc3NjVweDtmb250LXN0eWxlOm5vcm1hbDtmb250LXZhcmlhbnQ6bm9ybWFsO2ZvbnQtd2VpZ2h0Om5vcm1hbDtmb250LXN0cmV0Y2g6bm9ybWFsO2xpbmUtaGVpZ2h0OjEyNSU7bGV0dGVyLXNwYWNpbmc6MHB4O3dvcmQtc3BhY2luZzowcHg7ZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lO2ZvbnQtZmFtaWx5OlZlcmRhbmE7LWlua3NjYXBlLWZvbnQtc3BlY2lmaWNhdGlvbjpWZXJkYW5hIgogICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuCiAgICAgICAgIHk9IjY0MC4yMDUyNiIKICAgICAgICAgeD0iNTcyLjgzMjE1IgogICAgICAgICBpZD0idHNwYW40OTgxIgogICAgICAgICBzb2RpcG9kaTpyb2xlPSJsaW5lIj5DZW50cmFsPC90c3Bhbj48L3RleHQ+CiAgICA8dGV4dAogICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIKICAgICAgIHN0eWxlPSJmb250LXNpemU6OS42NTgzNzc2NXB4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7bGluZS1oZWlnaHQ6MTI1JTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7Zm9udC1mYW1pbHk6VmVyZGFuYTstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlZlcmRhbmEiCiAgICAgICB4PSI1NzUuMDg5NjYiCiAgICAgICB5PSI2NzAuOTAzNSIKICAgICAgIGlkPSJ0ZXh0NDk4MyIKICAgICAgIHNvZGlwb2RpOmxpbmVzcGFjaW5nPSIxMjUlIj48dHNwYW4KICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSIKICAgICAgICAgaWQ9InRzcGFuNDk4NSIKICAgICAgICAgeD0iNTc1LjA4OTY2IgogICAgICAgICB5PSI2NzAuOTAzNSI+RG91Z2xhczwvdHNwYW4+PC90ZXh0PgogICAgPHRleHQKICAgICAgIHhtbDpzcGFjZT0icHJlc2VydmUiCiAgICAgICBzdHlsZT0iZm9udC1zaXplOjkuNjU4Mzc3NjVweDtmb250LXN0eWxlOm5vcm1hbDtmb250LXZhcmlhbnQ6bm9ybWFsO2ZvbnQtd2VpZ2h0Om5vcm1hbDtmb250LXN0cmV0Y2g6bm9ybWFsO2xpbmUtaGVpZ2h0OjEyNSU7bGV0dGVyLXNwYWNpbmc6MHB4O3dvcmQtc3BhY2luZzowcHg7ZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lO2ZvbnQtZmFtaWx5OlZlcmRhbmE7LWlua3NjYXBlLWZvbnQtc3BlY2lmaWNhdGlvbjpWZXJkYW5hIgogICAgICAgeD0iNDk5LjQ4OTYyIgogICAgICAgeT0iMTAwOC42MDY5IgogICAgICAgaWQ9InRleHQ1MDQ3IgogICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiPjx0c3BhbgogICAgICAgICBzb2RpcG9kaTpyb2xlPSJsaW5lIgogICAgICAgICBpZD0idHNwYW41MDQ5IgogICAgICAgICB4PSI0OTkuNDg5NjIiCiAgICAgICAgIHk9IjEwMDguNjA2OSI+NDd0aDwvdHNwYW4+PC90ZXh0PgogICAgPHRleHQKICAgICAgIHhtbDpzcGFjZT0icHJlc2VydmUiCiAgICAgICBzdHlsZT0iZm9udC1zaXplOjkuNjU4Mzc3NjVweDtmb250LXN0eWxlOm5vcm1hbDtmb250LXZhcmlhbnQ6bm9ybWFsO2ZvbnQtd2VpZ2h0Om5vcm1hbDtmb250LXN0cmV0Y2g6bm9ybWFsO2xpbmUtaGVpZ2h0OjEyNSU7bGV0dGVyLXNwYWNpbmc6MHB4O3dvcmQtc3BhY2luZzowcHg7ZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lO2ZvbnQtZmFtaWx5OlZlcmRhbmE7LWlua3NjYXBlLWZvbnQtc3BlY2lmaWNhdGlvbjpWZXJkYW5hIgogICAgICAgeD0iMjE2LjY0NTQzIgogICAgICAgeT0iNzI1Ljk4Mjk3IgogICAgICAgaWQ9InRleHQ1MDUxIgogICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiPjx0c3BhbgogICAgICAgICBzb2RpcG9kaTpyb2xlPSJsaW5lIgogICAgICAgICBpZD0idHNwYW41MDUzIgogICAgICAgICB4PSIyMTYuNjQ1NDMiCiAgICAgICAgIHk9IjcyNS45ODI5NyI+S2VsbG9nZzwvdHNwYW4+PC90ZXh0PgogICAgPGZsb3dSb290CiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIgogICAgICAgaWQ9ImZsb3dSb290NTA1NSIKICAgICAgIHN0eWxlPSJmb250LXNpemU6MThweDtmb250LXN0eWxlOm5vcm1hbDtmb250LXZhcmlhbnQ6bm9ybWFsO2ZvbnQtd2VpZ2h0Om5vcm1hbDtmb250LXN0cmV0Y2g6bm9ybWFsO2xpbmUtaGVpZ2h0OjEyNSU7bGV0dGVyLXNwYWNpbmc6MHB4O3dvcmQtc3BhY2luZzowcHg7ZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lO2ZvbnQtZmFtaWx5OlZlcmRhbmE7LWlua3NjYXBlLWZvbnQtc3BlY2lmaWNhdGlvbjpWZXJkYW5hIgogICAgICAgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCwyODcuMzYyMTgpIj48Zmxvd1JlZ2lvbgogICAgICAgICBpZD0iZmxvd1JlZ2lvbjUwNTciPjxyZWN0CiAgICAgICAgICAgaWQ9InJlY3Q1MDU5IgogICAgICAgICAgIHdpZHRoPSIzNDMuNTcxNDQiCiAgICAgICAgICAgaGVpZ2h0PSIxMDMuNTcxNDMiCiAgICAgICAgICAgeD0iMTkuMjg1NzE1IgogICAgICAgICAgIHk9IjE3LjE0Mjg1NyIKICAgICAgICAgICBzdHlsZT0iZm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zdHJldGNoOm5vcm1hbDtmb250LWZhbWlseTpWZXJkYW5hOy1pbmtzY2FwZS1mb250LXNwZWNpZmljYXRpb246VmVyZGFuYSIgLz48L2Zsb3dSZWdpb24+PGZsb3dQYXJhCiAgICAgICAgIGlkPSJmbG93UGFyYTUwNjEiPjwvZmxvd1BhcmE+PC9mbG93Um9vdD4gICAgPHRleHQKICAgICAgIHRyYW5zZm9ybT0ibWF0cml4KDAsMSwtMSwwLDAsMCkiCiAgICAgICBzb2RpcG9kaTpsaW5lc3BhY2luZz0iMTI1JSIKICAgICAgIGlkPSJ0ZXh0NDYwNy03IgogICAgICAgeT0iLTUwOC4xODk3MyIKICAgICAgIHg9Ijc3NC44NzU2MSIKICAgICAgIHN0eWxlPSJmb250LXNpemU6OS42NTgzNzc2NXB4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7bGluZS1oZWlnaHQ6MTI1JTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7Zm9udC1mYW1pbHk6VmVyZGFuYTstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlZlcmRhbmEiCiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIj48dHNwYW4KICAgICAgICAgeT0iLTUwOC4xODk3MyIKICAgICAgICAgeD0iNzc0Ljg3NTYxIgogICAgICAgICBpZD0idHNwYW40NjA5LTciCiAgICAgICAgIHNvZGlwb2RpOnJvbGU9ImxpbmUiPk1jQ2xlYW48L3RzcGFuPjwvdGV4dD4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtmaWxsOm5vbmU7c3Ryb2tlOiMzMzMzNjY7c3Ryb2tlLXdpZHRoOjFweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7bWFya2VyOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7ZW5hYmxlLWJhY2tncm91bmQ6YWNjdW11bGF0ZSIKICAgICAgIGQ9Im0gMzY0LjE1OTk5LDY1OC40Mjg5MSAyOTkuNTEwMjMsLTEuMDEwMTYgYyA2LjQ5ODcyLC0wLjAyMTkgNi45NzcxOSw5LjI1NDEyIDE2LjU5NjMxLDkuMzkyNDcgMTIuMDU0MjcsMC4xNzMzOSAyOS4xMTA4MywtMC41MzU3MiA1NC4xMTQzNywtMC4zMDExIgogICAgICAgaWQ9InBhdGg1NDQwIgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgIHRyYW5zZm9ybT0idHJhbnNsYXRlKDAsMjg3LjM2MjE4KSIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY3NzYyIgLz4KICAgIDx0ZXh0CiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIgogICAgICAgc3R5bGU9ImZvbnQtc2l6ZTo5LjY1ODM3NzY1cHg7Zm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zdHJldGNoOm5vcm1hbDtsaW5lLWhlaWdodDoxMjUlO2xldHRlci1zcGFjaW5nOjBweDt3b3JkLXNwYWNpbmc6MHB4O2ZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtmb250LWZhbWlseTpWZXJkYW5hOy1pbmtzY2FwZS1mb250LXNwZWNpZmljYXRpb246VmVyZGFuYSIKICAgICAgIHg9IjM3My45OTMwNCIKICAgICAgIHk9Ijk0NC4zNTc1NCIKICAgICAgIGlkPSJ0ZXh0NTA0Ny05IgogICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiPjx0c3BhbgogICAgICAgICBzb2RpcG9kaTpyb2xlPSJsaW5lIgogICAgICAgICBpZD0idHNwYW41MDQ5LTMiCiAgICAgICAgIHg9IjM3My45OTMwNCIKICAgICAgICAgeT0iOTQ0LjM1NzU0Ij5NYWNBcnRodXI8L3RzcGFuPjwvdGV4dD4KICAgIDx0ZXh0CiAgICAgICB0cmFuc2Zvcm09Im1hdHJpeCgwLDEsLTEsMCwwLDApIgogICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiCiAgICAgICBpZD0idGV4dDQ2MDctNy0xIgogICAgICAgeT0iLTQ5MC4yNDU5NyIKICAgICAgIHg9Ijc4MC44NDYwNyIKICAgICAgIHN0eWxlPSJmb250LXNpemU6OS42NTgzNzc2NXB4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7bGluZS1oZWlnaHQ6MTI1JTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7Zm9udC1mYW1pbHk6VmVyZGFuYTstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlZlcmRhbmEiCiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIj48dHNwYW4KICAgICAgICAgeT0iLTQ5MC4yNDU5NyIKICAgICAgICAgeD0iNzgwLjg0NjA3IgogICAgICAgICBpZD0idHNwYW40NjA5LTctOSIKICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSI+U2VuZWNhPC90c3Bhbj48L3RleHQ+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImNvbG9yOiMwMDAwMDA7ZmlsbDpub25lO3N0cm9rZTojMzMzMzY2O3N0cm9rZS13aWR0aDoxcHg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2Utb3BhY2l0eToxO3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2UtZGFzaG9mZnNldDowO21hcmtlcjpub25lO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICBkPSJtIDM2Ny42OTU1Myw1MzcuMjEwNiAxNDEuMjgzMDMsLTEuMDEwMTUgYyA2LjQ4OTk5LC0wLjA0NjQgMTIuNzgxMTQsNy4yMzU0NSAxOS4xOTI5LDcuMzIzNiA1NS45MjM2MiwwLjc2ODkgMTU4LjY4OTk3LC0wLjE3MzMzIDIzNi41MTQwMiwtMS4wMTAxNSA3LjgzOTU2LC0wLjA4NDMgMjIuNjMxNDcsLTE5Ljg1MzU1IDMwLjMwNDU3LC0yMC40NTU1OSAyMi4yNjU4OSwtMS4zNTE4MSA0NS4xNzk0NSwtMC41MDUwNyA2Ny42ODAyMiwtMC41MDUwNyAxNi4xNDczMSwtMC42MzI0MSAzLjYxMDE2LDIwLjcwODEzIDI2Ljc2OTA0LDIwLjcwODEzIGwgMjQzLjQ0Njc5LC0xLjAxMDE2IgogICAgICAgaWQ9InBhdGg1NDk2IgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgIHRyYW5zZm9ybT0idHJhbnNsYXRlKDAsMjg3LjM2MjE4KSIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY3NzY2NjY2MiIC8+CiAgICA8dGV4dAogICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIKICAgICAgIHN0eWxlPSJmb250LXNpemU6OS42NTgzNzc2NXB4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7bGluZS1oZWlnaHQ6MTI1JTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7Zm9udC1mYW1pbHk6VmVyZGFuYTstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlZlcmRhbmEiCiAgICAgICB4PSI2ODUuMjA4MTMiCiAgICAgICB5PSI4MjcuNTMwODIiCiAgICAgICBpZD0idGV4dDQzMTAtNy04IgogICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiPjx0c3BhbgogICAgICAgICBzb2RpcG9kaTpyb2xlPSJsaW5lIgogICAgICAgICBpZD0idHNwYW40MzEyLTYtNiIKICAgICAgICAgeD0iNjg1LjIwODEzIgogICAgICAgICB5PSI4MjcuNTMwODIiPlBhd25lZTwvdHNwYW4+PC90ZXh0PgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJjb2xvcjojMDAwMDAwO2ZpbGw6bm9uZTtzdHJva2U6IzMzMzM2NjtzdHJva2Utd2lkdGg6MXB4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLW9wYWNpdHk6MTtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLWRhc2hvZmZzZXQ6MDttYXJrZXI6bm9uZTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIgogICAgICAgZD0iTSA1NTQuMjg1NzIsNzIxLjQyODU3IDU1MCw1NDMuMjE0MjkgNTQ3LjE0Mjg2LDEwMi41IDU0Ni43ODU3MiwyMy4yMTQyODUiCiAgICAgICBpZD0icGF0aDU1MTkiCiAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCwyODcuMzYyMTgpIiAvPgogICAgPHRleHQKICAgICAgIHhtbDpzcGFjZT0icHJlc2VydmUiCiAgICAgICBzdHlsZT0iZm9udC1zaXplOjkuNjU4Mzc3NjVweDtmb250LXN0eWxlOm5vcm1hbDtmb250LXZhcmlhbnQ6bm9ybWFsO2ZvbnQtd2VpZ2h0Om5vcm1hbDtmb250LXN0cmV0Y2g6bm9ybWFsO2xpbmUtaGVpZ2h0OjEyNSU7bGV0dGVyLXNwYWNpbmc6MHB4O3dvcmQtc3BhY2luZzowcHg7ZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lO2ZvbnQtZmFtaWx5OlZlcmRhbmE7LWlua3NjYXBlLWZvbnQtc3BlY2lmaWNhdGlvbjpWZXJkYW5hIgogICAgICAgeD0iNTI5LjYyNTMxIgogICAgICAgeT0iLTU1MC44NDc3OCIKICAgICAgIGlkPSJ0ZXh0NDU0My01IgogICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiCiAgICAgICB0cmFuc2Zvcm09Im1hdHJpeCgwLDEsLTEsMCwwLDApIj48dHNwYW4KICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSIKICAgICAgICAgaWQ9InRzcGFuNDU0NS0wIgogICAgICAgICB4PSI1MjkuNjI1MzEiCiAgICAgICAgIHk9Ii01NTAuODQ3NzgiPkJyb2Fkd2F5PC90c3Bhbj48L3RleHQ+CiAgPC9nPgo8L3N2Zz4K\",\"xPosKeyName\":\"xPos\",\"yPosKeyName\":\"yPos\",\"posFunction\":\"return {x: origXPos, y: origYPos};\",\"markerOffsetX\":0.5,\"markerOffsetY\":1,\"showTooltip\":true,\"autocloseTooltip\":true,\"labelFunction\":\"var deviceType = dsData[dsIndex]['deviceType'];\\r\\nif (typeof deviceType !== undefined) {\\r\\n if (deviceType == \\\"energy meter\\\") {\\r\\n return '${entityName}, ${energy:2} kWt';\\r\\n } else if (deviceType == \\\"thermometer\\\") {\\r\\n return '${entityName}, ${temperature:2} °C';\\r\\n }\\r\\n}\",\"tooltipFunction\":\"var deviceType = dsData[dsIndex]['deviceType'];\\r\\nif (typeof deviceType !== undefined) {\\r\\n if (deviceType == \\\"energy meter\\\") {\\r\\n return '${entityName}
Energy: ${energy:2} kWt
';\\r\\n } else if (deviceType == \\\"thermometer\\\") {\\r\\n return '${entityName}
Temperature: ${temperature:2} °C
';\\r\\n }\\r\\n}\",\"playDelay\":500,\"keyNameDir\":\"wind_dir\",\"keyNamePower\":\"wind_speed\",\"arrowImageUrl\":\"data:image/png;base64,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\",\"ArrowLenAt4\":40,\"ArrowWidth\":40},\"title\":\"TimeseriesPolygonImageMap\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"legendConfig\":{\"position\":\"bottom\",\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false}}"
62 | }
63 | }
64 | ]
65 | }
--------------------------------------------------------------------------------
/TimedependentPolygonmapTBWidget/maputils.json:
--------------------------------------------------------------------------------
1 | {
2 | "widgetsBundle": {
3 | "alias": "maputils",
4 | "title": "MapUtils",
5 | "image": null
6 | },
7 | "widgetTypes": [
8 | {
9 | "alias": "newwidget",
10 | "name": "NewWidget",
11 | "descriptor": {
12 | "type": "latest",
13 | "sizeX": 7.5,
14 | "sizeY": 6,
15 | "resources": [],
16 | "templateHtml": "",
17 | "templateCss": "",
18 | "controllerScript": "self.onInit = function() {\n self.ctx.map = new TbMapWidgetV2('openstreet-map',\n false, self.ctx);\n\n self.mapleaflet = self.ctx.map.map.map;\n //self.mapleaflet.panBy([200, 300]);\n self.mapleaflet.setView([32, 34.95], 9);\n\n\n\n}\n\n\nself.onDataUpdated = function() {\n\n for (var i in self.mapleaflet._layers) {\n if (self.mapleaflet._layers[i]._path !==\n undefined) {\n try {\n self.mapleaflet.removeLayer(self.mapleaflet\n ._layers[i]);\n } catch (e) {\n console.log(\"problem with \" + e + self.mapleaflet\n ._layers[i]);\n }\n }\n }\n\n\n for (i = 0; i < self.ctx.data.length; i++) {\n\n var dataitem = self.ctx.data[i];\n var value = String(dataitem.data[0][1]).replace(\n /'/g, '\"');\n\n var parsed = JSON.parse(value);\n\n try {\n geojsonFeature = parsed.features;\n } catch (err) {\n geojsonFeature = null\n }\n\n var myStyle = {\n \"color\": dataitem.dataKey.color,\n \"opacity\": 0.8\n };\n\n try {\n L.geoJSON(geojsonFeature, {\n style: myStyle\n }).addTo(self.mapleaflet);\n } catch (e) {\n console.log(e);\n }\n self.ctx.map.update();\n\n\n /*\n\n try {\n L.geoJSON(\n states, {\n style: function(\n feature\n ) {\n switch (\n feature\n .properties\n .party\n ) {\n case 'Republican':\n return {\n color: \"#ff0000\"\n };\n }\n }\n }).addTo(\n self.mapleaflet\n );\n console.log(\n \"Added Features\"\n );\n } catch (e) {\n console.log(e);\n\n }\n \n \n var states = [{\n \"type\": \"Feature\",\n \"properties\": {},\n \"geometry\": {\n \"type\": \"Polygon\",\n \"coordinates\": [\n [\n [32,\n 35.09981253905925\n ],\n [32, 35.1],\n [31.8, 34]\n ]\n ]\n }\n }];\n\n \n\n //L.polygon(parsed['x'], {\n // color: dataitem.dataKey.color,\n // fillColor: dataitem.dataKey.color,\n // fillOpacity: 0.5\n // }).addTo(self.mapleaflet);\n*/\n }\n\n}\n\n\nself.onResize = function() {\n self.ctx.map.resize();\n}\n\nself.getSettingsSchema = function() {\n var tbScheme = TbMapWidgetV2.settingsSchema(\n 'openstreet-map');\n\n return tbScheme;\n}\n\nself.getDataKeySettingsSchema =\n function() {\n return TbMapWidgetV2.dataKeySettingsSchema(\n 'openstreet-map');\n }\n\nself.actionSources = function() {\n return TbMapWidgetV2.actionSources();\n}\n\nself.onDestroy = function() {}",
19 | "settingsSchema": "{\n \"schema\": {\n \"typehttp://127.0.0.1:8080/#\": \"object\",\n \"title\": \"TimeseriesTableSettings\",\n \"properties\": {\n \"values\": {\n \"title\": \"Isopleth Names\",\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n }\n }\n },\n \"required\": []\n },\n \"form\": [\n \"values\"\n ]\n}",
20 | "dataKeySettingsSchema": "",
21 | "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"NewWidget\"}"
22 | }
23 | }
24 | ]
25 | }
--------------------------------------------------------------------------------
/TimedependentPolygonmapTBWidget/polyimagemap.js:
--------------------------------------------------------------------------------
1 | self.onInit = function () {
2 | var elemap = $('#TimeseriesPolygonImageMap');
3 | self.ctx.map = new TbMapWidgetV2('image-map', false, self.ctx, undefined, elemap);
4 | self.posFunc = self.ctx.map.map.posFunction;
5 | setTimeout(() => {
6 | self.mapleaflet = self.ctx.map.map.map;
7 | window.TimeseriesPolygonImageMapSelf = self;
8 | self.onResize();
9 | self.onDataUpdated();
10 |
11 | console.log(self);
12 | }, 10);
13 | };
14 |
15 | self.resizeMap = function () {
16 | if (!self.mapleaflet || !self.mapleaflet._container) {
17 | return;
18 | }
19 | self.mapleaflet._container.style.height = (self.ctx.height - 30) + "px";
20 | self.mapleaflet.invalidateSize();
21 | };
22 |
23 | self.clearPolygons = function () {
24 | if (!self.mapleaflet || !self.mapleaflet._layers) {
25 | return;
26 | }
27 | for (var i in self.mapleaflet._layers) {
28 | if (self.mapleaflet._layers[i]._path !== undefined) {
29 | try {
30 | self.mapleaflet.removeLayer(self.mapleaflet._layers[i]);
31 | } catch (e) {
32 | console.log("problem with " + e + self.mapleaflet._layers[i]);
33 | }
34 | }
35 | }
36 | };
37 |
38 | function parseJson(json) {
39 | var value = String(json).replace(/'/g, '\"');
40 | return JSON.parse(value);
41 | }
42 |
43 | function lerp(a, b, f) {
44 | return a + f * (b - a);
45 | }
46 |
47 | self.posOnMap = function (v) {
48 | const bounds = self.ctx.map.map.imageOverlay._bounds;
49 | var ne = bounds._northEast,
50 | sw = bounds._southWest;
51 | var pos = self.posFunc(v[0], v[1]);
52 | pos.y = lerp(ne.lat, sw.lat, pos.y);
53 | pos.x = lerp(sw.lng, ne.lng, pos.x);
54 | return [pos.x, pos.y];
55 | };
56 |
57 | self.posFuncGeoJson = function (geojson) {
58 | geojson.forEach((g) => {
59 | g.geometry.coordinates = g.geometry.coordinates.map(shape => shape.map(self.posOnMap));
60 | });
61 | return geojson;
62 | };
63 |
64 | self.showPolygons = function (parsed, color) {
65 | try {
66 | var geojsonFeature = self.posFuncGeoJson(parsed.features);
67 | L.geoJSON(geojsonFeature, {
68 | style: { "color": color, "opacity": 0.8 }
69 | }).addTo(self.mapleaflet);
70 | } catch (e) {
71 | console.log(e);
72 | }
73 | };
74 |
75 | self.getCurrIndex = function () {
76 | return parseInt($('#TimeFrame')[0].value);
77 | };
78 |
79 | self.setCurrIndex = function (val) {
80 | $('#TimeFrame')[0].value = val;
81 | self.onDataUpdated();
82 | };
83 |
84 | self.getPolygonFrames = function () {
85 | const jsons = self.ctx.data.filter(prop => prop.data.length > 0 && typeof prop.data[0][1] === "string");
86 | var maxRange = 0;
87 | const polygonFrames = jsons.map(prop => {
88 | const frames = parseJson(prop.data[0][1]);
89 | maxRange = Math.max(Math.max.apply(this, frames.map(b => b.index)));
90 | return { frames: frames, color: prop.dataKey.color };
91 | });
92 | return [polygonFrames, maxRange];
93 | };
94 |
95 | self.showPolygonFrame = function (polygonFrames, index) {
96 | let valueName = '';
97 | self.clearPolygons();
98 | polygonFrames.forEach(polys => {
99 | const poly = polys.frames.find(frame => parseInt(frame.index) === index);
100 | if (poly) {
101 | valueName = poly.name;
102 | self.showPolygons(poly.value, polys.color);
103 | }
104 | });
105 | self.ctx.map.update();
106 | return valueName ? valueName : '';
107 | };
108 |
109 | self.getDirectionTelemetry = function () {
110 | // console.log(self.ctx.data);
111 | const nonJsons = self.ctx.data.filter(prop => prop.data.length > 0 && typeof prop.data[0][1] !== "string");
112 | let ret = {};
113 | nonJsons.forEach(prop => {
114 | const t = prop.datasource.name;
115 | ret[t] = ret[t] || {};
116 | const kind = prop.dataKey.name;
117 | ret[t][kind] = prop.data[0][1];
118 | });
119 | return Object.values(ret);
120 | };
121 |
122 | self.showDirectionMarker = function (s) {
123 | if (s.latitude === undefined || s.longitude === undefined) return undefined;
124 | let dir = s[self.ctx.settings.keyNameDir];
125 | if (dir === undefined) return undefined;
126 | let power = s[self.ctx.settings.keyNamePower];
127 | if (power === undefined) power = 4;
128 | const powerSize = parseFloat(power) * self.ctx.settings.ArrowLenAt4 / 4;
129 | if (/(Win32|Win64)/i.test(navigator.platform)) dir = (dir + 180) % 360;
130 |
131 | const pos = self.posOnMap([s.latitude, s.longitude]);
132 | var marker = L.marker([pos[1], pos[0]], {
133 | icon: L.icon({
134 | iconUrl: self.ctx.settings.arrowImageUrl,
135 | iconSize: [powerSize, parseFloat(self.ctx.settings.ArrowWidth)]
136 | }),
137 | rotationAngle: 360 - dir % 360, // counter clockwise
138 | rotationOrigin: 'center'
139 | }).addTo(self.mapleaflet);
140 |
141 | return marker;
142 | };
143 |
144 | self.onDataUpdated = function () {
145 | try {
146 | const index = self.getCurrIndex();
147 | [polygonFrames, maxRange] = self.getPolygonFrames();
148 | $('#TimeFrame')[0].max = maxRange;
149 | $('#TimeFrameLabel')[0].value = self.showPolygonFrame(polygonFrames, index);
150 | } catch (err) {
151 | // console.log("JSON error:", self.ctx.data, err);
152 | // return;
153 | }
154 |
155 | (self.windMarkers || []).forEach(m => m.removeFrom(self.mapleaflet));
156 | const dirs = self.getDirectionTelemetry();
157 | self.windMarkers = dirs.map(self.showDirectionMarker);
158 | self.windMarkers = self.windMarkers.filter(a => a);
159 | };
160 |
161 | self.setNext = function () {
162 | var maxRange = parseInt($('#TimeFrame')[0].max);
163 | if (maxRange >= 1) {
164 | self.setCurrIndex((self.getCurrIndex() + 1) % (maxRange + 1));
165 | } else {
166 | self.setCurrIndex(0);
167 | }
168 | };
169 |
170 | self.Play = function () {
171 | self.playing = !self.playing;
172 | $('#PlayButton').text(self.playing ? 'Stop' : 'Play');
173 | if (self.playing) {
174 | var cont = () => {
175 | self.setNext();
176 | if (self.playing) {
177 | setTimeout(cont, self.ctx.settings.playDelay);
178 | }
179 | };
180 | cont();
181 | }
182 | };
183 |
184 | self.onResize = function () {
185 | self.ctx.map.resize();
186 | self.resizeMap();
187 | };
188 |
189 | self.getSettingsSchema = function () {
190 | var tbScheme = JSON.parse(JSON.stringify(TbMapWidgetV2.settingsSchema('image-map')));
191 | // console.log(tbScheme);
192 | tbScheme.form.unshift(
193 | "playDelay",
194 | "keyNameDir",
195 | "keyNamePower",
196 | "ArrowLenAt4",
197 | "ArrowWidth",
198 | {
199 | "key": "arrowImageUrl",
200 | "type": "image"
201 | }
202 | );
203 | Object.assign(tbScheme.schema.properties,
204 | {
205 | "playDelay": {
206 | "title": "Play delay",
207 | "type": "number",
208 | "default": 500
209 | },
210 | "keyNameDir": {
211 | "title": "Direction key name",
212 | "type": "string",
213 | "default": "wind_dir"
214 | },
215 | "keyNamePower": {
216 | "title": "Power key name",
217 | "type": "string",
218 | "default": "wind_speed"
219 | },
220 | "ArrowLenAt4": {
221 | "title": "Arrow length in pixels when power/speed is 4",
222 | "type": "number",
223 | "default": 40
224 | },
225 | "ArrowWidth": {
226 | "title": "Arrow width in pixels",
227 | "type": "number",
228 | "default": 40
229 | },
230 | "arrowImageUrl": {
231 | "title": "Arrow image",
232 | "type": "string",
233 | "default": "data:image/png;base64,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"
234 | }
235 | });
236 | return tbScheme;
237 | };
238 | self.getDataKeySettingsSchema = function () {
239 | return TbMapWidgetV2.dataKeySettingsSchema('image-map');
240 | };
241 |
242 | self.actionSources = function () {
243 | return TbMapWidgetV2.actionSources();
244 | };
245 |
246 | self.onDestroy = function () { };
247 |
248 | (function () {
249 | // save these original methods before they are overwritten
250 | var proto_initIcon = L.Marker.prototype._initIcon;
251 | var proto_setPos = L.Marker.prototype._setPos;
252 |
253 | var oldIE = (L.DomUtil.TRANSFORM === 'msTransform');
254 |
255 | L.Marker.addInitHook(function () {
256 | var iconOptions = this.options.icon && this.options.icon.options;
257 | var iconAnchor = iconOptions && this.options.icon.options.iconAnchor;
258 | if (iconAnchor) {
259 | iconAnchor = (iconAnchor[0] + 'px ' + iconAnchor[1] + 'px');
260 | }
261 | this.options.rotationOrigin = this.options.rotationOrigin || iconAnchor || 'center bottom';
262 | this.options.rotationAngle = this.options.rotationAngle || 0;
263 |
264 | // Ensure marker keeps rotated during dragging
265 | this.on('drag', function (e) { e.target._applyRotation(); });
266 | });
267 |
268 | L.Marker.include({
269 | _initIcon: function () {
270 | proto_initIcon.call(this);
271 | },
272 |
273 | _setPos: function (pos) {
274 | proto_setPos.call(this, pos);
275 | this._applyRotation();
276 | },
277 |
278 | _applyRotation: function () {
279 | if (this.options.rotationAngle) {
280 | this._icon.style[L.DomUtil.TRANSFORM + 'Origin'] = this.options.rotationOrigin;
281 |
282 | // console.log(this.options.rotationAngle);
283 | // if(oldIE) {
284 | // // for IE 9, use the 2D rotation
285 | // this._icon.style[L.DomUtil.TRANSFORM] = 'rotate(' + this.options.rotationAngle + 'deg)';
286 | // } else {
287 | // // for modern browsers, prefer the 3D accelerated version
288 | this._icon.style[L.DomUtil.TRANSFORM] =
289 | this._icon.style[L.DomUtil.TRANSFORM].replace(/ rotateZ\(\d+deg\)/g, '')
290 | + ' rotateZ(' + this.options.rotationAngle + 'deg)';
291 | // }
292 | }
293 | },
294 |
295 | setRotationAngle: function (angle) {
296 | this.options.rotationAngle = angle;
297 | this.update();
298 | return this;
299 | },
300 |
301 | setRotationOrigin: function (origin) {
302 | this.options.rotationOrigin = origin;
303 | this.update();
304 | return this;
305 | }
306 | });
307 | })();
308 |
--------------------------------------------------------------------------------
/TimedependentPolygonmapTBWidget/pubsubGeoJSON.py:
--------------------------------------------------------------------------------
1 | #
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 | #
15 |
16 | import paho.mqtt.client as mqtt
17 | from time import sleep
18 | import random
19 | import numpy
20 | import geopandas
21 | from shapely.geometry import Point, Polygon
22 | import pprint
23 | import json
24 |
25 | broker="192.116.82.80"
26 | topic_pub='v1/devices/me/telemetry'
27 |
28 | attr_pub ='v1/devices/me/attributes'
29 |
30 | def on_message(client, userdata, message):
31 | print("message received " ,str(message.payload.decode("utf-8")))
32 | print("message topic=",message.topic)
33 | print("message qos=",message.qos)
34 | print("message retain flag=",message.retain)
35 |
36 | def on_connect(client, userdata, flags, rc):
37 | print("Connected flags"+str(flags)+"result code " +str(rc))
38 |
39 |
40 | class Device(object):
41 |
42 | def __init__(self,name):
43 | self.name = name
44 | self.client = mqtt.Client()
45 | self.client.username_pw_set(name)
46 | self.client.on_message=on_message
47 | self.client.on_connect= on_connect
48 | self.client.connect('192.116.82.80', 1883, 1)
49 | self.client.loop_start()
50 |
51 | def pub(self):
52 |
53 | R1 = random.randrange(3, 20)/100.
54 | R2 = R1 + random.randrange(3, 20)/100.
55 | R3 = R2 + random.randrange(3, 20)/100.
56 |
57 | angle = numpy.arange(0,2*numpy.pi,0.05)
58 | baseX = 35
59 | baseY = 32.5
60 |
61 | X1 = baseX + R1*numpy.sin(angle)
62 | Y1 = baseY + R1*numpy.cos(angle)
63 | A1 = Polygon([[i[0],i[1]] for i in zip(X1,Y1)])
64 | A1s = str([[i[0],i[1]] for i in zip(X1,Y1)])
65 |
66 |
67 | X2 = baseX + R2*numpy.sin(angle)
68 | Y2 = baseY + R2*numpy.cos(angle)
69 | A2 = Polygon([[i[0],i[1]] for i in zip(X2,Y2)])
70 |
71 |
72 | X3 = baseX + R3*numpy.sin(angle)
73 | Y3 = baseY + R3*numpy.cos(angle)
74 | A3 = Polygon([[i[0],i[1]] for i in zip(X3,Y3)])
75 |
76 | G = geopandas.GeoDataFrame({'geometry' : [A1,A2,A3]})
77 |
78 | totalD = G.diff()
79 | totalD.iloc[0] = G.iloc[0]
80 |
81 | jsonL = []
82 | for name,i in zip(["A","B","C"],range(totalD.size)):
83 | jsonL.append('"%s" : "%s"' %(name, totalD.iloc[i:i+1].to_json().replace('"',"'")))
84 |
85 | msg = '{%s}' % ",".join(jsonL)
86 | self.client.publish(topic_pub, msg)
87 |
88 | deviceName = ["A1_TEST_TOKEN"]
89 | DeviceList = [Device(x) for x in deviceName]
90 |
91 | while True:
92 | for d in DeviceList:
93 | d.pub()
94 | sleep(1)
95 |
--------------------------------------------------------------------------------
/TimedependentPolygonmapTBWidget/pubsubGeoJSONmulti.1.py:
--------------------------------------------------------------------------------
1 | #
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 | #
15 |
16 | import paho.mqtt.client as mqtt
17 | from time import sleep
18 | import random
19 | import numpy
20 | import geopandas
21 | from shapely.geometry import Point, Polygon
22 | import pprint
23 | import json
24 |
25 | broker = "192.116.82.80"
26 | topic_pub = 'v1/devices/me/telemetry'
27 |
28 | attr_pub = 'v1/devices/me/attributes'
29 |
30 |
31 | def on_message(client, userdata, message):
32 | print("message received ", str(message.payload.decode("utf-8")))
33 | print("message topic=", message.topic)
34 | print("message qos=", message.qos)
35 | print("message retain flag=", message.retain)
36 |
37 |
38 | def on_connect(client, userdata, flags, rc):
39 | print("Connected flags"+str(flags)+"result code " + str(rc))
40 |
41 |
42 | class Device(object):
43 |
44 | def __init__(self, name):
45 | self.name = name
46 | self.client = mqtt.Client()
47 | self.client.username_pw_set(name)
48 | self.client.on_message = on_message
49 | self.client.on_connect = on_connect
50 | self.client.connect('192.116.82.80', 1883, 1)
51 | self.client.loop_start()
52 |
53 | def oneFrame(self):
54 |
55 | R1 = random.randrange(3, 20)/100.
56 | R2 = R1 + random.randrange(3, 20)/100.
57 | R3 = R2 + random.randrange(3, 20)/100.
58 |
59 | angle = numpy.arange(0, 2*numpy.pi, 0.05)
60 | baseX = 35
61 | baseY = 32.5
62 |
63 | X1 = baseX + R1*numpy.sin(angle)
64 | Y1 = baseY + R1*numpy.cos(angle)
65 | A1 = Polygon([[i[0], i[1]] for i in zip(X1, Y1)])
66 | A1s = str([[i[0], i[1]] for i in zip(X1, Y1)])
67 |
68 | X2 = baseX + R2*numpy.sin(angle)
69 | Y2 = baseY + R2*numpy.cos(angle)
70 | A2 = Polygon([[i[0], i[1]] for i in zip(X2, Y2)])
71 |
72 | X3 = baseX + R3*numpy.sin(angle)
73 | Y3 = baseY + R3*numpy.cos(angle)
74 | A3 = Polygon([[i[0], i[1]] for i in zip(X3, Y3)])
75 |
76 | G = geopandas.GeoDataFrame({'geometry': [A1, A2, A3]})
77 |
78 | totalD = G.diff()
79 | totalD.iloc[0] = G.iloc[0]
80 |
81 | jsonL = []
82 | for name, i in zip(["A", "B", "C"], range(totalD.size)):
83 | jsonL.append('"%s" : "%s"' %
84 | (name, totalD.iloc[i:i+1].to_json().replace('"', "'")))
85 |
86 | return '{%s}' % ",".join(jsonL)
87 |
88 | def timeFrames(self):
89 | jsonL = []
90 | for idx in range(3):
91 | jsonL.append('{"name": "%s", "value": %s}' % (idx, self.oneFrame()))
92 |
93 | return '[%s]' % ",".join(jsonL)
94 |
95 | def pub(self):
96 | msg = self.timeFrames()
97 | # print(msg)
98 | # exit()
99 | self.client.publish(topic_pub, msg)
100 |
101 |
102 | deviceName = ["A1_TEST_TOKEN"]
103 | DeviceList = [Device(x) for x in deviceName]
104 |
105 | while True:
106 | for d in DeviceList:
107 | d.pub()
108 | sleep(1)
109 |
--------------------------------------------------------------------------------
/TimedependentPolygonmapTBWidget/pubsubGeoJSONmulti.py:
--------------------------------------------------------------------------------
1 | #
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 | #
15 |
16 | import paho.mqtt.client as mqtt
17 | from time import sleep
18 | import random
19 | import numpy
20 | import geopandas
21 | from shapely.geometry import Point, Polygon
22 | import pprint
23 | import json
24 |
25 | broker = "192.116.82.80"
26 | topic_pub = 'v1/devices/me/telemetry'
27 |
28 | attr_pub = 'v1/devices/me/attributes'
29 |
30 |
31 | def on_message(client, userdata, message):
32 | print("message received ", str(message.payload.decode("utf-8")))
33 | print("message topic=", message.topic)
34 | print("message qos=", message.qos)
35 | print("message retain flag=", message.retain)
36 |
37 |
38 | def on_connect(client, userdata, flags, rc):
39 | print("Connected flags"+str(flags)+"result code " + str(rc))
40 |
41 |
42 | class Device(object):
43 |
44 | def __init__(self, name):
45 | self.name = name
46 | self.client = mqtt.Client()
47 | self.client.username_pw_set(name)
48 | self.client.on_message = on_message
49 | self.client.on_connect = on_connect
50 | self.client.connect('192.116.82.80', 1883, 1)
51 | self.client.loop_start()
52 |
53 | def pub(self):
54 |
55 | props = {"A": [], "B": [], "C": []}
56 | for idx in range(10):
57 | R1 = random.randrange(3, 20)*4
58 | R2 = R1 + random.randrange(3, 20)*4
59 | R3 = R2 + random.randrange(3, 20)*4
60 |
61 | angle = numpy.arange(0, 2*numpy.pi, 0.8)
62 | image_bounds = {'mn': {'x': 154994, 'y': 563428}, 'mx': {'x': 155784, 'y': 564085}}
63 | baseX = (image_bounds['mn']['x'] + image_bounds['mx']['x'])/2
64 | baseY = (image_bounds['mn']['y'] + image_bounds['mx']['y'])/2
65 | # baseX = 154994 # min x
66 | # baseY = 563428 # min y
67 | # baseX = 155784 # max x
68 | # baseY = 564085 # max y
69 |
70 | X1 = baseX + R1*numpy.sin(angle)
71 | Y1 = baseY + R1*numpy.cos(angle)
72 | A1 = Polygon([[i[0], i[1]] for i in zip(X1, Y1)])
73 | A1s = str([[i[0], i[1]] for i in zip(X1, Y1)])
74 |
75 | X2 = baseX + R2*numpy.sin(angle)
76 | Y2 = baseY + R2*numpy.cos(angle)
77 | A2 = Polygon([[i[0], i[1]] for i in zip(X2, Y2)])
78 |
79 | X3 = baseX + R3*numpy.sin(angle)
80 | Y3 = baseY + R3*numpy.cos(angle)
81 | A3 = Polygon([[i[0], i[1]] for i in zip(X3, Y3)])
82 |
83 | G = geopandas.GeoDataFrame({'geometry': [A1, A2, A3]})
84 |
85 | totalD = G.diff()
86 | totalD.iloc[0] = G.iloc[0]
87 |
88 | for name, i in zip(["A", "B", "C"], range(totalD.size)):
89 | poly = totalD.iloc[i:i+1].to_json().replace('"', "'")
90 | sindex = idx
91 | polyindex = "{'index':'%s', 'name':'%s', 'value':%s}" % (idx, sindex, poly)
92 | props[name].append(polyindex)
93 |
94 | textprops = []
95 | for name in props:
96 | # proptext = props[name].to_json().replace('"', "'")
97 | # textprops.append('"%s": %s' % (name, proptext))
98 |
99 | text = '[%s]' % ",".join(props[name])
100 | textprops.append('"%s": "%s"' % (name, text))
101 |
102 | msg = '{%s}' % ",".join(textprops)
103 | # print(msg)
104 | # exit()
105 | self.client.publish(topic_pub, msg)
106 |
107 |
108 | deviceName = ["A1_TEST_TOKEN"]
109 | DeviceList = [Device(x) for x in deviceName]
110 |
111 | while True:
112 | for d in DeviceList:
113 | d.pub()
114 | sleep(5)
115 |
--------------------------------------------------------------------------------
/TimedependentPolygonmapTBWidget/pubsubWind.py:
--------------------------------------------------------------------------------
1 | #
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 | #
15 |
16 | import paho.mqtt.client as mqtt
17 | from time import sleep
18 | import random
19 | import numpy
20 | import pprint
21 | import json
22 |
23 | broker = "192.116.82.80"
24 | topic_pub = 'v1/devices/me/telemetry'
25 |
26 | attr_pub = 'v1/devices/me/attributes'
27 |
28 |
29 | def on_message(client, userdata, message):
30 | print("message received ", str(message.payload.decode("utf-8")))
31 | print("message topic=", message.topic)
32 | print("message qos=", message.qos)
33 | print("message retain flag=", message.retain)
34 |
35 |
36 | def on_connect(client, userdata, flags, rc):
37 | print("Connected flags"+str(flags)+"result code " + str(rc))
38 |
39 | class Device(object):
40 |
41 | def __init__(self, name):
42 | self.i = 10
43 | self.name = name
44 | self.client = mqtt.Client()
45 | self.client.username_pw_set(name)
46 | self.client.on_message = on_message
47 | self.client.on_connect = on_connect
48 | self.client.connect('192.116.82.80', 1883, 1)
49 | self.client.loop_start()
50 |
51 | def pub(self):
52 |
53 | props = {"wind_dir": self.i, "wind_speed": 10} # random.randrange(0,10)}
54 | self.i = (self.i + 10) % 360
55 | # props = {"wind_dir": 90.0, "wind_speed": 10} # random.randrange(0,10)}
56 | # props = {"wind_dir": random.randrange(0,360), "wind_speed": 10} # random.randrange(0,10)}
57 | msg = '%s' % str(props)
58 | self.client.publish(topic_pub, msg)
59 |
60 |
61 | deviceName = ["Wind1","Wind2","Wind3"]
62 | DeviceList = [Device(x) for x in deviceName]
63 |
64 | while True:
65 | for d in DeviceList:
66 | d.pub()
67 | sleep(1)
68 |
69 |
--------------------------------------------------------------------------------
/js/PolygonMap.js:
--------------------------------------------------------------------------------
1 | export default widget = {};
2 |
3 | widget.onInit = function() {
4 | widget.ctx.map = new TbMapWidgetV2('openstreet-map', false, widget.ctx);
5 | widget.mapleaflet = widget.ctx.map.map.map;
6 | //widget.mapleaflet.panBy([200, 300]);
7 | widget.mapleaflet.setView([32, 34.95], 9);
8 | }
9 |
10 | widget.onDataUpdated = function() {
11 | for (var i in widget.mapleaflet._layers) {
12 | if (widget.mapleaflet._layers[i]._path !==
13 | undefined) {
14 | try {
15 | widget.mapleaflet.removeLayer(widget.mapleaflet
16 | ._layers[i]);
17 | } catch (e) {
18 | console.log("problem with " + e + widget.mapleaflet._layers[i]);
19 | }
20 |
21 | }
22 | }
23 |
24 | for (i = 0; i < widget.ctx.data.length; i++) {
25 | try {
26 | var dataitem = widget.ctx.data[i];
27 | var value = String(dataitem.data[0][1]).replace(/'/g, '\"');
28 | var parsed = JSON.parse(value);
29 | geojsonFeature = parsed.features;
30 | } catch (err) {
31 | geojsonFeature = null
32 | }
33 | var myStyle = {
34 | "color": dataitem.dataKey.color,
35 | "opacity": 0.8
36 | };
37 | try {
38 | L.geoJSON(geojsonFeature, {
39 | style: myStyle
40 | }).addTo(widget.mapleaflet);
41 | } catch (e) {
42 | console.log(e);
43 | }
44 | widget.ctx.map.update();
45 | }
46 | }
47 | widget.onResize = function() {
48 | widget.ctx.map.resize();
49 | }
50 | widget.getSettingsSchema = function() {
51 | var tbScheme = TbMapWidgetV2.settingsSchema(
52 | 'openstreet-map');
53 | return tbScheme;
54 | }
55 | widget.getDataKeySettingsSchema = function() {
56 | return TbMapWidgetV2.dataKeySettingsSchema('openstreet-map');
57 | }
58 | widget.actionSources = function() {
59 | return TbMapWidgetV2.actionSources();
60 | }
61 | widget.onDestroy = function() {}
62 |
--------------------------------------------------------------------------------
/js/PolygonMap_orig.js:
--------------------------------------------------------------------------------
1 | self.onInit = function() {
2 | console.log(self);
3 | self.ctx.map = new TbMapWidgetV2('openstreet-map', false, self.ctx);
4 | self.mapleaflet = self.ctx.map.map.map;
5 | //self.mapleaflet.panBy([200, 300]);
6 | self.mapleaflet.setView([32, 34.95], 9);
7 | }
8 |
9 | self.onDataUpdated = function() {
10 | for (var i in self.mapleaflet._layers) {
11 | if (self.mapleaflet._layers[i]._path !==
12 | undefined) {
13 | try {
14 | self.mapleaflet.removeLayer(self.mapleaflet
15 | ._layers[i]);
16 | } catch (e) {
17 | console.log("problem with " + e + self.mapleaflet._layers[i]);
18 | }
19 |
20 | }
21 | }
22 |
23 | for (i = 0; i < self.ctx.data.length; i++) {
24 | try {
25 | var dataitem = self.ctx.data[i];
26 | var value = String(dataitem.data[0][1]).replace(/'/g, '\"');
27 | var parsed = JSON.parse(value);
28 | geojsonFeature = parsed.features;
29 | } catch (err) {
30 | geojsonFeature = null
31 | }
32 | var myStyle = {
33 | "color": dataitem.dataKey.color,
34 | "opacity": 0.8
35 | };
36 | try {
37 | L.geoJSON(geojsonFeature, {
38 | style: myStyle
39 | }).addTo(self.mapleaflet);
40 | } catch (e) {
41 | console.log(e);
42 | }
43 | self.ctx.map.update();
44 | }
45 | }
46 | self.onResize = function() {
47 | self.ctx.map.resize();
48 | }
49 | self.getSettingsSchema = function() {
50 | var tbScheme = TbMapWidgetV2.settingsSchema(
51 | 'openstreet-map');
52 | return tbScheme;
53 | }
54 | self.getDataKeySettingsSchema = function() {
55 | return TbMapWidgetV2.dataKeySettingsSchema('openstreet-map');
56 | }
57 | self.actionSources = function() {
58 | return TbMapWidgetV2.actionSources();
59 | }
60 | self.onDestroy = function() {}
61 |
--------------------------------------------------------------------------------
/widgets/PolygonMap.js:
--------------------------------------------------------------------------------
1 | self.onInit = function() {
2 | self.ctx.map = new TbMapWidgetV2('openstreet-map', false, self.ctx);
3 | self.mapleaflet = self.ctx.map.map.map;
4 | //self.mapleaflet.panBy([200, 300]);
5 | self.mapleaflet.setView([32, 34.95], 9);
6 | }
7 |
8 | self.onDataUpdated = function() {
9 | for (var i in self.mapleaflet._layers) {
10 | if (self.mapleaflet._layers[i]._path !==
11 | undefined) {
12 | try {
13 | self.mapleaflet.removeLayer(self.mapleaflet
14 | ._layers[i]);
15 | } catch (e) {
16 | console.log("problem with " + e + self.mapleaflet._layers[i]);
17 | }
18 |
19 | }
20 | }
21 |
22 | for (i = 0; i < self.ctx.data.length; i++) {
23 | try {
24 | var dataitem = self.ctx.data[i];
25 | var value = String(dataitem.data[0][1]).replace(/'/g, '\"');
26 | var parsed = JSON.parse(value);
27 | geojsonFeature = parsed.features;
28 | } catch (err) {
29 | geojsonFeature = null
30 | }
31 | var myStyle = {
32 | "color": dataitem.dataKey.color,
33 | "opacity": 0.8
34 | };
35 | try {
36 | L.geoJSON(geojsonFeature, {
37 | style: myStyle
38 | }).addTo(self.mapleaflet);
39 | } catch (e) {
40 | console.log(e);
41 | }
42 | self.ctx.map.update();
43 | }
44 | }
45 | self.onResize = function() {
46 | self.ctx.map.resize();
47 | }
48 | self.getSettingsSchema = function() {
49 | var tbScheme = TbMapWidgetV2.settingsSchema(
50 | 'openstreet-map');
51 | return tbScheme;
52 | }
53 | self.getDataKeySettingsSchema = function() {
54 | return TbMapWidgetV2.dataKeySettingsSchema('openstreet-map');
55 | }
56 | self.actionSources = function() {
57 | return TbMapWidgetV2.actionSources();
58 | }
59 | self.onDestroy = function() {}
60 |
--------------------------------------------------------------------------------
/widgets/TimeseriesPolygonImageMap.js:
--------------------------------------------------------------------------------
1 | self.onInit = function () {
2 | var elemap = $('#TimeseriesPolygonImageMap');
3 | self.ctx.map = new TbMapWidgetV2('image-map', false, self.ctx, undefined, elemap);
4 | self.posFunc = self.ctx.map.map.posFunction;
5 | setTimeout(() => {
6 | self.mapleaflet = self.ctx.map.map.map;
7 | window.TimeseriesPolygonImageMapSelf = self;
8 | self.onResize();
9 | self.onDataUpdated();
10 |
11 | console.log(self);
12 | }, 10);
13 | };
14 |
15 | self.resizeMap = function () {
16 | if (!self.mapleaflet || !self.mapleaflet._container) {
17 | return;
18 | }
19 | self.mapleaflet._container.style.height = (self.ctx.height - 30) + "px";
20 | self.mapleaflet.invalidateSize();
21 | };
22 |
23 | self.clearPolygons = function () {
24 | if (!self.mapleaflet || !self.mapleaflet._layers) {
25 | return;
26 | }
27 | for (var i in self.mapleaflet._layers) {
28 | if (self.mapleaflet._layers[i]._path !== undefined) {
29 | try {
30 | self.mapleaflet.removeLayer(self.mapleaflet._layers[i]);
31 | } catch (e) {
32 | console.log("problem with " + e + self.mapleaflet._layers[i]);
33 | }
34 | }
35 | }
36 | };
37 |
38 | function parseJson(json) {
39 | var value = String(json).replace(/'/g, '\"');
40 | return JSON.parse(value);
41 | }
42 |
43 | function lerp(a, b, f) {
44 | return a + f * (b - a);
45 | }
46 |
47 | self.posOnMap = function (v) {
48 | const bounds = self.ctx.map.map.imageOverlay._bounds;
49 | var ne = bounds._northEast,
50 | sw = bounds._southWest;
51 | var pos = self.posFunc(v[0], v[1]);
52 | pos.y = lerp(ne.lat, sw.lat, pos.y);
53 | pos.x = lerp(sw.lng, ne.lng, pos.x);
54 | return [pos.x, pos.y];
55 | };
56 |
57 | self.posFuncGeoJson = function (geojson) {
58 | geojson.forEach((g) => {
59 | g.geometry.coordinates = g.geometry.coordinates.map(shape => shape.map(self.posOnMap));
60 | });
61 | return geojson;
62 | };
63 |
64 | self.showPolygons = function (parsed, color) {
65 | try {
66 | var geojsonFeature = self.posFuncGeoJson(parsed.features);
67 | L.geoJSON(geojsonFeature, {
68 | style: { "color": color, "opacity": 0.8 }
69 | }).addTo(self.mapleaflet);
70 | } catch (e) {
71 | console.log(e);
72 | }
73 | };
74 |
75 | self.getCurrIndex = function () {
76 | return parseInt($('#TimeFrame')[0].value);
77 | };
78 |
79 | self.setCurrIndex = function (val) {
80 | $('#TimeFrame')[0].value = val;
81 | self.onDataUpdated();
82 | };
83 |
84 | self.getPolygonFrames = function () {
85 | const jsons = self.ctx.data.filter(prop => prop.data.length > 0 && typeof prop.data[0][1] === "string");
86 | var maxRange = 0;
87 | const polygonFrames = jsons.map(prop => {
88 | const frames = parseJson(prop.data[0][1]);
89 | maxRange = Math.max(Math.max.apply(this, frames.map(b => b.index)));
90 | return { frames: frames, color: prop.dataKey.color };
91 | });
92 | return [polygonFrames, maxRange];
93 | };
94 |
95 | self.showPolygonFrame = function (polygonFrames, index) {
96 | let valueName = '';
97 | self.clearPolygons();
98 | polygonFrames.forEach(polys => {
99 | const poly = polys.frames.find(frame => parseInt(frame.index) === index);
100 | if (poly) {
101 | valueName = poly.name;
102 | self.showPolygons(poly.value, polys.color);
103 | }
104 | });
105 | self.ctx.map.update();
106 | return valueName ? valueName : '';
107 | };
108 |
109 | self.getDirectionTelemetry = function () {
110 | // console.log(self.ctx.data);
111 | const nonJsons = self.ctx.data.filter(prop => prop.data.length > 0 && typeof prop.data[0][1] !== "string");
112 | let ret = {};
113 | nonJsons.forEach(prop => {
114 | const t = prop.datasource.name;
115 | ret[t] = ret[t] || {};
116 | const kind = prop.dataKey.name;
117 | ret[t][kind] = prop.data[0][1];
118 | });
119 | return Object.values(ret);
120 | };
121 |
122 | self.showDirectionMarker = function (s) {
123 | if (s.latitude === undefined || s.longitude === undefined) return undefined;
124 | let dir = s[self.ctx.settings.keyNameDir];
125 | if (dir === undefined) return undefined;
126 | let power = s[self.ctx.settings.keyNamePower];
127 | if (power === undefined) power = 4;
128 | const powerSize = parseFloat(power) * self.ctx.settings.ArrowLenAt4 / 4;
129 | if (/(Win32|Win64)/i.test(navigator.platform)) dir = (dir + 180) % 360;
130 |
131 | const pos = self.posOnMap([s.latitude, s.longitude]);
132 | var marker = L.marker([pos[1], pos[0]], {
133 | icon: L.icon({
134 | iconUrl: self.ctx.settings.arrowImageUrl,
135 | iconSize: [powerSize, parseFloat(self.ctx.settings.ArrowWidth)]
136 | }),
137 | rotationAngle: 360 - dir % 360, // counter clockwise
138 | rotationOrigin: 'center'
139 | }).addTo(self.mapleaflet);
140 |
141 | return marker;
142 | };
143 |
144 | self.onDataUpdated = function () {
145 | try {
146 | const index = self.getCurrIndex();
147 | [polygonFrames, maxRange] = self.getPolygonFrames();
148 | $('#TimeFrame')[0].max = maxRange;
149 | $('#TimeFrameLabel')[0].value = self.showPolygonFrame(polygonFrames, index);
150 | } catch (err) {
151 | // console.log("JSON error:", self.ctx.data, err);
152 | // return;
153 | }
154 |
155 | (self.windMarkers || []).forEach(m => m.removeFrom(self.mapleaflet));
156 | const dirs = self.getDirectionTelemetry();
157 | self.windMarkers = dirs.map(self.showDirectionMarker);
158 | self.windMarkers = self.windMarkers.filter(a => a);
159 | };
160 |
161 | self.setNext = function () {
162 | var maxRange = parseInt($('#TimeFrame')[0].max);
163 | if (maxRange >= 1) {
164 | self.setCurrIndex((self.getCurrIndex() + 1) % (maxRange + 1));
165 | } else {
166 | self.setCurrIndex(0);
167 | }
168 | };
169 |
170 | self.Play = function () {
171 | self.playing = !self.playing;
172 | $('#PlayButton').text(self.playing ? 'Stop' : 'Play');
173 | if (self.playing) {
174 | var cont = () => {
175 | self.setNext();
176 | if (self.playing) {
177 | setTimeout(cont, self.ctx.settings.playDelay);
178 | }
179 | };
180 | cont();
181 | }
182 | };
183 |
184 | self.onResize = function () {
185 | self.ctx.map.resize();
186 | self.resizeMap();
187 | };
188 |
189 | self.getSettingsSchema = function () {
190 | var tbScheme = JSON.parse(JSON.stringify(TbMapWidgetV2.settingsSchema('image-map')));
191 | // console.log(tbScheme);
192 | tbScheme.form.unshift(
193 | "playDelay",
194 | "keyNameDir",
195 | "keyNamePower",
196 | "ArrowLenAt4",
197 | "ArrowWidth",
198 | {
199 | "key": "arrowImageUrl",
200 | "type": "image"
201 | }
202 | );
203 | Object.assign(tbScheme.schema.properties,
204 | {
205 | "playDelay": {
206 | "title": "Play delay",
207 | "type": "number",
208 | "default": 500
209 | },
210 | "keyNameDir": {
211 | "title": "Direction key name",
212 | "type": "string",
213 | "default": "wind_dir"
214 | },
215 | "keyNamePower": {
216 | "title": "Power key name",
217 | "type": "string",
218 | "default": "wind_speed"
219 | },
220 | "ArrowLenAt4": {
221 | "title": "Arrow length in pixels when power/speed is 4",
222 | "type": "number",
223 | "default": 40
224 | },
225 | "ArrowWidth": {
226 | "title": "Arrow width in pixels",
227 | "type": "number",
228 | "default": 40
229 | },
230 | "arrowImageUrl": {
231 | "title": "Arrow image",
232 | "type": "string",
233 | "default": "data:image/png;base64,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"
234 | }
235 | });
236 | return tbScheme;
237 | };
238 | self.getDataKeySettingsSchema = function () {
239 | return TbMapWidgetV2.dataKeySettingsSchema('image-map');
240 | };
241 |
242 | self.actionSources = function () {
243 | return TbMapWidgetV2.actionSources();
244 | };
245 |
246 | self.onDestroy = function () { };
247 |
248 | (function () {
249 | // save these original methods before they are overwritten
250 | var proto_initIcon = L.Marker.prototype._initIcon;
251 | var proto_setPos = L.Marker.prototype._setPos;
252 |
253 | var oldIE = (L.DomUtil.TRANSFORM === 'msTransform');
254 |
255 | L.Marker.addInitHook(function () {
256 | var iconOptions = this.options.icon && this.options.icon.options;
257 | var iconAnchor = iconOptions && this.options.icon.options.iconAnchor;
258 | if (iconAnchor) {
259 | iconAnchor = (iconAnchor[0] + 'px ' + iconAnchor[1] + 'px');
260 | }
261 | this.options.rotationOrigin = this.options.rotationOrigin || iconAnchor || 'center bottom';
262 | this.options.rotationAngle = this.options.rotationAngle || 0;
263 |
264 | // Ensure marker keeps rotated during dragging
265 | this.on('drag', function (e) { e.target._applyRotation(); });
266 | });
267 |
268 | L.Marker.include({
269 | _initIcon: function () {
270 | proto_initIcon.call(this);
271 | },
272 |
273 | _setPos: function (pos) {
274 | proto_setPos.call(this, pos);
275 | this._applyRotation();
276 | },
277 |
278 | _applyRotation: function () {
279 | if (this.options.rotationAngle) {
280 | this._icon.style[L.DomUtil.TRANSFORM + 'Origin'] = this.options.rotationOrigin;
281 |
282 | // console.log(this.options.rotationAngle);
283 | // if(oldIE) {
284 | // // for IE 9, use the 2D rotation
285 | // this._icon.style[L.DomUtil.TRANSFORM] = 'rotate(' + this.options.rotationAngle + 'deg)';
286 | // } else {
287 | // // for modern browsers, prefer the 3D accelerated version
288 | this._icon.style[L.DomUtil.TRANSFORM] =
289 | this._icon.style[L.DomUtil.TRANSFORM].replace(/ rotateZ\(\d+deg\)/g, '')
290 | + ' rotateZ(' + this.options.rotationAngle + 'deg)';
291 | // }
292 | }
293 | },
294 |
295 | setRotationAngle: function (angle) {
296 | this.options.rotationAngle = angle;
297 | this.update();
298 | return this;
299 | },
300 |
301 | setRotationOrigin: function (origin) {
302 | this.options.rotationOrigin = origin;
303 | this.update();
304 | return this;
305 | }
306 | });
307 | })();
308 |
--------------------------------------------------------------------------------
/widgets/TimeseriesPolygonMap.js:
--------------------------------------------------------------------------------
1 | self.onInit = function () {
2 | var map = $('#TimeseriesPolygonMap');
3 | self.ctx.map = new TbMapWidgetV2('openstreet-map',
4 | false, self.ctx, undefined, map);
5 | self.mapleaflet = self.ctx.map.map.map;
6 | window.TimeseriesPolygonMapSelf = self;
7 | //self.mapleaflet.panBy([200, 300]);
8 | self.mapleaflet.setView([32.7, 34.95], 9);
9 | // self.resizeMap();
10 | // console.log(self);
11 | }
12 |
13 | self.resizeMap = function () {
14 | self.mapleaflet._container.style.height = (self.ctx
15 | .height - 30) + "px";
16 | }
17 |
18 | self.clearPolygons = function () {
19 | if (!self.mapleaflet._layers) return;
20 | for (var i in self.mapleaflet._layers) {
21 | if (self.mapleaflet._layers[i]._path !== undefined) {
22 | try {
23 | self.mapleaflet.removeLayer(self.mapleaflet._layers[i]);
24 | } catch (e) {
25 | console.log("problem with " + e + self.mapleaflet._layers[i]);
26 | }
27 |
28 | }
29 | }
30 |
31 | }
32 |
33 | function parseJson(json) {
34 | var value = String(json).replace(/'/g, '\"');
35 | return JSON.parse(value);
36 | }
37 | self.showPolygons = function (parsed, color) {
38 | try {
39 | geojsonFeature = parsed.features;
40 | } catch (err) {
41 | return;
42 | // geojsonFeature = null
43 | }
44 | var myStyle = {
45 | "color": color,
46 | "opacity": 0.8
47 | };
48 | try {
49 | L.geoJSON(geojsonFeature, {
50 | style: myStyle
51 | }).addTo(self.mapleaflet);
52 | } catch (e) {
53 | console.log(e);
54 | }
55 |
56 | };
57 |
58 | self.updateTimeRange = function (parsed) {
59 | var maxRange = 0;
60 | for (i = 0; i < parsed.length; i++) {
61 | // var prop = self.ctx.data[i].data[0][1];
62 | // console.log(prop);
63 | for (j = 0; j < parsed[i].length; j++) {
64 | if (parsed[i][j].index > maxRange) {
65 | maxRange = parsed[i][j].index;
66 | }
67 | }
68 | }
69 | $('#TimeFrame')[0].max = maxRange;
70 | };
71 |
72 | function findCurrFrame(frames, index) {
73 | if (frames instanceof Array) {
74 | for (var i = 0; i < frames.length; ++i) {
75 | if (parseInt(frames[i].index) === index) {
76 | return frames[i];
77 | }
78 | }
79 | }
80 | return undefined;
81 | }
82 |
83 | self.getCurrIndex = function () {
84 | return parseInt($('#TimeFrame')[0].value);
85 | }
86 | self.setCurrIndex = function (val) {
87 | // console.log('setCurrIndex', val);
88 | $('#TimeFrame')[0].value = val;
89 | self.onDataUpdated();
90 | }
91 | self.onDataUpdated = function () {
92 | try {
93 | var parsed = self.ctx.data.map(prop =>
94 | parseJson(
95 | prop.data[0][1]));
96 | self.updateTimeRange(parsed);
97 | var index = self.getCurrIndex();
98 | self.clearPolygons();
99 | // console.log(self.ctx.data);
100 | var valueName = undefined;
101 | for (i = 0; i < self.ctx.data.length; i++) {
102 | var poly = findCurrFrame(parsed[i], index);
103 | if (!poly) continue;
104 | valueName = poly.name;
105 | var color = self.ctx.data[i].dataKey.color;
106 | self.showPolygons(poly.value, color);
107 | }
108 | $('#TimeFrameLabel')[0].value = valueName ?
109 | valueName : '';
110 | } catch (err) { }
111 | self.ctx.map.update();
112 | }
113 |
114 | self.setNext = function () {
115 | var maxRange = parseInt($('#TimeFrame')[0].max);
116 | if (maxRange >= 1) {
117 | self.setCurrIndex((self.getCurrIndex() + 1) % (
118 | maxRange + 1));
119 | } else {
120 | self.setCurrIndex(0);
121 | }
122 | }
123 |
124 | self.Play = function () {
125 | self.playing = !self.playing
126 | $('#PlayButton').text(self.playing ? 'Stop' :
127 | 'Play');
128 | clearInterval(self.playInterval);
129 | if (self.playing) {
130 | var delay = self.ctx.settings.playDelay || 500;
131 | self.playInterval = setInterval((function () {
132 | self.setNext()
133 | }).bind(this), delay);
134 | }
135 | }
136 |
137 | self.onResize = function () {
138 | self.ctx.map.resize();
139 | // self.resizeMap();
140 | }
141 |
142 | self.getSettingsSchema = function () {
143 | var tbScheme = TbMapWidgetV2.settingsSchema(
144 | 'openstreet-map');
145 | return tbScheme;
146 | }
147 | self.getDataKeySettingsSchema = function () {
148 | return TbMapWidgetV2.dataKeySettingsSchema(
149 | 'openstreet-map');
150 | }
151 | self.actionSources = function () {
152 | return TbMapWidgetV2.actionSources();
153 | }
154 | self.onDestroy = function () { }
--------------------------------------------------------------------------------
/widgets/newWidget.js:
--------------------------------------------------------------------------------
1 | self.onInit = function () {
2 | var elemap = $('#TimeseriesPolygonImageMap');
3 | self.ctx.map = new TbMapWidgetV2('image-map', false, self.ctx, undefined, elemap);
4 | self.posFunc = self.ctx.map.map.posFunction;
5 | setTimeout(() => {
6 | self.mapleaflet = self.ctx.map.map.map;
7 | window.TimeseriesPolygonImageMapSelf = self;
8 | // self.onResize();
9 | // self.onDataUpdated();
10 |
11 | console.log(self);
12 | }, 10);
13 | };
14 |
15 | self.onDataUpdated = function () {
16 | self.drawPolygons();
17 | self.drawWindMarkers();
18 | };
19 |
20 | self.drawPolygons = function() {
21 | try {
22 | const index = self.getCurrIndex();
23 | [polygonFrames, maxRange] = self.getPolygonFrames();
24 | $('#TimeFrame')[0].max = maxRange;
25 | $('#TimeFrameLabel')[0].value = self.showPolygonFrame(polygonFrames, index);
26 | } catch (err) {
27 | console.log("JSON error:", self.ctx.data, err);
28 | return;
29 | }
30 | };
31 |
32 | self.clearPolygons = function () {
33 | if (!self.mapleaflet || !self.mapleaflet._layers) {
34 | return;
35 | }
36 | for (var i in self.mapleaflet._layers) {
37 | if (self.mapleaflet._layers[i]._path !== undefined) {
38 | try {
39 | self.mapleaflet.removeLayer(self.mapleaflet._layers[i]);
40 | } catch (e) {
41 | console.log("problem with " + e + self.mapleaflet._layers[i]);
42 | }
43 | }
44 | }
45 | };
46 |
47 | function parseJson(json) {
48 | if (!json || json === '') return undefined;
49 | var value = String(json).replace(/'/g, '\"');
50 | return JSON.parse(value);
51 | }
52 |
53 | function lerp(a, b, f) {
54 | return a + f * (b - a);
55 | }
56 |
57 | self.posOnMap = function (v) {
58 | if (self.ctx.map && self.ctx.map.map && self.ctx.map.map.imageOverlay && self.ctx.map.map.imageOverlay._bounds && self.posFunc) {
59 | const bounds = self.ctx.map.map.imageOverlay._bounds;
60 | var ne = bounds._northEast,
61 | sw = bounds._southWest;
62 | var pos = self.posFunc(v[0], v[1]);
63 | const y = lerp(ne.lat, sw.lat, pos.y);
64 | const x = lerp(sw.lng, ne.lng, pos.x);
65 | return [x, y];
66 | } else {
67 | return v;
68 | }
69 | };
70 |
71 | self.posFuncGeoJson = function (geojson) {
72 | geojson.forEach((g) => {
73 | g.geometry.coordinates = g.geometry.coordinates.map(shape => {
74 | return shape.map(self.posOnMap);
75 | });
76 | });
77 | return geojson;
78 | };
79 |
80 | self.showPolygons = function (parsed, color) {
81 | try {
82 | var geojsonFeature = self.posFuncGeoJson(parsed.features);
83 | L.geoJSON(geojsonFeature, {
84 | style: { "color": color, "opacity": 0.8 }
85 | }).addTo(self.mapleaflet);
86 | } catch (e) {
87 | console.log(e);
88 | }
89 | };
90 |
91 | self.getCurrIndex = function () {
92 | return parseInt($('#TimeFrame')[0].value);
93 | };
94 |
95 | self.setCurrIndex = function (val) {
96 | $('#TimeFrame')[0].value = val;
97 | self.onDataUpdated();
98 | };
99 |
100 | self.getPolygonFrames = function () {
101 | var maxRange = 0;
102 | const polygonFrames = [];
103 | for (var prop in self.ctx.data) {
104 | const data = self.ctx.data[prop].data;
105 | if (!data || !data.length || typeof data[0][1] !== "string" || data[0][1][0]!=='{') continue;
106 | const frames = parseJson(data[0][1]);
107 | if (frames === undefined) continue;
108 | if (frames.length && frames[0].index) {
109 | const indices = frames.map(b => b.index);
110 | maxRange = Math.max(maxRange, Math.max.apply(this, indices));
111 | }
112 | polygonFrames.push({
113 | frames: frames,
114 | color: self.ctx.data[prop].dataKey.color
115 | });
116 | }
117 | return [polygonFrames, maxRange];
118 | };
119 |
120 | self.showPolygonFrame = function (polygonFrames, index) {
121 | let valueName = '';
122 | self.clearPolygons();
123 | polygonFrames.forEach(polys => {
124 | let poly = polys.frames;
125 | if (polys.frames.length) {
126 | poly = polys.frames.find(frame => parseInt(frame.index) === index);
127 | if (poly) {
128 | valueName = poly.name;
129 | self.showPolygons(poly.value, polys.color);
130 | }
131 | } else if (poly) {
132 | self.showPolygons(poly, polys.color);
133 | }
134 | });
135 | self.ctx.map.update();
136 | return valueName ? valueName : '';
137 | };
138 |
139 | /////////////// WIND //////////////////
140 | self.drawWindMarkers = function() {
141 | if (!self.mapleaflet) return undefined;
142 | (self.windMarkers || []).forEach(m => m.removeFrom(self.mapleaflet));
143 | const dirs = self.getDirectionTelemetry();
144 | console.log(dirs);
145 | self.windMarkers = dirs.map(self.showDirectionMarker);
146 | self.windMarkers = self.windMarkers.filter(a => a);
147 | };
148 |
149 | self.getDirectionTelemetry = function () {
150 | // console.log(self.ctx.data);
151 | const nonJsons = self.ctx.data.filter(prop => prop.data.length > 0 && typeof prop.data[0][1] !== "string");
152 | let devices = {};
153 | nonJsons.forEach(prop => {
154 | const t = prop.datasource.name;
155 | devices[t] = devices[t] || {device: t};
156 | const kind = prop.dataKey.name;
157 | devices[t][kind] = prop.data[0][1];
158 | });
159 | return Object.values(devices);
160 | };
161 |
162 | self.showDirectionMarker = function (s) {
163 | if (s.xPos === undefined || s.yPos === undefined) return undefined;
164 | dir = 0;
165 | power = 1;
166 | if (s.u !== undefined && s.v !== undefined) {
167 | dir = Math.atan2(s.v, s.u);
168 | power = Math.sqrt(s.u * s.u + s.v * s.v);
169 | }
170 | const powerSize = power * self.ctx.settings.ArrowLenAt4;
171 | let inverseArrow = /(Win32|Win64)/i.test(navigator.platform);
172 | if (self.ctx.settings.InverseArrow) {
173 | inverseArrow = !inverseArrow;
174 | }
175 |
176 | if (inverseArrow) dir = (dir + 180) % 360;
177 |
178 | // const pos = self.posOnMap([s.latitude, s.longitude]);
179 | const pos = self.posOnMap([s.xPos, s.yPos]);
180 | var marker = L.marker([pos[1], pos[0]], {
181 | icon: L.icon({
182 | iconUrl: self.ctx.settings.arrowImageUrl,
183 | iconSize: [powerSize, parseFloat(self.ctx.settings.ArrowWidth)]
184 | }),
185 | rotationAngle: 360 - dir % 360, // counter clockwise
186 | rotationOrigin: 'center'
187 | }).addTo(self.mapleaflet);
188 |
189 | return marker;
190 | };
191 |
192 | self.setNext = function () {
193 | var maxRange = parseInt($('#TimeFrame')[0].max);
194 | if (maxRange >= 1) {
195 | self.setCurrIndex((self.getCurrIndex() + 1) % (maxRange + 1));
196 | } else {
197 | self.setCurrIndex(0);
198 | }
199 | };
200 |
201 | self.Play = function () {
202 | self.playing = !self.playing;
203 | $('#PlayButton').text(self.playing ? 'Stop' : 'Play');
204 | if (self.playing) {
205 | var cont = () => {
206 | self.setNext();
207 | if (self.playing) {
208 | setTimeout(cont, self.ctx.settings.playDelay);
209 | }
210 | };
211 | cont();
212 | }
213 | };
214 |
215 | self.onResize = function () {
216 | self.ctx.map.resize();
217 | self.resizeMap();
218 | };
219 |
220 | self.resizeMap = function () {
221 | if (!self.mapleaflet || !self.mapleaflet._container) {
222 | return;
223 | }
224 | self.mapleaflet._container.style.height = (self.ctx.height - 30) + "px";
225 | self.mapleaflet.invalidateSize();
226 | };
227 |
228 | function addProp(tbScheme, name, type, props) {
229 | if (tbScheme.groupInfoes[0].GroupTitle !== 'Argos') {
230 | tbScheme.groupInfoes.forEach(inf => inf.formIndex += 1);
231 | tbScheme.groupInfoes.unshift({formIndex: 0, GroupTitle: "Argos"});
232 | tbScheme.form.unshift([]);
233 | }
234 | if (type) {
235 | tbScheme.form[0].push({key: name, type: type});
236 | } else {
237 | tbScheme.form[0].push(name);
238 | }
239 | tbScheme.schema.properties[name] = props;
240 | }
241 |
242 | self.getSettingsSchema = function () {
243 | var tbScheme = JSON.parse(JSON.stringify(TbMapWidgetV2.settingsSchema('image-map')));
244 | console.log(tbScheme);
245 | addProp(tbScheme, "InverseArrow", 0, {"title": "Inverse Arrow's U and V directions","type": "boolean","default": false});
246 | addProp(tbScheme, "playDelay", 0, {"title": "Play delay","type": "number","default": 500});
247 | addProp(tbScheme, "ArrowLenAt4", 0, {
248 | "title": "Arrow length in pixels when power/speed is 4",
249 | "type": "number",
250 | "default": 40
251 | });
252 | addProp(tbScheme, "ArrowWidth", 0, {
253 | "title": "Arrow width in pixels",
254 | "type": "number",
255 | "default": 40
256 | });
257 | addProp(tbScheme, "arrowImageUrl", "image" , {
258 | "title": "Arrow image",
259 | "type": "string",
260 | "default": "data:image/png;base64,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"
261 | });
262 | return tbScheme;
263 | };
264 | self.getDataKeySettingsSchema = function () {
265 | return TbMapWidgetV2.dataKeySettingsSchema('image-map');
266 | };
267 |
268 | self.actionSources = function () {
269 | return TbMapWidgetV2.actionSources();
270 | };
271 |
272 | self.onDestroy = function () { };
273 |
274 | (function () {
275 | // save these original methods before they are overwritten
276 | var proto_initIcon = L.Marker.prototype._initIcon;
277 | var proto_setPos = L.Marker.prototype._setPos;
278 |
279 | var oldIE = (L.DomUtil.TRANSFORM === 'msTransform');
280 |
281 | L.Marker.addInitHook(function () {
282 | var iconOptions = this.options.icon && this.options.icon.options;
283 | var iconAnchor = iconOptions && this.options.icon.options.iconAnchor;
284 | if (iconAnchor) {
285 | iconAnchor = (iconAnchor[0] + 'px ' + iconAnchor[1] + 'px');
286 | }
287 | this.options.rotationOrigin = this.options.rotationOrigin || iconAnchor || 'center bottom';
288 | this.options.rotationAngle = this.options.rotationAngle || 0;
289 |
290 | // Ensure marker keeps rotated during dragging
291 | this.on('drag', function (e) { e.target._applyRotation(); });
292 | });
293 |
294 | L.Marker.include({
295 | _initIcon: function () {
296 | proto_initIcon.call(this);
297 | },
298 |
299 | _setPos: function (pos) {
300 | proto_setPos.call(this, pos);
301 | this._applyRotation();
302 | },
303 |
304 | _applyRotation: function () {
305 | if (this.options.rotationAngle) {
306 | this._icon.style[L.DomUtil.TRANSFORM + 'Origin'] = this.options.rotationOrigin;
307 |
308 | // console.log(this.options.rotationAngle);
309 | // if(oldIE) {
310 | // // for IE 9, use the 2D rotation
311 | // this._icon.style[L.DomUtil.TRANSFORM] = 'rotate(' + this.options.rotationAngle + 'deg)';
312 | // } else {
313 | // // for modern browsers, prefer the 3D accelerated version
314 | this._icon.style[L.DomUtil.TRANSFORM] =
315 | this._icon.style[L.DomUtil.TRANSFORM].replace(/ rotateZ\(\d+deg\)/g, '')
316 | + ' rotateZ(' + this.options.rotationAngle + 'deg)';
317 | // }
318 | }
319 | },
320 |
321 | setRotationAngle: function (angle) {
322 | this.options.rotationAngle = angle;
323 | this.update();
324 | return this;
325 | },
326 |
327 | setRotationOrigin: function (origin) {
328 | this.options.rotationOrigin = origin;
329 | this.update();
330 | return this;
331 | }
332 | });
333 | })();
334 |
--------------------------------------------------------------------------------