Browse Source

Merge branch 'master' of https://tree.clementinecomputing.com/clementinecomputing/RideLogicAVLS

clementinecomputing 4 years ago
parent
commit
629d3594cf

+ 1 - 0
.gitignore

@@ -96,3 +96,4 @@ node_modules
 # ---> custom
 #
 *~
+.data

+ 13 - 0
experimental/README.md

@@ -0,0 +1,13 @@
+Experimental Development
+===
+
+This directory is for experimental code
+
+---
+
+To create a protobuf to JSON converter:
+
+```
+browserify pb2json.js --s pb2json > pb2jsonlib.js
+```
+

+ 239 - 0
experimental/gtfs-realtime-succinct.proto

@@ -0,0 +1,239 @@
+syntax = "proto2";
+option java_package = "com.google.transit.realtime";
+package transit_realtime;
+message FeedMessage {
+  required FeedHeader header = 1;
+  repeated FeedEntity entity = 2;
+  extensions 1000 to 1999;
+  extensions 9000 to 9999;
+}
+message FeedHeader {
+  required string gtfs_realtime_version = 1;
+  enum Incrementality {
+    FULL_DATASET = 0;
+    DIFFERENTIAL = 1;
+  }
+  optional Incrementality incrementality = 2 [default = FULL_DATASET];
+  optional uint64 timestamp = 3;
+  extensions 1000 to 1999;
+  extensions 9000 to 9999;
+}
+message FeedEntity {
+  required string id = 1;
+  optional bool is_deleted = 2 [default = false];
+  optional TripUpdate trip_update = 3;
+  optional VehiclePosition vehicle = 4;
+  optional Alert alert = 5;
+  optional Shape shape = 6;
+  extensions 1000 to 1999;
+  extensions 9000 to 9999;
+}
+message TripUpdate {
+  required TripDescriptor trip = 1;
+  optional VehicleDescriptor vehicle = 3;
+  message StopTimeEvent {
+    optional int32 delay = 1;
+    optional int64 time = 2;
+    optional int32 uncertainty = 3;
+    extensions 1000 to 1999;
+    extensions 9000 to 9999;
+  }
+  message StopTimeUpdate {
+    optional uint32 stop_sequence = 1;
+    optional string stop_id = 4;
+    optional StopTimeEvent arrival = 2;
+    optional StopTimeEvent departure = 3;
+    optional VehiclePosition.OccupancyStatus departure_occupancy_status = 7;
+    enum ScheduleRelationship {
+      SCHEDULED = 0;
+      SKIPPED = 1;
+      NO_DATA = 2;
+      UNSCHEDULED = 3;
+    }
+    optional ScheduleRelationship schedule_relationship = 5
+        [default = SCHEDULED];
+    message StopTimeProperties {
+      optional string assigned_stop_id = 1;
+      extensions 1000 to 1999;
+      extensions 9000 to 9999;
+    }
+    optional StopTimeProperties stop_time_properties = 6;
+    extensions 1000 to 1999;
+    extensions 9000 to 9999;
+  }
+  repeated StopTimeUpdate stop_time_update = 2;
+  optional uint64 timestamp = 4;
+  optional int32 delay = 5;
+  message TripProperties {
+    optional string trip_id = 1;
+    optional string start_date = 2;
+    optional string start_time = 3;
+    optional string shape_id = 4;
+    extensions 1000 to 1999;
+    extensions 9000 to 9999;
+  }
+  optional TripProperties trip_properties = 6;
+  extensions 1000 to 1999;
+  extensions 9000 to 9999;
+}
+message VehiclePosition {
+  optional TripDescriptor trip = 1;
+  optional VehicleDescriptor vehicle = 8;
+  optional Position position = 2;
+  optional uint32 current_stop_sequence = 3;
+  optional string stop_id = 7;
+  enum VehicleStopStatus {
+    INCOMING_AT = 0;
+    STOPPED_AT = 1;
+    IN_TRANSIT_TO = 2;
+  }
+  optional VehicleStopStatus current_status = 4 [default = IN_TRANSIT_TO];
+  optional uint64 timestamp = 5;
+  enum CongestionLevel {
+    UNKNOWN_CONGESTION_LEVEL = 0;
+    RUNNING_SMOOTHLY = 1;
+    STOP_AND_GO = 2;
+    CONGESTION = 3;
+    SEVERE_CONGESTION = 4;  // People leaving their cars.
+  }
+  optional CongestionLevel congestion_level = 6;
+  enum OccupancyStatus {
+    EMPTY = 0;
+    MANY_SEATS_AVAILABLE = 1;
+    FEW_SEATS_AVAILABLE = 2;
+    STANDING_ROOM_ONLY = 3;
+    CRUSHED_STANDING_ROOM_ONLY = 4;
+    FULL = 5;
+    NOT_ACCEPTING_PASSENGERS = 6;
+    NO_DATA_AVAILABLE = 7;
+    NOT_BOARDABLE = 8;
+  }
+  optional OccupancyStatus occupancy_status = 9;
+  optional uint32 occupancy_percentage = 10;
+  message CarriageDetails {
+    optional string id = 1;
+    optional string label = 2;
+    optional OccupancyStatus occupancy_status = 3 [default = NO_DATA_AVAILABLE];
+    optional int32 occupancy_percentage = 4 [default = -1];
+    optional uint32 carriage_sequence = 5;
+    extensions 1000 to 1999;
+    extensions 9000 to 9999;
+  }
+  repeated CarriageDetails multi_carriage_details = 11;
+  extensions 1000 to 1999;
+  extensions 9000 to 9999;
+}
+message Alert {
+  repeated TimeRange active_period = 1;
+  repeated EntitySelector informed_entity = 5;
+  enum Cause {
+    UNKNOWN_CAUSE = 1;
+    OTHER_CAUSE = 2;        // Not machine-representable.
+    TECHNICAL_PROBLEM = 3;
+    STRIKE = 4;             // Public transit agency employees stopped working.
+    DEMONSTRATION = 5;      // People are blocking the streets.
+    ACCIDENT = 6;
+    HOLIDAY = 7;
+    WEATHER = 8;
+    MAINTENANCE = 9;
+    CONSTRUCTION = 10;
+    POLICE_ACTIVITY = 11;
+    MEDICAL_EMERGENCY = 12;
+  }
+  optional Cause cause = 6 [default = UNKNOWN_CAUSE];
+  enum Effect {
+    NO_SERVICE = 1;
+    REDUCED_SERVICE = 2;
+    SIGNIFICANT_DELAYS = 3;
+    DETOUR = 4;
+    ADDITIONAL_SERVICE = 5;
+    MODIFIED_SERVICE = 6;
+    OTHER_EFFECT = 7;
+    UNKNOWN_EFFECT = 8;
+    STOP_MOVED = 9;
+    NO_EFFECT = 10;
+    ACCESSIBILITY_ISSUE = 11;
+  }
+  optional Effect effect = 7 [default = UNKNOWN_EFFECT];
+  optional TranslatedString url = 8;
+  optional TranslatedString header_text = 10;
+  optional TranslatedString description_text = 11;
+  optional TranslatedString tts_header_text = 12;
+  optional TranslatedString tts_description_text = 13;
+  enum SeverityLevel {
+	UNKNOWN_SEVERITY = 1;
+	INFO = 2;
+	WARNING = 3;
+	SEVERE = 4;
+  }
+  optional SeverityLevel severity_level = 14 [default = UNKNOWN_SEVERITY];
+  extensions 1000 to 1999;
+  extensions 9000 to 9999;
+}
+message TimeRange {
+  optional uint64 start = 1;
+  optional uint64 end = 2;
+  extensions 1000 to 1999;
+  extensions 9000 to 9999;
+}
+message Position {
+  required float latitude = 1;
+  required float longitude = 2;
+  optional float bearing = 3;
+  optional double odometer = 4;
+  optional float speed = 5;
+  extensions 1000 to 1999;
+  extensions 9000 to 9999;
+}
+message TripDescriptor {
+  optional string trip_id = 1;
+  optional string route_id = 5;
+  optional uint32 direction_id = 6;
+  optional string start_time = 2;
+  optional string start_date = 3;
+  enum ScheduleRelationship {
+    SCHEDULED = 0;
+    ADDED = 1;
+    UNSCHEDULED = 2;
+    CANCELED = 3;
+    REPLACEMENT = 5 [deprecated=true];
+    DUPLICATED = 6;
+  }
+  optional ScheduleRelationship schedule_relationship = 4;
+  extensions 1000 to 1999;
+  extensions 9000 to 9999;
+}
+message VehicleDescriptor {
+  optional string id = 1;
+  optional string label = 2;
+  optional string license_plate = 3;
+  extensions 1000 to 1999;
+  extensions 9000 to 9999;
+}
+message EntitySelector {
+  optional string agency_id = 1;
+  optional string route_id = 2;
+  optional int32 route_type = 3;
+  optional TripDescriptor trip = 4;
+  optional string stop_id = 5;
+  optional uint32 direction_id = 6;
+  extensions 1000 to 1999;
+  extensions 9000 to 9999;
+}
+message TranslatedString {
+  message Translation {
+    required string text = 1;
+    optional string language = 2;
+    extensions 1000 to 1999;
+    extensions 9000 to 9999;
+  }
+  repeated Translation translation = 1;
+  extensions 1000 to 1999;
+  extensions 9000 to 9999;
+}
+message Shape {
+  optional string shape_id = 1;
+  optional string encoded_polyline = 2;
+  extensions 1000 to 1999;
+  extensions 9000 to 9999;
+}

