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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,6 @@ android
.DS_Store

# Temporary files created by Metro to check the health of the file watcher
.metro-health-check*
.metro-health-check*

pnpm-lock.yaml
56 changes: 49 additions & 7 deletions app/(tabs)/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import * as turf from "@turf/turf";
import { Stack } from "expo-router";
import { useCallback, useMemo, useRef, useState, useEffect } from "react";
import { View } from "react-native";
import MapView, { Polygon, Marker, LatLng } from "react-native-maps";
import MapView, { Polygon, Marker, LatLng, Polyline } from "react-native-maps";
import { useSafeAreaInsets } from "react-native-safe-area-context";
import Toast from "react-native-toast-message";

Expand All @@ -17,6 +17,7 @@ import {
useAvoidanceAreas,
useConstructionAreas,
useInsertAvoidanceArea,
getRoute
} from "~/utils/api-hooks";
import useMapIcons from "~/utils/useMapIcons";

Expand All @@ -27,6 +28,7 @@ import {
type LocationDetailsBottomSheetRef,
} from "~/components/LocationDetailsBottomSheet";
import { searchPlaces, getPlaceDetails } from "~/utils/googlePlaces";
import decode from "~/utils/decode_polyline";

export default function Home() {
// hooks
Expand All @@ -44,6 +46,7 @@ export default function Home() {
const [clickedPoint, setClickedPoint] = useState<LatLng | null>(null);
const [reportStep, setReportStep] = useState(0);
const [zoomLevel, setZoomLevel] = useState(15);
const [Route, setRoute] = useState<LatLng[] | null>(null);

// Minimum zoom level to show POIs (higher = more zoomed in)
const MIN_ZOOM_FOR_POIS = 16;
Expand All @@ -55,6 +58,7 @@ export default function Home() {
const { data: constructionAreas } = useConstructionAreas();
const { data: POIs } = usePOIs();
const { mutateAsync: insertAvoidanceArea } = useInsertAvoidanceArea();
// getRoute([[-97.733785,30.282635],[-97.733731,30.285145]], [[[-97.734269,30.284691],[-97.733454,30.284654],[-97.733669,30.283366],[-97.734708,30.283932],[-97.734269,30.284691]]]);

const testGooglePlaces = async () => {
console.log("Testing Google Places...");
Expand All @@ -71,6 +75,7 @@ export default function Home() {
testGooglePlaces();
}, []);


const getMapIcon = useCallback(
(poiType: any, metadata: any) => {
switch (poiType) {
Expand Down Expand Up @@ -125,6 +130,7 @@ export default function Home() {

// Handle avoidance area click
const handleAvoidanceAreaPress = (polygonId: string) => {
if (polygonId[0] == 'C') return; // construction areas
if (isReportMode) return;
avoidanceAreaBottomSheetRef.current?.present({ id: polygonId });
};
Expand All @@ -133,8 +139,6 @@ export default function Home() {
const handlePOIPress = (poi: any) => {
if (isReportMode) return;
poiBottomSheetRef.current?.present({ poi });
if (polygonId[0] == 'C') return; // construction areas
bottomSheetRef.current?.present({ id: polygonId });
};

const polygons = useMemo(
Expand Down Expand Up @@ -184,8 +188,8 @@ export default function Home() {
const markers = useMemo(
() => {
if (POIs && !isReportMode) {
console.log("Pois");
console.log(POIs);
// console.log("Pois");
// console.log(POIs);
}

const poiMarkers = !isReportMode && zoomLevel >= MIN_ZOOM_FOR_POIS
Expand All @@ -199,7 +203,7 @@ export default function Home() {
icon: getMapIcon(poi.poi_type, poi.metadata) || undefined,
};
// 📝 ADDED CONSOLE LOGGING HERE
console.log(`POI Marker for ID ${marker.id}:`, marker);
// console.log(`POI Marker for ID ${marker.id}:`, marker);
return marker;
})
: [];
Expand Down Expand Up @@ -228,6 +232,30 @@ export default function Home() {
[POIs, aaPointsReport, mapIcons, getMapIcon, isReportMode, clickedPoint, zoomLevel],
);


const getDirections = (target: any[]) => {
const UT_TOWER = [-97.73942, 30.28614];
let res = getRoute([UT_TOWER, target.slice(0, 2)],
polygons.map((poly) => poly.coordinates.map((coord: any) => [coord.longitude, coord.latitude]))
)

// console.log(decode({value: res.routes.geometry}));
res.then((result) => {
// console.log(decode(result.routes[0].geometry));
setRoute(decode(result.routes[0].geometry).map(
(coord) => ({
latitude: coord[1],
longitude: coord[0]
})
));
});
// console.log(target.slice(0, 2));
// console.log(polygons.map((poly) => poly.coordinates.map((coord: any) => [coord.longitude, coord.latitude])))

// console.log(res);
}


const handleSelectLocation = async (location: {
id: string;
name: string;
Expand Down Expand Up @@ -304,7 +332,10 @@ export default function Home() {
<AvoidanceAreaBottomSheet ref={avoidanceAreaBottomSheetRef} />

{/* POI Bottom Sheet */}
<POIBottomSheet ref={poiBottomSheetRef} allPOIs={POIs ?? []} />
<POIBottomSheet ref={poiBottomSheetRef} allPOIs={POIs ?? []} getDirections={getDirections} />

{/* Routing Mode Overlay */}
{/* </> */}

{/* Location Details Bottom Sheet */}
<LocationDetailsBottomSheet ref={locationBottomSheetRef} />
Expand Down Expand Up @@ -341,6 +372,17 @@ export default function Home() {
/>
))}

{/* Render Polylines */}
{(Route !== null) && (
<Polyline
key="RouteLine"
coordinates={Route}
strokeColor="#50df49"
fillColor="rgba(255,0,0,0.5)"
strokeWidth={4}
/>
)}

{/* Render markers */}
{markers.map((marker) => (
<Marker
Expand Down
11 changes: 7 additions & 4 deletions components/POIBottomSheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,14 +69,16 @@ const getCardinalLabelFromNeighbors = (entrance: any, neighbors: any[]): string
interface POIBottomSheetProps {
ref: ForwardedRef<BottomSheetModal>;
allPOIs: any[];
getDirections: (target: any[]) => void;
}

interface POIContentProps {
poi: any;
allPOIs: any[];
getDirections: (target: any[]) => void;
}

const POIContent = ({ poi, allPOIs }: POIContentProps) => {
const POIContent = ({ poi, allPOIs, getDirections }: POIContentProps) => {
const mapIcons = useMapIcons();
const [selectedEntrance, setSelectedEntrance] = useState<string>("");
const [hours, setHours] = useState<string>("Loading...");
Expand Down Expand Up @@ -294,7 +296,8 @@ const POIContent = ({ poi, allPOIs }: POIContentProps) => {
backgroundColor: "#BF5700", height: 41.32, paddingHorizontal: 8,
borderRadius: 9.31, alignItems: "center", flexDirection: "row",
justifyContent: "center", marginBottom: 8,
}}>
}}
onPress={() => getDirections(poi.location_geojson.coordinates)}>
<Text style={{ fontFamily: "RobotoFlex", color: "white", fontSize: 16.79, fontWeight: "500" }}>
Get Directions
</Text>
Expand All @@ -304,7 +307,7 @@ const POIContent = ({ poi, allPOIs }: POIContentProps) => {
);
};

const POIBottomSheet = ({ ref, allPOIs }: POIBottomSheetProps) => {
const POIBottomSheet = ({ ref, allPOIs, getDirections }: POIBottomSheetProps) => {
const bottomTabBarHeight = useBottomTabBarHeight();

return (
Expand All @@ -319,7 +322,7 @@ const POIBottomSheet = ({ ref, allPOIs }: POIBottomSheetProps) => {
>
{({ data }) => {
if (!data?.poi) return null;
return <POIContent poi={data.poi} allPOIs={allPOIs} />;
return <POIContent poi={data.poi} allPOIs={allPOIs} getDirections={getDirections} />;
}}
</BottomSheetModal>
);
Expand Down
49 changes: 49 additions & 0 deletions components/RouteOverlaySheet.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { BottomSheetModal } from "@gorhom/bottom-sheet";
import { useBottomTabBarHeight } from "@react-navigation/bottom-tabs";
import { ForwardedRef } from "react";

import colors from "~/types/colors";


interface RouteContentProps {
route: any;
}

const RouteContent = ({ route } : RouteContentProps) => {
return (
<></>
)
}



interface RouteOverlaySheetProps {
ref: ForwardedRef<BottomSheetModal>;
}

interface RouteData {
route: any;
}

const RouteOverlaySheet = ({ ref }: RouteOverlaySheetProps) => {
const bottomTabBarHeight = useBottomTabBarHeight();

return (
<BottomSheetModal<RouteData>
ref={ref}
bottomInset={bottomTabBarHeight}
backgroundStyle={{ borderRadius: 32 }}
enableDynamicSizing={false}
snapPoints={["50%"]}
handleIndicatorStyle={{ backgroundColor: colors.theme.majorgridline, width: 80 }}
enableContentPanningGesture={false}
>
{({ data }) => {
if (!data?.route) return null;
return <RouteContent route={data.route} />;
}}
</BottomSheetModal>
);
};

export default RouteOverlaySheet;
37 changes: 37 additions & 0 deletions utils/api-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,43 @@ class ApiClient {
return await response.text();
}


async getRoute(waypoints: any[], avoiding: any[]) {
const FEATURE_URL = "https://api.openrouteservice.org/v2/directions/wheelchair";
const TOKEN = process.env.EXPO_PUBLIC_OPENROUTE_KEY || "";

// multipoly format reference: https://en.wikipedia.org/wiki/GeoJSON

let res = await fetch(
FEATURE_URL,
{
method: "post",
headers: {
'Accept': 'application/json, application/geo+json, application/gpx+xml, img/png; charset=utf-8',
'Authorization': TOKEN,
'Content-Type': 'application/json; charset=utf-8'
},
body: JSON.stringify(
{"coordinates":waypoints,
"options":{
"avoid_polygons":{
"type":"MultiPolygon",
"coordinates":avoiding.map((poly) => [poly])
}
}
}
)

}
);
console.log(res);
if (!res.ok) throw new Error(`HTTP ${res.status} ${res.statusText}`);
const json = await res.json();
console.log(json);
return json;
}


// Get profile by ID
async getProfile(id: number) {
return this.request<Profile>(`/profiles?id=${id}`);
Expand Down
10 changes: 9 additions & 1 deletion utils/api-hooks.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// TanStack Query hooks for the Hono backend
import { useBottomTabBarHeight } from "@react-navigation/bottom-tabs";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { useMutation, useQuery, useQueryClient, UseQueryResult } from "@tanstack/react-query";
import { Polygon } from "geojson";
import { useSafeAreaInsets } from "react-native-safe-area-context";
import Toast from "react-native-toast-message";
Expand All @@ -17,6 +17,14 @@ export const queryKeys = {
profile: (id: number) => ["profile", id] as const,
};


// get route between 2+ points
export async function getRoute(waypoints: any[], avoiding: any[]) {
// TODO implement caching later
return await apiClient.getRoute(waypoints, avoiding)
}


// fetch all POIs
export function usePOIs() {
return useQuery({
Expand Down
48 changes: 48 additions & 0 deletions utils/decode_polyline.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Source - https://stackoverflow.com/q/40694161
// Posted by Neil Simpson
// Retrieved 2026-03-08, License - CC BY-SA 3.0

export default function decode( value: any ) {

var values = decode.integers( value )
var points = []

for( var i = 0; i < values.length; i += 2 ) {
points.push([
( values[ i + 1 ] += ( values[ i - 1 ] || 0 ) ) / 1e5,
( values[ i + 0 ] += ( values[ i - 2 ] || 0 ) ) / 1e5,
])
}

return points

}

decode.sign = function( value: any ) {
return value & 1 ? ~( value >>> 1 ) : ( value >>> 1 )
}

decode.integers = function( value: any ) {

var values = []
var byte = 0
var current = 0
var bits = 0

for( var i = 0; i < value.length; i++ ) {

byte = value.charCodeAt( i ) - 63
current = current | (( byte & 0x1F ) << bits )
bits = bits + 5

if( byte < 0x20 ) {
values.push( decode.sign( current ) )
current = 0
bits = 0
}

}

return values

}