diff --git a/prog/gtfs.py b/prog/gtfs.py
new file mode 100644
index 0000000000000000000000000000000000000000..db390ae4e2e94ef31673d3a47e1b9c6cac6ead3d
--- /dev/null
+++ b/prog/gtfs.py
@@ -0,0 +1,76 @@
+import datetime
+import csv
+
+from dataclasses import dataclass
+
+@dataclass
+class Stop:
+    id: int
+    name: str
+
+@dataclass
+class ShapePoint:
+    lat: str
+    lon: float
+    dist_traveled: float
+
+@dataclass
+class TripStop:
+    stop: Stop
+    shape_dist_traveled: float
+
+
+class GtfsDay():
+    def __init__(self, date, data_getter):
+        self.date = date
+        self.data_getter = data_getter
+        self.stops = None
+
+    async def get_file(self, name):
+        s = await self.data_getter(self.date, name)
+        return list(csv.DictReader(s.decode("utf-8").split("\n")))
+
+    async def load_stops(self):
+        if self.stops: return
+        d = await self.get_file("stops.txt")
+        if self.stops: return
+        self.stops = {
+                x["stop_id"]: Stop(x["stop_id"], x["stop_name"])
+                for x in d
+        }
+
+    async def get_shape_for_trip_id(self, trip_id):
+        for trip in await self.get_file("trips.txt"):
+            if trip["trip_id"] == trip_id:
+                shape_id = trip["shape_id"]
+        d = await self.get_file("shape_by_id/"+shape_id)
+        return [ ShapePoint(
+            float(x["shape_pt_lat"]), float(x["shape_pt_lon"]), float(x["shape_dist_traveled"])
+
+        ) for x in d ]
+
+    async def get_stops_for_trip_id(self, trip_id):
+        await self.load_stops()
+        d = await self.get_file("stop_times.txt")
+        return [ TripStop(
+            self.stops[x["stop_id"]],
+            float(x["shape_dist_traveled"])
+        ) for x in d if x["trip_id"] == trip_id]
+
+
+
+
+
+
+default_data_getter = None
+
+for_date_cache = {}
+
+def for_date(date, data_getter=None):
+    if isinstance(date, datetime.datetime):
+        date = date.date()
+    if date not in for_date_cache:
+        for_date_cache[date] = GtfsDay(date, data_getter or default_data_getter)
+    return for_date_cache[date]
+
+
diff --git a/prog/labeling-history-points.xml b/prog/labeling-history-points.xml
new file mode 100644
index 0000000000000000000000000000000000000000..4f9e4875f3b9a5c2678015d6bca8e04f9eac51fa
--- /dev/null
+++ b/prog/labeling-history-points.xml
@@ -0,0 +1,124 @@
+<labeling type="simple">
+ <settings calloutType="simple">
+  <text-style tabStopDistanceUnit="Point" forcedItalic="0" fontFamily="Open Sans" fontWordSpacing="0" fontItalic="0" fontSize="10" fontSizeMapUnitScale="3x:0,0,0,0,0,0" isExpression="1" fontStrikeout="0" forcedBold="0" capitalization="0" blendMode="0" textColor="255,1,1,255,rgb:1,0.00392156862745098,0.00392156862745098,1" textOpacity="1" fontKerning="1" fontUnderline="0" multilineHeight="1" fontWeight="50" fontLetterSpacing="0" fontSizeUnit="Point" stretchFactor="100" namedStyle="Regular" allowHtml="0" fieldName="'R:' || &quot;repeated&quot; || ' W:' || &quot;without_data&quot;" legendString="Aa" textOrientation="horizontal" previewBkgrdColor="255,255,255,255,rgb:1,1,1,1" tabStopDistance="80" useSubstitutions="0" multilineHeightUnit="Percentage" tabStopDistanceMapUnitScale="3x:0,0,0,0,0,0">
+   <families/>
+   <text-buffer bufferNoFill="1" bufferColor="250,250,250,255,rgb:0.98039215686274506,0.98039215686274506,0.98039215686274506,1" bufferSizeMapUnitScale="3x:0,0,0,0,0,0" bufferJoinStyle="128" bufferBlendMode="0" bufferOpacity="1" bufferSizeUnits="MM" bufferDraw="0" bufferSize="1"/>
+   <text-mask maskSizeUnits="MM" maskSize="1.5" maskedSymbolLayers="" maskOpacity="1" maskJoinStyle="128" maskEnabled="0" maskSizeMapUnitScale="3x:0,0,0,0,0,0" maskSize2="1.5" maskType="0"/>
+   <background shapeSVGFile="" shapeType="0" shapeFillColor="255,255,255,255,rgb:1,1,1,1" shapeJoinStyle="64" shapeRadiiX="0" shapeSizeY="0" shapeRadiiMapUnitScale="3x:0,0,0,0,0,0" shapeBlendMode="0" shapeOffsetUnit="Point" shapeSizeType="0" shapeOffsetX="0" shapeBorderWidthMapUnitScale="3x:0,0,0,0,0,0" shapeOpacity="1" shapeRadiiUnit="Point" shapeOffsetY="0" shapeRadiiY="0" shapeSizeX="0" shapeSizeUnit="Point" shapeRotationType="0" shapeRotation="0" shapeOffsetMapUnitScale="3x:0,0,0,0,0,0" shapeBorderWidth="0" shapeSizeMapUnitScale="3x:0,0,0,0,0,0" shapeBorderWidthUnit="Point" shapeBorderColor="128,128,128,255,rgb:0.50196078431372548,0.50196078431372548,0.50196078431372548,1" shapeDraw="1">
+    <symbol name="markerSymbol" is_animated="0" clip_to_extent="1" frame_rate="10" force_rhr="0" type="marker" alpha="1">
+     <data_defined_properties>
+      <Option type="Map">
+       <Option name="name" value="" type="QString"/>
+       <Option name="properties"/>
+       <Option name="type" value="collection" type="QString"/>
+      </Option>
+     </data_defined_properties>
+     <layer pass="0" enabled="1" class="SimpleMarker" id="" locked="0">
+      <Option type="Map">
+       <Option name="angle" value="0" type="QString"/>
+       <Option name="cap_style" value="square" type="QString"/>
+       <Option name="color" value="145,82,45,255,rgb:0.56862745098039214,0.32156862745098042,0.17647058823529413,1" type="QString"/>
+       <Option name="horizontal_anchor_point" value="1" type="QString"/>
+       <Option name="joinstyle" value="bevel" type="QString"/>
+       <Option name="name" value="circle" type="QString"/>
+       <Option name="offset" value="0,0" type="QString"/>
+       <Option name="offset_map_unit_scale" value="3x:0,0,0,0,0,0" type="QString"/>
+       <Option name="offset_unit" value="MM" type="QString"/>
+       <Option name="outline_color" value="35,35,35,255,rgb:0.13725490196078433,0.13725490196078433,0.13725490196078433,1" type="QString"/>
+       <Option name="outline_style" value="solid" type="QString"/>
+       <Option name="outline_width" value="0" type="QString"/>
+       <Option name="outline_width_map_unit_scale" value="3x:0,0,0,0,0,0" type="QString"/>
+       <Option name="outline_width_unit" value="MM" type="QString"/>
+       <Option name="scale_method" value="diameter" type="QString"/>
+       <Option name="size" value="2" type="QString"/>
+       <Option name="size_map_unit_scale" value="3x:0,0,0,0,0,0" type="QString"/>
+       <Option name="size_unit" value="MM" type="QString"/>
+       <Option name="vertical_anchor_point" value="1" type="QString"/>
+      </Option>
+      <data_defined_properties>
+       <Option type="Map">
+        <Option name="name" value="" type="QString"/>
+        <Option name="properties"/>
+        <Option name="type" value="collection" type="QString"/>
+       </Option>
+      </data_defined_properties>
+     </layer>
+    </symbol>
+    <symbol name="fillSymbol" is_animated="0" clip_to_extent="1" frame_rate="10" force_rhr="0" type="fill" alpha="1">
+     <data_defined_properties>
+      <Option type="Map">
+       <Option name="name" value="" type="QString"/>
+       <Option name="properties"/>
+       <Option name="type" value="collection" type="QString"/>
+      </Option>
+     </data_defined_properties>
+     <layer pass="0" enabled="1" class="SimpleFill" id="" locked="0">
+      <Option type="Map">
+       <Option name="border_width_map_unit_scale" value="3x:0,0,0,0,0,0" type="QString"/>
+       <Option name="color" value="255,255,255,255,rgb:1,1,1,1" type="QString"/>
+       <Option name="joinstyle" value="bevel" type="QString"/>
+       <Option name="offset" value="0,0" type="QString"/>
+       <Option name="offset_map_unit_scale" value="3x:0,0,0,0,0,0" type="QString"/>
+       <Option name="offset_unit" value="MM" type="QString"/>
+       <Option name="outline_color" value="128,128,128,255,rgb:0.50196078431372548,0.50196078431372548,0.50196078431372548,1" type="QString"/>
+       <Option name="outline_style" value="no" type="QString"/>
+       <Option name="outline_width" value="0" type="QString"/>
+       <Option name="outline_width_unit" value="Point" type="QString"/>
+       <Option name="style" value="solid" type="QString"/>
+      </Option>
+      <data_defined_properties>
+       <Option type="Map">
+        <Option name="name" value="" type="QString"/>
+        <Option name="properties"/>
+        <Option name="type" value="collection" type="QString"/>
+       </Option>
+      </data_defined_properties>
+     </layer>
+    </symbol>
+   </background>
+   <shadow shadowOffsetMapUnitScale="3x:0,0,0,0,0,0" shadowOffsetGlobal="1" shadowRadius="1.5" shadowOpacity="0.69999999999999996" shadowColor="0,0,0,255,rgb:0,0,0,1" shadowOffsetDist="1" shadowRadiusUnit="MM" shadowRadiusAlphaOnly="0" shadowOffsetUnit="MM" shadowDraw="0" shadowOffsetAngle="135" shadowBlendMode="6" shadowUnder="0" shadowRadiusMapUnitScale="3x:0,0,0,0,0,0" shadowScale="100"/>
+   <dd_properties>
+    <Option type="Map">
+     <Option name="name" value="" type="QString"/>
+     <Option name="properties"/>
+     <Option name="type" value="collection" type="QString"/>
+    </Option>
+   </dd_properties>
+   <substitutions/>
+  </text-style>
+  <text-format autoWrapLength="0" useMaxLineLengthForAutoWrap="1" multilineAlign="3" leftDirectionSymbol="&lt;" placeDirectionSymbol="0" plussign="0" wrapChar="" reverseDirectionSymbol="0" formatNumbers="0" decimals="3" addDirectionSymbol="0" rightDirectionSymbol=">"/>
+  <placement maximumDistance="0" repeatDistanceMapUnitScale="3x:0,0,0,0,0,0" lineAnchorPercent="0.5" centroidWhole="0" allowDegraded="0" distMapUnitScale="3x:0,0,0,0,0,0" offsetUnits="MM" geometryGeneratorType="PointGeometry" labelOffsetMapUnitScale="3x:0,0,0,0,0,0" overrunDistanceMapUnitScale="3x:0,0,0,0,0,0" distUnits="MM" overrunDistance="0" rotationAngle="0" priority="5" lineAnchorType="0" xOffset="0" placement="6" prioritization="PreferCloser" rotationUnit="AngleDegrees" maximumDistanceMapUnitScale="3x:0,0,0,0,0,0" yOffset="0" repeatDistanceUnits="MM" offsetType="1" fitInPolygonOnly="0" preserveRotation="1" geometryGenerator="" lineAnchorTextPoint="FollowPlacement" quadOffset="4" maxCurvedCharAngleIn="25" layerType="PointGeometry" centroidInside="0" dist="0" repeatDistance="0" overlapHandling="PreventOverlap" predefinedPositionOrder="TR,TL,BR,BL,R,L,TSR,BSR" overrunDistanceUnit="MM" geometryGeneratorEnabled="0" maximumDistanceUnit="MM" maxCurvedCharAngleOut="-25" placementFlags="10" polygonPlacementFlags="2" lineAnchorClipping="0"/>
+  <rendering obstacle="1" scaleVisibility="0" unplacedVisibility="0" zIndex="0" obstacleType="1" mergeLines="0" scaleMax="0" drawLabels="1" fontMinPixelSize="3" obstacleFactor="1" upsidedownLabels="0" limitNumLabels="0" minFeatureSize="0" scaleMin="0" fontLimitPixelSize="0" labelPerPart="0" fontMaxPixelSize="10000" maxNumLabels="2000"/>
+  <dd_properties>
+   <Option type="Map">
+    <Option name="name" value="" type="QString"/>
+    <Option name="properties"/>
+    <Option name="type" value="collection" type="QString"/>
+   </Option>
+  </dd_properties>
+  <callout type="simple">
+   <Option type="Map">
+    <Option name="anchorPoint" value="pole_of_inaccessibility" type="QString"/>
+    <Option name="blendMode" value="0" type="int"/>
+    <Option name="ddProperties" type="Map">
+     <Option name="name" value="" type="QString"/>
+     <Option name="properties"/>
+     <Option name="type" value="collection" type="QString"/>
+    </Option>
+    <Option name="drawToAllParts" value="false" type="bool"/>
+    <Option name="enabled" value="0" type="QString"/>
+    <Option name="labelAnchorPoint" value="point_on_exterior" type="QString"/>
+    <Option name="lineSymbol" value="&lt;symbol name=&quot;symbol&quot; is_animated=&quot;0&quot; clip_to_extent=&quot;1&quot; frame_rate=&quot;10&quot; force_rhr=&quot;0&quot; type=&quot;line&quot; alpha=&quot;1&quot;>&lt;data_defined_properties>&lt;Option type=&quot;Map&quot;>&lt;Option name=&quot;name&quot; value=&quot;&quot; type=&quot;QString&quot;/>&lt;Option name=&quot;properties&quot;/>&lt;Option name=&quot;type&quot; value=&quot;collection&quot; type=&quot;QString&quot;/>&lt;/Option>&lt;/data_defined_properties>&lt;layer pass=&quot;0&quot; enabled=&quot;1&quot; class=&quot;SimpleLine&quot; id=&quot;{2a0c46cd-8232-46a8-a05d-0402453f19ae}&quot; locked=&quot;0&quot;>&lt;Option type=&quot;Map&quot;>&lt;Option name=&quot;align_dash_pattern&quot; value=&quot;0&quot; type=&quot;QString&quot;/>&lt;Option name=&quot;capstyle&quot; value=&quot;square&quot; type=&quot;QString&quot;/>&lt;Option name=&quot;customdash&quot; value=&quot;5;2&quot; type=&quot;QString&quot;/>&lt;Option name=&quot;customdash_map_unit_scale&quot; value=&quot;3x:0,0,0,0,0,0&quot; type=&quot;QString&quot;/>&lt;Option name=&quot;customdash_unit&quot; value=&quot;MM&quot; type=&quot;QString&quot;/>&lt;Option name=&quot;dash_pattern_offset&quot; value=&quot;0&quot; type=&quot;QString&quot;/>&lt;Option name=&quot;dash_pattern_offset_map_unit_scale&quot; value=&quot;3x:0,0,0,0,0,0&quot; type=&quot;QString&quot;/>&lt;Option name=&quot;dash_pattern_offset_unit&quot; value=&quot;MM&quot; type=&quot;QString&quot;/>&lt;Option name=&quot;draw_inside_polygon&quot; value=&quot;0&quot; type=&quot;QString&quot;/>&lt;Option name=&quot;joinstyle&quot; value=&quot;bevel&quot; type=&quot;QString&quot;/>&lt;Option name=&quot;line_color&quot; value=&quot;60,60,60,255,rgb:0.23529411764705882,0.23529411764705882,0.23529411764705882,1&quot; type=&quot;QString&quot;/>&lt;Option name=&quot;line_style&quot; value=&quot;solid&quot; type=&quot;QString&quot;/>&lt;Option name=&quot;line_width&quot; value=&quot;0.3&quot; type=&quot;QString&quot;/>&lt;Option name=&quot;line_width_unit&quot; value=&quot;MM&quot; type=&quot;QString&quot;/>&lt;Option name=&quot;offset&quot; value=&quot;0&quot; type=&quot;QString&quot;/>&lt;Option name=&quot;offset_map_unit_scale&quot; value=&quot;3x:0,0,0,0,0,0&quot; type=&quot;QString&quot;/>&lt;Option name=&quot;offset_unit&quot; value=&quot;MM&quot; type=&quot;QString&quot;/>&lt;Option name=&quot;ring_filter&quot; value=&quot;0&quot; type=&quot;QString&quot;/>&lt;Option name=&quot;trim_distance_end&quot; value=&quot;0&quot; type=&quot;QString&quot;/>&lt;Option name=&quot;trim_distance_end_map_unit_scale&quot; value=&quot;3x:0,0,0,0,0,0&quot; type=&quot;QString&quot;/>&lt;Option name=&quot;trim_distance_end_unit&quot; value=&quot;MM&quot; type=&quot;QString&quot;/>&lt;Option name=&quot;trim_distance_start&quot; value=&quot;0&quot; type=&quot;QString&quot;/>&lt;Option name=&quot;trim_distance_start_map_unit_scale&quot; value=&quot;3x:0,0,0,0,0,0&quot; type=&quot;QString&quot;/>&lt;Option name=&quot;trim_distance_start_unit&quot; value=&quot;MM&quot; type=&quot;QString&quot;/>&lt;Option name=&quot;tweak_dash_pattern_on_corners&quot; value=&quot;0&quot; type=&quot;QString&quot;/>&lt;Option name=&quot;use_custom_dash&quot; value=&quot;0&quot; type=&quot;QString&quot;/>&lt;Option name=&quot;width_map_unit_scale&quot; value=&quot;3x:0,0,0,0,0,0&quot; type=&quot;QString&quot;/>&lt;/Option>&lt;data_defined_properties>&lt;Option type=&quot;Map&quot;>&lt;Option name=&quot;name&quot; value=&quot;&quot; type=&quot;QString&quot;/>&lt;Option name=&quot;properties&quot;/>&lt;Option name=&quot;type&quot; value=&quot;collection&quot; type=&quot;QString&quot;/>&lt;/Option>&lt;/data_defined_properties>&lt;/layer>&lt;/symbol>" type="QString"/>
+    <Option name="minLength" value="0" type="double"/>
+    <Option name="minLengthMapUnitScale" value="3x:0,0,0,0,0,0" type="QString"/>
+    <Option name="minLengthUnit" value="MM" type="QString"/>
+    <Option name="offsetFromAnchor" value="0" type="double"/>
+    <Option name="offsetFromAnchorMapUnitScale" value="3x:0,0,0,0,0,0" type="QString"/>
+    <Option name="offsetFromAnchorUnit" value="MM" type="QString"/>
+    <Option name="offsetFromLabel" value="0" type="double"/>
+    <Option name="offsetFromLabelMapUnitScale" value="3x:0,0,0,0,0,0" type="QString"/>
+    <Option name="offsetFromLabelUnit" value="MM" type="QString"/>
+   </Option>
+  </callout>
+ </settings>
+</labeling>
diff --git a/prog/main.py b/prog/main.py
index 5541e76a362de629070566d6eca222d938a1553f..6b0f5de56c41620582042f93060d167d768ecf06 100755
--- a/prog/main.py
+++ b/prog/main.py
@@ -6,7 +6,7 @@ from qgis.PyQt.QtXml import QDomDocument, QDomElement
 from qgis.PyQt.QtWidgets import *
 from qgis.PyQt.QtCore import *
 from qgis.PyQt.QtGui import *