+ 114 - 46
experimental/gtfs_example_write.py

@@ -1,45 +1,48 @@
-#!/usr/bin/python3.6
+#!/usr/bin/python3
 #
 
-from google.transit import gtfs_realtime_pb2
 from google.transit import gtfs_realtime_pb2 as gtfspb
 import urllib.request
 import sys
 
-print("ok")
-
-print(gtfs_realtime_pb2)
-
-def gtfs_vehicle_position(vp = None):
+def gtfs_vehicle_position(data, vp = None):
   if vp is None:
     vp = gtfspb.VehiclePosition()
 
-  vp.trip.trip_id = "trip_ok"
-  vp.trip.route_id = "route_ok"
-  vp.trip.direction_id = 0
-  vp.trip.start_time = "start_time_ok"
-  vp.trip.start_date = "start_date_ok"
-  vp.trip.schedule_relationship = gtfspb.TripDescriptor.ScheduleRelationship.SCHEDULED
-
-
-  vp.stop_id = "x"
-  vp.current_stop_sequence = 3
-  vp.current_status = gtfspb.VehiclePosition.VehicleStopStatus.INCOMING_AT
-  vp.timestamp = 123
-  vp.congestion_level = gtfspb.VehiclePosition.CongestionLevel.UNKNOWN_CONGESTION_LEVEL
-  vp.occupancy_status = gtfspb.VehiclePosition.OccupancyStatus.NO_DATA_AVAILABLE
-  vp.occupancy_percentage = 0
-
-  vp.vehicle.id = "9999"
-  vp.vehicle.label = "label_ok"
-  vp.vehicle.license_plate = "unknown"
-
-  vp.position.latitude = 0
-  vp.position.longitude = 0
-  vp.position.bearing = 90
-  vp.position.odometer = 0
-  vp.position.speed = 0
-  #vp.position = p
+  if "trip" in data:
+    trip = data["trip"]
+
+    if "trip_id" in trip:  vp.trip.trip_id = trip["trip_id"]
+    if "route_id" in trip: vp.trip.route_id = trip["route_id"]
+    if "direction_id" in trip: vp.trip.direction_id = trip["direction_id"]
+    if "start_time" in trip: vp.trip.start_time = trip["start_time"]
+    if "start_date" in trip: vp.trip.start_date = trip["start_date"]
+    if "schedule_relationship" in trip: vp.trip.schedule_relationship = trip["schedule_relationship"]
+    #vp.trip.schedule_relationship = gtfspb.TripDescriptor.ScheduleRelationship.SCHEDULED
+
+
+  if "stop_id" in data: vp.stop_id = data["stop_id"]
+  if "current_stop_sequence" in data: vp.current_stop_sequence = data["current_stop_sequence"]
+  if "current_state" in data: vp.current_status = data["current_state"]
+  #vp.current_status = gtfspb.VehiclePosition.VehicleStopStatus.INCOMING_AT
+  if "timestamp" in data: vp.timestamp = data["timestamp"]
+  if "congestion_level" in data: vp.congestion_level = data["congestion_level"]
+  if "occupancy_status" in data: vp.occupancy_status = data["occupancy_status"]
+  #vp.congestion_level = gtfspb.VehiclePosition.CongestionLevel.UNKNOWN_CONGESTION_LEVEL
+  #vp.occupancy_status = gtfspb.VehiclePosition.OccupancyStatus.NO_DATA_AVAILABLE
+  if "occupancy_percentage" in data: vp.occupancy_percentage = data["occupancy_percentage"]
+
+  if "vehicle_id" in data: vp.vehicle.id = data["vehicle_id"]
+  if "label" in data: vp.vehicle.label = data["label"]
+  if "license_plate" in data: vp.vehicle.license_plate = data["license_plate"]
+
+  if "position" in data:
+    p = data["position"]
+    if "latitude" in p: vp.position.latitude = p["latitude"]
+    if "longitude" in p: vp.position.longitude = p["longitude"]
+    if "bearing" in p: vp.position.bearing = p["bearing"]
+    if "odometer" in p: vp.position.odometer = p["odometer"]
+    if "speed" in p: vp.position.speed = p["speed"]
   return vp
 
 def gtfs_alert():
