Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions obs/bin/obs_face.py
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,9 @@ def main():

parser.add_argument('-v', '--verbose', action='store_true', help='be verbose')

parser.add_argument('-c', '--chunk', required=False, action='store', default=100, type=float,
help='Chop segments on road page to max length in meter.')

args = parser.parse_args()

coloredlogs.install(level=logging.DEBUG if args.verbose else logging.INFO,
Expand Down Expand Up @@ -393,6 +396,8 @@ def main():
if args.annotate or args.collect or args.visualization:
logging.info('Loading OpenStreetMap data')
map_source = OSMDataSource(cache_dir=args.path_cache)
if args.chunk is not None:
map_source.chunk_size = args.chunk

if args.annotate or args.collect:
if not args.input:
Expand Down
43 changes: 20 additions & 23 deletions obs/face/geojson/ExportRoadAnnotations.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,16 +43,26 @@ def add_measurements(self, measurements):
for sample in measurements:
self.n_samples += 1
# filter measurements
if not ("OSM_way_id" in sample):
continue;
way_id = sample["OSM_way_id"]
way_orientation = 1 if sample["OSM_way_orientation"] == -1 else 0

if sample["latitude"] is None or sample["longitude"] is None or sample["distance_overtaker"] is None \
or self.only_confirmed_measurements and (sample["confirmed"] is not True) \
or not sample["has_OSM_annotations"]:
if not (way_id in self.way_statistics):
way = self.map_source.get_way_by_id(way_id)
if way:
self.way_statistics[way_id] = WayStatistics(way_id, way)
if way_id in self.way_statistics and sample["speed"] != 0:
self.way_statistics[way_id].n_ticks[way_orientation] += 1

continue

self.n_valid += 1

way_id = sample["OSM_way_id"]
value = sample["distance_overtaker"]
way_orientation = sample["OSM_way_orientation"]

self.map_source.ensure_coverage([sample["latitude"]], [sample["longitude"]])

Expand All @@ -68,13 +78,15 @@ def add_measurements(self, measurements):
self.n_grouped += 1
else:
logging.warning("way not found in map")
self.way_statistics[way_id].n_ticks[way_orientation] += 1

def finalize(self):
log.info("%s samples, %s valid", self.n_samples, self.n_valid)
features = []
for way_stats in self.way_statistics.values():
way_stats.finalize()
if not any(way_stats.valid):
# if not any(way_stats.valid):
if not any(way_stats.n_ticks):
continue

for i in range(1 if way_stats.oneway else 2):
Expand All @@ -91,19 +103,14 @@ def finalize(self):
coordinates = []

feature = {"type": "Feature",
"properties": {"distance_overtaker_mean": way_stats.d_mean[i],
"distance_overtaker_median": way_stats.d_median[i],
"distance_overtaker_minimum": way_stats.d_minimum[i],
"distance_overtaker_n": way_stats.n[i],
"distance_overtaker_n_below_limit": way_stats.n_lt_limit[i],
"distance_overtaker_n_above_limit": way_stats.n_geq_limit[i],
"distance_overtaker_limit": way_stats.d_limit,
"distance_overtaker_measurements": way_stats.samples[i],
"properties": {"distance_overtaker_limit": way_stats.d_limit,
"distance_overtaker_measurements": sorted(way_stats.samples[i], key = float),
"zone": way_stats.zone,
"direction": direction,
"name": way_stats.name,
"way_id": way_stats.way_id,
"valid": way_stats.valid[i],
"ticks": way_stats.n_ticks[i],
},
"geometry": {"type": "LineString", "coordinates": coordinates}}

Expand All @@ -124,12 +131,10 @@ def __init__(self, way_id, way):
self.n = [0, 0]
self.n_lt_limit = [0, 0]
self.n_geq_limit = [0, 0]
self.n_ticks = [0, 0]

self.way_id = way_id
self.valid = [False, False]
self.d_mean = [0, 0]
self.d_median = [0, 0]
self.d_minimum = [0, 0]

self.zone = "unknown"
self.oneway = False
Expand All @@ -156,19 +161,11 @@ def __init__(self, way_id, way):

def add_sample(self, sample, orientation):
if np.isfinite(sample):
i = 1 if orientation == -1 else 0
self.samples[i].append(sample)
self.samples[orientation].append(sample)
return self

def finalize(self):
for i in range(2):
samples = np.array(self.samples[i])
if len(samples) > 0:
self.n[i] = len(samples)
self.d_mean[i] = np.mean(samples)
self.d_median[i] = np.median(samples)
self.d_minimum[i] = np.min(samples)
if self.d_limit is not None:
self.n_lt_limit[i] = int((samples < self.d_limit).sum())
self.n_geq_limit[i] = int((samples >= self.d_limit).sum())
self.valid[i] = True
8 changes: 5 additions & 3 deletions obs/face/osm/DataSource.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ def __init__(self, cache_dir="cache", tile_zoom=14):
self.loaded_tiles = []
self.tile_source = TileSource()
self.tile_zoom = tile_zoom
self.chunk_size = 100

def ensure_coverage(self, lat, lon, extend=0.0):
tiles = self.tile_source.get_required_tiles(lat, lon, self.tile_zoom, extend=extend)
Expand Down Expand Up @@ -65,9 +66,10 @@ def add_tile(self, tile):
# add way objects, and store
for way_id, way in ways.items():
if way_id not in self.ways:
w = Way(way_id, way, nodes)
self.ways[way_id] = w
self.way_container.insert(w)
w = Way.create(way_id, way, nodes, self.chunk_size)
self.ways.update(w)
for id in w:
self.way_container.insert(w[id])

# update tile list
self.loaded_tiles.append(tile)
Expand Down
74 changes: 71 additions & 3 deletions obs/face/osm/Way.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@


class Way:
def __init__(self, way_id, way, all_nodes):
def __init__(self, way_id, way, nodes_way):
self.way_id = way_id

if "tags" in way:
Expand All @@ -13,8 +13,6 @@ def __init__(self, way_id, way, all_nodes):
self.tags = {}

# determine points
nodes_way = [all_nodes[i] for i in way["nodes"]]

lat = np.array([n["lat"] for n in nodes_way])
lon = np.array([n["lon"] for n in nodes_way])
self.points_lat_lon = np.stack((lat, lon), axis=1)
Expand All @@ -35,10 +33,78 @@ def __init__(self, way_id, way, all_nodes):
# direction
dx = np.diff(x)
dy = np.diff(y)
self.seg_length = np.hypot(dx, dy)
self.direction = np.arctan2(dy, dx)

self.directionality_bicycle, self.directionality_motorized = self.get_way_directionality(way)

@staticmethod
def subid(way_id, nodes):
if len(nodes) == 0:
return way_id
return str(way_id)+'.'+str(len(nodes))

@staticmethod
def create(way_id, way, all_nodes, max_len):
ways = {}
# determine points
nodes = [all_nodes[i] for i in way["nodes"]]
lat = np.array([n["lat"] for n in nodes])
lon = np.array([n["lon"] for n in nodes])

# bounding box
a = (min(lat), min(lon))
b = (max(lat), max(lon))

# define the local map around the center of the bounding box
lat_0 = (a[0] + b[0]) * 0.5
lon_0 = (a[1] + b[1]) * 0.5
local_map = LocalMap(lat_0, lon_0)
x, y = local_map.transfer_to(lat, lon)
dx = np.diff(x)
dy = np.diff(y)
seg_length = np.hypot(dx, dy)
slen = 0
newnodes = [nodes[0]]
first = 0
# split long segments with intermediate nodes
for i in range(len(seg_length)):
n = math.floor((seg_length[i] - max_len / 2) / max_len)
if n > 0:
for s in range(n):
f1 = (n - s) / (n + 1)
f2 = (s + 1) / (n + 1)
latx = lat[i] * f1 + lat[i+1] * f2
lonx = lon[i] * f1 + lon[i+1] * f2
if math.isnan(latx) or math.isnan(lonx):
f1 = f2
node_id = nodes[i]['id']+ (s+1) * 0x1000000
node = {'type':'node', 'id':node_id, 'lat':latx, 'lon':lonx}
all_nodes[node_id] = node
newnodes.append(node)
w_id = Way.subid(way_id, ways)
ways[w_id] = Way(w_id, way, newnodes[first:])
first = len(newnodes) - 1
slen = 0
# add last segment
newnodes.append(nodes[i+1])
w_id = Way.subid(way_id, ways)
ways[w_id] = Way(w_id, way, newnodes[first:])
first = len(newnodes) - 1
slen = 0
else:
newnodes.append(nodes[i+1])
slen += seg_length[i]
if (slen > max_len and i != first):
w_id = Way.subid(way_id, ways)
ways[w_id] = Way(w_id, way, newnodes[first:])
first = i
slen = 0
if slen > 0:
w_id = Way.subid(way_id, ways)
ways[w_id] = Way(w_id, way, newnodes[first:])
return ways

def get_axis_aligned_bounding_box(self):
return self.a, self.b

Expand Down Expand Up @@ -118,6 +184,8 @@ def get_way_coordinates(self, reverse=False, lateral_offset=0):
# then move the point
c_i = c[i] + n_i * lateral_offset
c_i = self.local_map.transfer_from(c_i[0], c_i[1])
if math.isnan(c_i[0]) or math.isnan(c_i[1]):
n_next = 0
coordinates.append([c_i[0], c_i[1]])

return coordinates
Expand Down
11 changes: 6 additions & 5 deletions visualization/OBS.js
Original file line number Diff line number Diff line change
Expand Up @@ -172,11 +172,12 @@ class Palette {

paletteUrban = new Palette(
{
0.0: [64, 0, 0, 255],
1.4999: [196, 0, 0, 255],
1.5: [196, 196, 0, 255],
2.0: [0, 196, 0, 255],
2.55: [0, 255, 0, 255],
0.0: [196, 0, 0, 255],
0.8: [196, 0, 0, 255],
1.3: [245, 141, 0, 255],
1.5: [94, 188, 7, 255],
2.0: [94, 188, 7, 255],
2.55: [0, 196, 0, 255],
},
[0, 0, 196, 255]
)
Expand Down
Loading