From ad0c8e31248fcbffba9f195569e9a944d70b6171 Mon Sep 17 00:00:00 2001 From: Yandrik Date: Sun, 21 Apr 2024 10:47:29 +0200 Subject: [PATCH] feat: complete working navigation functions --- lib/controllers/map_controller.dart | 5 +- lib/data/geo/model.dart | 1 + lib/data/geo/model.freezed.dart | 34 ++- lib/data/geo/parser.dart | 3 +- lib/nav/graph.dart | 322 ++++++++++++---------------- lib/nav/graph.freezed.dart | 60 ------ lib/util/util.dart | 3 +- test/graph_tests.dart | 65 ++++++ test/scratch_1 | 5 + 9 files changed, 237 insertions(+), 261 deletions(-) create mode 100644 test/scratch_1 diff --git a/lib/controllers/map_controller.dart b/lib/controllers/map_controller.dart index 3d2411d..c0356a0 100644 --- a/lib/controllers/map_controller.dart +++ b/lib/controllers/map_controller.dart @@ -2,7 +2,6 @@ import 'dart:convert'; import 'package:anyhow/anyhow.dart'; import 'package:flutter_map/flutter_map.dart'; -import 'package:flutter_map/src/gestures/positioned_tap_detector_2.dart'; import 'package:geojson_vi/geojson_vi.dart'; import 'package:get/get.dart'; import 'package:latlong2/latlong.dart'; @@ -95,8 +94,8 @@ class MyMapController extends GetxController { // print(feature?.properties); if (feature == null) continue; // print(feature.properties); - final parsed = parseFeature( - feature.properties ?? {}, feature.geometry); + final parsed = parseFeature(feature.properties ?? {}, + feature.geometry, feature.id); if (parsed case Ok(:final ok)) { featuresList.add(ok); } diff --git a/lib/data/geo/model.dart b/lib/data/geo/model.dart index 7621e70..cbcd82a 100644 --- a/lib/data/geo/model.dart +++ b/lib/data/geo/model.dart @@ -20,6 +20,7 @@ class Feature with _$Feature { required GeoJSONGeometry geometry, int? level, String? building, + required String id, }) = _Feature; bool isPolygon() { diff --git a/lib/data/geo/model.freezed.dart b/lib/data/geo/model.freezed.dart index 3c901d9..fdbd365 100644 --- a/lib/data/geo/model.freezed.dart +++ b/lib/data/geo/model.freezed.dart @@ -22,6 +22,7 @@ mixin _$Feature { GeoJSONGeometry get geometry => throw _privateConstructorUsedError; int? get level => throw _privateConstructorUsedError; String? get building => throw _privateConstructorUsedError; + String get id => throw _privateConstructorUsedError; @JsonKey(ignore: true) $FeatureCopyWith get copyWith => throw _privateConstructorUsedError; @@ -38,7 +39,8 @@ abstract class $FeatureCopyWith<$Res> { String? description, GeoJSONGeometry geometry, int? level, - String? building}); + String? building, + String id}); $FeatureTypeCopyWith<$Res> get type; } @@ -62,6 +64,7 @@ class _$FeatureCopyWithImpl<$Res, $Val extends Feature> Object? geometry = null, Object? level = freezed, Object? building = freezed, + Object? id = null, }) { return _then(_value.copyWith( name: null == name @@ -88,6 +91,10 @@ class _$FeatureCopyWithImpl<$Res, $Val extends Feature> ? _value.building : building // ignore: cast_nullable_to_non_nullable as String?, + id: null == id + ? _value.id + : id // ignore: cast_nullable_to_non_nullable + as String, ) as $Val); } @@ -113,7 +120,8 @@ abstract class _$$FeatureImplCopyWith<$Res> implements $FeatureCopyWith<$Res> { String? description, GeoJSONGeometry geometry, int? level, - String? building}); + String? building, + String id}); @override $FeatureTypeCopyWith<$Res> get type; @@ -136,6 +144,7 @@ class __$$FeatureImplCopyWithImpl<$Res> Object? geometry = null, Object? level = freezed, Object? building = freezed, + Object? id = null, }) { return _then(_$FeatureImpl( name: null == name @@ -162,6 +171,10 @@ class __$$FeatureImplCopyWithImpl<$Res> ? _value.building : building // ignore: cast_nullable_to_non_nullable as String?, + id: null == id + ? _value.id + : id // ignore: cast_nullable_to_non_nullable + as String, )); } } @@ -175,7 +188,8 @@ class _$FeatureImpl extends _Feature { this.description, required this.geometry, this.level, - this.building}) + this.building, + required this.id}) : super._(); @override @@ -190,10 +204,12 @@ class _$FeatureImpl extends _Feature { final int? level; @override final String? building; + @override + final String id; @override String toString() { - return 'Feature(name: $name, type: $type, description: $description, geometry: $geometry, level: $level, building: $building)'; + return 'Feature(name: $name, type: $type, description: $description, geometry: $geometry, level: $level, building: $building, id: $id)'; } @override @@ -209,12 +225,13 @@ class _$FeatureImpl extends _Feature { other.geometry == geometry) && (identical(other.level, level) || other.level == level) && (identical(other.building, building) || - other.building == building)); + other.building == building) && + (identical(other.id, id) || other.id == id)); } @override int get hashCode => Object.hash( - runtimeType, name, type, description, geometry, level, building); + runtimeType, name, type, description, geometry, level, building, id); @JsonKey(ignore: true) @override @@ -230,7 +247,8 @@ abstract class _Feature extends Feature { final String? description, required final GeoJSONGeometry geometry, final int? level, - final String? building}) = _$FeatureImpl; + final String? building, + required final String id}) = _$FeatureImpl; const _Feature._() : super._(); @override @@ -246,6 +264,8 @@ abstract class _Feature extends Feature { @override String? get building; @override + String get id; + @override @JsonKey(ignore: true) _$$FeatureImplCopyWith<_$FeatureImpl> get copyWith => throw _privateConstructorUsedError; diff --git a/lib/data/geo/parser.dart b/lib/data/geo/parser.dart index 3d526bd..9b03b79 100644 --- a/lib/data/geo/parser.dart +++ b/lib/data/geo/parser.dart @@ -4,7 +4,7 @@ import 'package:uninav/data/geo/model.dart'; import 'package:yaml/yaml.dart'; Result parseFeature( - Map properties, GeoJSONGeometry geometry) { + Map properties, GeoJSONGeometry geometry, String id) { final name = properties['name'] as String?; final description_yaml = properties['description'] as String? ?? ''; final layer = properties['layer'] as String?; @@ -110,6 +110,7 @@ Result parseFeature( geometry: geometry, level: level, building: building, + id: id, )); } diff --git a/lib/nav/graph.dart b/lib/nav/graph.dart index 261424d..9183b56 100644 --- a/lib/nav/graph.dart +++ b/lib/nav/graph.dart @@ -1,11 +1,11 @@ import 'package:anyhow/anyhow.dart'; import 'package:collection/collection.dart'; -import 'package:directed_graph/directed_graph.dart'; import 'package:fast_immutable_collections/fast_immutable_collections.dart'; +import 'package:flutter/foundation.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; -import 'package:geojson_vi/geojson_vi.dart'; import 'package:get/get.dart'; import 'package:latlong2/latlong.dart'; +import 'package:rust_core/iter.dart'; import 'package:uninav/data/geo/model.dart'; import 'package:uninav/util/geojson_util.dart'; import 'package:uninav/util/util.dart'; @@ -38,6 +38,12 @@ class GraphFeature with _$GraphFeature { double metersTo(GraphFeature other) => distanceTo(other, "meters"); + String get id => when( + buildingFloor: (floor, building) => building.id, + portal: (fromFloor, from, toFloor, to, baseFeature) => baseFeature.id, + basicFeature: (floor, building, feature) => feature.id, + ); + @override String toString() { return when( @@ -48,9 +54,102 @@ class GraphFeature with _$GraphFeature { 'Feature (${formatFeatureTitle(feature)} ($building:$floor))', ); } + + @override + int get hashCode { + return when( + buildingFloor: (floor, building) => Object.hash(floor, building), + portal: (fromFloor, from, toFloor, to, baseFeature) => + Object.hash(fromFloor, from, toFloor, to, baseFeature), + basicFeature: (floor, building, feature) => + Object.hash(floor, building, feature), + ); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + + return other is GraphFeature && + other.when( + buildingFloor: (floor, building) => + this is BuildingFloor && + (this as BuildingFloor).floor == floor && + (this as BuildingFloor).building == building, + portal: (fromFloor, from, toFloor, to, baseFeature) => + this is Portal && + (this as Portal).fromFloor == fromFloor && + (this as Portal).from == from && + (this as Portal).toFloor == toFloor && + (this as Portal).to == to && + (this as Portal).baseFeature == baseFeature, + basicFeature: (floor, building, feature) => + this is BasicFeature && + (this as BasicFeature).floor == floor && + (this as BasicFeature).building == building && + (this as BasicFeature).feature == feature, + ); + } } -bool eq(String? a, String? b) => a?.toLowerCase() == b?.toLowerCase(); +class Graph { + final List<(GraphFeature, double, GraphFeature)> _edges = []; + final HashSet _nodes = HashSet(); + final HashSet<(GraphFeature, GraphFeature)> _edgesSet = HashSet(); + + Iterable get nodes => _nodes.iter(); + + void addNode(GraphFeature node) { + _nodes.add(node); + if (node is BasicFeature && node.feature.name == 'H22') { + print(node); + print(node.hashCode); + } + } + + void addEdge(GraphFeature from, GraphFeature to, double weight) { + addNode(from); + addNode(to); + if (!_edgesSet.contains((from, to))) { + _edgesSet.add((from, to)); + _edges.add((from, weight, to)); + } + if (!_edgesSet.contains((to, from))) { + _edgesSet.add((to, from)); + _edges.add((to, weight, from)); + } + } + + List<(GraphFeature, double, GraphFeature)> getEdges(GraphFeature node) { + return _edges.where((edge) => edge.$1 == node).toList(); + } + + bool contains(GraphFeature node) { + return _nodes.contains(node); + } + + bool containsEdge(GraphFeature from, GraphFeature to) { + return _edgesSet.contains((from, to)); + } + + @override + String toString() { + return 'Graph(_edges: $_edges, _nodes: $_nodes, _edgesSet: $_edgesSet)'; + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + + return other is Graph && + listEquals(other._edges, _edges) && + setEquals(other._nodes, _nodes) && + setEquals(other._edgesSet, _edgesSet); + } + + @override + int get hashCode => _edges.hashCode ^ _nodes.hashCode ^ _edgesSet.hashCode; +} IList wrap(Feature feature, int floor, String buildingFrom) { return feature.type @@ -60,7 +159,10 @@ IList wrap(Feature feature, int floor, String buildingFrom) { lift: (floors) => stairPortalGenerator(floors, floor, feature, 99), door: (connections) => doorPortalGenerator(connections, floor, buildingFrom, feature), - orElse: () => [GraphFeature.basicFeature(floor, buildingFrom, feature)], + orElse: () => [ + GraphFeature.basicFeature( + floor, feature.building ?? buildingFrom, feature) + ], ) .lock; } @@ -70,8 +172,7 @@ List doorPortalGenerator( final portals = []; for (final connection in connections.where((c) => !eq(c, from))) { - portals.add(GraphFeature.portal( - floor, from, floor, connection.toLowerCase(), feature)); + portals.add(GraphFeature.portal(floor, from, floor, connection, feature)); } return portals; @@ -83,12 +184,12 @@ List stairPortalGenerator( final portals = []; for (int i = 1; i <= maxDist; i++) { if (floors.contains(floor - i)) { - portals.add(GraphFeature.portal(floor, feature.building!.toLowerCase(), - floor - i, feature.building!.toLowerCase(), feature)); + portals.add(GraphFeature.portal( + floor, feature.building!, floor - i, feature.building!, feature)); } if (floors.contains(floor + i)) { - portals.add(GraphFeature.portal(floor, feature.building!.toLowerCase(), - floor + i, feature.building!.toLowerCase(), feature)); + portals.add(GraphFeature.portal( + floor, feature.building!, floor + i, feature.building!, feature)); } } return portals; @@ -142,117 +243,34 @@ List findAdjacent( return adjacentFeatures; } -List createGraphList( - GraphFeature origin, List allFeatures, - [Set? visited]) { +Graph makeGraph(GraphFeature origin, List allFeatures, + [Graph? graph]) { // final usedFeatures = [origin]; - visited ??= {origin}; + graph ??= Graph(); + graph.addNode(origin); final adjacent = findAdjacent(origin, allFeatures); - for (final feature in adjacent.asSet()..removeAll(visited)) { - visited.add(feature); - final deeper = createGraphList(feature, allFeatures, visited); - visited.addAll(deeper); + for (final feature in adjacent.asSet()..removeAll(graph.nodes)) { + graph.addEdge(origin, feature, origin.metersTo(feature)); + final _ = makeGraph(feature, allFeatures, graph); + // graph.addAll(deeper); } - return visited.toList(); -} - -Map> createGraphMap( - GraphFeature origin, List allFeatures) { - final graphList = createGraphList(origin, allFeatures); - final graphMap = >{}; - for (final node in graphList) { - final adjacents = node.when( - buildingFloor: (floor, building) { - return graphList - .where((f) => - f is Portal && - eq(f.from, building.name) && - f.fromFloor == floor || - f is BasicFeature && - eq(f.building, building.name) && - f.floor == floor) - .map((f) => f.when( - portal: (fromFloor, from, toFloor, to, baseFeature) => ( - f, - f.metersTo(node), - ), - basicFeature: (floor, building, feature) => - (f, f.metersTo(node)), - buildingFloor: (floor, building) => throw StateError( - "BUG: createGraphMap(): BuildingFloors shouldn't " - "be matched by BuildingFloors"), - )); - }, - portal: (fromFloor, from, toFloor, to, baseFeature) { - return graphList - .where((f) => - f is BuildingFloor && - eq(f.building.name, to) && - f.floor == toFloor) - .map((f) => f.when( - portal: (fromFloor, from, toFloor, to, baseFeature) => - throw StateError( - "BUG: createGraphMap(): Portals shouldn't " - "be matched by Portals"), - basicFeature: (floor, building, feature) => throw StateError( - "BUG: createGraphMap(): BasicFeatures shouldn't " - "be matched by BasicFeatures"), - buildingFloor: (floor, building) => ( - f, - f.metersTo(node) + - 5 /* 5 extra meters for all portals. TODO: smarter!*/ - ), - )); - }, - basicFeature: (floor, building, feature) { - return graphList - .where((f) => - f is BuildingFloor && - eq(f.building.name, building) && - f.floor == floor) - .map((f) => f.when( - portal: (fromFloor, from, toFloor, to, baseFeature) => - throw StateError( - "BUG: createGraphMap(): Portal shouldn't be matched " - "by BasicFeature"), - basicFeature: (floor, building, feature) => throw StateError( - "BUG: createGraphMap(): BasicFeatures shouldn't " - "be matched by BasicFeatures"), - buildingFloor: (floor, building) => (f, f.metersTo(node)), - )); - }, - ); - - graphMap[node] = - Map.fromEntries(adjacents.map((tup) => MapEntry(tup.$1, tup.$2))); - } - - return graphMap; -} - -WeightedDirectedGraph createGraph( - GraphFeature origin, List allFeatures) { - final map = createGraphMap(origin, allFeatures); - final graph = WeightedDirectedGraph( - map, - summation: sum, - zero: 0.0, - comparator: (a, b) => compareGraphFeatures(a, b), - ); return graph; } -Result> findShortestPath( - GraphFeature origin, GraphFeature destination, List allFeatures, - [heuristicVariant = "zero", heuristicMultiplier = 0.2]) { - var graph = createGraphMap(origin, allFeatures); +Result> findShortestPath(GraphFeature origin, + bool Function(GraphFeature) destinationSelector, List allFeatures, + {heuristicVariant = "zero", heuristicMultiplier = 0.2}) { + Graph graph = makeGraph(origin, allFeatures); - if (!(graph.keys.contains(origin) && - graph.values.firstWhereOrNull((vals) => vals.containsKey(destination)) != - null)) { + final GraphFeature? destination = + graph.nodes.firstWhereOrNull(destinationSelector); + + if (!(graph.contains(origin) && + destination != null && + graph.contains(destination))) { return bail("Origin or destination not in graph"); } @@ -299,10 +317,10 @@ Result> findShortestPath( } // expand node - final adjacents = graph[node]!; - for (final entry in adjacents.entries) { - final adjNode = entry.key; - final adjCost = entry.value; + final edges = graph.getEdges(node); + for (final entry in edges) { + final adjNode = entry.$3; + final adjCost = entry.$2; if (closedlist.contains(adjNode)) { continue; @@ -349,77 +367,3 @@ Result> findShortestPath( return bail("No path found"); } - -/// Compares two [GraphFeature] instances and determines their relative order. -/// -/// The comparison is based on the specific subtypes and properties of the -/// [GraphFeature] instances. The comparison logic is as follows: -/// -/// 1. If both instances are [BuildingFloor], they are compared first by the -/// building name and then by the floor number. -/// 2. If one instance is a [Portal] and the other is a [BuildingFloor] or -/// [BasicFeature], the [Portal] is considered greater. -/// 3. If both instances are [Portal], they are compared first by the `from` -/// property, then by the `to` property, and finally by the `baseFeature` name. -/// 4. If one instance is a [BasicFeature] and the other is a [BuildingFloor] or -/// [Portal], the [BasicFeature] is considered greater. -/// 5. If both instances are [BasicFeature], they are compared first by the -/// building name, then by the floor number, and finally by the feature name. -/// -/// Returns a negative value if [a] is considered "less than" [b], a positive -/// value if [a] is considered "greater than" [b], and zero if they are considered -/// equal. -/// -/// This function can be used as a comparator for sorting or ordering -/// [GraphFeature] instances. -int compareGraphFeatures(GraphFeature a, GraphFeature b) { - return a.when( - buildingFloor: (floorA, buildingA) { - return b.when( - buildingFloor: (floorB, buildingB) { - final buildingComparison = buildingA.name.compareTo(buildingB.name); - if (buildingComparison != 0) { - return buildingComparison; - } - return floorA.compareTo(floorB); - }, - portal: (fromFloorB, fromB, toFloorB, toB, baseFeatureB) => -1, - basicFeature: (floorB, buildingB, featureB) => -1, - ); - }, - portal: (fromFloorA, fromA, toFloorA, toA, baseFeatureA) { - return b.when( - buildingFloor: (floorB, buildingB) => 1, - portal: (fromFloorB, fromB, toFloorB, toB, baseFeatureB) { - final fromComparison = fromA.compareTo(fromB); - if (fromComparison != 0) { - return fromComparison; - } - final toComparison = toA.compareTo(toB); - if (toComparison != 0) { - return toComparison; - } - return baseFeatureA.name.compareTo(baseFeatureB.name); - }, - basicFeature: (floorB, buildingB, featureB) => -1, - ); - }, - basicFeature: (floorA, buildingA, featureA) { - return b.when( - buildingFloor: (floorB, buildingB) => 1, - portal: (fromFloorB, fromB, toFloorB, toB, baseFeatureB) => 1, - basicFeature: (floorB, buildingB, featureB) { - final buildingComparison = buildingA.compareTo(buildingB); - if (buildingComparison != 0) { - return buildingComparison; - } - final floorComparison = floorA.compareTo(floorB); - if (floorComparison != 0) { - return floorComparison; - } - return featureA.name.compareTo(featureB.name); - }, - ); - }, - ); -} diff --git a/lib/nav/graph.freezed.dart b/lib/nav/graph.freezed.dart index 56f5d61..b97f70b 100644 --- a/lib/nav/graph.freezed.dart +++ b/lib/nav/graph.freezed.dart @@ -144,24 +144,6 @@ class _$BuildingFloorImpl extends BuildingFloor { @override final Feature building; - @override - String toString() { - return 'GraphFeature.buildingFloor(floor: $floor, building: $building)'; - } - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$BuildingFloorImpl && - (identical(other.floor, floor) || other.floor == floor) && - (identical(other.building, building) || - other.building == building)); - } - - @override - int get hashCode => Object.hash(runtimeType, floor, building); - @JsonKey(ignore: true) @override @pragma('vm:prefer-inline') @@ -341,29 +323,6 @@ class _$PortalImpl extends Portal { @override final Feature baseFeature; - @override - String toString() { - return 'GraphFeature.portal(fromFloor: $fromFloor, from: $from, toFloor: $toFloor, to: $to, baseFeature: $baseFeature)'; - } - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$PortalImpl && - (identical(other.fromFloor, fromFloor) || - other.fromFloor == fromFloor) && - (identical(other.from, from) || other.from == from) && - (identical(other.toFloor, toFloor) || other.toFloor == toFloor) && - (identical(other.to, to) || other.to == to) && - (identical(other.baseFeature, baseFeature) || - other.baseFeature == baseFeature)); - } - - @override - int get hashCode => - Object.hash(runtimeType, fromFloor, from, toFloor, to, baseFeature); - @JsonKey(ignore: true) @override @pragma('vm:prefer-inline') @@ -529,25 +488,6 @@ class _$BasicFeatureImpl extends BasicFeature { @override final Feature feature; - @override - String toString() { - return 'GraphFeature.basicFeature(floor: $floor, building: $building, feature: $feature)'; - } - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$BasicFeatureImpl && - (identical(other.floor, floor) || other.floor == floor) && - (identical(other.building, building) || - other.building == building) && - (identical(other.feature, feature) || other.feature == feature)); - } - - @override - int get hashCode => Object.hash(runtimeType, floor, building, feature); - @JsonKey(ignore: true) @override @pragma('vm:prefer-inline') diff --git a/lib/util/util.dart b/lib/util/util.dart index 4d65f4e..c236c36 100644 --- a/lib/util/util.dart +++ b/lib/util/util.dart @@ -1,7 +1,8 @@ -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:uninav/data/geo/model.dart'; +bool eq(String? a, String? b) => a?.toLowerCase() == b?.toLowerCase(); + String formatDuration(Duration duration) { final days = duration.inDays; final hours = duration.inHours.remainder(24); diff --git a/test/graph_tests.dart b/test/graph_tests.dart index a7ea265..1e72a25 100644 --- a/test/graph_tests.dart +++ b/test/graph_tests.dart @@ -22,6 +22,7 @@ String formatGraphFeature(GraphFeature feature) { void main() { TestWidgetsFlutterBinding.ensureInitialized(); + group('findAdjacent', () { late MyMapController mapController; late List allFeatures; @@ -34,6 +35,69 @@ void main() { allFeatures = mapController.features; }); + test('generates a graph (new)', () { + // Find a building feature + // final buildingFeature = allFeatures.firstWhere((f) => f.type is Building); + final buildingFeature = allFeatures + .firstWhere((f) => f.type is Building && eq(f.name, 'o28')); + + // final endFeature = allFeatures + // .firstWhere((f) => f.type is Building && eq(f.name, 'o25')); + + // final targetFeature = allFeatures + // .firstWhere((f) => f.type is LectureHall && eq(f.name, 'H22')); + + // final wrapped = + // wrap(targetFeature, targetFeature.level!, targetFeature.building!); + // print(wrapped); + + final graph = makeGraph( + wrap(buildingFeature, 2, buildingFeature.name).first, allFeatures); + + final wrapped = [ + graph.nodes.firstWhere((element) => + element is BasicFeature && eq(element.feature.name, "H22")) + ]; + + print(graph.contains(wrapped.first)); // print(graph); + print(wrapped.first.hashCode); // print(graph); + // print(wrap(targetFeature, targetFeature.level!, targetFeature.building!) + // .first + // .hashCode); + // print(wrapped.first == + // wrap(targetFeature, targetFeature.level!, targetFeature.building!) + // .first); + + // print(graph.toString()); + }); + + test('tries to find a path through the graph using own method', () async { + // Find a building feature + // final buildingFeature = allFeatures.firstWhere((f) => f.type is Building); + final startFeature = allFeatures + .firstWhere((f) => f.type is Building && eq(f.name, 'o25')); + + // final endFeature = allFeatures + // .firstWhere((f) => f.type is Building && eq(f.name, 'o25')); + + final endFeature = allFeatures + .firstWhere((f) => f.type is LectureHall && eq(f.name, 'H22')); + + print(endFeature); + + final path = findShortestPath( + wrap(startFeature, 4, startFeature.name).first, + (f) => f is BasicFeature && eq(f.feature.name, 'H1'), + // wrap(endFeature, 2, "o28").first, + allFeatures, + ); + print(path + .unwrap() + .map((e) => "${formatGraphFeature(e.$1)} (${e.$2}m)") + .join(' -> ')); + }); + +/* test('generates a graph', () { // Find a building feature // final buildingFeature = allFeatures.firstWhere((f) => f.type is Building); @@ -172,5 +236,6 @@ void main() { print(path.map(formatGraphFeature).join('\n')); }); */ + */ }); } diff --git a/test/scratch_1 b/test/scratch_1 new file mode 100644 index 0000000..778272e --- /dev/null +++ b/test/scratch_1 @@ -0,0 +1,5 @@ +tter: Graph(_edges: [(Floor (O28:2), 18.07235664768292, Portal (O28:2 -> outside:2)), (Portal (O28:2 -> outside:2), 18.07235664768292, Floor (O28:2)), (Floor (O28:2), 15.297104122554744, Portal (O28:2 -> outside:2)), (Portal (O28:2 -> outside:2), 15.297104122554744, Floor (O28:2)), (Floor (O28:2), 19.415128786653703, Portal (O28:2 -> o27:2)), (Portal (O28:2 -> o27:2), 19.415128786653703, Floor (O28:2)), (Portal (O28:2 -> o27:2), 33.716274727948495, Floor (O27:2)), (Floor (O27:2), 33.716274727948495, Portal (O28:2 -> o27:2)), (Floor (O27:2), 33.716274727948495, Portal (O27:2 -> o28:2)), (Portal (O27:2 -> o28:2), 33.716274727948495, Floor (O27:2)), (Floor (O27:2), 34.66654589617496, Portal (O27:2 -> outside:2)), (Portal (O27:2 -> outside:2), 34.66654589617496, Floor (O27:2)), (Floor (O27:2), 33.65243562786605, Portal (O27:2 -> o26:2)), (Portal (O27:2 -> o26:2), 33.65243562786605, Floor (O27:2)), (Portal (O27:2 -> o26:2), 35.39850450113622, Floor (O26:2)), (Floor (O26:2), 35.39850450113622, Portal (O27:2 -> o26:2)), (Floor (O26:2), 35.39850450113622, Portal (O26:2 -> o27:2)), (Portal (O26:2 -> o27:2), 35.39850450113622, Floor (O26:2)), (Floor (O26:2), 33.21619853357419, Portal (O26:2 -> o25:2)), (Portal (O26:2 -> o25:2), 33.21619853357419, Floor (O26:2)), (Portal (O26:2 -> o25:2), 33.961650590271105, Floor (O25:2)), (Floor (O25:2), 33.961650590271105, Portal (O26:2 -> o25:2)), (Floor (O25:2), 33.961650590271105, Portal (O25:2 -> o26:2)), (Portal (O25:2 -> o26:2), 33.961650590271105, Floor (O25:2)), (Floor (O25:2), 14.982164244123666, Portal (o25:2 -> o25:1)), (Portal (o25:2 -> o25:1), 14.982164244123666, Floor (O25:2)), (Portal (o25:2 -> o25:1), 14.982164244123666, Floor (O25:1)), (Floor (O25:1), 14.982164244123666, Portal (o25:2 -> o25:1)), (Floor (O25:1), 35.42612086226272, Portal (O25:1 -> o26:1)), (Portal (O25:1 -> o26:1), 35.42612086226272, Floor (O25:1)), (Portal (O25:1 -> o26:1), 33.055913391413526, Floor (O26:1)), (Floor (O26:1), 33.055913391413526, Portal (O25:1 -> o26:1)), (Floor (O26:1), 35.44085443938852, Portal (O26:1 -> o27:1)), (Portal (O26:1 -> o27:1), 35.44085443938852, Floor (O26:1)), (Portal (O26:1 -> o27:1), 33.57434121790288, Floor (O27:1)), (Floor (O27:1), 33.57434121790288, Portal (O26:1 -> o27:1)), (Floor (O27:1), 33.53469906357586, Portal (O27:1 -> o28:1)), (Portal (O27:1 -> o28:1), 33.53469906357586, Floor (O27:1)), (Portal (O27:1 -> o28:1), 19.57252235392747, Floor (O28:1)), (Floor (O28:1), 19.57252235392747, Portal (O27:1 -> o28:1)), (Floor (O28:1), 14.938904363079567, Portal (O28:1 -> outside:1)), (Portal (O28:1 -> outside:1), 14.938904363079567, Floor (O28:1)), (Floor (O28:1), 19.57252235392747, Portal (O28:1 -> o27:1)), (Portal (O28:1 -> o27:1), 19.57252235392747, Floor (O28:1)), (Floor (O28:1), 15.523953818709412, Portal (o28:1 -> o28:2)), (Portal (o28:1 -> o28:2), 15.523953818709412, Floor (O28:1)), (Floor (O27:1), 34.61826780986434, Portal (O27:1 -> outside:1)), (Portal (O27:1 -> outside:1), 34.61826780986434, Floor (O27:1)), (Floor (O27:1), 33.57434121790288, Portal (O27:1 -> o26:1)), (Portal (O27:1 -> o26:1), 33.57434121790288, Floor (O27:1)), (Floor (O27:1), 8.741341563928485, Portal (o27:1 -> o27:2)), (Portal (o27:1 -> o27:2), 8.741341563928485, Floor (O27:1)), (Floor (O27:1), 8.741341563928485, Portal (o27:1 -> o27:3)), (Portal (o27:1 -> o27:3), 8.741341563928485, Floor (O27:1)), (Portal (o27:1 -> o27:3), 8.741341563928485, Floor (O27:3)), (Floor (O27:3), 8.741341563928485, Portal (o27:1 -> o27:3)), (Floor (O27:3), 8.741341563928485, Portal (o27:3 -> o27:2)), (Portal (o27:3 -> o27:2), 8.741341563928485, Floor (O27:3)), (Floor (O27:3), 8.741341563928485, Portal (o27:3 -> o27:4)), (Portal (o27:3 -> o27:4), 8.741341563928485, Floor (O27:3)), (Portal (o27:3 -> o27:4), 8.741341563928485, Floor (O27:4)), (Floor (O27:4), 8.741341563928485, Portal (o27:3 -> o27:4)), (Floor (O27:4), 8.741341563928485, Portal (o27:4 -> o27:3)), (Portal (o27:4 -> o27:3), 8.741341563928485, Floor (O27:4)), (Floor (O27:4), 8.741341563928485, Portal (o27:4 -> o27:5)), (Portal (o27:4 -> o27:5), 8.741341563928485, Floor (O27:4)), (Portal (o27:4 -> o27:5), 8.741341563928485, Floor (O27:5)), (Floor (O27:5), 8.741341563928485, Portal (o27:4 -> o27:5)), (Floor (O27:5), 34.41278976013284, Portal (O27:5 -> o26:5)), (Portal (O27:5 -> o26:5), 34.41278976013284, Floor (O27:5)), (Portal (O27:5 -> o26:5), 36.133989131190056, Floor (O26:5)), (Floor (O26:5), 36.133989131190056, Portal (O27:5 -> o26:5)), (Floor (O26:5), 36.133989131190056, Portal (O26:5 -> o27:5)), (Portal (O26:5 -> o27:5), 36.133989131190056, Floor (O26:5)), (Floor (O26:5), 33.103599026141175, Portal (O26:5 -> o25:5)), (Portal (O26:5 -> o25:5), 33.103599026141175, Floor (O26:5)), (Portal (O26:5 -> o25:5), 34.96026330165253, Floor (O25:5)), (Floor (O25:5), 34.96026330165253, Portal (O26:5 -> o25:5)), (Floor (O25:5), 34.96026330165253, Portal (O25:5 -> o26:5)), (Portal (O25:5 -> o26:5), 34.96026330165253, Floor (O25:5)), (Floor (O25:5), 14.982164244123666, Portal (o25:5 -> o25:4)), (Portal (o25:5 -> o25:4), 14.982164244123666, Floor (O25:5)), (Portal (o25:5 -> o25:4), 14.982164244123666, Floor (O25:4)), (Floor (O25:4), 14.982164244123666, Portal (o25:5 -> o25:4)), (Floor (O25:4), 34.5505388701605, Portal (O25:4 -> o26:4)), (Portal (O25:4 -> o26:4), 34.5505388701605, Floor (O25:4)), (Portal (O25:4 -> o26:4), 33.23550110159449, Floor (O26:4)), (Floor (O26:4), 33.23550110159449, Portal (O25:4 -> o26:4)), (Floor (O26:4), 33.23550110159449, Portal (O26:4 -> o25:4)), (Portal (O26:4 -> o25:4), 33.23550110159449, Floor (O26:4)), (Floor (O26:4), 5.280842345576146, Portal (o26:4 -> o26:3)), (Portal (o26:4 -> o26:3), 5.280842345576146, Floor (O26:4)), (Portal (o26:4 -> o26:3), 5.280842345576146, Floor (O26:3)), (Floor (O26:3), 5.280842345576146, Portal (o26:4 -> o26:3)), (Floor (O26:3), 33.050131509364974, Portal (O26:3 -> o25:3)), (Portal (O26:3 -> o25:3), 33.050131509364974, Floor (O26:3)), (Portal (O26:3 -> o25:3), 35.86891218212622, Floor (O25:3)), (Floor (O25:3), 35.86891218212622, Portal (O26:3 -> o25:3)), (Floor (O25:3), 35.86891218212622, Portal (O25:3 -> o26:3)), (Portal (O25:3 -> o26:3), 35.86891218212622, Floor (O25:3)), (Floor (O25:3), 14.982164244123666, Portal (o25:3 -> o25:2)), (Portal (o25:3 -> o25:2), 14.982164244123666, Floor (O25:3)), (Floor (O25:3), 14.982164244123666, Portal (o25:3 -> o25:4)), (Portal (o25:3 -> o25:4), 14.982164244123666, Floor (O25:3)), (Floor (O25:3), 14.982164244123666, Portal (o25:3 -> o25:1)), (Portal (o25:3 -> o25:1), 14.982164244123666, Floor (O25:3)), (Floor (O25:3), 14.982164244123666, Portal (o25:3 -> o25:5)), (Portal (o25:3 -> o25:5), 14.982164244123666, Floor (O25:3)), (Floor (O25:3), 14.982164244123666, Portal (o25:3 -> o25:6)), (Portal (o25:3 -> o25:6), 14.982164244123666, Floor (O25:3)), (Portal (o25:3 -> o25:6), 14.982164244123666, Floor (O25:6)), (Floor (O25:6), 14.982164244123666, Portal (o25:3 -> o25:6)), (Floor (O25:6), 14.982164244123666, Portal (o25:6 -> o25:5)), (Portal (o25:6 -> o25:5), 14.982164244123666, Floor (O25:6)), (Floor (O25:6), 14.982164244123666, Portal (o25:6 -> o25:4)), (Portal (o25:6 -> o25:4), 14.982164244123666, Floor (O25:6)), (Floor (O25:6), 14.982164244123666, Portal (o25:6 -> o25:3)), (Portal (o25:6 -> o25:3), 14.982164244123666, Floor (O25:6)), (Floor (O25:6), 14.982164244123666, Portal (o25:6 -> o25:2)), (Portal (o25:6 -> o25:2), 14.982164244123666, Floor (O25:6)), (Floor (O25:6), 14.982164244123666, Portal (o25:6 -> o25:1)), (Portal (o25:6 -> o25:1), 14.982164244123666, Floor (O25:6)), (Floor (O25:6), 10.254873079454464, Portal (o25:6 -> o25:5)), (Portal (o25:6 -> o25:5), 10.254873079454464, Floor (O25:6)), (Floor (O25:6), 16.89055037771207, Feature (Toilet (Male) (O25:6))), (Feature (Toilet (Male) (O25:6)), 16.89055037771207, Floor (O25:6)), (Floor (O25:6), 15.140217616206352, Feature (Toilet (Female) (O25:6))), (Feature (Toilet (Female) (O25:6)), 15.140217616206352, Floor (O25:6)), (Floor (O25:3), 10.254873079454464, Portal (o25:3 -> o25:2)), (Portal (o25:3 -> o25:2), 10.254873079454464, Floor (O25:3)), (Floor (O25:3), 10.254873079454464, Portal (o25:3 -> o25:4)), (Portal (o25:3 -> o25:4), 10.254873079454464, Floor (O25:3)), (Floor (O25:3), 15.140217616206352, Feature (Toilet (Female) (O25:3))), (Feature (Toilet (Female) (O25:3)), 15.140217616206352, Floor (O25:3)), (Floor (O26:3), 5.280842345576146, Portal (o26:3 -> o26:2)), (Portal (o26:3 -> o26:2), 5.280842345576146, Floor (O26:3)), (Floor (O26:3), 5.280842345576146, Portal (o26:3 -> o26:4)), (Portal (o26:3 -> o26:4), 5.280842345576146, Floor (O26:3)), (Floor (O26:3), 5.280842345576146, Portal (o26:3 -> o26:1)), (Portal (o26:3 -> o26:1), 5.280842345576146, Floor (O26:3)), (Floor (O26:3), 5.280842345576146, Portal (o26:3 -> o26:5)), (Portal (o26:3 -> o26:5), 5.280842345576146, Floor (O26:3)), (Floor (O26:3), 0.6475921055060576, Portal (o26:3 -> o26:2)), (Portal (o26:3 -> o26:2), 0.6475921055060576, Floor (O26:3)), (Floor (O26:3), 0.6475921055060576, Portal (o26:3 -> o26:4)), (Portal (o26:3 -> o26:4), 0.6475921055060576, Floor (O26:3)), (Floor (O26:3), 5.022398921265917, Feature (Toilet (Female) (O26:3))), (Feature (Toilet (Female) (O26:3)), 5.022398921265917, Floor (O26:3)), (Floor (O26:3), 7.040031415830704, Feature (Toilet (Male) (O26:3))), (Feature (Toilet (Male) (O26:3)), 7.040031415830704, Floor (O26:3)), (Floor (O26:4), 5.280842345576146, Portal (o26:4 -> o26:5)), (Portal (o26:4 -> o26:5), 5.280842345576146, Floor (O26:4)), (Floor (O26:4), 5.280842345576146, Portal (o26:4 -> o26:2)), (Portal (o26:4 -> o26:2), 5.280842345576146, Floor (O26:4)), (Floor (O26:4), 5.280842345576146, Portal (o26:4 -> o26:1)), (Portal (o26:4 -> o26:1), 5.280842345576146, Floor (O26:4)), (Floor (O26:4), 0.6475921055060576, Portal (o26:4 -> o26:3)), (Portal (o26:4 -> o26:3), 0.6475921055060576, Floor (O26:4)), (Floor (O26:4), 0.6475921055060576, Portal (o26:4 -> o26:5)), (Portal (o26:4 -> o26:5), 0.6475921055060576, Floor (O26:4)), (Floor (O25:4), 14.982164244123666, Portal (o25:4 -> o25:3)), (Portal (o25:4 -> o25:3), 14.982164244123666, Floor (O25:4)), (Floor (O25:4), 14.982164244123666, Portal (o25:4 -> o25:5)), (Portal (o25:4 -> o25:5), 14.982164244123666, Floor (O25:4)), (Floor (O25:4), 14.982164244123666, Portal (o25:4 -> o25:2)), (Portal (o25:4 -> o25:2), 14.982164244123666, Floor (O25:4)), (Floor (O25:4), 14.982164244123666, Portal (o25:4 -> o25:6)), (Portal (o25:4 -> o25:6), 14.982164244123666, Floor (O25:4)), (Floor (O25:4), 14.982164244123666, Portal (o25:4 -> o25:1)), (Portal (o25:4 -> o25:1), 14.982164244123666, Floor (O25:4)), (Floor (O25:4), 10.254873079454464, Portal (o25:4 -> o25:3)), (Portal (o25:4 -> o25:3), 10.254873079454464, Floor (O25:4)), (Floor (O25:4), 10.254873079454464, Portal (o25:4 -> o25:5)), (Portal (o25:4 -> o25:5), 10.254873079454464, Floor (O25:4)), (Floor (O25:4), 15.192637699652515, Feature (Toilet (Female) (O25:4))), (Feature (Toilet (Female) (O25:4)), 15.192637699652515, Floor (O25:4)), (Floor (O25:4), 16.965711828794596, Feature (Toilet (Male) (O25:4))), (Feature (Toilet (Male) (O25:4)), 16.965711828794596, Floor (O25:4)), (Floor (O25:5), 14.982164244123666, Portal (o25:5 -> o25:6)), (Portal (o25:5 -> o25:6), 14.982164244123666, Floor (O25:5)), (Floor (O25:5), 14.982164244123666, Portal (o25:5 -> o25:3)), (Portal (o25:5 -> o25:3), 14.982164244123666, Floor (O25:5)), (Floor (O25:5), 14.982164244123666, Portal (o25:5 -> o25:2)), (Portal (o25:5 -> o25:2), 14.982164244123666, Floor (O25:5)), (Floor (O25:5), 14.982164244123666, Portal (o25:5 -> o25:1)), (Portal (o25:5 -> o25:1), 14.982164244123666, Floor (O25:5)), (Floor (O25:5), 10.254873079454464, Portal (o25:5 -> o25:4)), (Portal (o25:5 -> o25:4), 10.254873079454464, Floor (O25:5)), (Floor (O25:5), 10.254873079454464, Portal (o25:5 -> o25:6)), (Portal (o25:5 -> o25:6), 10.254873079454464, Floor (O25:5)), (Floor (O25:5), 16.89055037771207, Feature (Toilet (Male) (O25:5))), (Feature (Toilet (Male) (O25:5)), 16.89055037771207, Floor (O25:5)), (Floor (O25:5), 15.140217616206352, Feature (Toilet (Female) (O25:5))), (Feature (Toilet (Female) (O25:5)), 15.140217616206352, Floor (O25:5)), (Floor (O26:5), 5.280842345576146, Portal (o26:5 -> o26:4)), (Portal (o26:5 -> o26:4), 5.280842345576146, Floor (O26:5)), (Floor (O26:5), 5.280842345576146, Portal (o26:5 -> o26:3)), (Portal (o26:5 -> o26:3), 5.280842345576146, Floor (O26:5)), (Floor (O26:5), 5.280842345576146, Portal (o26:5 -> o26:2)), (Portal (o26:5 -> o26:2), 5.280842345576146, Floor (O26:5)), (Floor (O26:5), 5.280842345576146, Portal (o26:5 -> o26:1)), (Portal (o26:5 -> o26:1), 5.280842345576146, Floor (O26:5)), (Floor (O26:5), 0.6475921055060576, Portal (o26:5 -> o26:4)), (Portal (o26:5 -> o26:4), 0.6475921055060576, Floor (O26:5)), (Floor (O26:5), 6.987861594769889, Feature (Toilet (Male) (O26:5))), (Feature (Toilet (Male) (O26:5)), 6.987861594769889, Floor (O26:5)), (Floor (O26:5), 4.658397084661239, Feature (Toilet (Female) (O26:5))), (Feature (Toilet (Female) (O26:5)), 4.658397084661239, Floor (O26:5)), (Floor (O27:5), 8.741341563928485, Portal (o27:5 -> o27:4)), (Portal (o27:5 -> o27:4), 8.741341563928485, Floor (O27:5)), (Floor (O27:5), 8.741341563928485, Portal (o27:5 -> o27:3)), (Portal (o27:5 -> o27:3), 8.741341563928485, Floor (O27:5)), (Floor (O27:5), 8.741341563928485, Portal (o27:5 -> o27:2)), (Portal (o27:5 -> o27:2), 8.741341563928485, Floor (O27:5)), (Floor (O27:5), 8.741341563928485, Portal (o27:5 -> o27:1)), (Portal (o27:5 -> o27:1), 8.741341563928485, Floor (O27:5)), (Floor (O27:5), 1.5933440815988473, Portal (o27:5 -> o27:4)), (Portal (o27:5 -> o27:4), 1.5933440815988473, Floor (O27:5)), (Floor (O27:5), 9.092479538765458, Feature (Toilet (Male) (O27:5))), (Feature (Toilet (Male) (O27:5)), 9.092479538765458, Floor (O27:5)), (Floor (O27:5), 7.742917987289784, Feature (Toilet (Female) (O27:5))), (Feature (Toilet (Female) (O27:5)), 7.742917987289784, Floor (O27:5)), (Floor (O27:4), 8.741341563928485, Portal (o27:4 -> o27:2)), (Portal (o27:4 -> o27:2), 8.741341563928485, Floor (O27:4)), (Floor (O27:4), 8.741341563928485, Portal (o27:4 -> o27:1)), (Portal (o27:4 -> o27:1), 8.741341563928485, Floor (O27:4)), (Floor (O27:4), 1.5933440815988473, Portal (o27:4 -> o27:3)), (Portal (o27:4 -> o27:3), 1.5933440815988473, Floor (O27:4)), (Floor (O27:4), 1.5933440815988473, Portal (o27:4 -> o27:5)), (Portal (o27:4 -> o27:5), 1.5933440815988473, Floor (O27:4)), (Floor (O27:4), 8.838019278296693, Feature (Toilet (Male) (O27:4))), (Feature (Toilet (Male) (O27:4)), 8.838019278296693, Floor (O27:4)), (Floor (O27:4), 7.6240873669103, Feature (Toilet (Female) (O27:4))), (Feature (Toilet (Female) (O27:4)), 7.6240873669103, Floor (O27:4)), (Floor (O27:3), 8.741341563928485, Portal (o27:3 -> o27:1)), (Portal (o27:3 -> o27:1), 8.741341563928485, Floor (O27:3)), (Floor (O27:3), 8.741341563928485, Portal (o27:3 -> o27:5)), (Portal (o27:3 -> o27:5), 8.741341563928485, Floor (O27:3)), (Floor (O27:3), 1.5933440815988473, Portal (o27:3 -> o27:2)), (Portal (o27:3 -> o27:2), 1.5933440815988473, Floor (O27:3)), (Floor (O27:3), 1.5933440815988473, Portal (o27:3 -> o27:4)), (Portal (o27:3 -> o27:4), 1.5933440815988473, Floor (O27:3)), (Floor (O27:3), 9.092479538765458, Feature (Toilet (Male) (O27:3))), (Feature (Toilet (Male) (O27:3)), 9.092479538765458, Floor (O27:3)), (Floor (O27:3), 7.6240873669103, Feature (Toilet (Female) (O27:3))), (Feature (Toilet (Female) (O27:3)), 7.6240873669103, Floor (O27:3)), (Floor (O27:1), 8.741341563928485, Portal (o27:1 -> o27:4)), (Portal (o27:1 -> o27:4), 8.741341563928485, Floor (O27:1)), (Floor (O27:1), 8.741341563928485, Portal (o27:1 -> o27:5)), (Portal (o27:1 -> o27:5), 8.741341563928485, Floor (O27:1)), (Floor (O27:1), 1.5933440815988473, Portal (o27:1 -> o27:2)), (Portal (o27:1 -> o27:2), 1.5933440815988473, Floor (O27:1)), (Floor (O27:1), 15.827973142404486, Feature (Toilet (Male) (O27:1))), (Feature (Toilet (Male) (O27:1)), 15.827973142404486, Floor (O27:1)), (Floor (O27:1), 14.249191255430645, Feature (Toilet (Handicap) (O27:1))), (Feature (Toilet (Handicap) (O27:1)), 14.249191255430645, Floor (O27:1)), (Floor (O27:1), 14.99117262866505, Feature (Toilet (Female) (O27:1))), (Feature (Toilet (Female) (O27:1)), 14.99117262866505, Floor (O27:1)), (Floor (O26:1), 33.055913391413526, Portal (O26:1 -> o25:1)), (Portal (O26:1 -> o25:1), 33.055913391413526, Floor (O26:1)), (Floor (O26:1), 5.280842345576146, Portal (o26:1 -> o26:2)), (Portal (o26:1 -> o26:2), 5.280842345576146, Floor (O26:1)), (Floor (O26:1), 5.280842345576146, Portal (o26:1 -> o26:3)), (Portal (o26:1 -> o26:3), 5.280842345576146, Floor (O26:1)), (Floor (O26:1), 5.280842345576146, Portal (o26:1 -> o26:4)), (Portal (o26:1 -> o26:4), 5.280842345576146, Floor (O26:1)), (Floor (O26:1), 5.280842345576146, Portal (o26:1 -> o26:5)), (Portal (o26:1 -> o26:5), 5.280842345576146, Floor (O26:1)), (Floor (O26:1), 6.739252553616535, Portal (o26:1 -> o26:2)), (Portal (o26:1 -> o26:2), 6.739252553616535, Floor (O26:1)), (Floor (O26:1), 7.7615843304974765, Feature (Toilet (Female) (O26:1))), (Feature (Toilet (Female) (O26:1)), 7.7615843304974765, Floor (O26:1)), (Floor (O26:1), 6.447862668417072, Feature (Toilet (Male) (O26:1))), (Feature (Toilet (Male) (O26:1)), 6.447862668417072, Floor (O26:1)), (Floor (O25:1), 14.982164244123666, Portal (o25:1 -> o25:2)), (Portal (o25:1 -> o25:2), 14.982164244123666, Floor (O25:1)), (Floor (O25:1), 14.982164244123666, Portal (o25:1 -> o25:3)), (Portal (o25:1 -> o25:3), 14.982164244123666, Floor (O25:1)), (Floor (O25:1), 14.982164244123666, Portal (o25:1 -> o25:4)), (Portal (o25:1 -> o25:4), 14.982164244123666, Floor (O25:1)), (Floor (O25:1), 14.982164244123666, Portal (o25:1 -> o25:5)), (Portal (o25:1 -> o25:5), 14.982164244123666, Floor (O25:1)), (Floor (O25:1), 14.982164244123666, Portal (o25:1 -> o25:6)), (Portal (o25:1 -> o25:6), 14.982164244123666, Floor (O25:1)), (Floor (O25:1), 15.32295226803648, Portal (o25:1 -> o25:2)), (Portal (o25:1 -> o25:2), 15.32295226803648, Floor (O25:1)), (Floor (O25:1), 15.383217303763654, Feature (Toilet (Female) (O25:1))), (Feature (Toilet (Female) (O25:1)), 15.383217303763654, Floor (O25:1)), (Floor (O25:2), 14.982164244123666, Portal (o25:2 -> o25:3)), (Portal (o25:2 -> o25:3), 14.982164244123666, Floor (O25:2)), (Floor (O25:2), 14.982164244123666, Portal (o25:2 -> o25:4)), (Portal (o25:2 -> o25:4), 14.982164244123666, Floor (O25:2)), (Floor (O25:2), 14.982164244123666, Portal (o25:2 -> o25:5)), (Portal (o25:2 -> o25:5), 14.982164244123666, Floor (O25:2)), (Floor (O25:2), 14.982164244123666, Portal (o25:2 -> o25:6)), (Portal (o25:2 -> o25:6), 14.982164244123666, Floor (O25:2)), (Floor (O25:2), 15.32295226803648, Portal (o25:2 -> o25:1)), (Portal (o25:2 -> o25:1), 15.32295226803648, Floor (O25:2)), (Floor (O25:2), 10.254873079454464, Portal (o25:2 -> o25:3)), (Portal (o25:2 -> o25:3), 10.254873079454464, Floor (O25:2)), (Floor (O25:2), 9.663267747513421, Feature (Toilet (Male) (O25:2))), (Feature (Toilet (Male) (O25:2)), 9.663267747513421, Floor (O25:2)), (Floor (O25:2), 12.98374429013414, Feature (Toilet (Female) (O25:2))), (Feature (Toilet (Female) (O25:2)), 12.98374429013414, Floor (O25:2)), (Floor (O25:2), 21.75073634035963, Feature (H1 (Lecture Hall) (O25:2))), (Feature (H1 (Lecture Hall) (O25:2)), 21.75073634035963, Floor (O25:2)), (Floor (O25:2), 31.186227532907925, Feature (H2 (Lecture Hall) (O25:2))), (Feature (H2 (Lecture Hall) (O25:2)), 31.186227532907925, Floor (O25:2)), (Floor (O25:2), 30.55269310603863, Feature (H4 (Lecture Hall) (O25:2))), (Feature (H4 (Lecture Hall) (O25:2)), 30.55269310603863, Floor (O25:2)), (Floor (O26:2), 5.280842345576146, Portal (o26:2 -> o26:1)), (Portal (o26:2 -> o26:1), 5.280842345576146, Floor (O26:2)), (Floor (O26:2), 5.280842345576146, Portal (o26:2 -> o26:3)), (Portal (o26:2 -> o26:3), 5.280842345576146, Floor (O26:2)), (Floor (O26:2), 5.280842345576146, Portal (o26:2 -> o26:4)), (Portal (o26:2 -> o26:4), 5.280842345576146, Floor (O26:2)), (Floor (O26:2), 5.280842345576146, Portal (o26:2 -> o26:5)), (Portal (o26:2 -> o26:5), 5.280842345576146, Floor (O26:2)), (Floor (O26:2), 6.739252553616535, Portal (o26:2 -> o26:1)), (Portal (o26:2 -> o26:1), 6.739252553616535, Floor (O26:2)), (Floor (O26:2), 0.6475921055060576, Portal (o26:2 -> o26:3)), (Portal (o26:2 -> o26:3), 0.6475921055060576, Floor (O26:2)), (Floor (O26:2), 4.958247524042617, Feature (Toilet (Female) (O26:2))), (Feature (Toilet (Female) (O26:2)), 4.958247524042617, Floor (O26:2)), (Floor (O26:2), 4.799584204840353, Feature (Toilet (Male) (O26:2))), (Feature (Toilet (Male) (O26:2)), 4.799584204840353, Floor (O26:2)), (Floor (O27:2), 25.363322050366754, Feature (H20 (Lecture Hall) (O27:2))), (Feature (H20 (Lecture Hall) (O27:2)), 25.363322050366754, Floor (O27:2)), (Floor (O27:2), 8.741341563928485, Portal (o27:2 -> o27:1)), (Portal (o27:2 -> o27:1), 8.741341563928485, Floor (O27:2)), (Floor (O27:2), 8.741341563928485, Portal (o27:2 -> o27:3)), (Portal (o27:2 -> o27:3), 8.741341563928485, Floor (O27:2)), (Floor (O27:2), 8.741341563928485, Portal (o27:2 -> o27:4)), (Portal (o27:2 -> o27:4), 8.741341563928485, Floor (O27:2)), (Floor (O27:2), 8.741341563928485, Portal (o27:2 -> o27:5)), (Portal (o27:2 -> o27:5), 8.741341563928485, Floor (O27:2)), (Floor (O27:2), 1.5933440815988473, Portal (o27:2 -> o27:1)), (Portal (o27:2 -> o27:1), 1.5933440815988473, Floor (O27:2)), (Floor (O27:2), 1.5933440815988473, Portal (o27:2 -> o27:3)), (Portal (o27:2 -> o27:3), 1.5933440815988473, Floor (O27:2)), (Floor (O27:2), 15.798261001671795, Feature (Toilet (Male) (O27:2))), (Feature (Toilet (Male) (O27:2)), 15.798261001671795, Floor (O27:2)), (Floor (O27:2), 14.77030088479878, Feature (Toilet (Female) (O27:2))), (Feature (Toilet (Female) (O27:2)), 14.77030088479878, Floor (O27:2)), (Floor (O28:2), 8.13785064011948, Feature (H22 (Lecture Hall) (O28:2))), (Feature (H22 (Lecture Hall) (O28:2)), 8.13785064011948, Floor (O28:2)), (Floor (O28:2), 15.523953818709412, Portal (o28:2 -> o28:1)), (Portal (o28:2 -> o28:1), 15.523953818709412, Floor (O28:2))], _nodes: {Portal (o26:5 -> o26:1), Portal (o25:4 -> o25:5), Portal (o27:4 -> o27:2), Portal (o25:1 -> o25:2), Floor (O26:1), Feature (H1 (Lecture Hall) (O25:2)), Portal (o26:1 -> o26:2), Floor (O25:2), Portal (o25:5 -> o25:1), Portal (o27:2 -> o27:1), Portal (o26:2 -> o26:5), Feature (Toilet (Male) (O25:4)), Portal (o25:2 -> o25:3), Portal (O25:2 -> o26:2), Portal (o25:3 -> o25:4), Portal (o26:3 -> o26:1), Feature (Toilet (Female) (O27:3)), Portal (o27:4 -> o27:5), Portal (o25:3 -> o25:5), Portal (o25:6 -> o25:5), Portal (o27:2 -> o27:5), Portal (o27:5 -> o27:4), Floor (O27:4), Portal (o27:4 -> o27:3), Portal (o27:2 -> o27:4), Feature (Toilet (Female) (O25:2)), Portal (O27:1 -> outside:1), Portal (o26:4 -> o26:3), Portal (O26:1 -> o27:1), Portal (o27:1 -> o27:2), Floor (O27:3), Portal (o25:4 -> o25:2), Portal (o25:3 -> o25:4), Feature (Toilet (Male) (O27:2)), Portal (o25:1 -> o25:2), Portal (o25:6 -> o25:4), Portal (o25:4 -> o25:6), Feature (H20 (Lecture Hall) (O27:2)), Portal (O27:2 -> o26:2), Portal (o27:4 -> o27:5), Portal (O26:5 -> o25:5), Portal (o25:2 -> o25:4), Feature (Toilet (Male) (O27:4)), Portal (o26:4 -> o26:5), Feature (Toilet (Female) (O26:3)), Portal (o26:2 -> o26:1), Portal (O28:1 -> outside:1), Portal (O28:2 -> o27:2), Feature (H2 (Lecture Hall) (O25:2)), Portal (o27:3 -> o27:2), Portal (O25:3 -> o26:3), Portal (o25:5 -> o25:4), Feature (Toilet (Female) (O27:4)), Portal (o27:5 -> o27:1), Portal (o25:3 -> o25:1), Portal (o25:2 -> o25:6), Portal (o27:3 -> o27:1), Portal (o26:4 -> o26:3), Feature (Toilet (Female) (O25:1)), Portal (o27:3 -> o27:4), Portal (o25:5 -> o25:4), Portal (o26:5 -> o26:2), Portal (o27:3 -> o27:2), Portal (O27:5 -> o26:5), Portal (O27:1 -> o26:1), Portal (o25:2 -> o25:1), Feature (Toilet (Male) (O25:5)), Portal (o26:5 -> o26:4), Portal (o26:3 -> o26:4), Portal (o25:3 -> o25:2), Portal (o25:3 -> o25:6), Portal (o27:2 -> o27:3), Portal (O28:2 -> outside:2), Portal (o27:2 -> o27:1), Portal (O26:1 -> o25:1), Feature (Toilet (Handicap) (O27:1)), Portal (o27:1 -> o27:3), Portal (o25:4 -> o25:5), Portal (o27:3 -> o27:5), Feature (Toilet (Male) (O27:1)), Feature (Toilet (Male) (O27:5)), Floor (O27:2), Floor (O25:6), Portal (o28:2 -> o28:1), Portal (o26:3 -> o26:2), Portal (o26:1 -> o26:3), Portal (o26:5 -> o26:4), Feature (Toilet (Male) (O26:3)), Portal (O26:4 -> o25:4), Floor (O26:5), Feature (Toilet (Female) (O26:2)), Feature (Toilet (Male) (O25:6)), Portal (o25:4 -> o25:3), Portal (o27:5 -> o27:2), Feature (Toilet (Female) (O25:3)), Feature (Toilet (Male) (O25:2)), Portal (O27:2 -> o28:2), Portal (o25:5 -> o25:3), Portal (o25:5 -> o25:6), Floor (O28:2), Feature (H22 (Lecture Hall) (O28:2)), Portal (o27:1 -> o27:2), Portal (o25:3 -> o25:2), Portal (o26:2 -> o26:3), Portal (O27:2 -> outside:2), Portal (o27:5 -> o27:4), Portal (o25:6 -> o25:5), Portal (o26:3 -> o26:4), Portal (o26:2 -> o26:4), Portal (o25:4 -> o25:3), Portal (o26:4 -> o26:1), Feature (Toilet (Male) (O26:2)), Floor (O25:4), Feature (Toilet (Female) (O25:4)), Portal (o26:4 -> o26:2), Portal (o26:1 -> o26:2), Portal (o25:1 -> o25:3), Portal (o25:5 -> o25:6), Feature (Toilet (Male) (O27:3)), Floor (O26:2), Portal (O26:2 -> o27:2), Feature (Toilet (Female) (O25:5)), Feature (Toilet (Male) (O26:1)), Feature (Toilet (Female) (O27:5)), Portal (o25:2 -> o25:3), Portal (o27:4 -> o27:3), Floor (O25:3), Portal (o28:1 -> o28:2), Portal (o26:3 -> o26:2), Portal (O25:1 -> o26:1), Portal (o25:2 -> o25:5), Portal (o27:3 -> o27:4), Feature (H4 (Lecture Hall) (O25:2)), Portal (o27:1 -> o27:4), Floor (O25:5), Portal (o25:2 -> o25:1), Portal (o27:5 -> o27:3), Portal (o25:6 -> o25:3), Feature (Toilet (Female) (O27:2)), Portal (O26:2 -> o25:2), Floor (O27:5), Portal (O28:1 -> o27:1), Portal (o25:1 -> o25:4), Feature (Toilet (Female) (O27:1)), Portal (o27:1 -> o27:5), Portal (O25:4 -> o26:4), Portal (o26:1 -> o26:5), Portal (o26:1 -> o26:4), Floor (O28:1), Feature (Toilet (Female) (O26:5)), Portal (o25:4 -> o25:1), Floor (O27:1), Portal (o25:6 -> o25:1), Portal (O26:5 -> o27:5), Portal (o26:2 -> o26:3), Portal (o27:4 -> o27:1), Portal (O28:2 -> outside:2), Portal (o26:2 -> o26:1), Portal (o26:5 -> o26:3), Portal (O26:3 -> o25:3), Floor (O26:3), Portal (o25:6 -> o25:2), Floor (O26:4), Feature (Toilet (Female) (O26:1)), Portal (o25:1 -> o25:6), Portal (o25:5 -> o25:2), Feature (Toilet (Female) (O25:6)), Portal (o26:4 -> o26:5), Portal (O25:5 -> o26:5), Portal (O27:1 -> o28:1), Portal (o27:2 -> o27:3), Floor (O25:1), Feature (Toilet (Male) (O26:5)), Portal (o25:1 -> o25:5), Portal (o26:3 -> o26:5)}, _edgesSet: {(Floor (O25:1), Portal (O25:1 -> o26:1)), (Portal (o25:5 -> o25:2), Floor (O25:5)), (Floor (O25:2), Portal (o25:2 -> o25:1)), (Floor (O25:3), Portal (o25:3 -> o25:5)), (Feature (Toilet (Male) (O26:3)), Floor (O26:3)), (Floor (O26:1), Portal (o26:1 -> o26:2)), (Floor (O25:3), Portal (O25:3 -> o26:3)), (Portal (o25:5 -> o25:3), Floor (O25:5)), (Feature (Toilet (Female) (O26:1)), Floor (O26:1)), (Portal (O25:1 -> o26:1), Floor (O26:1)), (Portal (o25:4 -> o25:5), Floor (O25:4)), (Portal (O27:5 -> o26:5), Floor (O27:5)), (Floor (O27:1), Portal (O27:1 -> o26:1)), (Floor (O27:4), Portal (o27:4 -> o27:5)), (Portal (o26:2 -> o26:3), Floor (O26:2)), (Floor (O26:2), Portal (o26:2 -> o26:3)), (Portal (o26:1 -> o26:3), Floor (O26:1)), (Floor (O27:4), Portal (o27:3 -> o27:4)), (Floor (O26:5), Feature (Toilet (Male) (O26:5))), (Portal (o27:3 -> o27:4), Floor (O27:3)), (Portal (O27:2 -> outside:2), Floor (O27:2)), (Floor (O27:1), Portal (o27:1 -> o27:5)), (Portal (o25:4 -> o25:6), Floor (O25:4)), (Floor (O25:6), Feature (Toilet (Female) (O25:6))), (Floor (O27:2), Portal (o27:2 -> o27:3)), (Portal (o26:2 -> o26:4), Floor (O26:2)), (Portal (o27:4 -> o27:2), Floor (O27:4)), (Floor (O25:4), Portal (o25:4 -> o25:5)), (Feature (Toilet (Male) (O25:6)), Floor (O25:6)), (Portal (o26:1 -> o26:4), Floor (O26:1)), (Floor (O27:2), Portal (o27:2 -> o27:1)), (Portal (o27:4 -> o27:1), Floor (O27:4)), (Portal (O25:3 -> o26:3), Floor (O25:3)), (Floor (O25:5), Portal (o25:5 -> o25:6)), (Floor (O25:5), Portal (o25:5 -> o25:3)), (Floor (O27:5), Feature (Toilet (Female) (O27:5))), (Portal (o25:2 -> o25:6), Floor (O25:2)), (Portal (o27:4 -> o27:5), Floor (O27:4)), (Portal (o27:4 -> o27:3), Floor (O27:4)), (Portal (O26:5 -> o25:5), Floor (O26:5)), (Portal (o26:1 -> o26:2), Floor (O26:1)), (Portal (o25:2 -> o25:3), Floor (O25:2)), (Portal (O28:2 -> outside:2), Floor (O28:2)), (Floor (O25:2), Portal (o25:2 -> o25:3)), (Floor (O27:5), Portal (o27:4 -> o27:5)), (Floor (O28:1), Portal (O27:1 -> o28:1)), (Floor (O27:4), Feature (Toilet (Male) (O27:4))), (Portal (o27:4 -> o27:3), Floor (O27:4)), (Floor (O25:6), Portal (o25:6 -> o25:1)), (Floor (O25:5), Portal (o25:5 -> o25:6)), (Portal (O25:4 -> o26:4), Floor (O26:4)), (Floor (O27:5), Portal (o27:5 -> o27:1)), (Floor (O26:2), Portal (o26:2 -> o26:3)), (Floor (O26:1), Portal (o26:1 -> o26:5)), (Portal (o25:2 -> o25:4), Floor (O25:2)), (Portal (o26:5 -> o26:4), Floor (O26:5)), (Portal (o28:1 -> o28:2), Floor (O28:1)), (Floor (O27:5), Portal (o27:5 -> o27:2)), (Floor (O26:2), Portal (O26:2 -> o25:2)), (Floor (O27:5), Portal (O27:5 -> o26:5)), (Feature (Toilet (Male) (O27:1)), Floor (O27:1)), (Floor (O27:5), Portal (o27:5 -> o27:4)), (Floor (O25:6), Feature (Toilet (Male) (O25:6))), (Floor (O26:2), Portal (o26:2 -> o26:5)), (Floor (O26:3), Portal (o26:3 -> o26:2)), (Portal (o26:4 -> o26:2), Floor (O26:4)), (Floor (O25:1), Portal (o25:1 -> o25:6)), (Portal (o27:1 -> o27:2), Floor (O27:1)), (Portal (o27:2 -> o27:1), Floor (O27:2)), (Floor (O27:2), Feature (H20 (Lecture Hall) (O27:2))), (Floor (O26:5), Portal (o26:5 -> o26:2)), (Floor (O27:1), Portal (o27:1 -> o27:3)), (Portal (O26:3 -> o25:3), Floor (O26:3)), (Floor (O26:3), Portal (o26:4 -> o26:3)), (Portal (o25:1 -> o25:2), Floor (O25:1)), (Portal (O26:1 -> o25:1), Floor (O26:1)), (Floor (O25:3), Portal (o25:3 -> o25:2)), (Portal (o25:3 -> o25:2), Floor (O25:3)), (Floor (O26:1), Feature (Toilet (Female) (O26:1))), (Floor (O27:3), Feature (Toilet (Male) (O27:3))), (Portal (o26:4 -> o26:3), Floor (O26:4)), (Portal (o25:4 -> o25:3), Floor (O25:4)), (Floor (O26:5), Portal (o26:5 -> o26:4)), (Floor (O28:1), Portal (O28:1 -> outside:1)), (Floor (O26:1), Portal (O25:1 -> o26:1)), (Portal (o25:4 -> o25:1), Floor (O25:4)), (Floor (O27:2), Portal (O27:2 -> outside:2)), (Portal (O27:1 -> o28:1), Floor (O27:1)), (Feature (Toilet (Female) (O26:2)), Floor (O26:2)), (Floor (O26:2), Feature (Toilet (Female) (O26:2))), (Portal (o27:2 -> o27:4), Floor (O27:2)), (Floor (O25:6), Portal (o25:3 -> o25:6)), (Floor (O26:1), Portal (o26:1 -> o26:3)), (Portal (o25:3 -> o25:4), Floor (O25:3)), (Floor (O27:2), Portal (O27:2 -> o28:2)), (Floor (O26:3), Feature (Toilet (Male) (O26:3))), (Portal (O25:4 -> o26:4), Floor (O25:4)), (Floor (O25:4), Portal (o25:5 -> o25:4)), (Floor (O26:2), Portal (o26:2 -> o26:4)), (Feature (H20 (Lecture Hall) (O27:2)), Floor (O27:2)), (Feature (Toilet (Male) (O27:2)), Floor (O27:2)), (Floor (O27:2), Feature (Toilet (Male) (O27:2))), (Feature (Toilet (Male) (O26:2)), Floor (O26:2)), (Feature (Toilet (Female) (O27:5)), Floor (O27:5)), (Feature (Toilet (Handicap) (O27:1)), Floor (O27:1)), (Portal (o27:1 -> o27:3), Floor (O27:1)), (Portal (o25:1 -> o25:6), Floor (O25:1)), (Portal (o26:4 -> o26:5), Floor (O26:4)), (Floor (O27:3), Portal (o27:1 -> o27:3)), (Floor (O28:2), Portal (o28:2 -> o28:1)), (Portal (o27:1 -> o27:2), Floor (O27:1)), (Portal (o26:5 -> o26:2), Floor (O26:5)), (Portal (O26:2 -> o25:2), Floor (O26:2)), (Portal (o25:3 -> o25:4), Floor (O25:3)), (Floor (O25:6), Portal (o25:6 -> o25:5)), (Floor (O27:2), Portal (o27:2 -> o27:3)), (Floor (O27:4), Feature (Toilet (Female) (O27:4))), (Portal (o27:3 -> o27:2), Floor (O27:3)), (Floor (O25:5), Portal (o25:5 -> o25:4)), (Floor (O25:2), Portal (O26:2 -> o25:2)), (Feature (Toilet (Female) (O25:4)), Floor (O25:4)), (Feature (Toilet (Male) (O25:4)), Floor (O25:4)), (Floor (O27:1), Portal (o27:1 -> o27:2)), (Portal (o26:3 -> o26:1), Floor (O26:3)), (Feature (Toilet (Male) (O25:2)), Floor (O25:2)), (Portal (O27:1 -> outside:1), Floor (O27:1)), (Portal (o25:2 -> o25:3), Floor (O25:2)), (Portal (o25:2 -> o25:1), Floor (O25:2)), (Floor (O27:4), Portal (o27:4 -> o27:3)), (Floor (O27:1), Portal (o27:1 -> o27:4)), (Portal (O28:2 -> outside:2), Floor (O28:2)), (Feature (Toilet (Female) (O27:3)), Floor (O27:3)), (Portal (O27:2 -> o28:2), Floor (O27:2)), (Portal (O26:3 -> o25:3), Floor (O25:3)), (Floor (O25:3), Portal (o25:3 -> o25:4)), (Floor (O26:1), Portal (O26:1 -> o27:1)), (Portal (O26:4 -> o25:4), Floor (O26:4)), (Portal (o26:1 -> o26:2), Floor (O26:1)), (Portal (o25:1 -> o25:3), Floor (O25:1)), (Portal (o27:1 -> o27:5), Floor (O27:1)), (Portal (O26:2 -> o25:2), Floor (O25:2)), (Portal (o27:5 -> o27:1), Floor (O27:5)), (Floor (O27:1), Portal (o27:1 -> o27:2)), (Floor (O25:4), Portal (o25:4 -> o25:3)), (Floor (O25:3), Portal (o25:3 -> o25:4)), (Floor (O26:3), Portal (O26:3 -> o25:3)), (Portal (o25:1 -> o25:2), Floor (O25:1)), (Floor (O25:1), Portal (o25:1 -> o25:2)), (Portal (O28:2 -> o27:2), Floor (O27:2)), (Floor (O25:2), Feature (Toilet (Female) (O25:2))), (Portal (o27:4 -> o27:5), Floor (O27:4)), (Floor (O28:2), Portal (O28:2 -> outside:2)), (Floor (O25:4), Portal (o25:4 -> o25:5)), (Feature (H22 (Lecture Hall) (O28:2)), Floor (O28:2)), (Portal (O27:1 -> o26:1), Floor (O27:1)), (Floor (O25:3), Portal (O26:3 -> o25:3)), (Floor (O26:5), Portal (O27:5 -> o26:5)), (Floor (O26:1), Portal (o26:1 -> o26:2)), (Portal (o27:5 -> o27:4), Floor (O27:5)), (Portal (o26:3 -> o26:4), Floor (O26:3)), (Floor (O26:1), Portal (o26:1 -> o26:4)), (Portal (o27:5 -> o27:4), Floor (O27:5)), (Floor (O26:5), Portal (O26:5 -> o25:5)), (Feature (Toilet (Female) (O25:5)), Floor (O25:5)), (Portal (o25:3 -> o25:5), Floor (O25:3)), (Floor (O25:4), Portal (o25:4 -> o25:6)), (Portal (O26:5 -> o25:5), Floor (O25:5)), (Feature (H1 (Lecture Hall) (O25:2)), Floor (O25:2)), (Floor (O25:4), Portal (o25:4 -> o25:2)), (Portal (o25:3 -> o25:2), Floor (O25:3)), (Portal (o25:6 -> o25:5), Floor (O25:6)), (Portal (o26:3 -> o26:4), Floor (O26:3)), (Portal (o25:1 -> o25:4), Floor (O25:1)), (Floor (O26:1), Feature (Toilet (Male) (O26:1))), (Feature (Toilet (Male) (O27:5)), Floor (O27:5)), (Floor (O25:6), Portal (o25:6 -> o25:2)), (Floor (O26:3), Portal (o26:3 -> o26:4)), (Portal (o26:1 -> o26:5), Floor (O26:1)), (Floor (O26:5), Portal (o26:5 -> o26:3)), (Portal (o25:2 -> o25:5), Floor (O25:2)), (Floor (O27:3), Portal (o27:3 -> o27:2)), (Portal (o25:4 -> o25:2), Floor (O25:4)), (Portal (o27:2 -> o27:3), Floor (O27:2)), (Floor (O25:2), Portal (o25:2 -> o25:3)), (Floor (O27:3), Portal (o27:3 -> o27:1)), (Portal (O25:1 -> o26:1), Floor (O25:1)), (Floor (O27:4), Portal (o27:4 -> o27:2)), (Floor (O25:5), Portal (o25:5 -> o25:2)), (Floor (O26:2), Feature (Toilet (Male) (O26:2))), (Floor (O25:5), Portal (O26:5 -> o25:5)), (Floor (O25:1), Portal (o25:1 -> o25:5)), (Portal (o26:5 -> o26:3), Floor (O26:5)), (Floor (O28:1), Portal (o28:1 -> o28:2)), (Floor (O26:3), Portal (o26:3 -> o26:2)), (Floor (O26:3), Portal (o26:3 -> o26:5)), (Floor (O27:2), Portal (o27:2 -> o27:5)), (Portal (o25:4 -> o25:3), Floor (O25:4)), (Portal (o27:2 -> o27:5), Floor (O27:2)), (Floor (O27:3), Portal (o27:3 -> o27:2)), (Floor (O28:2), Portal (O28:2 -> outside:2)), (Floor (O26:5), Feature (Toilet (Female) (O26:5))), (Portal (O25:5 -> o26:5), Floor (O25:5)), (Portal (o25:2 -> o25:1), Floor (O25:1)), (Floor (O25:6), Portal (o25:6 -> o25:4)), (Floor (O25:2), Feature (H2 (Lecture Hall) (O25:2))), (Floor (O25:1), Portal (o25:1 -> o25:4)), (Floor (O26:4), Portal (o26:4 -> o26:1)), (Floor (O25:4), Portal (O25:4 -> o26:4)), (Floor (O28:2), Portal (O28:2 -> o27:2)), (Portal (o25:3 -> o25:6), Floor (O25:6)), (Floor (O25:1), Portal (o25:1 -> o25:2)), (Floor (O27:1), Portal (O27:1 -> o28:1)), (Portal (O28:2 -> o27:2), Floor (O28:2)), (Floor (O27:3), Portal (o27:3 -> o27:4)), (Portal (O28:1 -> outside:1), Floor (O28:1)), (Floor (O26:4), Portal (o26:4 -> o26:3)), (Portal (o26:3 -> o26:5), Floor (O26:3)), (Floor (O28:2), Feature (H22 (Lecture Hall) (O28:2))), (Portal (o26:4 -> o26:3), Floor (O26:3)), (Floor (O25:1), Portal (o25:1 -> o25:3)), (Floor (O25:2), Portal (o25:2 -> o25:4)), (Floor (O25:6), Portal (o25:6 -> o25:5)), (Floor (O27:3), Portal (o27:3 -> o27:4)), (Portal (o28:2 -> o28:1), Floor (O28:2)), (Floor (O25:2), Feature (Toilet (Male) (O25:2))), (Portal (o27:3 -> o27:4), Floor (O27:4)), (Floor (O27:5), Portal (o27:5 -> o27:3)), (Portal (o25:5 -> o25:4), Floor (O25:4)), (Floor (O27:4), Portal (o27:4 -> o27:1)), (Floor (O26:2), Portal (o26:2 -> o26:1)), (Floor (O26:1), Portal (O26:1 -> o25:1)), (Portal (o27:4 -> o27:5), Floor (O27:5)), (Floor (O27:1), Feature (Toilet (Male) (O27:1))), (Portal (o26:3 -> o26:2), Floor (O26:3)), (Floor (O26:5), Portal (o26:5 -> o26:4)), (Floor (O25:3), Portal (o25:3 -> o25:1)), (Floor (O25:4), Portal (o25:4 -> o25:1)), (Feature (Toilet (Male) (O25:5)), Floor (O25:5)), (Portal (o25:6 -> o25:2), Floor (O25:6)), (Feature (Toilet (Female) (O25:6)), Floor (O25:6)), (Floor (O25:3), Portal (o25:3 -> o25:6)), (Floor (O25:5), Feature (Toilet (Female) (O25:5))), (Floor (O25:2), Portal (o25:2 -> o25:6)), (Floor (O25:6), Portal (o25:6 -> o25:3)), (Portal (o26:2 -> o26:3), Floor (O26:2)), (Feature (Toilet (Female) (O27:4)), Floor (O27:4)), (Floor (O27:4), Portal (o27:4 -> o27:3)), (Floor (O27:2), Portal (O28:2 -> o27:2)), (Floor (O25:2), Portal (o25:2 -> o25:1)), (Floor (O25:2), Portal (O25:2 -> o26:2)), (Portal (o25:3 -> o25:1), Floor (O25:3)), (Portal (o27:2 -> o27:1), Floor (O27:2)), (Portal (o27:2 -> o27:3), Floor (O27:2)), (Floor (O25:2), Feature (H4 (Lecture Hall) (O25:2))), (Floor (O27:1), Feature (Toilet (Female) (O27:1))), (Floor (O27:2), Feature (Toilet (Female) (O27:2))), (Portal (o25:3 -> o25:6), Floor (O25:3)), (Floor (O26:3), Portal (o26:3 -> o26:4)), (Portal (o26:2 -> o26:5), Floor (O26:2)), (Floor (O27:1), Feature (Toilet (Handicap) (O27:1))), (Portal (o27:3 -> o27:1), Floor (O27:3)), (Floor (O25:5), Portal (O25:5 -> o26:5)), (Feature (H4 (Lecture Hall) (O25:2)), Floor (O25:2)), (Portal (O27:2 -> o26:2), Floor (O26:2)), (Feature (Toilet (Male) (O27:4)), Floor (O27:4)), (Floor (O25:3), Portal (o25:3 -> o25:2)), (Floor (O25:4), Feature (Toilet (Female) (O25:4))), (Floor (O27:1), Portal (O26:1 -> o27:1)), (Floor (O25:1), Portal (o25:2 -> o25:1)), (Feature (Toilet (Female) (O26:5)), Floor (O26:5)), (Portal (o27:3 -> o27:5), Floor (O27:3)), (Floor (O25:5), Feature (Toilet (Male) (O25:5))), (Floor (O26:2), Portal (O27:2 -> o26:2)), (Floor (O26:2), Portal (o26:2 -> o26:1)), (Floor (O25:3), Feature (Toilet (Female) (O25:3))), (Portal (O26:1 -> o27:1), Floor (O27:1)), (Floor (O27:1), Portal (O27:1 -> outside:1)), (Floor (O27:2), Portal (o27:2 -> o27:4)), (Floor (O26:3), Feature (Toilet (Female) (O26:3))), (Feature (Toilet (Male) (O26:5)), Floor (O26:5)), (Portal (O25:2 -> o26:2), Floor (O25:2)), (Floor (O26:4), Portal (o26:4 -> o26:5)), (Feature (Toilet (Female) (O25:1)), Floor (O25:1)), (Floor (O26:4), Portal (o26:4 -> o26:5)), (Portal (O27:2 -> o26:2), Floor (O27:2)), (Floor (O25:5), Portal (o25:5 -> o25:1)), (Floor (O26:5), Portal (O26:5 -> o27:5)), (Floor (O27:2), Portal (O27:2 -> o26:2)), (Portal (o27:1 -> o27:3), Floor (O27:3)), (Floor (O26:5), Portal (o26:5 -> o26:1)), (Portal (O26:1 -> o27:1), Floor (O26:1)), (Floor (O27:5), Feature (Toilet (Male) (O27:5))), (Feature (Toilet (Female) (O25:2)), Floor (O25:2)), (Feature (Toilet (Female) (O25:3)), Floor (O25:3)), (Portal (o26:4 -> o26:5), Floor (O26:4)), (Portal (o25:5 -> o25:6), Floor (O25:5)), (Floor (O27:4), Portal (o27:4 -> o27:5)), (Portal (o26:4 -> o26:1), Floor (O26:4)), (Portal (o27:5 -> o27:3), Floor (O27:5)), (Portal (o25:6 -> o25:4), Floor (O25:6)), (Floor (O28:1), Portal (O28:1 -> o27:1)), (Portal (O28:1 -> o27:1), Floor (O28:1)), (Floor (O25:5), Portal (o25:5 -> o25:4)), (Feature (Toilet (Female) (O27:1)), Floor (O27:1)), (Portal (o25:6 -> o25:3), Floor (O25:6)), (Portal (o25:5 -> o25:4), Floor (O25:5)), (Feature (H2 (Lecture Hall) (O25:2)), Floor (O25:2)), (Feature (Toilet (Male) (O27:3)), Floor (O27:3)), (Portal (o26:5 -> o26:4), Floor (O26:5)), (Portal (O26:5 -> o27:5), Floor (O26:5)), (Portal (o27:3 -> o27:2), Floor (O27:3)), (Floor (O25:1), Feature (Toilet (Female) (O25:1))), (Portal (o25:6 -> o25:1), Floor (O25:6)), (Floor (O27:2), Portal (o27:2 -> o27:1)), (Floor (O27:5), Portal (o27:5 -> o27:4)), (Portal (o26:5 -> o26:1), Floor (O26:5)), (Portal (o25:5 -> o25:6), Floor (O25:5)), (Feature (Toilet (Male) (O26:1)), Floor (O26:1)), (Feature (Toilet (Female) (O26:3)), Floor (O26:3)), (Floor (O25:2), Feature (H1 (Lecture Hall) (O25:2))), (Portal (O26:2 -> o27:2), Floor (O26:2)), (Floor (O27:3), Feature (Toilet (Female) (O27:3))), (Portal (o26:4 -> o26:3), Floor (O26:4)), (Floor (O26:4), Portal (O26:4 -> o25:4)), (Floor (O26:2), Portal (O26:2 -> o27:2)), (Portal (o25:2 -> o25:1), Floor (O25:2)), (Floor (O26:4), Portal (o26:4 -> o26:2)), (Portal (o26:2 -> o26:1), Floor (O26:2)), (Portal (o26:3 -> o26:2), Floor (O26:3)), (Floor (O25:2), Portal (o25:2 -> o25:5)), (Portal (o27:5 -> o27:2), Floor (O27:5)), (Portal (o25:5 -> o25:4), Floor (O25:5)), (Floor (O26:4), Portal (o26:4 -> o26:3)), (Portal (O27:1 -> o28:1), Floor (O28:1)), (Feature (Toilet (Female) (O27:2)), Floor (O27:2)), (Portal (o25:1 -> o25:5), Floor (O25:1)), (Floor (O26:4), Portal (O25:4 -> o26:4)), (Portal (O27:5 -> o26:5), Floor (O26:5)), (Portal (o27:3 -> o27:4), Floor (O27:3)), (Portal (o25:4 -> o25:5), Floor (O25:4)), (Portal (o27:1 -> o27:4), Floor (O27:1)), (Portal (o26:2 -> o26:1), Floor (O26:2)), (Portal (o25:5 -> o25:1), Floor (O25:5)), (Floor (O27:3), Portal (o27:3 -> o27:5)), (Portal (o25:6 -> o25:5), Floor (O25:6)), (Floor (O26:3), Portal (o26:3 -> o26:1)), (Floor (O25:4), Portal (o25:4 -> o25:3)), (Floor (O25:4), Feature (Toilet (Male) (O25:4)))}) +flutter: Feature(name: H22, type: FeatureType.lectureHall(), description: Big Lecture Hall, often used for Info Lectures, geometry: GeoJSONPolygon(type: GeoJSONType.polygon, coordinates: [[[9.958066, 48.423145], [9.957847, 48.423064], [9.957945, 48.42296], [9.958, 48.422943], [9.95818, 48.42301], [9.958066, 48.423145]]]), level: 2, building: o28) + + +In this graph, I can't find H22. But it's there. Why can't I find it? \ No newline at end of file