@@ -50,33 +53,98 @@ def gtfs_trip_update():
   u = gtfspb.TripUpdate()
   return u
 
-def gtfs_feed_entity(f = None):
+def gtfs_feed_entity(data, f = None):
   if f is None:
     f = gtfspb.FeedEntity()
-  f.id = "ok"
-  gtfs_vehicle_position(f.vehicle)
+
+  if "id" in data: f.id = data["id"]
+
+  #if not ("#type" in data): return f
+  #if data["#type"] == "VehiclePosition":
+  if "vehicle" in data:
+    gtfs_vehicle_position(data["vehicle"], f.vehicle)
   return f
 
-def gtfs_feed_message(fm = None):
+def gtfs_feed_message(data, fm = None):
   if fm is None:
     fm = gtfspb.FeedMessage()
-  fm.header.gtfs_realtime_version = "2.0"
-  fm.header.timestamp = 0
-  for i in range(2):
-    gtfs_feed_entity(fm.entity.add())
+
+  if "header" in data:
+    h = data["header"]
+    if "gtfs_realtime_version" in h: fm.header.gtfs_realtime_version = h["gtfs_realtime_version"]
+    if "timestamp" in h: fm.header.timestamp = h["timestamp"]
+
+  if "entity" in data:
+    for ent in data["entity"]:
+      gtfs_feed_entity(ent, fm.entity.add())
   return fm
 
