feat: major progress
This commit is contained in:
parent
ba71707514
commit
be359980bb
30
.metadata
Normal file
30
.metadata
Normal file
@ -0,0 +1,30 @@
|
||||
# This file tracks properties of this Flutter project.
|
||||
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||
#
|
||||
# This file should be version controlled and should not be manually edited.
|
||||
|
||||
version:
|
||||
revision: "54e66469a933b60ddf175f858f82eaeb97e48c8d"
|
||||
channel: "stable"
|
||||
|
||||
project_type: app
|
||||
|
||||
# Tracks metadata for the flutter migrate command
|
||||
migration:
|
||||
platforms:
|
||||
- platform: root
|
||||
create_revision: 54e66469a933b60ddf175f858f82eaeb97e48c8d
|
||||
base_revision: 54e66469a933b60ddf175f858f82eaeb97e48c8d
|
||||
- platform: android
|
||||
create_revision: 54e66469a933b60ddf175f858f82eaeb97e48c8d
|
||||
base_revision: 54e66469a933b60ddf175f858f82eaeb97e48c8d
|
||||
|
||||
# User provided section
|
||||
|
||||
# List of Local paths (relative to this file) that should be
|
||||
# ignored by the migrate tool.
|
||||
#
|
||||
# Files that are not part of the templates will be ignored by default.
|
||||
unmanaged_files:
|
||||
- 'lib/main.dart'
|
||||
- 'ios/Runner.xcodeproj/project.pbxproj'
|
16
README.md
16
README.md
@ -1,16 +1,8 @@
|
||||
# uninav
|
||||
|
||||
A new Flutter project.
|
||||
App to navigate in Universities. Developed at Uni Ulm, but made to be adaptable for all other universities, or complex indoor navigation environments
|
||||
|
||||
## Getting Started
|
||||
## Building and such
|
||||
|
||||
This project is a starting point for a Flutter application.
|
||||
|
||||
A few resources to get you started if this is your first Flutter project:
|
||||
|
||||
- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
|
||||
- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook)
|
||||
|
||||
For help getting started with Flutter development, view the
|
||||
[online documentation](https://docs.flutter.dev/), which offers tutorials,
|
||||
samples, guidance on mobile development, and a full API reference.
|
||||
run `flutter run` for running the app on your device.
|
||||
run `dart run build_runner build` to run the code generator
|
@ -26,3 +26,7 @@ linter:
|
||||
|
||||
# Additional information about this file can be found at
|
||||
# https://dart.dev/guides/language/analysis-options
|
||||
|
||||
analyzer:
|
||||
errors:
|
||||
invalid_annotation_target: ignore
|
2070
assets/geo/uulm_beta.geojson
Normal file
2070
assets/geo/uulm_beta.geojson
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get_core/src/get_main.dart';
|
||||
import 'package:get/get_navigation/get_navigation.dart';
|
||||
import 'package:uninav/map.dart';
|
||||
|
||||
class MyDrawer extends StatelessWidget {
|
||||
@override
|
||||
@ -20,9 +23,11 @@ class MyDrawer extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(Icons.home),
|
||||
title: Text('Home'),
|
||||
leading: Icon(Icons.map),
|
||||
title: Text('Map Page'),
|
||||
onTap: () {
|
||||
Get.back(); // close drawer
|
||||
Get.toNamed('/map');
|
||||
// Handle menu item tap
|
||||
},
|
||||
),
|
||||
@ -30,6 +35,8 @@ class MyDrawer extends StatelessWidget {
|
||||
leading: Icon(Icons.settings),
|
||||
title: Text('Settings'),
|
||||
onTap: () {
|
||||
Get.back(); // close drawer
|
||||
Get.toNamed('/settings');
|
||||
// Handle menu item tap
|
||||
},
|
||||
),
|
17
lib/components/hamburger_menu.dart
Normal file
17
lib/components/hamburger_menu.dart
Normal file
@ -0,0 +1,17 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class HamburgerMenu extends StatelessWidget {
|
||||
const HamburgerMenu({
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return IconButton(
|
||||
icon: Icon(Icons.menu),
|
||||
onPressed: () {
|
||||
Scaffold.of(context).openDrawer();
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
256
lib/components/map_render_level.dart
Normal file
256
lib/components/map_render_level.dart
Normal file
@ -0,0 +1,256 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_map/flutter_map.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:latlong2/latlong.dart';
|
||||
import 'package:uninav/controllers/map_controller.dart';
|
||||
import 'package:uninav/data/geo/model.dart';
|
||||
import 'package:uninav/map.dart';
|
||||
import 'package:uninav/util/geomath.dart';
|
||||
|
||||
List<Widget> renderLevel(int level) {
|
||||
return <Widget>[
|
||||
LevelLayer(
|
||||
filter: (feature) =>
|
||||
feature.level == level && feature.type is LectureHall,
|
||||
polyConstructor: (feature) => feature
|
||||
.getPolygon(
|
||||
constructor: (pts) => Polygon(
|
||||
points: pts,
|
||||
color: Colors.orange.withOpacity(0.2),
|
||||
borderColor: Colors.orange,
|
||||
isFilled: true,
|
||||
borderStrokeWidth: 1,
|
||||
),
|
||||
)
|
||||
.unwrap(),
|
||||
markerConstructor: (feature) => Marker(
|
||||
width: 150,
|
||||
height: 60,
|
||||
point: feature.getPoint().unwrap(),
|
||||
builder: (cx) => Center(
|
||||
child: Column(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.class_,
|
||||
color: Colors.black,
|
||||
),
|
||||
Text('${feature.name}'),
|
||||
],
|
||||
),
|
||||
),
|
||||
)),
|
||||
LevelLayer(
|
||||
filter: (feature) => feature.level == level && feature.type is Room,
|
||||
polyConstructor: (feature) => feature
|
||||
.getPolygon(
|
||||
constructor: (pts) => Polygon(
|
||||
points: pts,
|
||||
color: Colors.green.withOpacity(0.2),
|
||||
borderColor: Colors.green,
|
||||
isFilled: true,
|
||||
borderStrokeWidth: 1,
|
||||
),
|
||||
)
|
||||
.unwrap(),
|
||||
),
|
||||
LevelLayer(
|
||||
filter: (feature) => feature.level == level && feature.type is Door,
|
||||
markerConstructor: (feature) {
|
||||
final point = feature.getPoint().unwrap();
|
||||
return Marker(
|
||||
width: 20,
|
||||
height: 20,
|
||||
point: point,
|
||||
builder: (ctx) => const Icon(
|
||||
Icons.door_front_door,
|
||||
color: Colors.brown,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
LevelLayer(
|
||||
filter: (feature) => feature.level == level && feature.type is Toilet,
|
||||
markerConstructor: (feature) {
|
||||
final type = (feature.type as Toilet).toilet_type;
|
||||
IconData icon;
|
||||
switch (type.toLowerCase()) {
|
||||
case 'male':
|
||||
icon = Icons.male;
|
||||
break;
|
||||
case 'female':
|
||||
icon = Icons.female;
|
||||
break;
|
||||
case 'handicap':
|
||||
icon = Icons.wheelchair_pickup;
|
||||
break;
|
||||
default:
|
||||
print("WARN: Toilet didn't have recognizable type! "
|
||||
"(Level ${feature.level}, Name ${feature.name}, "
|
||||
"Location: ${feature.getPoint().unwrap()})");
|
||||
icon = Icons.wc;
|
||||
break;
|
||||
}
|
||||
|
||||
final point = feature.getPoint().unwrap();
|
||||
return Marker(
|
||||
width: 20,
|
||||
height: 20,
|
||||
point: point,
|
||||
builder: (ctx) => Icon(
|
||||
icon,
|
||||
color: Colors.purple,
|
||||
),
|
||||
rotateAlignment: Alignment.center,
|
||||
);
|
||||
},
|
||||
),
|
||||
LevelLayer(
|
||||
filter: (feature) =>
|
||||
feature.type is Stairs &&
|
||||
(feature.type as Stairs).connects_levels.contains(level),
|
||||
markerConstructor: (feature) {
|
||||
final point = feature.getPoint().unwrap();
|
||||
return Marker(
|
||||
width: 20,
|
||||
height: 20,
|
||||
point: point,
|
||||
builder: (ctx) => Icon(
|
||||
Icons.stairs_outlined,
|
||||
color: Colors.deepPurple.shade300,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
LevelLayer(
|
||||
filter: (feature) =>
|
||||
feature.type is Lift &&
|
||||
(feature.type as Lift).connects_levels.contains(level),
|
||||
markerConstructor: (feature) {
|
||||
final point = feature.getPoint().unwrap();
|
||||
return Marker(
|
||||
width: 20,
|
||||
height: 20,
|
||||
point: point,
|
||||
builder: (ctx) => const Icon(
|
||||
Icons.elevator_outlined,
|
||||
color: Colors.deepPurple,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
class LevelLayer extends StatelessWidget {
|
||||
final bool Function(Feature)? filter;
|
||||
final Polygon Function(Feature)? polyConstructor;
|
||||
final Marker Function(LatLng, String)? polyCenterMarkerConstructor;
|
||||
final Marker Function(Feature)? markerConstructor;
|
||||
final int? level;
|
||||
|
||||
const LevelLayer({
|
||||
this.level,
|
||||
this.filter,
|
||||
this.polyConstructor,
|
||||
this.polyCenterMarkerConstructor,
|
||||
this.markerConstructor,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final myMapController = Get.find<MyMapController>();
|
||||
|
||||
return Obx(() {
|
||||
final List<Polygon> filteredPolygons = [];
|
||||
final List<Marker> polygonCenterMarkers = [];
|
||||
final List<Marker> filteredMarkers = [];
|
||||
|
||||
for (final feature in myMapController.features) {
|
||||
if (filter == null || filter!(feature)) {
|
||||
if (feature.isPolygon()) {
|
||||
if (polyConstructor != null) {
|
||||
filteredPolygons.add(polyConstructor!(feature));
|
||||
} else {
|
||||
filteredPolygons.add(feature.getPolygon().unwrap());
|
||||
}
|
||||
|
||||
// calculate polygon center
|
||||
final center =
|
||||
polygonCenterMinmax(feature.getPolygon().unwrap().points);
|
||||
if (polyCenterMarkerConstructor != null) {
|
||||
polygonCenterMarkers
|
||||
.add(polyCenterMarkerConstructor!(center, feature.name));
|
||||
} else {
|
||||
polygonCenterMarkers.add(Marker(
|
||||
width: 100,
|
||||
height: 100,
|
||||
point: center,
|
||||
builder: (cx) => Center(
|
||||
child: Text(
|
||||
feature.name,
|
||||
style: const TextStyle(
|
||||
color: Colors.black54,
|
||||
// backgroundColor: Colors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
));
|
||||
}
|
||||
} else if (feature.isPoint()) {
|
||||
if (markerConstructor != null) {
|
||||
filteredMarkers.add(markerConstructor!(feature));
|
||||
} else {
|
||||
final point = feature.getPoint().unwrap();
|
||||
filteredMarkers.add(Marker(
|
||||
width: 100,
|
||||
height: 100,
|
||||
point: point,
|
||||
builder: (cx) => Center(
|
||||
child: Text(
|
||||
feature.name,
|
||||
style: const TextStyle(
|
||||
color: Colors.black54,
|
||||
// backgroundColor: Colors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// print(filteredPolygons.length);
|
||||
// print(filteredPolygons);
|
||||
|
||||
// print(filteredPolygons[0].points[0]);
|
||||
// print(myMapController.features.length);
|
||||
|
||||
final List<Widget> widgets = [];
|
||||
if (filteredPolygons.isNotEmpty) {
|
||||
if (polyConstructor != null) {
|
||||
widgets.add(PolygonLayer(polygons: filteredPolygons));
|
||||
} else {
|
||||
widgets.add(PolygonLayer(
|
||||
polygons: filteredPolygons
|
||||
.map((poly) => Polygon(
|
||||
points: poly.points,
|
||||
borderColor: Colors.black26,
|
||||
borderStrokeWidth: 2.0,
|
||||
))
|
||||
.toList()));
|
||||
}
|
||||
widgets.add(MarkerLayer(
|
||||
markers: polygonCenterMarkers,
|
||||
rotate: true,
|
||||
));
|
||||
}
|
||||
|
||||
if (filteredMarkers.isNotEmpty) {
|
||||
widgets.add(MarkerLayer(markers: filteredMarkers, rotate: true));
|
||||
}
|
||||
|
||||
return Stack(children: widgets);
|
||||
});
|
||||
}
|
||||
}
|
67
lib/controllers/map_controller.dart
Normal file
67
lib/controllers/map_controller.dart
Normal file
@ -0,0 +1,67 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:anyhow/anyhow.dart';
|
||||
import 'package:geojson/geojson.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:uninav/data/geo/model.dart';
|
||||
import 'package:uninav/data/geo/parser.dart';
|
||||
|
||||
class MyMapController extends GetxController {
|
||||
// constructor that calls loadgeojson with a default geojson string
|
||||
final RxList<Feature> features = <Feature>[].obs;
|
||||
final currentLevel = 1.obs;
|
||||
final levels = <int>[1].obs;
|
||||
@override
|
||||
onInit() async {
|
||||
print("init");
|
||||
ever(features, refreshLevels);
|
||||
super.onInit();
|
||||
}
|
||||
|
||||
void refreshLevels(List<Feature> curFeatures) {
|
||||
print("refreshLevels");
|
||||
final newLevels = <int>[1];
|
||||
for (final feature in curFeatures) {
|
||||
if (feature.level != null && !newLevels.contains(feature.level)) {
|
||||
newLevels.add(feature.level!);
|
||||
}
|
||||
}
|
||||
newLevels.sort();
|
||||
levels.value = newLevels;
|
||||
}
|
||||
|
||||
Result<void> setLevel(int level) {
|
||||
// check that level is in features
|
||||
if (!levels.contains(level)) {
|
||||
return bail('Level $level is not in features');
|
||||
}
|
||||
|
||||
currentLevel.value = level;
|
||||
return const Ok(());
|
||||
}
|
||||
|
||||
Future<void> loadGeoJson(String geoJsonString) async {
|
||||
try {
|
||||
// print(geoJsonString);
|
||||
|
||||
final featuresList = <Feature>[];
|
||||
|
||||
final geojson = GeoJson();
|
||||
|
||||
await geojson.parse(geoJsonString);
|
||||
|
||||
for (final feature in geojson.features) {
|
||||
print(feature.properties);
|
||||
final parsed =
|
||||
parseFeature(feature.properties ?? <String, dynamic>{}, feature);
|
||||
if (parsed case Ok(:final ok)) {
|
||||
featuresList.add(ok);
|
||||
}
|
||||
}
|
||||
|
||||
features.value = featuresList;
|
||||
} catch (e) {
|
||||
print('Error parsing GeoJSON: $e');
|
||||
}
|
||||
}
|
||||
}
|
70
lib/data/geo/model.dart
Normal file
70
lib/data/geo/model.dart
Normal file
@ -0,0 +1,70 @@
|
||||
import 'package:anyhow/anyhow.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_map/flutter_map.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:geojson/geojson.dart';
|
||||
import 'package:latlong2/latlong.dart';
|
||||
|
||||
part 'model.freezed.dart';
|
||||
|
||||
@freezed
|
||||
class Feature with _$Feature {
|
||||
const Feature._();
|
||||
|
||||
const factory Feature({
|
||||
required String name,
|
||||
required FeatureType type,
|
||||
String? description,
|
||||
required dynamic geometry,
|
||||
int? level,
|
||||
}) = _Feature;
|
||||
|
||||
bool isPolygon() {
|
||||
return geometry is GeoJsonFeature<GeoJsonPolygon>;
|
||||
}
|
||||
|
||||
bool isPoint() {
|
||||
return geometry is GeoJsonFeature<GeoJsonPoint>;
|
||||
}
|
||||
|
||||
Result<Polygon> getPolygon({Polygon Function(List<LatLng>)? constructor}) {
|
||||
if (isPolygon()) {
|
||||
constructor ??= (pts) => Polygon(
|
||||
points: pts, borderColor: Colors.black26, borderStrokeWidth: 2.0);
|
||||
final polygon = geometry as GeoJsonFeature<GeoJsonPolygon>;
|
||||
// print(polygon.geometry!.geoSeries[0].geoPoints);
|
||||
final points = polygon.geometry!.geoSeries[0].geoPoints
|
||||
.map((e) => LatLng(e.latitude, e.longitude))
|
||||
.toList();
|
||||
|
||||
// print(points);
|
||||
return Ok(constructor(points));
|
||||
} else {
|
||||
return bail("Feature Geometry is not a Polygon");
|
||||
}
|
||||
}
|
||||
|
||||
Result<LatLng> getPoint() {
|
||||
if (isPoint()) {
|
||||
final point = geometry as GeoJsonFeature<GeoJsonPoint>;
|
||||
return Ok(LatLng(point.geometry!.geoPoint.latitude,
|
||||
point.geometry!.geoPoint.longitude));
|
||||
} else {
|
||||
return bail("Feature Geometry is not a Point");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@freezed
|
||||
class FeatureType with _$FeatureType {
|
||||
// multiple feature types like lecture hall, toliet, ...
|
||||
const factory FeatureType.building() = Building;
|
||||
const factory FeatureType.lectureHall() = LectureHall;
|
||||
const factory FeatureType.room() = Room;
|
||||
const factory FeatureType.door(List<String> connects) = Door;
|
||||
const factory FeatureType.toilet(String toilet_type) = Toilet;
|
||||
const factory FeatureType.stairs(List<int> connects_levels) = Stairs;
|
||||
const factory FeatureType.lift(List<int> connects_levels) = Lift;
|
||||
const factory FeatureType.publicTransport(
|
||||
List<String> bus_lines, List<String> tram_lines) = PublicTransport;
|
||||
}
|
1864
lib/data/geo/model.freezed.dart
Normal file
1864
lib/data/geo/model.freezed.dart
Normal file
File diff suppressed because it is too large
Load Diff
142
lib/data/geo/parser.dart
Normal file
142
lib/data/geo/parser.dart
Normal file
@ -0,0 +1,142 @@
|
||||
import 'package:anyhow/anyhow.dart';
|
||||
import 'package:geojson/geojson.dart';
|
||||
import 'package:uninav/data/geo/model.dart';
|
||||
import 'package:yaml/yaml.dart';
|
||||
|
||||
Result<Feature> parseFeature(
|
||||
Map<String, dynamic> properties, dynamic geometry) {
|
||||
final name = properties['name'] as String?;
|
||||
final description_yaml = properties['description'] as String? ?? '';
|
||||
final layer = properties['layer'] as String?;
|
||||
|
||||
int? level;
|
||||
|
||||
// check if layer key contains a digit using \d+ regex
|
||||
|
||||
final layerNum = RegExp(r'\d+').firstMatch(layer ?? '');
|
||||
if (layerNum != null) {
|
||||
level = int.parse(layerNum.group(0)!);
|
||||
}
|
||||
|
||||
// try parse yaml
|
||||
if (description_yaml == null) {
|
||||
print("warn: Description key is missing for feature $name");
|
||||
}
|
||||
|
||||
if (layer == null) {
|
||||
return bail("Layer key \'layer\' is missing for feature $name");
|
||||
}
|
||||
dynamic yaml;
|
||||
try {
|
||||
yaml = loadYaml(description_yaml);
|
||||
} on YamlException catch (e) {
|
||||
return bail("Couldn't parse YAML in description for feature $name: $e");
|
||||
}
|
||||
|
||||
yaml = yaml as YamlMap? ?? {};
|
||||
final description = yaml['desription'] as String?;
|
||||
|
||||
print("yaml: $yaml");
|
||||
|
||||
var raw_type = yaml['type'] as String?;
|
||||
if (raw_type == null && layer?.toLowerCase() == 'buildings') {
|
||||
raw_type = 'building';
|
||||
}
|
||||
|
||||
if (raw_type == null) {
|
||||
return bail("Type key \'type\' is missing for feature $name");
|
||||
}
|
||||
|
||||
FeatureType type;
|
||||
|
||||
try {
|
||||
switch (raw_type) {
|
||||
case 'building':
|
||||
type = FeatureType.building();
|
||||
case 'lift':
|
||||
final list = getYamlList<int>(yaml, 'connects_levels')
|
||||
.expect("Couldn't parse 'connects_levels' for feature $name");
|
||||
type = FeatureType.lift(list);
|
||||
break;
|
||||
case 'stairs':
|
||||
final list = getYamlList<int>(yaml, 'connects_levels')
|
||||
.expect("Couldn't parse 'connects_levels' for feature $name");
|
||||
type = FeatureType.stairs(list);
|
||||
break;
|
||||
case 'lecture_hall':
|
||||
type = FeatureType.lectureHall();
|
||||
break;
|
||||
case 'room':
|
||||
type = FeatureType.room();
|
||||
break;
|
||||
case 'door':
|
||||
final list = getYamlList<String>(yaml, 'connects')
|
||||
.expect("Couldn't parse 'connects' for feature $name");
|
||||
type = FeatureType.door(list);
|
||||
break;
|
||||
case 'toilet':
|
||||
final toiletType = getYamlKey<String>(yaml, 'toilet_type')
|
||||
.expect("Couldn't parse 'toilet_type' for feature $name");
|
||||
type = FeatureType.toilet(toiletType);
|
||||
break;
|
||||
case 'public_transport':
|
||||
final busLines = getYamlList<dynamic>(yaml, 'bus_lines')
|
||||
.expect("Couldn't parse 'bus_lines' for feature $name");
|
||||
final tramLines = getYamlList<dynamic>(yaml, 'tram_lines')
|
||||
.expect("Couldn't parse 'tram_lines' for feature $name");
|
||||
|
||||
type = FeatureType.publicTransport(
|
||||
stringifyList(busLines)
|
||||
.expect('Couldn\'t stringify \'bus_lines\' for feature $name'),
|
||||
stringifyList(tramLines)
|
||||
.expect('Couldn\'t stringify \'tram_lines\' for feature $name'),
|
||||
);
|
||||
break;
|
||||
default:
|
||||
return bail("Unknown feature type $raw_type for feature $name");
|
||||
}
|
||||
} catch (e) {
|
||||
return bail("Failed to parse feature type for feature $name: $e");
|
||||
}
|
||||
|
||||
return Ok(Feature(
|
||||
name: name ?? 'Unknown',
|
||||
type: type,
|
||||
description: description,
|
||||
geometry: geometry,
|
||||
level: level,
|
||||
));
|
||||
}
|
||||
|
||||
Result<List<String>> stringifyList(List<dynamic> tramLines) {
|
||||
try {
|
||||
return Ok(tramLines.map((e) => e.toString()).toList());
|
||||
} catch (e) {
|
||||
return bail("Couldn't stringify list: $e");
|
||||
}
|
||||
}
|
||||
|
||||
Result<List<T>> getYamlList<T>(YamlMap yaml, String key) {
|
||||
try {
|
||||
print('yaml is ${yaml[key]}');
|
||||
final val = (yaml[key] as YamlList?);
|
||||
if (val == null) {
|
||||
return bail("Key $key is missing in yaml");
|
||||
}
|
||||
return Ok(val.cast<T>());
|
||||
} catch (e) {
|
||||
return bail("Failed to parse yaml key $key as ${T.toString()}: $e");
|
||||
}
|
||||
}
|
||||
|
||||
Result<T> getYamlKey<T>(YamlMap yaml, String key) {
|
||||
try {
|
||||
final val = yaml[key] as T?;
|
||||
if (val == null) {
|
||||
return bail("Key $key is missing in yaml");
|
||||
}
|
||||
return Ok(val);
|
||||
} catch (e) {
|
||||
return bail("Failed to parse yaml key $key as ${T.toString()}: $e");
|
||||
}
|
||||
}
|
@ -1,8 +1,11 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get_navigation/src/root/get_material_app.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:uninav/controllers/map_controller.dart';
|
||||
import 'package:uninav/map.dart';
|
||||
import 'package:uninav/settings.dart';
|
||||
|
||||
void main() {
|
||||
Get.put(MyMapController());
|
||||
runApp(const MyApp());
|
||||
}
|
||||
|
||||
@ -33,7 +36,11 @@ class MyApp extends StatelessWidget {
|
||||
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
|
||||
useMaterial3: true,
|
||||
),
|
||||
home: MapPage(),
|
||||
initialRoute: '/map',
|
||||
getPages: [
|
||||
GetPage(name: '/map', page: () => const MapPage()),
|
||||
GetPage(name: '/settings', page: () => const SettingsPage()),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
387
lib/map.dart
387
lib/map.dart
@ -1,9 +1,16 @@
|
||||
import 'package:anim_search_bar/anim_search_bar.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_map/flutter_map.dart';
|
||||
import 'package:flutter_map/plugin_api.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:latlong2/latlong.dart';
|
||||
import 'package:uninav/components/drawer..dart';
|
||||
import 'package:rust_core/slice.dart';
|
||||
import 'package:uninav/components/drawer.dart';
|
||||
import 'package:uninav/components/hamburger_menu.dart';
|
||||
import 'package:uninav/controllers/map_controller.dart';
|
||||
import 'package:uninav/data/geo/model.dart';
|
||||
import 'package:uninav/util/geomath.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
class MapPage extends StatelessWidget {
|
||||
@ -12,18 +19,10 @@ class MapPage extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
drawer: MyDrawer(),
|
||||
appBar: AppBar(
|
||||
title: const Text('Map'),
|
||||
leading: Builder(
|
||||
builder: (context) {
|
||||
return IconButton(
|
||||
icon: Icon(Icons.menu),
|
||||
onPressed: () {
|
||||
Scaffold.of(context).openDrawer();
|
||||
},
|
||||
);
|
||||
}
|
||||
),
|
||||
leading: HamburgerMenu(),
|
||||
|
||||
// right side expanding search bar widget
|
||||
actions: [
|
||||
@ -39,29 +38,365 @@ class MapPage extends StatelessWidget {
|
||||
},
|
||||
),
|
||||
]),
|
||||
drawer: MyDrawer(),
|
||||
body: FlutterMap(
|
||||
floatingActionButton: FloatingActionButton(
|
||||
onPressed: () async {
|
||||
// Add onPressed logic here
|
||||
await Get.find<MyMapController>().loadGeoJson(
|
||||
await rootBundle.loadString('assets/geo/uulm_beta.geojson'));
|
||||
},
|
||||
child: const Icon(Icons.add),
|
||||
),
|
||||
body: Stack(
|
||||
children: [
|
||||
FlutterMap(
|
||||
mapController: MapController(),
|
||||
options: MapOptions(
|
||||
center: LatLng(51.5, -0.09),
|
||||
zoom: 13.0,
|
||||
center: LatLng(48.422766, 9.9564),
|
||||
zoom: 16.0,
|
||||
// camera constraints
|
||||
maxZoom: 19,
|
||||
),
|
||||
children: [
|
||||
TileLayer(
|
||||
urlTemplate: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
|
||||
subdomains: const ['a', 'b', 'c'],
|
||||
urlTemplate: "https://tile.openstreetmap.org/{z}/{x}/{y}.png",
|
||||
maxZoom: 19,
|
||||
),
|
||||
|
||||
// buildings
|
||||
LevelLayer(
|
||||
filter: (feature) => feature.type is Building,
|
||||
),
|
||||
|
||||
// public transport
|
||||
LevelLayer(
|
||||
filter: (feature) =>
|
||||
feature.level == null && feature.type is PublicTransport,
|
||||
polyCenterMarkerConstructor: (center, name) => Marker(
|
||||
width: 100,
|
||||
height: 100,
|
||||
point: center,
|
||||
builder: (cx) => const Center(
|
||||
child: Icon(
|
||||
Icons.train,
|
||||
color: Colors.black,
|
||||
),
|
||||
),
|
||||
),
|
||||
polyConstructor: (feature) => feature
|
||||
.getPolygon(
|
||||
constructor: (pts) => Polygon(
|
||||
points: pts,
|
||||
color: Colors.green.withOpacity(0.2),
|
||||
borderColor: Colors.green,
|
||||
isFilled: true,
|
||||
borderStrokeWidth: 1,
|
||||
))
|
||||
.unwrap(),
|
||||
),
|
||||
|
||||
// current level
|
||||
Obx(
|
||||
() => Stack(
|
||||
children: renderLevel(
|
||||
Get.find<MyMapController>().currentLevel.value),
|
||||
),
|
||||
RichAttributionWidget(attributions: [
|
||||
TextSourceAttribution(
|
||||
'OpenStreetMap contributors',
|
||||
onTap: () =>
|
||||
launchUrl(Uri.parse('https://openstreetmap.org/copyright')),
|
||||
)
|
||||
])
|
||||
// PolygonLayer(polygons: myGeoJson.polygons),
|
||||
// PolylineLayer(polylines: myGeoJson.polylines),
|
||||
// MarkerLayer(markers: myGeoJson.markers)
|
||||
|
||||
// RichAttributionWidget(attributions: [
|
||||
// TextSourceAttribution(
|
||||
// 'OpenStreetMap contributors',
|
||||
// onTap: () =>
|
||||
// launchUrl(Uri.parse('https://openstreetmap.org/copyright')),
|
||||
// )
|
||||
// ]),
|
||||
],
|
||||
),
|
||||
Positioned(
|
||||
left: 16,
|
||||
bottom: 16,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(
|
||||
color: Colors.grey,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||
child: Obx(
|
||||
() => DropdownButton<int>(
|
||||
value: Get.find<MyMapController>().currentLevel.value,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.onSurface),
|
||||
dropdownColor: Theme.of(context).colorScheme.surface,
|
||||
onChanged: (int? newValue) {
|
||||
if (newValue != null) {
|
||||
Get.find<MyMapController>().setLevel(newValue);
|
||||
}
|
||||
// Handle dropdown value change
|
||||
},
|
||||
items: Get.find<MyMapController>()
|
||||
.levels
|
||||
.map<DropdownMenuItem<int>>((int value) {
|
||||
return DropdownMenuItem<int>(
|
||||
value: value,
|
||||
child: Text("Level $value"),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
),
|
||||
)),
|
||||
],
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
List<Widget> renderLevel(int level) {
|
||||
return <Widget>[
|
||||
LevelLayer(
|
||||
filter: (feature) =>
|
||||
feature.level == level && feature.type is LectureHall,
|
||||
polyConstructor: (feature) => feature
|
||||
.getPolygon(
|
||||
constructor: (pts) => Polygon(
|
||||
points: pts,
|
||||
color: Colors.orange.withOpacity(0.2),
|
||||
borderColor: Colors.orange,
|
||||
isFilled: true,
|
||||
borderStrokeWidth: 1,
|
||||
),
|
||||
)
|
||||
.unwrap(),
|
||||
markerConstructor: (feature) => Marker(
|
||||
width: 150,
|
||||
height: 60,
|
||||
point: feature.getPoint().unwrap(),
|
||||
builder: (cx) => Center(
|
||||
child: Column(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.class_,
|
||||
color: Colors.black,
|
||||
),
|
||||
Text('${feature.name}'),
|
||||
],
|
||||
),
|
||||
),
|
||||
)),
|
||||
LevelLayer(
|
||||
filter: (feature) => feature.level == level && feature.type is Room,
|
||||
polyConstructor: (feature) => feature
|
||||
.getPolygon(
|
||||
constructor: (pts) => Polygon(
|
||||
points: pts,
|
||||
color: Colors.green.withOpacity(0.2),
|
||||
borderColor: Colors.green,
|
||||
isFilled: true,
|
||||
borderStrokeWidth: 1,
|
||||
),
|
||||
)
|
||||
.unwrap(),
|
||||
),
|
||||
LevelLayer(
|
||||
filter: (feature) => feature.level == level && feature.type is Door,
|
||||
markerConstructor: (feature) {
|
||||
final point = feature.getPoint().unwrap();
|
||||
return Marker(
|
||||
width: 20,
|
||||
height: 20,
|
||||
point: point,
|
||||
builder: (ctx) => const Icon(
|
||||
Icons.door_front_door,
|
||||
color: Colors.brown,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
LevelLayer(
|
||||
filter: (feature) => feature.level == level && feature.type is Toilet,
|
||||
markerConstructor: (feature) {
|
||||
final type = (feature.type as Toilet).toilet_type;
|
||||
IconData icon;
|
||||
switch (type.toLowerCase()) {
|
||||
case 'male':
|
||||
icon = Icons.male;
|
||||
break;
|
||||
case 'female':
|
||||
icon = Icons.female;
|
||||
break;
|
||||
case 'handicap':
|
||||
icon = Icons.wheelchair_pickup;
|
||||
break;
|
||||
default:
|
||||
print("WARN: Toilet didn't have recognizable type! "
|
||||
"(Level ${feature.level}, Name ${feature.name}, "
|
||||
"Location: ${feature.getPoint().unwrap()})");
|
||||
icon = Icons.wc;
|
||||
break;
|
||||
}
|
||||
|
||||
final point = feature.getPoint().unwrap();
|
||||
return Marker(
|
||||
width: 20,
|
||||
height: 20,
|
||||
point: point,
|
||||
builder: (ctx) => Icon(
|
||||
icon,
|
||||
color: Colors.purple,
|
||||
),
|
||||
rotateAlignment: Alignment.center,
|
||||
);
|
||||
},
|
||||
),
|
||||
LevelLayer(
|
||||
filter: (feature) =>
|
||||
feature.type is Stairs &&
|
||||
(feature.type as Stairs).connects_levels.contains(level),
|
||||
markerConstructor: (feature) {
|
||||
final point = feature.getPoint().unwrap();
|
||||
return Marker(
|
||||
width: 20,
|
||||
height: 20,
|
||||
point: point,
|
||||
builder: (ctx) => Icon(
|
||||
Icons.stairs_outlined,
|
||||
color: Colors.deepPurple.shade300,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
LevelLayer(
|
||||
filter: (feature) =>
|
||||
feature.type is Lift &&
|
||||
(feature.type as Lift).connects_levels.contains(level),
|
||||
markerConstructor: (feature) {
|
||||
final point = feature.getPoint().unwrap();
|
||||
return Marker(
|
||||
width: 20,
|
||||
height: 20,
|
||||
point: point,
|
||||
builder: (ctx) => const Icon(
|
||||
Icons.elevator_outlined,
|
||||
color: Colors.deepPurple,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
class LevelLayer extends StatelessWidget {
|
||||
final bool Function(Feature)? filter;
|
||||
final Polygon Function(Feature)? polyConstructor;
|
||||
final Marker Function(LatLng, String)? polyCenterMarkerConstructor;
|
||||
final Marker Function(Feature)? markerConstructor;
|
||||
final int? level;
|
||||
|
||||
const LevelLayer({
|
||||
this.level,
|
||||
this.filter,
|
||||
this.polyConstructor,
|
||||
this.polyCenterMarkerConstructor,
|
||||
this.markerConstructor,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final myMapController = Get.find<MyMapController>();
|
||||
|
||||
return Obx(() {
|
||||
final List<Polygon> filteredPolygons = [];
|
||||
final List<Marker> polygonCenterMarkers = [];
|
||||
final List<Marker> filteredMarkers = [];
|
||||
|
||||
for (final feature in myMapController.features) {
|
||||
if (filter == null || filter!(feature)) {
|
||||
if (feature.isPolygon()) {
|
||||
if (polyConstructor != null) {
|
||||
filteredPolygons.add(polyConstructor!(feature));
|
||||
} else {
|
||||
filteredPolygons.add(feature.getPolygon().unwrap());
|
||||
}
|
||||
|
||||
// calculate polygon center
|
||||
final center =
|
||||
polygonCenterMinmax(feature.getPolygon().unwrap().points);
|
||||
if (polyCenterMarkerConstructor != null) {
|
||||
polygonCenterMarkers
|
||||
.add(polyCenterMarkerConstructor!(center, feature.name));
|
||||
} else {
|
||||
polygonCenterMarkers.add(Marker(
|
||||
width: 100,
|
||||
height: 100,
|
||||
point: center,
|
||||
builder: (cx) => Center(
|
||||
child: Text(
|
||||
feature.name,
|
||||
style: const TextStyle(
|
||||
color: Colors.black54,
|
||||
// backgroundColor: Colors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
));
|
||||
}
|
||||
} else if (feature.isPoint()) {
|
||||
if (markerConstructor != null) {
|
||||
filteredMarkers.add(markerConstructor!(feature));
|
||||
} else {
|
||||
final point = feature.getPoint().unwrap();
|
||||
filteredMarkers.add(Marker(
|
||||
width: 100,
|
||||
height: 100,
|
||||
point: point,
|
||||
builder: (cx) => Center(
|
||||
child: Text(
|
||||
feature.name,
|
||||
style: const TextStyle(
|
||||
color: Colors.black54,
|
||||
// backgroundColor: Colors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// print(filteredPolygons.length);
|
||||
// print(filteredPolygons);
|
||||
|
||||
// print(filteredPolygons[0].points[0]);
|
||||
// print(myMapController.features.length);
|
||||
|
||||
final List<Widget> widgets = [];
|
||||
if (filteredPolygons.isNotEmpty) {
|
||||
if (polyConstructor != null) {
|
||||
widgets.add(PolygonLayer(polygons: filteredPolygons));
|
||||
} else {
|
||||
widgets.add(PolygonLayer(
|
||||
polygons: filteredPolygons
|
||||
.map((poly) => Polygon(
|
||||
points: poly.points,
|
||||
borderColor: Colors.black26,
|
||||
borderStrokeWidth: 2.0,
|
||||
))
|
||||
.toList()));
|
||||
}
|
||||
widgets.add(MarkerLayer(
|
||||
markers: polygonCenterMarkers,
|
||||
rotate: true,
|
||||
));
|
||||
}
|
||||
|
||||
if (filteredMarkers.isNotEmpty) {
|
||||
widgets.add(MarkerLayer(markers: filteredMarkers, rotate: true));
|
||||
}
|
||||
|
||||
return Stack(children: widgets);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
21
lib/settings copy.dart
Normal file
21
lib/settings copy.dart
Normal file
@ -0,0 +1,21 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:uninav/components/drawer.dart';
|
||||
import 'package:uninav/components/hamburger_menu.dart';
|
||||
|
||||
class SettingsPage extends StatelessWidget {
|
||||
const SettingsPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Settings'),
|
||||
leading: HamburgerMenu(),
|
||||
),
|
||||
drawer: MyDrawer(),
|
||||
body: const Center(
|
||||
child: Text('TODO'),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
21
lib/settings.dart
Normal file
21
lib/settings.dart
Normal file
@ -0,0 +1,21 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:uninav/components/drawer.dart';
|
||||
import 'package:uninav/components/hamburger_menu.dart';
|
||||
|
||||
class SettingsPage extends StatelessWidget {
|
||||
const SettingsPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Settings'),
|
||||
leading: HamburgerMenu(),
|
||||
),
|
||||
drawer: MyDrawer(),
|
||||
body: const Center(
|
||||
child: Text('TODO'),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
37
lib/util/geomath.dart
Normal file
37
lib/util/geomath.dart
Normal file
@ -0,0 +1,37 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:latlong2/latlong.dart';
|
||||
|
||||
LatLng polygonCenterMinmax(List<LatLng> polygon) {
|
||||
double minLat = double.infinity;
|
||||
double maxLat = double.negativeInfinity;
|
||||
double minLng = double.infinity;
|
||||
double maxLng = double.negativeInfinity;
|
||||
|
||||
for (LatLng point in polygon) {
|
||||
minLat = min(minLat, point.latitude);
|
||||
maxLat = max(maxLat, point.latitude);
|
||||
minLng = min(minLng, point.longitude);
|
||||
maxLng = max(maxLng, point.longitude);
|
||||
}
|
||||
|
||||
double centerLat = (minLat + maxLat) / 2;
|
||||
double centerLng = (minLng + maxLng) / 2;
|
||||
|
||||
return LatLng(centerLat, centerLng);
|
||||
}
|
||||
|
||||
LatLng polygonCenterAvg(List<LatLng> polygon) {
|
||||
double sumLat = 0;
|
||||
double sumLng = 0;
|
||||
|
||||
for (LatLng point in polygon) {
|
||||
sumLat += point.latitude;
|
||||
sumLng += point.longitude;
|
||||
}
|
||||
|
||||
double centerLat = sumLat / polygon.length;
|
||||
double centerLng = sumLng / polygon.length;
|
||||
|
||||
return LatLng(centerLat, centerLng);
|
||||
}
|
328
pubspec.lock
328
pubspec.lock
@ -1,6 +1,22 @@
|
||||
# Generated by pub
|
||||
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||
packages:
|
||||
_fe_analyzer_shared:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: _fe_analyzer_shared
|
||||
sha256: "0b2f2bd91ba804e53a61d757b986f89f1f9eaed5b11e4b2f5a2468d86d6c9fc7"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "67.0.0"
|
||||
analyzer:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: analyzer
|
||||
sha256: "37577842a27e4338429a1cbc32679d508836510b056f1eedf0c8d20e39c1383d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.4.1"
|
||||
anim_search_bar:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -9,6 +25,22 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.3"
|
||||
anyhow:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: anyhow
|
||||
sha256: "8fcd8a16ab1dadcae0d77b880c4cda85367876ef9da6ca7fd68ce6ce51679bbe"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
args:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: args
|
||||
sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.5.0"
|
||||
async:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -25,6 +57,70 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
build:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build
|
||||
sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.1"
|
||||
build_config:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build_config
|
||||
sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
build_daemon:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build_daemon
|
||||
sha256: "0343061a33da9c5810b2d6cee51945127d8f4c060b7fbdd9d54917f0a3feaaa1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.1"
|
||||
build_resolvers:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build_resolvers
|
||||
sha256: "339086358431fa15d7eca8b6a36e5d783728cf025e559b834f4609a1fcfb7b0a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.2"
|
||||
build_runner:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: build_runner
|
||||
sha256: "3ac61a79bfb6f6cc11f693591063a7f19a7af628dc52f141743edac5c16e8c22"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.9"
|
||||
build_runner_core:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build_runner_core
|
||||
sha256: "4ae8ffe5ac758da294ecf1802f2aff01558d8b1b00616aa7538ea9a8a5d50799"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.3.0"
|
||||
built_collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: built_collection
|
||||
sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.1.1"
|
||||
built_value:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: built_value
|
||||
sha256: c7913a9737ee4007efedaffc968c049fd0f3d0e49109e778edc10de9426005cb
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "8.9.2"
|
||||
characters:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -33,6 +129,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
checked_yaml:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: checked_yaml
|
||||
sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.3"
|
||||
clock:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -41,6 +145,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
code_builder:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: code_builder
|
||||
sha256: f692079e25e7869c14132d39f223f8eec9830eb76131925143b2129c4bb01b37
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.10.0"
|
||||
collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -49,6 +161,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.18.0"
|
||||
convert:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: convert
|
||||
sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.1"
|
||||
crypto:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -65,6 +185,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.8"
|
||||
dart_style:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: dart_style
|
||||
sha256: "99e066ce75c89d6b29903d788a7bb9369cf754f7b24bf70bf4b6d6d6b26853b9"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.6"
|
||||
extra_pedantic:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -81,6 +209,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.1"
|
||||
file:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: file
|
||||
sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.0.0"
|
||||
fixnum:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -120,6 +256,30 @@ packages:
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
freezed:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: freezed
|
||||
sha256: a434911f643466d78462625df76fd9eb13e57348ff43fe1f77bbe909522c67a1
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.5.2"
|
||||
freezed_annotation:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: freezed_annotation
|
||||
sha256: c3fd9336eb55a38cc1bbd79ab17573113a8deccd0ecbbf926cca3c62803b5c2d
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.1"
|
||||
frontend_server_client:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: frontend_server_client
|
||||
sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.0"
|
||||
geodesy:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -152,6 +312,22 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.6.6"
|
||||
glob:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: glob
|
||||
sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
graphs:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: graphs
|
||||
sha256: aedc5a15e78fc65a6e23bcd927f24c64dd995062bcd1ca6eda65a3cff92a4d19
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.1"
|
||||
http:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -160,6 +336,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.13.6"
|
||||
http_multi_server:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http_multi_server
|
||||
sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.2.1"
|
||||
http_parser:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -176,6 +360,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.19.0"
|
||||
io:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: io
|
||||
sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.4"
|
||||
iso:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -184,6 +376,30 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
js:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: js
|
||||
sha256: c1b2e9b5ea78c45e1a0788d29606ba27dc5f71f019f32ca5140f61ef071838cf
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.1"
|
||||
json_annotation:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: json_annotation
|
||||
sha256: b10a7b2ff83d83c777edba3c6a0f97045ddadd56c944e1a23a3fdf43a1bf4467
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.8.1"
|
||||
json_serializable:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: json_serializable
|
||||
sha256: aa1f5a8912615733e0fdc7a02af03308933c93235bdc8d50d0b0c8a8ccb0b969
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.7.1"
|
||||
latlong2:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -232,6 +448,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.1"
|
||||
logging:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: logging
|
||||
sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.0"
|
||||
matcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -264,6 +488,22 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
mime:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: mime
|
||||
sha256: "2e123074287cc9fd6c09de8336dae606d1ddb88d9ac47358826db698c176a1f2"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.5"
|
||||
package_config:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: package_config
|
||||
sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
path:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -296,6 +536,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.1"
|
||||
pool:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pool
|
||||
sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.5.1"
|
||||
proj4dart:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -304,6 +552,46 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
pub_semver:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pub_semver
|
||||
sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
pubspec_parse:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pubspec_parse
|
||||
sha256: c63b2876e58e194e4b0828fcb080ad0e06d051cb607a6be51a9e084f47cb9367
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.3"
|
||||
rust_core:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: rust_core
|
||||
sha256: "3b61f6e32dc8af47dc8fa27b662c5ace9db713fd4dd5e9fd77400e6577d51fb8"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.5.3"
|
||||
shelf:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shelf
|
||||
sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.1"
|
||||
shelf_web_socket:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shelf_web_socket
|
||||
sha256: "9ca081be41c60190ebcb4766b2486a7d50261db7bd0f5d9615f2d653637a84c1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.4"
|
||||
sky_engine:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
@ -317,6 +605,22 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
source_gen:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_gen
|
||||
sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.5.0"
|
||||
source_helper:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_helper
|
||||
sha256: "6adebc0006c37dd63fe05bca0a929b99f06402fc95aa35bf36d67f5c06de01fd"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.4"
|
||||
source_span:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -349,6 +653,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
stream_transform:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stream_transform
|
||||
sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
string_scanner:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -381,6 +693,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.6.1"
|
||||
timing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: timing
|
||||
sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.1"
|
||||
tuple:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -493,6 +813,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "13.0.0"
|
||||
watcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: watcher
|
||||
sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
web:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
15
pubspec.yaml
15
pubspec.yaml
@ -39,10 +39,14 @@ dependencies:
|
||||
yaml: ^3.1.2
|
||||
surrealdb: ^0.8.0
|
||||
geojson: ^1.0.0
|
||||
flutter_map: ^4.0.0
|
||||
latlong2: ^0.8.2
|
||||
flutter_map: 7.0.0-dev.1
|
||||
# latlong2: ^0.9.0
|
||||
url_launcher: ^6.2.6
|
||||
anim_search_bar: ^2.0.3
|
||||
freezed_annotation: ^2.4.1
|
||||
json_annotation: ^4.8.1
|
||||
rust_core: ^0.5.3
|
||||
anyhow: ^1.3.0
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
@ -54,6 +58,9 @@ dev_dependencies:
|
||||
# package. See that file for information about deactivating specific lint
|
||||
# rules and activating additional ones.
|
||||
flutter_lints: ^3.0.0
|
||||
build_runner: ^2.4.9
|
||||
freezed: ^2.5.2
|
||||
json_serializable: ^6.7.1
|
||||
|
||||
# For information on the generic Dart part of this file, see the
|
||||
# following page: https://dart.dev/tools/pub/pubspec
|
||||
@ -67,7 +74,9 @@ flutter:
|
||||
uses-material-design: true
|
||||
|
||||
# To add assets to your application, add an assets section, like this:
|
||||
# assets:
|
||||
assets:
|
||||
# - assets/geo/
|
||||
- assets/geo/uulm_beta.geojson
|
||||
# - images/a_dot_burr.jpeg
|
||||
# - images/a_dot_ham.jpeg
|
||||
|
||||
|
1
test/geojson_tests.dart
Normal file
1
test/geojson_tests.dart
Normal file
@ -0,0 +1 @@
|
||||
|
Loading…
Reference in New Issue
Block a user