feat: complete working navigation functions
This commit is contained in:
parent
3de71e2a5a
commit
ad0c8e3124
@ -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 ?? <String, dynamic>{}, feature.geometry);
|
||||
final parsed = parseFeature(feature.properties ?? <String, dynamic>{},
|
||||
feature.geometry, feature.id);
|
||||
if (parsed case Ok(:final ok)) {
|
||||
featuresList.add(ok);
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ class Feature with _$Feature {
|
||||
required GeoJSONGeometry geometry,
|
||||
int? level,
|
||||
String? building,
|
||||
required String id,
|
||||
}) = _Feature;
|
||||
|
||||
bool isPolygon() {
|
||||
|
@ -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<Feature> 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;
|
||||
|
@ -4,7 +4,7 @@ import 'package:uninav/data/geo/model.dart';
|
||||
import 'package:yaml/yaml.dart';
|
||||
|
||||
Result<Feature> parseFeature(
|
||||
Map<String, dynamic> properties, GeoJSONGeometry geometry) {
|
||||
Map<String, dynamic> 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<Feature> parseFeature(
|
||||
geometry: geometry,
|
||||
level: level,
|
||||
building: building,
|
||||
id: id,
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -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),
|
||||
);
|
||||
}
|
||||
|
||||
bool eq(String? a, String? b) => a?.toLowerCase() == b?.toLowerCase();
|
||||
@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,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class Graph {
|
||||
final List<(GraphFeature, double, GraphFeature)> _edges = [];
|
||||
final HashSet<GraphFeature> _nodes = HashSet();
|
||||
final HashSet<(GraphFeature, GraphFeature)> _edgesSet = HashSet();
|
||||
|
||||
Iterable<GraphFeature> 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<GraphFeature> wrap(Feature feature, int floor, String buildingFrom) {
|
||||
return feature.type
|
||||
@ -60,7 +159,10 @@ IList<GraphFeature> 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<GraphFeature> doorPortalGenerator(
|
||||
final portals = <GraphFeature>[];
|
||||
|
||||
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<GraphFeature> stairPortalGenerator(
|
||||
final portals = <GraphFeature>[];
|
||||
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<GraphFeature> findAdjacent(
|
||||
return adjacentFeatures;
|
||||
}
|
||||
|
||||
List<GraphFeature> createGraphList(
|
||||
GraphFeature origin, List<Feature> allFeatures,
|
||||
[Set<GraphFeature>? visited]) {
|
||||
Graph makeGraph(GraphFeature origin, List<Feature> allFeatures,
|
||||
[Graph? graph]) {
|
||||
// final usedFeatures = <GraphFeature>[origin];
|
||||
|
||||
visited ??= <GraphFeature>{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<GraphFeature, Map<GraphFeature, double>> createGraphMap(
|
||||
GraphFeature origin, List<Feature> allFeatures) {
|
||||
final graphList = createGraphList(origin, allFeatures);
|
||||
final graphMap = <GraphFeature, Map<GraphFeature, double>>{};
|
||||
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<GraphFeature, double> createGraph(
|
||||
GraphFeature origin, List<Feature> allFeatures) {
|
||||
final map = createGraphMap(origin, allFeatures);
|
||||
final graph = WeightedDirectedGraph<GraphFeature, double>(
|
||||
map,
|
||||
summation: sum,
|
||||
zero: 0.0,
|
||||
comparator: (a, b) => compareGraphFeatures(a, b),
|
||||
);
|
||||
return graph;
|
||||
}
|
||||
|
||||
Result<List<(GraphFeature, double)>> findShortestPath(
|
||||
GraphFeature origin, GraphFeature destination, List<Feature> allFeatures,
|
||||
[heuristicVariant = "zero", heuristicMultiplier = 0.2]) {
|
||||
var graph = createGraphMap(origin, allFeatures);
|
||||
Result<List<(GraphFeature, double)>> findShortestPath(GraphFeature origin,
|
||||
bool Function(GraphFeature) destinationSelector, List<Feature> 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<List<(GraphFeature, double)>> 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<List<(GraphFeature, double)>> 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);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@ -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')
|
||||
|
@ -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);
|
||||
|
@ -22,6 +22,7 @@ String formatGraphFeature(GraphFeature feature) {
|
||||
|
||||
void main() {
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('findAdjacent', () {
|
||||
late MyMapController mapController;
|
||||
late List<Feature> 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'));
|
||||
});
|
||||
*/
|
||||
*/
|
||||
});
|
||||
}
|
||||
|
5
test/scratch_1
Normal file
5
test/scratch_1
Normal file
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user