-feedmsg = gtfs_realtime_pb2.FeedMessage()
-#vp = gtfs_realtime_pb2.VehiclePosition()
-vp = gtfs_vehicle_position()
+###
+###
+###
+
+vehicle_position_data = {
+  "trip" : {
+    "trip_id": "testid4",
+    "route_id": "testroute3",
+    "direction_id": 0,
+    "start_time": "teststartime...",
+    "start_date": "teststartdate...",
+    "schedule_relationship": gtfspb.TripDescriptor.ScheduleRelationship.SCHEDULED
+  },
+
+  "stop_id" : "teststop",
+  "curent_stop_sequence": 3,
+  "current_state" : gtfspb.VehiclePosition.VehicleStopStatus.INCOMING_AT,
+  "timestamp" : 123,
+  "congestion_level" : gtfspb.VehiclePosition.CongestionLevel.UNKNOWN_CONGESTION_LEVEL,
+  "occupancy_status" : gtfspb.VehiclePosition.OccupancyStatus.NO_DATA_AVAILABLE,
+  "occupancy_percentage" : 1,
+  "vehicle_id" : "testvehicleid0",
+  "label" : "testlabel",
+  "license_plate" : "testlicenseplate",
+  "position" : {
+    "latitude" : 42.9,
+    "longitude" : -78.1,
+    "bearing" : 90.0,
+    "odometer" : 1.0,
+    "speed" : 0.1
+  }
+}
+
+feed_entity_data = {
+  "id" : "testfeedid",
+  "#type": "VehiclePosition",
+  "vehicle": vehicle_position_data
+}
+
+feed_message_data = {
+  "header" : {
+    "gtfs_realtime_version" : "2.0",
+    "timestamp" : 0
+  },
+  "entity" : [
+    feed_entity_data,
+    feed_entity_data
+  ]
+}
+
+
+###
+###
+###
+
+
+
+vp = gtfs_vehicle_position(vehicle_position_data)
 
 vp.stop_id = "ok"
 print(">>>\n", vp)
 
-fe = gtfs_feed_entity()
+fe = gtfs_feed_entity(feed_entity_data)
 print("feed entity>>>\n", fe)
 
-feedmsg = gtfs_feed_message()
+feedmsg = gtfs_feed_message(feed_message_data)
 print("feed message>>>\n", feedmsg)
 
 

+ 6 - 0
experimental/pb-compile

@@ -3,3 +3,9 @@
 protoc gtfs-realtime.proto --js_out=import_style=commonjs,binary:.
 mkdir -p gtfs_realtime_pb2
 protoc gtfs-realtime.proto  --python_out=gtfs_realtime_pb2
+
+pip install --upgrade google-api-python-client
+pip3 install --upgrade google-api-python-client
+
+pip install --upgrade gtfs-realtime-bindings
+pip3 install --upgrade gtfs-realtime-bindings

+ 11 - 9
experimental/pb2json.js

@@ -1,16 +1,18 @@
+
+// simple wrapper to convert from protobuf to JSON
+//
+
 var jspb = require("google-protobuf");
 var goog = jspb;
 var pb = require("./gtfs-realtime_pb.js");
 
-var fs = require("fs");
-
-var payload = fs.readFileSync("./test-gtfs.pb");
-
-var fm = pb.FeedMessage.deserializeBinary( new Uint8Array(payload) );
+function pb2json(payload) {
+  var fm = pb.FeedMessage.deserializeBinary( new Uint8Array(payload) );
+  return fm.toObject();
+}
 
-var x = fm.toObject();
+module.exports = {
+  "pb2json" : pb2json
+}
 
