Skip to content
This repository has been archived by the owner on Dec 11, 2024. It is now read-only.

Commit

Permalink
✨ Navigation from current position
Browse files Browse the repository at this point in the history
  • Loading branch information
gregatm committed Jan 31, 2023
1 parent 45ffc48 commit ea99d4b
Show file tree
Hide file tree
Showing 14 changed files with 193 additions and 57 deletions.
66 changes: 59 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
# indoor_navigation
# Kindona

## What is KID?

KID is a flutter-based application for helping users orientate inside of buildings. It provides the user
Kindona is a flutter-based application for helping users orientate inside of buildings. It provides the user
with a map (similar google maps and the like), with the option to differentiate between the different
floors inside the building.

## How can I use KID?
## Table of Contents
- [How can I use Kindona?](#how-can-i-use-kindona)
- [Creating the map](#creating-the-map)
- [Features](#features)
- [How to build](#how-to-build)
- [Contributing](#contributing)
- [License](#license)
- [Contact](#contact)

## How can I use Kindona?

KID provides the framework for visualising and navigating the building, but it is missing the data
Kindona provides the framework for visualising and navigating the building, but it is missing the data
of the building. This has to be provided by you. As soon as you have build the map, you can just
drop the resulting map in the app, build and run the app. No coding needed.

Expand All @@ -26,11 +33,56 @@ want to include your own custom data for the inside of the building which usuall
the OSM maps. Therefore another guide was written to cover that part and can be found
[here](docs/map-creation.md) in the repository.

## Features

* Map of the building
* Floor selection for detailed view
* Room search
* Path finding between rooms
* Showing your current location
* Path finding between your current location and a room
* Displaying room information (WIP)

## How to build

The application is based on [flutter](https://flutter.dev/) which is required to build the app.
Once flutter is properly set up you can run the app with

```
flutter run
```
```

### Target platforms

The primary development platform is android.

iOS and web compatibility is planned

## Contributing

Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are **greatly appreciated**.

If you have a suggestion that would make this better, please open an issue with the tag "enhancement", fork the repo and create a pull request. You can also simply open an issue with the tag "enhancement".

1. Open an issue with the tag "enhancement"
2. Fork the Project
3. Create your Feature Branch (`git checkout -b feature/AmazingFeature`)
4. Commit your Changes (`git commit -m 'Add some AmazingFeature'`)
5. Push to the Branch (`git push origin feature/AmazingFeature`)
6. Open a Pull Request

The Flutter Team has a great guide [here](https://docs.flutter.dev/get-started/install) how to set up everything needed.

We also would suggest looking into the Flutter Team's style guide [here](https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo)

More about this in the [CODE_OF_CONDUCT](/CODE_OF_CONDUCT.md) file.


## License

Distributed under the MIT License. See [LICENSE](LICENSE) file for more information.


## Contact

it@M - opensource@muenchen.de
5 changes: 3 additions & 2 deletions lib/l10n/app_de.arb
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
{
"loadinghint": "Lade Daten",
"apptitle": "Indoor Navigator",
"apptitle": "Kindona",
"navigationtarget" : "Ziel",
"navigationstart": "Start",
"search": "Suchen",
"publictoilet": "WC",
"publictoiletmale": "Herren-WC",
"publictoiletfemale": "Damen-WC",
"floor": "Stock"
"floor": "Stock",
"yourposition": "Deine Position"
}
10 changes: 7 additions & 3 deletions lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"apptitle": "Indoor Navigation",
"apptitle": "Kindona",
"@apptitle": {
"description": "Title of the application"
},
Expand Down Expand Up @@ -28,11 +28,15 @@
"description": "Toilet, for males"
},
"publictoiletfemale": "WC (female)",
"@publictoiletfemale": {
"description": "Toilet, for females"
},
"floor": "Floor",
"@floor": {
"description": "Floor"
},
"@publictoiletfemale": {
"description": "Toilet, for females"
"yourposition": "Your Position",
"@yourposition": {
"description": "Menu item for your current position"
}
}
2 changes: 1 addition & 1 deletion lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return
MaterialApp(
title: 'Flutter Demo',
title: 'Kindona Demo',
theme: ThemeData(colorScheme: const ColorScheme.light()),
localizationsDelegates: AppLocalizations.localizationsDelegates,
supportedLocales: AppLocalizations.supportedLocales,
Expand Down
2 changes: 1 addition & 1 deletion lib/map/overlay/map_control_overlay.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class MapControlOverlay extends StatefulWidget {
final ViewModel viewModel;
final Map<int, String?>? indoorLevels;
final void Function() onPressed;
final Pos? Function() position;
final Stream<Pos?> position;

const MapControlOverlay({super.key, required this.viewModel, this.indoorLevels, required this.onPressed, required this.position});

Expand Down
35 changes: 29 additions & 6 deletions lib/map/overlay/position_overlay.dart
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import 'dart:async';

import 'package:flutter/material.dart';

import 'package:indoor_navigation/services/ble_service.dart';

class PositionOverlay extends StatefulWidget {
late void Function() onPressed;
late Pos? Function() position;
final void Function() onPressed;
final Stream<Pos?> position;

PositionOverlay({Key? key, required this.onPressed, required this.position}) : super(key: key);
const PositionOverlay({Key? key, required this.onPressed, required this.position}) : super(key: key);

@override
State<StatefulWidget> createState() {
Expand All @@ -16,13 +18,34 @@ class PositionOverlay extends StatefulWidget {

class _PositionOverlayState extends State<PositionOverlay> {

late StreamSubscription sub;
bool positioned = false;

@override
void initState() {
super.initState();
sub = widget.position.listen((event) {
if (positioned != (event != null)) {
setState(() {
positioned = event != null;
});
}
});
}

@override
void dispose() {
super.dispose();
sub.cancel();
}

@override
Widget build(BuildContext context) {
Widget icon;
if (widget.position() == null) {
icon = Icon(Icons.location_searching_sharp, color: Theme.of(context).disabledColor);
} else {
if (positioned) {
icon = Icon(Icons.my_location_sharp, color: Theme.of(context).buttonTheme.colorScheme!.primary);
} else {
icon = Icon(Icons.location_searching_sharp, color: Theme.of(context).disabledColor);
}
return RawMaterialButton(
onPressed: widget.onPressed,
Expand Down
10 changes: 9 additions & 1 deletion lib/map/user_position_marker.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,14 @@ class UserPositionMarker extends MarkerByItemDataStore {

Pos? position;

late StreamSubscription sub;

UserPositionMarker({
required this.symbolCache,
required this.displayModel,
this.position
}) {
PositionService.observe.listen((pos) { position = pos; setRepaint(); });
sub = PositionService.observe.listen((pos) { position = pos; setRepaint(); });
}

@override
Expand All @@ -45,4 +47,10 @@ class UserPositionMarker extends MarkerByItemDataStore {
await marker.initResources(symbolCache);
return marker;
}

@override
void dispose() {
super.dispose();
sub.cancel();
}
}
4 changes: 4 additions & 0 deletions lib/navigation/navigation.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ class IndoorNode extends Vertex with Node<IndoorNode> {
}
}

class YourPosition extends IndoorNode {
YourPosition() : super("YourPosition", latLong: const LatLong(0, 0), level: 0);
}

abstract class IndoorWay extends IndoorNode {
final Way way;

Expand Down
4 changes: 3 additions & 1 deletion lib/services/ble_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@ class BleService {
late Tracker _tracker;
late LMA _lma;

final Region region = Region(identifier: 'changememaybe' /*,proximityUUID: 'consider setting this if you are using ibeacons' */);
// Set the proximityUUID to the uuid of your iBeacons to prefilter
// the discovered ble devices
final Region region = Region(identifier: 'changememaybe' /*,proximityUUID: 'considerchangingme'*/);

BleService({
this.onLocationServicesDisabled,
Expand Down
8 changes: 4 additions & 4 deletions lib/services/position_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ import 'package:rxdart/rxdart.dart';

class PositionService {

static final Subject<Pos> _inject = BehaviorSubject<Pos>();
static final BehaviorSubject<Pos> inject = BehaviorSubject<Pos>();

static Stream<Pos> get observe => _inject.stream;
static Stream<Pos> get observe => inject.stream;

final GpsService gpsService = GpsService();
final BleService bleService = BleService();
Expand Down Expand Up @@ -55,12 +55,12 @@ class PositionService {

void _newGpsPositon(Position p) {
if (!_bleActive) {
_inject.add(Pos(p.latitude, p.longitude));
inject.add(Pos(p.latitude, p.longitude));
}
}

void _newBlePosition(Pos p) {
_inject.add(p);
inject.add(p);
_resetTimer();
_bleActive = true;
}
Expand Down
4 changes: 2 additions & 2 deletions lib/services/selectedroute.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import 'package:indoor_navigation/navigation/navigation.dart';
import 'package:rxdart/rxdart.dart';

class SelectedRoute {
static final BehaviorSubject<List<Room>> routeStream = BehaviorSubject();
static final BehaviorSubject<List<IndoorNode>> routeStream = BehaviorSubject();

static Stream<List<Room>> get observe => routeStream.stream;
static Stream<List<IndoorNode>> get observe => routeStream.stream;
}
56 changes: 47 additions & 9 deletions lib/views/map_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,19 @@ class MapView extends StatefulWidget {
}
class _MapViewState extends State<MapView> {

late PositionService gps;
PositionService? gps;
Pos? _pos;
late StreamSubscription sub;
late StreamSubscription subRoute;
MarkerByItemDataStore markerStore = MarkerByItemDataStore();

@override
Widget build(BuildContext context) {
if (!kIsWeb) {
if (!kIsWeb && gps == null) {
gps = MapContext
.of(context)
.positionService;
gps.startPositioning();
gps!.startPositioning();
}

var mapFile = MapContext.of(context).map;
Expand All @@ -47,7 +47,7 @@ class _MapViewState extends State<MapView> {
viewModel: viewModel,
indoorLevels: MapContext.of(context).levels,
onPressed: _setViewModelLocationToPosition,
position: () => _pos
position: PositionService.observe
));
viewModel.addOverlay(DistanceOverlay(viewModel));

Expand All @@ -57,7 +57,7 @@ class _MapViewState extends State<MapView> {
@override
void initState() {
super.initState();
sub = PositionService.observe.listen((pos) => setState(() => _pos = pos));
sub = PositionService.observe.listen((pos) => _pos = pos);
subRoute = SelectedRoute.observe.listen(_buildWayMarker);
}

Expand All @@ -69,21 +69,59 @@ class _MapViewState extends State<MapView> {
positionListener?.cancel();
roomListener?.cancel();
if (!kIsWeb) {
gps.stopPositioning();
gps?.stopPositioning();
}
super.dispose();
}

void _buildWayMarker(List<Room> route) {
void _buildWayMarker(List<IndoorNode> route) {
if (route.length >= 2) {
var from = route[0];
var to = route[1];
if (from is YourPosition) {
from = retrieveYourPosition();
}
if (to is YourPosition) {
to = retrieveYourPosition();
}

var path = navigate(MapContext.of(context).indoorMap, from, to).toList();

if (from.id == 'outside') {
path[0] = IndoorNode("ypf", latLong: PositionService.inject.value, level: 0);
}

if (to.id == 'outside') {
path[path.length-1] = IndoorNode("ypt", latLong: PositionService.inject.value, level: 0);
}

markerStore.addMarker(
fromPath(MapContext.of(context).displayModel,
navigate(MapContext.of(context).indoorMap, route[0], route[1]).toList())
fromPath(MapContext.of(context).displayModel, path)
..item = "wayMarker"
);
}
}

IndoorNode retrieveYourPosition() {

var position = PositionService.inject.value;

// Ignores level of position, how am I supposed to know that, huh?
var maybeRoom = MapContext.of(context)
.indoorMap.graph.keys.whereType<IndoorWay>()
.where((element) => containsPoint(element.way, position));

IndoorNode room;
if (maybeRoom.isNotEmpty) {
room = maybeRoom.first;
} else {
room = MapContext.of(context)
.indoorMap.outside;
}

return room;
}

StreamSubscription? tapListener;
StreamSubscription? positionListener;
StreamSubscription? roomListener;
Expand Down
Loading

0 comments on commit ea99d4b

Please sign in to comment.