-import sip
+import math
 
 from qasync import QEventLoop, asyncClose, asyncSlot, QApplication
 import asyncio
@@ -17,8 +17,11 @@ import datetime
 import json
 import sys, os
 import pprint
+import gtfs
 
 
+window_title_prefix = "jr"
+
 QgsApplication.setPrefixPath("/usr/", True)
 qgs = QgsApplication([], True)
 qgs.initQgis()
@@ -36,16 +39,129 @@ class ClickableLabel(QLabel):
             self.clicked.emit()
 
 
+def dist(a, b):
+    assert 40 < a[0] and a[0] < 60, a[0]
+    assert  5 < a[1] and a[1] < 25, a[1]
+    assert 40 < b[0] and b[0] < 60, b[0]
+    assert  5 < b[1] and b[1] < 25, b[1]
+    #           lat                     lon
+    return math.sqrt(((a[0]-b[0])*111.2)**2 + ((a[1]-b[1])*71.50)**2)
+
 communications = {}
-async def get_communication(cmd='ssh hluk.fnuk.eu /mnt/jr/prog/run_py server.py'):
+async def get_communication(cmd='ssh localhost /aux/jiri/jr/prog/run_py server.py'):
     if cmd not in communications:
         s = await communication.SSHRunSocket().connect(cmd)
         communications[cmd] = communication.DownloadServer(s)
     return communications[cmd]
 