-console.log(JSON.stringify(x));
 
-//deserializeBinary();
-//pb proto.transit_realtime.TranslatedString

+ 164 - 0
experimental/ridelogic_avls.py

@@ -0,0 +1,164 @@
+#!/usr/bin/python3
+#
+
+# library to convert from JSON data to realtime GTFS
+# protobuf objects
+#
+
+from google.transit import gtfs_realtime_pb2 as gtfspb
+import urllib.request
+import sys
+
+def gtfs_vehicle_position(data, vp = None):
+  if vp is None:
+    vp = gtfspb.VehiclePosition()
+
+  if "trip" in data:
+    trip = data["trip"]
+
+    if "trip_id" in trip:  vp.trip.trip_id = trip["trip_id"]
+    if "route_id" in trip: vp.trip.route_id = trip["route_id"]
+    if "direction_id" in trip: vp.trip.direction_id = trip["direction_id"]
+    if "start_time" in trip: vp.trip.start_time = trip["start_time"]
+    if "start_date" in trip: vp.trip.start_date = trip["start_date"]
+    if "schedule_relationship" in trip: vp.trip.schedule_relationship = trip["schedule_relationship"]
+    #vp.trip.schedule_relationship = gtfspb.TripDescriptor.ScheduleRelationship.SCHEDULED
+
+
+  if "stop_id" in data: vp.stop_id = data["stop_id"]
+  if "current_stop_sequence" in data: vp.current_stop_sequence = data["current_stop_sequence"]
+  if "current_state" in data: vp.current_status = data["current_state"]
+  #vp.current_status = gtfspb.VehiclePosition.VehicleStopStatus.INCOMING_AT
+  if "timestamp" in data: vp.timestamp = data["timestamp"]
+  if "congestion_level" in data: vp.congestion_level = data["congestion_level"]
+  if "occupancy_status" in data: vp.occupancy_status = data["occupancy_status"]
+  #vp.congestion_level = gtfspb.VehiclePosition.CongestionLevel.UNKNOWN_CONGESTION_LEVEL
+  #vp.occupancy_status = gtfspb.VehiclePosition.OccupancyStatus.NO_DATA_AVAILABLE
+  if "occupancy_percentage" in data: vp.occupancy_percentage = data["occupancy_percentage"]
+
+  if "vehicle_id" in data: vp.vehicle.id = data["vehicle_id"]
+  if "label" in data: vp.vehicle.label = data["label"]
+  if "license_plate" in data: vp.vehicle.license_plate = data["license_plate"]
+
+  if "position" in data:
+    p = data["position"]
+    if "latitude" in p: vp.position.latitude = p["latitude"]
+    if "longitude" in p: vp.position.longitude = p["longitude"]
+    if "bearing" in p: vp.position.bearing = p["bearing"]
+    if "odometer" in p: vp.position.odometer = p["odometer"]
+    if "speed" in p: vp.position.speed = p["speed"]
+  return vp
+
+def gtfs_alert():
+  a = gtfspb.Alert()
+  return a
+
+def gtfs_trip_update():
+  u = gtfspb.TripUpdate()
+  return u
+
+def gtfs_feed_entity(data, f = None):
+  if f is None:
+    f = gtfspb.FeedEntity()
+
+  if "id" in data: f.id = data["id"]
+
+  if "vehicle" in data:
+    gtfs_vehicle_position(data["vehicle"], f.vehicle)
+  return f
+
+def gtfs_feed_message(data, fm = None):
+  if fm is None:
+    fm = gtfspb.FeedMessage()
+
+  if "header" in data:
+    h = data["header"]
+    if "gtfs_realtime_version" in h: fm.header.gtfs_realtime_version = h["gtfs_realtime_version"]
+    if "timestamp" in h: fm.header.timestamp = h["timestamp"]
+
+  if "entity" in data:
+    for ent in data["entity"]:
+      gtfs_feed_entity(ent, fm.entity.add())
+  return fm
+
+###
+###
+###
+
+def write_protobuf(ofn, feed_message_data):
+  feedmsg = gtfs_feed_message(feed_message_data)
+  fp = open(ofn, "wb")
+  fp.write(feedmsg.SerializeToString())
+  fp.close()
+
+
+def example_usage():
+  vehicle_position_data = {
+    "trip" : {
+      "trip_id": "testid4",
+      "route_id": "testroute3",
+      "direction_id": 0,
+      "start_time": "teststartime...",
+      "start_date": "teststartdate...",
+      "schedule_relationship": gtfspb.TripDescriptor.ScheduleRelationship.SCHEDULED
+    },
+
+    "stop_id" : "teststop",
+    "curent_stop_sequence": 3,
+    "current_state" : gtfspb.VehiclePosition.VehicleStopStatus.INCOMING_AT,
+    "timestamp" : 123,
+    "congestion_level" : gtfspb.VehiclePosition.CongestionLevel.UNKNOWN_CONGESTION_LEVEL,
+    "occupancy_status" : gtfspb.VehiclePosition.OccupancyStatus.NO_DATA_AVAILABLE,
+    "occupancy_percentage" : 1,
+    "vehicle_id" : "testvehicleid0",
+    "label" : "testlabel",
+    "license_plate" : "testlicenseplate",
+    "position" : {
+      "latitude" : 42.9,
+      "longitude" : -78.1,
+      "bearing" : 90.0,
+      "odometer" : 1.0,
+      "speed" : 0.1
+    }
+  }
+
+  feed_entity_data = {
+    "id" : "testfeedid",
+    "#type": "VehiclePosition",
+    "vehicle": vehicle_position_data
+  }
+
+  feed_message_data = {
+    "header" : {
+      "gtfs_realtime_version" : "2.0",
+      "timestamp" : 0
+    },
+    "entity" : [
+      feed_entity_data,
+      feed_entity_data
+    ]
+  }
+
+
+  ###
+  ###
+  ###
+
+
+
+  vp = gtfs_vehicle_position(vehicle_position_data)
+
+  vp.stop_id = "ok"
+  print(">>>\n", vp)
+
+  fe = gtfs_feed_entity(feed_entity_data)
+  print("feed entity>>>\n", fe)
+
+  feedmsg = gtfs_feed_message(feed_message_data)
+  print("feed message>>>\n", feedmsg)
+
+
+  fp = open("outpy.pb", "wb")
+  fp.write(feedmsg.SerializeToString())
+  fp.close()
+

+ 104 - 0
experimental/synthetic_avls_source.py

@@ -0,0 +1,104 @@
+#!/usr/bin/python3
+
+
+# simple test program to generate synthetic bus position
+# data (AVLS)
+
+import json
+import sys
+import time
+import math
+
+import ridelogic_avls as rl
+
+if len(sys.argv) < 3:
+  print("provide path and stop geojson files")
+  sys.exit(0)
+
+json_path_fn = sys.argv[1]
+json_stop_fn = sys.argv[2]
+
+json_path = {}
+json_stop = {}
+
+
+with open(json_path_fn) as fp:
+  json_path = json.loads(fp.read())
+
+with open(json_stop_fn) as fp:
+  json_stop = json.loads(fp.read())
+
+_path = json_path["features"][0]["geometry"]["coordinates"]
+
+bus_ctx = {
+  "update_freq" : 1000,
+  "gps_path": _path,
+  "bus": [
+    {
+      "id": "bus0",
+      "idx" : 0,
+      "idx_dir" : 1,
+      "bearing":0,
+      "longitude": _path[0][0],
+      "latitude": _path[0][1]
+    },
+    {
+      "id":"bus1",
+      "idx": len(_path)-1,
+      "idx_dir" : -1,
+      "bearing":0,
+      "longitude": _path[-1][0],
+      "latitude": _path[-1][1]
+    }
+  ]
+}
+
+while True:
+
+  feed_msg = {
+    "header" : { "gtfs_realtime_version":"2.0", "timestamp":int(time.time()) },
+    "entity":[]
+  }
+
+
+
+  for bus in bus_ctx["bus"]:
+
+    print(bus["id"], bus["longitude"], bus["latitude"], bus["bearing"])
+
+    lng_prv = bus["longitude"]
+    lat_prv = bus["latitude"]
+
+
+    bus["longitude"] = bus_ctx["gps_path"][bus["idx"]][0]
+    bus["latitude"] = bus_ctx["gps_path"][bus["idx"]][1]
+    bus["idx"] += bus["idx_dir"]
+    if bus["idx"] < 0:
+      bus["idx"] = 0
+      bus["idx_dir"] = 1
+    if bus["idx"] >= len(bus_ctx["gps_path"]):
+      bus["idx"] = len(bus_ctx["gps_path"])-1
+      bus["idx_dir"] = -1
+
+    lng_cur = bus["longitude"]
+    lat_cur = bus["latitude"]
+
+    bus["bearing"] = int(180.0 * math.atan2(lng_cur - lng_prv, lat_cur - lat_prv) / math.pi)
+
+    entity = {
+      "id" : bus["id"],
+      "vehicle" : {
+        "position" : {
+          "longitude":lng_cur,
+          "latitude":lat_cur,
+          "bearing":bus["bearing"]
+        }
+      }
+    }
+
+    feed_msg["entity"].append(entity);
+
+
+  rl.write_protobuf("test_gtfs.pb", feed_msg)
+
+  time.sleep( float(bus_ctx["update_freq"])/1000.0 )