-class Vehicle:
-    def __init__(self, json):
+async def gtfs_default_data_getter(date, filename):
+    c =  await get_communication()
+    return await c.gtfs_get_file(date, filename)
+
+gtfs.default_data_getter = gtfs_default_data_getter
+
+def layer_updated(layer):
+    prov = layer.dataProvider()
+    layer.updateExtents()
+    layer.triggerRepaint()
+    prov.reloadData()
+
+
+
+class Trip:
+    def __init__(self, trip_id, date):
+        self.trip_id = trip_id
+        self.date = date
+
+class HistoryPoint:
+    def __init__(self, json, capture_time):
+        self.capture_time = capture_time
         self.json = json
+        self.state_position = json['properties']['last_position']['state_position']
+        self.openapi_shape_dist_traveled = json['properties']['last_position']['shape_dist_traveled']
+        self.lon, self.lat = json["geometry"]["coordinates"]
+        self.repeated = 0
+
+class TripHistory:
+    def __init__(self, trip):
+        self.trip = trip
+        self.history = []
+
+    async def load_gtfs_shape(self):
+        self.gtfs_shape = await gtfs.for_date(self.trip.date).get_shape_for_trip_id(self.trip.trip_id)
+        self.stops = await gtfs.for_date(self.trip.date).get_stops_for_trip_id(self.trip.trip_id)
+
+
+    async def load_history(self, dt_from, dt_to):
+        tps = await get_data_of_trip(self.trip.trip_id, dt_from, dt_to)
+
+        tps_new = []
+        for dt, tp in tps:
+            self.add_history_point(dt, tp)
+            if tp is not None:
+                if len(tps_new) and tp["geometry"]["coordinates"] == tps_new[-1][1]["geometry"]["coordinates"]:
+                    if tps_new[-1][1] != tp:
+                        print("Same coordinates but different data:")
+                        pprint.pp(tps_new[-1][1])
+                        print("---------------------")
+                        pprint.pp(tp)
+                        print("=====================")
+                    tps_new[-1][2]["repeated"] += 1
+                else:
+                    tps_new.append((dt, tp, {"repeated": 0, "without_data": 0}))
+            else:
+               if len(tps_new):
+                    tps_new[-1][2]["without_data"] += 1
+        tps = tps_new
+
+    def add_history_point(self, dt, json):
+        if json is not None:
+            if len(self.history) and json["geometry"]["coordinates"] == self.history[-1].json["geometry"]["coordinates"]:
+                if self.history[-1].json != json:
+                    print("Same coordinates but different data:")
+                    pprint.pp(self.history[-1].json)
+                    print("---------------------")
+                    pprint.pp(json)
+                    print("=====================")
+                self.history[-1].repeated += 1
+            else:
+                hp = HistoryPoint(json, dt)
+                if hp.state_position in ['on_track', 'at_stop']:
+                    if len(self.history):
+                        last_shape_point = self.history[-1].shape_point
+                        if last_shape_point is None:
+                            last_shape_point = 0 # We are on the begin of the track (last point was in before track state)
+                    else:
+                        last_shape_point = None # We don't know where we are
+                    hp.shape_point = min(
+                            range(len(self.gtfs_shape)),
+                            key=lambda i: dist((hp.lat, hp.lon), (self.gtfs_shape[i].lat, self.gtfs_shape[i].lon))
+                                               + (0.01*abs(self.gtfs_shape[i].dist_traveled - self.gtfs_shape[last_shape_point].dist_traveled) if last_shape_point is not None else 0)
+                    )
+                    i = hp.shape_point
+                    print(i, last_shape_point, dist((hp.lat, hp.lon), (self.gtfs_shape[i].lat, self.gtfs_shape[i].lon)), self.gtfs_shape[i].dist_traveled - self.gtfs_shape[last_shape_point].dist_traveled if last_shape_point is not None else None)
+                    hp.shape_point_dist_traveled = self.gtfs_shape[hp.shape_point].dist_traveled
+                else:
+                    hp.shape_point = hp.shape_point_dist_traveled = None
+                    print(hp.state_position)
+
+                self.history.append(hp)
+
+        else:
+           if len(tps_new):
+               ...
+                # tps_new[-1][2]["without_data"] += 1
+
+
+
+
+
+
+class TripPoint:
+    def __init__(self, json, capture_time):
+        self.json = json
+        self.trip = Trip(json["properties"]["trip"]["gtfs"]["trip_id"], capture_time.date())
+        self.capture_time = capture_time
 
 
 class MainWind(QMainWindow):
@@ -60,7 +176,6 @@ class MainWind(QMainWindow):
         self.data_layer = QgsVectorLayer(uri,
                            'PID realtime',
                            'memory')
-
         # self.data_layer.setAutoRefreshEnabled(True)
         # self.data_layer.setAutoRefreshInterval(500)
         d = QDomDocument()
@@ -72,6 +187,7 @@ class MainWind(QMainWindow):
 
         # set the map canvas layer set
         self.map.canvas.setLayers([self.data_layer, orm, mapy_cz])
+        self.map.addLayer(self.data_layer)
 
 
         self.feature_identifier = MainTool(self, self.map.canvas)
@@ -81,6 +197,29 @@ class MainWind(QMainWindow):
 
         self.current_data = []
 
+        self._tmp = QPushButton()
+        self._tmp.clicked.connect(self.tmp)
+
+        dockWidget = QDockWidget("Dock Widget")
+        dockWidget.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea)
+        dockWidget.setWidget(self._tmp)
+        self.addDockWidget(Qt.LeftDockWidgetArea, dockWidget)
+
+    @asyncSlot()
+    async def tmp(self):
+        dt = datetime.datetime(2024, 7, 4, 8, 4, 20)
+        await self.load_data(dt)
+        t = Trip('156_324_240701', dt.date())
+        th = TripHistory(t)
+        await th.load_gtfs_shape()
+        await th.load_history(dt-datetime.timedelta(minutes=3), dt)
+        self.plot_gtfs_shape(th.trip, th.gtfs_shape)
+        self.plot_history_shape_mapping(th)
+        self.plot_history(th)
+
+        history_graph = HistoryGraph(th)
+        history_graph.show()
+        tmp_windows[history_graph]=history_graph
 
 
     def set_date_time(self):