BIN
experimental/test-gtfs.pb


BIN
html/data/star-3.png


BIN
html/data/star_gw_sq.png


+ 1 - 0
html/index.html

@@ -17,6 +17,7 @@
   </head>
   <body>
     <div id="map" class="map"><div id="popup"></div></div>
+    <script src="js/pb2jsonlib.js"></script>
     <script src="js/RideLogicAVLS.js"></script>
   </body>
 </html>

+ 193 - 17
html/js/RideLogicAVLS.js

@@ -17,7 +17,11 @@ var VectorLayer = ol.layer.Vector;
 var OSM = ol.source.OSM;
 var fromLonLat =  ol.proj.fromLonLat;
 
-var defaultLonLat = [ -76.5019, 42.4440];
+var LineString = ol.geom.LineString;
+
+//var defaultLonLat = [ -76.5019, 42.4440];
+//var defaultLonLat = [ -76.1805, 42.6012 ];
+var defaultLonLat = [ -76.2705, 42.5512 ];
 var defaultWebMerc = fromLonLat(defaultLonLat);
 
 
@@ -29,11 +33,6 @@ function _rnd(a,b) {
   return Math.random()*(b-a) + a;
 }
 
-//var vectorSource = new VectorSource({ features: [iconFeature], });
-var vectorSource = new VectorSource();
-
-var _icon = [];
-
 
 function example_change_pos() {
 
@@ -46,13 +45,13 @@ function example_change_pos() {
 
 
   _icon[0].feature.getGeometry().setCoordinates(merc);
-  vectorLayer.getSource().changed();
+  busLayer.getSource().changed();
 }
 
 function example_change_opacity(p) {
   p = ((typeof p === "undefined") ? 0.0 : p);
   _icon[0].style.getImage().setOpacity(p);
-  vectorLayer.getSource().changed();
+  busLayer.getSource().changed();
 }
 
 // icon can't change image src dynamically,
@@ -67,10 +66,15 @@ function example_change_image() {
       src: 'data/bus_gw_90.png',
     });
   _icon[0].style.setImage(_nuimg);
-  vectorLayer.getSource().changed();
+  busLayer.getSource().changed();
 }
 