@@ -98,10 +237,10 @@ class MainWind(QMainWindow):
         for fname, data in (await c.get_data(dt)).items():
             proc = await asyncio.create_subprocess_exec("gunzip", stdout=asyncio.subprocess.PIPE, stdin=asyncio.subprocess.PIPE)
             stdout, stderr = await proc.communicate(data)
-            self.show_data(stdout)
+            self.show_data(stdout, dt)
             return
 
-    def show_data(self, source_json):
+    def show_data(self, source_json, capture_time):
         current_data = []
         feats = []
 
@@ -129,9 +268,9 @@ class MainWind(QMainWindow):
                 feat["vehicle_type"] = str(dato_vehicle_type["description_cs"])
             feats.append(feat)
 
-            vehicle = Vehicle(json=dato)
-            vehicle.point = point
-            current_data.append(vehicle)
+            tp = TripPoint(json=dato, capture_time=capture_time)
+            tp.point = point
+            current_data.append(tp)
 
         self.current_data = current_data
 
@@ -142,6 +281,143 @@ class MainWind(QMainWindow):
         self.data_layer.triggerRepaint()
         print("repaintRequested done")
 
+    def plot_gtfs_shape(self, trip, shape):
+        uri = "LineString"
+        shape_line_layer = QgsVectorLayer(uri,
+                           f"Shape of {trip.trip_id}",
+                           'memory')
+        shape_line_layer.renderer().symbol().setWidth(1.3)
+        shape_line_layer.renderer().symbol().setColor(QColor.fromRgb(0, 255, 0))
+
+        shape_line_prov = shape_line_layer.dataProvider()
+
+        shape_line_fields = shape_line_layer.fields()
+        feat = QgsFeature(shape_line_fields)
+        points = []
+        for x in shape:
+            points.append(QgsPointXY(float(x.lon), float(x.lat)))
+        geometry = QgsGeometry.fromPolylineXY(points)
+        feat.setGeometry(geometry)
+        shape_line_prov.addFeatures([feat])
+
+        shape_line_layer.updateExtents()
+        shape_line_layer.triggerRepaint()
+        shape_line_prov.reloadData()
+
+        self.map.addLayer(shape_line_layer)
+
+        shape_line_layer.triggerRepaint()
+        print("plot_gtfs END")
+
+    def plot_history(self,  th: TripHistory):
+        # tps_new = []
+        # for dt, tp in tps:
+        #     if tp is not None:
+        #         if len(tps_new) and tp["geometry"]["coordinates"] == tps_new[-1][1]["geometry"]["coordinates"]:
+        #             if tps_new[-1][1] != tp:
+        #                 print("Same coordinates but different data:")
+        #                 pprint.pp(tps_new[-1][1])
+        #                 print("---------------------")
+        #                 pprint.pp(tp)
+        #                 print("=====================")
+        #             tps_new[-1][2]["repeated"] += 1
+        #         else:
+        #             tps_new.append((dt, tp, {"repeated": 0, "without_data": 0}))
+        #     else:
+        #        if len(tps_new):
+        #             tps_new[-1][2]["without_data"] += 1
+        # tps = tps_new
+
+
+
+        uri = "LineString"
+        history_line_layer = QgsVectorLayer(uri,
+                           f"History of {th.trip.trip_id}",
+                           'memory')
+        history_line_layer.renderer().symbol().setWidth(1.3)
+        history_line_layer.renderer().symbol().setColor(QColor.fromRgb(255, 0, 0))
+
+        uri = "Point?field=repeated:int&field=without_data:int&index=yes"
+        history_points_layer = QgsVectorLayer(uri,
+                           f"History of {th.trip.trip_id}",
+                           'memory')
+        d = QDomDocument()
+        d.setContent(open("labeling-history-points.xml").read())
+        labeling = QgsAbstractVectorLayerLabeling.create(d.documentElement(), QgsReadWriteContext())
+        history_points_layer.setLabeling(labeling)
+        history_points_layer.setLabelsEnabled(True)
+
+        history_line_prov = history_line_layer.dataProvider()
+        history_points_prov = history_points_layer.dataProvider()
+
+        history_line_fields = history_line_layer.fields()
+        history_points_fields = history_points_layer.fields()
+
+        feat = QgsFeature(history_line_fields)
+        history_points_feats = []
+        line_points = []
+        for hp in th.history:
+            point = QgsPointXY(hp.lon, hp.lat)
+            line_points.append(point)
+        geometry = QgsGeometry.fromPolylineXY(line_points)
+        feat.setGeometry(geometry)
+        history_line_prov.addFeatures([feat])
+
+
+        for hp in th.history:
+            feat = QgsFeature(history_points_fields)
+            point = QgsPointXY(hp.lon, hp.lat)
+            geometry = QgsGeometry.fromPointXY(point)
+            feat.setGeometry(geometry)
+            # feat["repeated"] = tp_data["repeated"]
+            # feat["without_data"] = tp_data["without_data"]
+            history_points_feats.append(feat)
+        history_points_prov.addFeatures(history_points_feats)
+
+        history_line_layer.updateExtents()
+        history_line_layer.triggerRepaint()
+        history_points_layer.updateExtents()
+        history_points_layer.triggerRepaint()
+
+        history_line_prov.reloadData()
+        history_points_prov.reloadData()
+
+
+        self.map.addLayer(history_line_layer)
+        self.map.addLayer(history_points_layer)
+
+    def plot_history_shape_mapping(self, th: TripHistory):
+        uri = "LineString"
+        layer = QgsVectorLayer(uri,
+                           f"Shape history mapping of {th.trip.trip_id}",
+                           'memory')
+        layer.renderer().symbol().setWidth(1.3)
+        layer.renderer().symbol().setColor(QColor.fromRgb(0, 0, 255))
+
+        prov = layer.dataProvider()
+
+        fields = layer.fields()
+        feats = []
+        for hp in th.history:
+            if hp.shape_point is not None:
+                feat = QgsFeature(fields)
+                x = th.gtfs_shape[hp.shape_point]
+                points = [
+                        QgsPointXY(hp.lon, hp.lat),
+                        QgsPointXY(x.lon, x.lat)
+                ]
+                geometry = QgsGeometry.fromPolylineXY(points)
+                feat.setGeometry(geometry)
+                feats.append(feat)
+
+        prov.addFeatures(feats)
+
+        layer_updated(layer)
+        self.map.addLayer(layer)
+
+
+
+
 
 class SetDateTimeCaptureLabel(ClickableLabel):
     def __init__(self, dt, window_set_date_time):
@@ -258,15 +534,102 @@ class SetDateTimeWind(QWidget):
         c = await get_communication()
 
         _10s_labels = [[[] for j in range(6)] for _ in range(60)]
-        dts = await c.list_realtime_data(date, date+datetime.timedelta(hours=1))
+        dts = await c.list_realtime_data(date, date+datetime.timedelta(minutes=1))
 
-        # TODO Check změny 
+        if date != datetime.datetime.combine(self._calendar.selectedDate().toPyDate(), datetime.time(self.hour,0,0)):
+            return # Something changed during await
 
         for dt in dts:
             _10s_labels[dt.minute][dt.second//10].append(SetDateTimeCaptureLabel(dt, self))
 
         self.update_10s_layouts(_10s_labels)
 
+async def get_data_of_trip(trip_id, date_from, date_to):
+    c = await get_communication()
+    dts = await c.list_realtime_data(date_from, date_to)
+    out = []
+    for dt in dts:
+        tc = None
+        print("GET", dt)
+        for fname, data in (await c.get_data(dt)).items():
+            proc = await asyncio.create_subprocess_exec("gunzip", stdout=asyncio.subprocess.PIPE, stdin=asyncio.subprocess.PIPE)
+            stdout, stderr = await proc.communicate(data)
+            data = json.loads(stdout)
+            for dato in data["features"]:
+                if dato["properties"]["trip"]["gtfs"]["trip_id"] == trip_id:
+                    tc = dato
+            break
+        out.append((dt, tc))
+    return out
+
+
+
+class SmallTripPointLabel(ClickableLabel):
+    def __init__(self, tp: TripPoint, on_click=None):
+        super().__init__()
+        if on_click:
+            self.on_click = on_click
+            self.clicked.connect(self.on_click)
+        self.tp = tp
+        route_short_name = tp.json["properties"]["trip"]["gtfs"]["route_short_name"]
+        trip_id = tp.json["properties"]["trip"]["gtfs"]["trip_id"]
+        self.setText(f"{route_short_name} – {trip_id}")
+
+class SmallTripPointLabelSelector(SmallTripPointLabel):
+    def __init__(self, tp: TripPoint, trip_point_selector):
+        super().__init__(tp)
+        self.trip_point_selector = trip_point_selector
+        self.clicked.connect(self.on_click)
+
+    def on_click(self):
+        TripPointWindow(self.tp, self.trip_point_selector.mwnd)
+        self.trip_point_selector.hide()
+
+
+
+class TripPointSelector(QWidget):
+    def __init__(self, tps, mwnd):
+        self.mwnd = mwnd
+        super().__init__()
+        self.tps = tps
+        self._layout = QVBoxLayout()
+        self.setLayout(self._layout)
+        self._labels = [SmallTripPointLabelSelector(tp, self) for tp in tps]
+        for l in self._labels:
+            self._layout.addWidget(l)
+        self.setWindowTitle(f"{window_title_prefix} - point selector")
+        self.show()
+        tmp_windows[self] = self
+
+
+tmp_windows = {}
+
+class TripPointWindow(QWidget):
+    def __init__(self, tp, mwnd):
+        super().__init__()
+        self.mwnd = mwnd
+        self._layout = QVBoxLayout()
+        self.setLayout(self._layout)
+        self.tp = tp
+        self._tmp = SmallTripPointLabel(tp)
+        self._layout.addWidget(self._tmp)
+        self.setLayout(self._layout)
+        self.setWindowTitle(f"{window_title_prefix} - trip point TODO")
+        self.show()
+        tmp_windows[self] = self
+
+        self._plot_history_button = QPushButton("Plot history")
+        self._layout.addWidget(self._plot_history_button)
+        self._plot_history_button.clicked.connect(self.plot_history)
+
+        self._json = QLabel(pprint.pformat(tp.json))
+        self._layout.addWidget(self._json)
+
+    @asyncSlot()
+    async def plot_history(self):
+        await self.mwnd.plot_gtfs_shape(self.tp)
+        await self.mwnd.plot_history(self.tp, self.tp.capture_time-datetime.timedelta(minutes=30), self.tp.capture_time)
+
 
 
 
@@ -278,13 +641,19 @@ class MainTool(QgsMapTool):
     def canvasClicked(self, e):
         pt = e.mapPoint()
         data_by_dist = sorted([(crs_transform.transform(x.point).distance(pt), x) for x in self.wnd.current_data], key=lambda x:x[0])[:10]
-        for d, x in data_by_dist:
-            print(d, pt, x.point)
+
+        scale = self.wnd.map.canvas.scale()
+        data_near = [(d, x) for d, x in data_by_dist if d/scale < 0.005]
+        tp_selector = TripPointSelector([x for d, x in data_near], self.wnd)
+        for d, x in data_near:
+            print()
+            print(d/scale, pt, x.point)
             pprint.pp(x.json)
+            print()
 
 
-    def canvasMoveEvent(self, e):
-        print("canvasMoveEvent", e)
+    #def canvasMoveEvent(self, e):
+        #print("canvasMoveEvent", e)
 
     def canvasPressEvent(self, e):
         print("canvasPressEvent", e)
@@ -296,6 +665,7 @@ class MainTool(QgsMapTool):
 
 
 
+
 class MapWidget(QWidget):
     def __init__(self):
         QWidget.__init__(self)
@@ -344,6 +714,8 @@ class MapWidget(QWidget):
 
         self.canvas.setDestinationCrs(mapy_cz.crs())
 
+        self.data_layers = []
+
     async def zoomIn(self):
         self.canvas.setMapTool(self.toolZoomIn)
 
@@ -354,6 +726,13 @@ class MapWidget(QWidget):
     def pan(self):
         self.canvas.setMapTool(self.toolPan)
 
+    def addLayer(self, layer):
+        self.data_layers.append(layer)
+        self.pushLayers()
+
+    def pushLayers(self):
+        self.canvas.setLayers(list(reversed(self.data_layers)) + [mapy_cz])
+
 
 
 # create Qt application
@@ -367,10 +746,77 @@ mapy_cz  = QgsRasterLayer('http-header:referer=https://mapy.cz/ &referer=https:/
 # create main window
 wnd = MainWind()
 canvas = wnd.map.canvas
+wnd.setWindowTitle(f"{window_title_prefix}")
 wnd.show()
 
 
 
+from matplotlib.backends.backend_qt5 \
+    import FigureCanvas as FigureCanvas
+from matplotlib.backends.backend_qt5 \
+    import NavigationToolbar2 as NavigationToolbar
+import matplotlib.pyplot as plt
+
+
+
+
+
+import sys
+import time
+
+import numpy as np
+
+from matplotlib.backends.backend_qtagg import FigureCanvas
+from matplotlib.backends.backend_qtagg import \
+    NavigationToolbar2QT as NavigationToolbar
+from matplotlib.figure import Figure
+
+
+class Graph(QWidget):
+    def __init__(self):
+        super().__init__()
+        layout = QVBoxLayout()
+        self.setLayout(layout)
+
+
+        canvas = FigureCanvas(Figure(figsize=(5, 3)))
+        layout.addWidget(canvas)
+        layout.addWidget(NavigationToolbar(canvas, self))
+
+        self.ax = canvas.figure.subplots()
+
+
+class HistoryGraph(Graph):
+    def __init__(self, th: TripHistory):
+        super().__init__()
+        x = []
+        y = []
+        for hp in th.history:
+            if hp.shape_point_dist_traveled is not None:
+                x.append(hp.shape_point_dist_traveled)
+                y.append(hp.capture_time)
+        self._line, = self.ax.plot(x, y)
+        self._line.figure.canvas.draw()
+
+        xticks = []
+        xlabels = []
+        for stop in th.stops:
+            x = stop.shape_dist_traveled
+            self.ax.axvline(x=x, ymin=0, ymax=1, color="black") 
+            #self.ax.xlabel(xy =(((x-x_bounds[0])/(x_bounds[1]-x_bounds[0])),1.01), xycoords='axes fraction', verticalalignment='top', horizontalalignment='center' , rotation = 270, text=stop.stop.name)
+            #self.ax.text(x, 0.5, stop.stop.name, rotation=90, transform=ax.get_xaxis_text1_transform(0)[0])
+            #self.ax.text(x, 0.5, stop.stop.name, rotation=90, transform=ax.get_xaxis_text1_transform(0)[0])
+            xticks.append(x)
+            xlabels.append(stop.stop.name)
+        self.ax.set_xticks(xticks)
+        self.ax.set_xticklabels(xlabels)
+
+
+
+#tmp = HistoryGraph()
+#tmp.show()
+
+
 # run!
 event_loop = QEventLoop(app)
 asyncio.set_event_loop(event_loop)