-for (var ii=0; ii<10; ii++) {
+//---
+
+//var vectorSource = new VectorSource({ features: [iconFeature], });
+var vectorSource = new VectorSource();
+var _icon = [];
+for (var ii=0; ii<2; ii++) {
 
   var w = 1/32.0;
   let lonlat = [
@@ -79,11 +83,8 @@ for (var ii=0; ii<10; ii++) {
   let merc = fromLonLat(lonlat);
 
   var iconFeature = new Feature({
-    //geometry: new Point([10, 20]),
     geometry: new Point([merc[0], merc[1]]),
-    name: 'Null Island',
-    population: 4000,
-    rainfall: 500,
+    name: 'bus'
   });
 
   var iconStyle = new Style({
@@ -95,27 +96,38 @@ for (var ii=0; ii<10; ii++) {
     }),
   });
 
+  iconStyle.getImage().setOpacity(0.0);
+
   _icon.push({ "style": iconStyle, "feature": iconFeature });
   iconFeature.setStyle(iconStyle);
   vectorSource.addFeature(iconFeature);
 
 }
 
-const vectorLayer = new VectorLayer({
+//---
+
+
+const busLayer = new VectorLayer({
   source: vectorSource,
+  zIndex: 2
 });
 
+//--
+
 const rasterLayer = new TileLayer({
   //source: ol.source.OSM()
   source: new ol.source.OSM(),
 });
 
+//--
+
 const map = new Map({
-  layers: [rasterLayer, vectorLayer],
+  //layers: [rasterLayer, vectorLayer, routeLayer],
+  layers: [rasterLayer, busLayer ],
   target: document.getElementById('map'),
   view: new View({
     center: defaultWebMerc,
-    zoom: 13,
+    zoom: 11,
   }),
 });
 
@@ -160,3 +172,167 @@ setInterval( function() {
 }, 1000);
 
 
+//-----
+//
+
+function update_buses(json_gtfs) {
+
+  var bearingIdxLookup = [ "0", "45", "90", "135", "180", "225", "270", "315" ];
+
+  for (let ii=0; ii<json_gtfs.entityList.length; ii++) {
+    let id = json_gtfs.entityList[ii].id;
+    let p = json_gtfs.entityList[ii].vehicle.position;
+    let lat = p.latitude;
+    let lon = p.longitude;
+    let bearing = p.bearing;
+
+
+    let lonlat = [ lon , lat ];
+    let merc = fromLonLat(lonlat);
+
+
+    _icon[ii].feature.getGeometry().setCoordinates(merc);
+
+    bearing = (bearing % 360 );
+    if (bearing < 0) { bearing += 360.0; }
+    let iheading = Math.floor( (parseInt( bearing ) + 23) / 45 );
+    if (iheading > 7) { iheading = 0; }
+
+    var _nuimg = new Icon({
+        anchor: [0.5, 46],
+        anchorXUnits: 'fraction',
+        anchorYUnits: 'pixels',
+        src: 'data/bus_gw_' + bearingIdxLookup[iheading] + '.png',
+      });
+    _icon[ii].style.setImage(_nuimg);
+    _icon[ii].style.getImage().setOpacity(1.0);
+
+    //console.log(">>", id, lat, lon, bearing);
+  }
+
+  busLayer.getSource().changed();
+}
+
+function fetch_gtfs_vehicle_position() {
+  let xhr = new XMLHttpRequest();
+  xhr.onload = function() {
+    var ab = xhr.response;
+    var json_pb  = pb2json.pb2json(ab);
+    //console.log(">>>", json_pb);
+
+    update_buses(json_pb)
+  };
+
+  xhr.responseType = "arraybuffer";
+  xhr.open("GET", "VehiclePosition.pb");
+  xhr.send();
+}
+
+//--
+
+function update_route_layer(geojson) {
+  var route_points = geojson.features[0].geometry.coordinates;
+  var merc_points = [];
+  for (let ii=0; ii<route_points.length; ii++) {
+    merc_points.push(ol.proj.transform(route_points[ii], 'EPSG:4326', 'EPSG:3857'));
+  }
+
+  var routeLine = new ol.Feature({
+    geometry: new ol.geom.LineString(merc_points)
+  });
+
+  var routeLineVector = new ol.source.Vector({});
+  routeLineVector.addFeature(routeLine);
+
+
+  var routeLayer = new VectorLayer({
+    source: routeLineVector,
+    style: new ol.style.Style({
+      fill: new ol.style.Fill({ color: 'rgb(0,0,255,0.25)', weight: 4, opacity: 0.5 }),
+      stroke: new ol.style.Stroke({ color: 'rgb(0,0,255,0.25)', width: 6, opacity: 0.5 })
+    }),
+    zIndex: 0
+  });
+
+  map.addLayer(routeLayer);
+
+}
+
+function update_stop_layer(geojson) {
+  let stop_points = geojson.features[0].properties.route_stops;
+
+  let merc_points = [];
+  for (let ii=0; ii<stop_points.length; ii++) {
+    console.log(stop_points[ii].stop);
+    console.log(stop_points[ii].stop.geometry);
+    console.log(stop_points[ii].stop.geometry.coordinates);
+    let pnt = stop_points[ii].stop.geometry.coordinates;
+    merc_points.push(ol.proj.transform(pnt, 'EPSG:4326', 'EPSG:3857'));
+  }
+
+  console.log("...");
+
+  var stopVectorSource = new VectorSource();
+
+  for (var ii=0; ii<merc_points.length; ii++) {
+    let _stop = stop_points[ii].stop;
+
+    let _stop_name = _stop.stop_name;
+
+    console.log(merc_points[ii]);
+
+    var iconFeature = new Feature({
+      geometry: new Point([merc_points[ii][0], merc_points[ii][1]]),
+      name: _stop_name
+    });
+
+    var iconStyle = new Style({
+      image: new Icon({
+        anchor: [0.5, 46],
+        anchorXUnits: 'fraction',
+        anchorYUnits: 'pixels',
+        src: 'data/star-3.png',
+      }),
+    });
+
+    iconStyle.getImage().setOpacity(0.8);
+    iconFeature.setStyle(iconStyle);
+    stopVectorSource.addFeature(iconFeature);
+
+  }
+
+  var stopLayer = new VectorLayer({
+    source: stopVectorSource,
+    zIndex: 0
+  });
+
+  map.addLayer(stopLayer);
+
+
+  console.log(stop_points);
+
+
+}
+
+function fetch_geojson(url) {
+  let xhr = new XMLHttpRequest();
+  xhr.onload = function() {
+    var geojson = xhr.response;
+    update_route_layer(geojson);
+    update_stop_layer(geojson);
+  };
+
+  xhr.responseType = "json";
+  xhr.open("GET", url, true);
+  xhr.send();
+
+}
+
+//--
+
+function init() {
+  setInterval(fetch_gtfs_vehicle_position, 1000);
+  setTimeout( function() { fetch_geojson("geojson/test_route.geojson"); }, 100);
+}
+
+init();

File diff suppressed because it is too large
+ 9502 - 0
html/js/pb2jsonlib.js