├── components
├── Listitem.jsx
└── ListItemView.jsx
├── README.md
├── navigation-bar
├── index.jsx
└── LinkingConfig.jsx
├── screens
├── DashboardPage.jsx
├── SplashLoginPage.jsx
└── MapPage.jsx
├── assets
├── cat.jpg
├── icon.png
├── logo.png
├── favicon.png
├── google.png
├── google2.png
└── splash.png
├── .expo-shared
└── assets.json
├── .gitignore
├── server
├── models.js
├── server.js
├── apiRouter.js
└── controllers
│ ├── userController.js
│ └── mapController.js
├── babel.config.js
├── app.json
├── LICENSE
├── package.json
└── App.js
/components/Listitem.jsx:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Trail-Traffic-App
--------------------------------------------------------------------------------
/components/ListItemView.jsx:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/navigation-bar/index.jsx:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/screens/DashboardPage.jsx:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/navigation-bar/LinkingConfig.jsx:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/cat.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/caseyescovedo/Trail-Traffic-App/HEAD/assets/cat.jpg
--------------------------------------------------------------------------------
/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/caseyescovedo/Trail-Traffic-App/HEAD/assets/icon.png
--------------------------------------------------------------------------------
/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/caseyescovedo/Trail-Traffic-App/HEAD/assets/logo.png
--------------------------------------------------------------------------------
/assets/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/caseyescovedo/Trail-Traffic-App/HEAD/assets/favicon.png
--------------------------------------------------------------------------------
/assets/google.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/caseyescovedo/Trail-Traffic-App/HEAD/assets/google.png
--------------------------------------------------------------------------------
/assets/google2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/caseyescovedo/Trail-Traffic-App/HEAD/assets/google2.png
--------------------------------------------------------------------------------
/assets/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/caseyescovedo/Trail-Traffic-App/HEAD/assets/splash.png
--------------------------------------------------------------------------------
/.expo-shared/assets.json:
--------------------------------------------------------------------------------
1 | {
2 | "12bb71342c6255bbf50437ec8f4441c083f47cdb74bd89160c15e4f43e52a1cb": true,
3 | "40b842e832070c58deac6aa9e08fa459302ee3f9da492c7e77d93d2fbf4a56fd": true
4 | }
5 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/**/*
2 | .expo/*
3 | npm-debug.*
4 | *.jks
5 | *.p8
6 | *.p12
7 | *.key
8 | *.mobileprovision
9 | *.orig.*
10 | web-build/
11 | .env
12 | secrets.js
13 |
14 | # macOS
15 | .DS_Store
16 |
--------------------------------------------------------------------------------
/server/models.js:
--------------------------------------------------------------------------------
1 | const secret = require('../secrets.js');
2 | const myURI = secret.postgres_uri;
3 |
4 |
5 | const URI = process.env.PG_URI || myURI;
6 |
7 | const { Pool } = require('pg');
8 |
9 | const pool = new Pool({connectionString: URI})
10 |
11 | module.exports = {
12 | query: function (text, params, callback) {
13 | return pool.query(text, params, callback);
14 | }
15 | };
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = function(api) {
2 | api.cache(true);
3 | return {
4 | presets: ['babel-preset-expo'],
5 | env: {
6 | production: {
7 | plugins: [["inline-dotenv",{
8 | path: './.env'
9 | }]]
10 | },
11 | development: {
12 | plugins: [["inline-dotenv",{
13 | path: './.env'
14 | }]]
15 | }
16 | }
17 | };
18 | };
19 |
--------------------------------------------------------------------------------
/server/server.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const PORT = 5001;
3 | const app = express();
4 | const apiRouter = require('./apiRouter');
5 |
6 | app.use('/api', apiRouter, (req, res) => {
7 | console.log(res.locals.data);
8 | res.status(200).send(res.locals.data);
9 | })
10 |
11 | app.use((err, req, res, next) => {
12 | let defaultErr = {
13 | log: 'Express error handler caught unknown middleware error',
14 | status: 400,
15 | message: { err: 'An error occurred' },
16 | };
17 |
18 | let errorObj = Object.assign(defaultErr, err);
19 | console.log(errorObj.log);
20 | return res.status(errorObj.status).send(errorObj.message);
21 | });
22 |
23 | app.listen(PORT, () => console.log(`🚀 Listening on PORT ${PORT}`));
--------------------------------------------------------------------------------
/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "expo": {
3 | "name": "trail-traffic",
4 | "slug": "trail-traffic",
5 | "version": "1.0.0",
6 | "orientation": "portrait",
7 | "icon": "./assets/icon.png",
8 | "splash": {
9 | "image": "./assets/splash.png",
10 | "resizeMode": "contain",
11 | "backgroundColor": "#ffffff"
12 | },
13 | "updates": {
14 | "fallbackToCacheTimeout": 0
15 | },
16 | "assetBundlePatterns": [
17 | "**/*"
18 | ],
19 | "ios": {
20 | "supportsTablet": true,
21 | "config": {
22 | "googleSignIn": {
23 | "reservedClientId": "com.googleusercontent.apps.745034174485-4pdp6ssn55soecvjskqjn3u96k7699g1"
24 | }
25 | }
26 | },
27 | "web": {
28 | "favicon": "./assets/favicon.png"
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/server/apiRouter.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const router = express.Router();
3 | const mapController = require('./controllers/mapController');
4 | const userController = require('./controllers/userController');
5 |
6 |
7 | router.get('/getData', mapController.getHeat, (req, res, next) => {
8 | return next();
9 | })
10 |
11 | // add user
12 | router.post('/addUser', userController.addUser, (req, res, next) => {
13 | return next();
14 | })
15 |
16 | // get faves
17 | router.get('/getFaves', userController.getFavorites, (req, res, next) => {
18 | return next();
19 | })
20 |
21 | // add faves
22 | router.post('/addFaves', userController.addFavorite, userController.getFavorites, (req, res, next) => {
23 | return next();
24 | })
25 |
26 | // delete faves
27 | router.delete('/deleteFaves', userController.deleteFavorite, userController.getFavorites, (req, res, next) => {
28 | return next();
29 | })
30 |
31 |
32 | module.exports = router;
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Trail-Traffic
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "main": "node_modules/expo/AppEntry.js",
3 | "scripts": {
4 | "start": "expo start",
5 | "android": "expo start --android",
6 | "ios": "expo start --ios",
7 | "web": "expo start --web",
8 | "eject": "expo eject",
9 | "dev": "concurrently \"expo start\" \"nodemon ./server/server.js\""
10 | },
11 | "dependencies": {
12 | "@react-native-community/google-signin": "^5.0.0",
13 | "@react-native-community/masked-view": "0.1.10",
14 | "@react-navigation/bottom-tabs": "^5.10.0",
15 | "@react-navigation/native": "^5.8.0",
16 | "@react-navigation/stack": "^5.10.0",
17 | "dotenv": "^8.2.0",
18 | "expo": "^39.0.3",
19 | "expo-font": "~8.3.0",
20 | "expo-google-app-auth": "^8.1.3",
21 | "expo-location": "~9.0.0",
22 | "expo-status-bar": "~1.0.2",
23 | "express": "^4.17.1",
24 | "node-fetch": "^2.6.1",
25 | "pg": "^8.4.2",
26 | "react": "^16.13.1",
27 | "react-dom": "16.13.1",
28 | "react-native": "https://github.com/expo/react-native/archive/sdk-39.0.4.tar.gz",
29 | "react-native-button": "^3.0.1",
30 | "react-native-elements": "^3.0.0-alpha.1",
31 | "react-native-gesture-handler": "~1.7.0",
32 | "react-native-maps": "^0.27.1",
33 | "react-native-paper": "^4.2.0",
34 | "react-native-reanimated": "~1.13.0",
35 | "react-native-safe-area-context": "3.1.4",
36 | "react-native-screens": "~2.10.1",
37 | "react-native-vector-icons": "^7.1.0",
38 | "react-native-web": "~0.13.12"
39 | },
40 | "devDependencies": {
41 | "@babel/core": "~7.9.0",
42 | "babel-plugin-inline-dotenv": "^1.6.0",
43 | "concurrently": "^5.3.0",
44 | "nodemon": "2.0.6"
45 | },
46 | "private": true
47 | }
48 |
--------------------------------------------------------------------------------
/server/controllers/userController.js:
--------------------------------------------------------------------------------
1 | const { query } = require("express");
2 | const db = require('../models.js');
3 | const userController = {};
4 |
5 | // ***** Add user ***** //
6 | userController.addUser = async (req, res, next) => {
7 | try {
8 | const queryText = 'INSERT INTO users (username) VALUES ($1) RETURNING *;';
9 | const { username } = req.body;
10 | const values = [username];
11 |
12 | await db.query(queryText, values,
13 | (err, response) => {
14 | res.locals.data = response.rows[0];
15 | })
16 | } catch(err) {
17 | return next(err);
18 | }
19 | }
20 |
21 | // ***** Get favorites, returns array of favorite names ***** //
22 | userController.getFavorites = async (req, res, next) => {
23 | try {
24 | const queryText = 'SELECT name FROM trails WHERE id = (SELECT trail_id FROM faves WHERE user_id=$1);';
25 | const { user_id } = req.body;
26 | const values = [user_id];
27 | await db.query(queryText, values,
28 | (err, response) => {
29 | res.locals.data = response.rows;
30 | })
31 | } catch(err) {
32 | return next(err);
33 | }
34 | }
35 |
36 | // ***** Add favorite ***** //
37 | userController.addFavorite = async (req, res, next) => {
38 | try {
39 | const queryText = 'INSERT INTO faves (user_id, trail_id) VALUES ($1, $2) RETURNING *;';
40 | const { user_id, trail_id } = req.body;
41 | const values = [user_id, trail_id];
42 |
43 | await db.query(queryText, values,
44 | (err, response) => {
45 | return next();
46 | }
47 | )
48 | } catch(err) {
49 | return next(err);
50 | }
51 | }
52 |
53 | // ***** Delete favorite ***** //
54 | userController.deleteFavorite = async (req, res, next) => {
55 | try{
56 | const queryText = 'DELETE FROM faves WHERE user_id = $1 AND trail_id = $2 RETURNING user_id;';
57 | const { user_id, trail_id } = req.body;
58 | const values = [user_id, trail_id];
59 |
60 | await db.query(queryText, values,
61 | (err, response) => {
62 | return next();
63 | })
64 | } catch(err) {
65 | return next(err);
66 | }
67 | }
68 |
69 | module.exports = userController;
--------------------------------------------------------------------------------
/screens/SplashLoginPage.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component, useEffect, useState } from "react";
2 | import {
3 | StyleSheet,
4 | Text,
5 | View,
6 | Button,
7 | Image,
8 | Alert,
9 | Modal,
10 | TouchableHighlight,
11 | TouchableOpacity,
12 | ScrollView,
13 | SafeAreaView,
14 | TouchableWithoutFeedback
15 | } from "react-native";
16 | import * as Google from "expo-google-app-auth";
17 | import secret from "../secrets"
18 |
19 | const IOS_CLIENT_ID = secret.google_client_id;
20 | const FB_APP_ID = secret.facebook_app_id;
21 |
22 | export function Splash({ navigation }) {
23 |
24 | signInWithGoogle = async () => {
25 | try {
26 | const result = await Google.logInAsync({
27 | iosClientId: IOS_CLIENT_ID,
28 | scopes: ["profile", "email"]
29 | });
30 |
31 | if (result.type === "success") {
32 | console.log("LoginScreen.js.js 21 | ", result.user.givenName);
33 | this.props.navigation.navigate("Profile", {
34 | username: result.user.givenName
35 | });
36 | return result.accessToken;
37 | } else {
38 | return { cancelled: true };
39 | }
40 | } catch (e) {
41 | console.log('LoginScreen.js.js 30 | Error with login', e);
42 | return { error: true };
43 | }
44 | };
45 |
46 | return (
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 | {/* */}
61 |
62 | );
63 | }
64 |
65 | const styles = StyleSheet.create({
66 | container: {
67 | flex: 1,
68 | backgroundColor: "red",
69 | },
70 | faveContainer: {
71 | flex: 1,
72 | backgroundColor: "#fff",
73 | },
74 | map: {
75 | flex: 1,
76 | },
77 | googleButton: {
78 | flexDirection: "row",
79 | alignItems: "center",
80 | backgroundColor: "#FFFFFF",
81 | borderWidth: 0.5,
82 | borderColor: "#fff",
83 | height: 40,
84 | borderRadius: 5,
85 | margin: 5,
86 | },
87 | })
--------------------------------------------------------------------------------
/server/controllers/mapController.js:
--------------------------------------------------------------------------------
1 | const fetch = require("node-fetch");
2 | const secret = require("../../secrets");
3 |
4 | const mapController = {};
5 |
6 | //assign API keys to variables
7 | const privateKey = secret.privateKey;
8 | const publicKey = secret.publicKey;
9 |
10 | //assign API forecast ids to locations
11 | const locations = {
12 | switzerFalls: secret.sw_falls_id,
13 | griffithPark: secret.griffith_park_id,
14 | elysianPark: secret.elysian_park_id,
15 | eatonCanyon: secret.eaton_canyon_id,
16 | runyonCanyon: secret.runyon_canyon_id,
17 | };
18 |
19 | //convert units from the API measurement to the frontend Heatmap measurement
20 | const conversion = {
21 | "-2": 10,
22 | "-1": 30,
23 | 0: 50,
24 | 1: 70,
25 | 2: 90,
26 | };
27 |
28 | const latlong = {
29 | switzerFalls: { latitude: 34.2638892, longitude: -118.1740902 },
30 | griffithPark: { latitude: 34.1281475, longitude: -118.3010914 },
31 | elysianPark: { latitude: 34.0820739, longitude: -118.2497133 },
32 | eatonCanyon: { latitude: 34.1783564, longitude: -118.0966051 },
33 | runyonCanyon: { latitude: 34.1193155, longitude: -118.353079 },
34 | };
35 |
36 | mapController.getHeat = (req, res, next) => {
37 | //init an empty array to hold promises that resolve to trailDataObjs
38 | const trailDataArray = [];
39 |
40 | //iterate through all locations
41 | for (let trail in locations) {
42 | //create a new promise for each location
43 | const trailDataPromise = new Promise((resolve, reject) => {
44 | //async operation: fetch request to API
45 | fetch(`https://besttime.app/api/v1/forecasts/now?api_key_public=${publicKey}&venue_id=${locations[trail]}`)
46 | .then((data) => {
47 | data.json().then((parsedData) => {
48 | // console.log('data returned from API', parsedData)
49 | const trailName = parsedData.venue_info.venue_name;
50 | const weight = conversion[parsedData.analysis.hour_analysis.intensity_nr];
51 | const trailData = {
52 | trailName,
53 | heatMap: {
54 | latitude: latlong[trail].latitude,
55 | longitude: latlong[trail].longitude,
56 | weight,
57 | }
58 | };
59 | //when promise resolves, trailDataObj is returned
60 | resolve(trailData);
61 | });
62 | })
63 | .catch((err) => {
64 | return next(err);
65 | })
66 | })
67 | //push promises into array
68 | trailDataArray.push(trailDataPromise)
69 | }
70 |
71 | //this waits for all promises to resolve to trailDataObjs
72 | Promise.all(trailDataArray)
73 | //then that array is saved into an object with all location names
74 | .then((trailDataArray) => {
75 | const heatMapStats = [];
76 | const trailNames = [];
77 | trailDataArray.forEach(dataObj => {
78 | console.log(dataObj);
79 | heatMapStats.push(dataObj.heatMap);
80 | trailNames.push(dataObj.trailName);
81 | })
82 | const mapInfo = {
83 | heatMapStats,
84 | trailNames
85 | }
86 | res.locals.data = mapInfo;
87 | })
88 | .then(() => next())
89 |
90 | };
91 |
92 | module.exports = mapController;
93 |
--------------------------------------------------------------------------------
/App.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from "react";
2 | import {
3 | StyleSheet,
4 | Text,
5 | View,
6 | Button,
7 | Image,
8 | Alert,
9 | Modal,
10 | TouchableHighlight,
11 | TouchableOpacity,
12 | ScrollView,
13 | SafeAreaView,
14 | } from "react-native";
15 | import { Card, ListItem } from "react-native-elements";
16 | import { NavigationContainer } from "@react-navigation/native";
17 | import { createBottomTabNavigator } from "@react-navigation/bottom-tabs";
18 | import Ionicons from "react-native-vector-icons/Ionicons";
19 | import Icon from "react-native-vector-icons/FontAwesome";
20 | import { MapPage } from "./screens/MapPage.jsx";
21 | import { Splash } from "./screens/SplashLoginPage.jsx";
22 |
23 | // import {
24 | // GoogleSignin,
25 | // GoogleSigninButton,
26 | // statusCodes,
27 | // } from "@react-native-community/google-signin";
28 |
29 | // GoogleSignin.configure();
30 |
31 | //========================================= FAVORITES PAGE ================================================//
32 |
33 | function Favorites({ navigation }) {
34 | const [trails, setTrails] = useState([]);
35 | const [like, setDislike] = useState({
36 | 0: true,
37 | 1: true,
38 | 2: true,
39 | 3: true,
40 | 4: true,
41 | });
42 |
43 | useEffect(() => {
44 | fetch("http://47.232.193.179:5001/api/getData")
45 | .then((res) => res.json())
46 | .then((res) => setTrails(res.trailNames))
47 | .catch((err) => console.log(err));
48 | }, []);
49 |
50 | return (
51 |
52 |
53 |
54 |
60 |
61 |
62 |
67 |
68 |
69 |
75 |
76 |
77 |
78 |
79 | Grumpy Cat
80 |
81 |
82 |
83 | Favorite Trails
84 |
85 | {trails.map((trail, i) => {
86 | return (
87 |
88 |
89 | {trail}
90 | setDislike(!like)}
95 | />
96 |
97 |
98 | );
99 | })}
100 |
101 |
102 | );
103 | }
104 |
105 | //========================================= MAIN APP COMPONENT ================================================//
106 |
107 | const Tab = createBottomTabNavigator();
108 |
109 | export default function App() {
110 | return (
111 |
112 |
113 | (
118 |
119 | ),
120 | }}
121 | />
122 | (
127 |
128 | ),
129 | }}
130 | />
131 | (
136 |
137 | ),
138 | }}
139 | />
140 |
141 |
142 | );
143 | }
144 |
145 | //========================================= STYLING ================================================//
146 |
147 | const styles = StyleSheet.create({
148 | container: {
149 | flex: 1,
150 | backgroundColor: "red",
151 | },
152 | faveContainer: {
153 | flex: 1,
154 | backgroundColor: "#fff",
155 | },
156 | map: {
157 | flex: 1,
158 | },
159 | googleButton: {
160 | flexDirection: "row",
161 | alignItems: "center",
162 | backgroundColor: "#FFFFFF",
163 | borderWidth: 0.5,
164 | borderColor: "#fff",
165 | height: 40,
166 | borderRadius: 5,
167 | margin: 5,
168 | },
169 | googleText: {
170 | color: "#616161",
171 | fontSize: 14,
172 | },
173 | titleBar: {
174 | flexDirection: "row",
175 | justifyContent: "space-between",
176 | marginTop: 24,
177 | marginHorizontal: 16,
178 | },
179 | profileImage: {
180 | width: 200,
181 | height: 200,
182 | borderRadius: 100,
183 | overflow: "hidden",
184 | },
185 | image: {
186 | flex: 1,
187 | height: undefined,
188 | width: undefined,
189 | },
190 | infoContainer: {
191 | alignSelf: "center",
192 | alignItems: "center",
193 | marginTop: 16,
194 | },
195 | text: {
196 | fontFamily: "HelveticaNeue",
197 | color: "#52575D",
198 | },
199 | faveTitle: {
200 | flexDirection: "row",
201 | alignSelf: "center",
202 | marginTop: 45,
203 | },
204 | add: {
205 | backgroundColor: "#41444B",
206 | position: "absolute",
207 | bottom: 0,
208 | right: 0,
209 | width: 55,
210 | height: 55,
211 | borderRadius: 30,
212 | alignItems: "center",
213 | justifyContent: "center",
214 | },
215 | cardContainer: {
216 | display: "flex",
217 | },
218 | heartIconRed: {
219 | color: "red",
220 | fontSize: 25,
221 | alignItems: "flex-end",
222 | },
223 | heartIconGray: {
224 | color: "#DCDCDC",
225 | fontSize: 25,
226 | alignItems: "flex-end",
227 | },
228 | trailListText: {
229 | fontSize: 20,
230 | textAlign: "center",
231 | },
232 | });
233 |
--------------------------------------------------------------------------------
/screens/MapPage.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from "react";
2 | import MapView, {
3 | PROVIDER_GOOGLE,
4 | Heatmap,
5 | Marker,
6 | Callout,
7 | } from "react-native-maps";
8 | import {
9 | StyleSheet,
10 | Text,
11 | View,
12 | Button,
13 | Switch,
14 | Modal,
15 | TouchableHighlight,
16 | TouchableOpacity,
17 | } from "react-native";
18 | import Ionicons from "react-native-vector-icons/Ionicons";
19 | // import {useTheme} from '@react-navigation/native'
20 |
21 | export function MapPage() {
22 | const [dark, setDark] = useState(false);
23 | const toggleSwitch = () => setDark((prevState) => !prevState);
24 | const [trails, setTrails] = useState([]);
25 | const [heatMapStats, setHeatMapStats] = useState([]);
26 |
27 | useEffect(() => {
28 | fetch("http://47.232.193.179:5001/api/getData")
29 | .then((res) => res.json())
30 | .then((res) => {
31 | setHeatMapStats(res.heatMapStats);
32 | setTrails(res.trailNames);
33 | })
34 |
35 | .catch((err) => console.log(err));
36 | }, []);
37 |
38 | return (
39 |
40 |
51 |
58 |
65 |
66 |
67 | {heatMapStats.map((marker, i) => (
68 |
75 |
76 |
77 |
78 |
79 | {trails[i]}
80 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 | ))}
92 |
101 |
102 |
103 | );
104 | }
105 |
106 | const styles = StyleSheet.create({
107 | container: {
108 | flex: 1,
109 | backgroundColor: "red",
110 | },
111 | map: {
112 | flex: 1,
113 | },
114 | // Callout bubble
115 | bubble: {
116 | flexDirection: "column",
117 | alignSelf: "flex-start",
118 | backgroundColor: "#fff",
119 | borderRadius: 6,
120 | borderColor: "#ccc",
121 | borderWidth: 0.5,
122 | padding: 15,
123 | width: 150,
124 | },
125 | // Arrow below the bubble
126 | arrow: {
127 | backgroundColor: "transparent",
128 | borderColor: "transparent",
129 | borderTopColor: "#fff",
130 | borderWidth: 16,
131 | alignSelf: "center",
132 | marginTop: -32,
133 | },
134 | arrowBorder: {
135 | backgroundColor: "transparent",
136 | borderColor: "transparent",
137 | borderTopColor: "#007a87",
138 | borderWidth: 16,
139 | alignSelf: "center",
140 | marginTop: -0.5,
141 | // marginBottom: -15
142 | },
143 | // Character name
144 | name: {
145 | fontSize: 16,
146 | marginBottom: 5,
147 | },
148 | // Character image
149 | image: {
150 | width: "100%",
151 | height: 80,
152 | },
153 | });
154 |
155 | const mapDarkMode = [
156 | {
157 | elementType: "geometry",
158 | stylers: [
159 | {
160 | color: "#1d2c4d",
161 | },
162 | ],
163 | },
164 | {
165 | elementType: "labels.text.fill",
166 | stylers: [
167 | {
168 | color: "#8ec3b9",
169 | },
170 | ],
171 | },
172 | {
173 | elementType: "labels.text.stroke",
174 | stylers: [
175 | {
176 | color: "#1a3646",
177 | },
178 | ],
179 | },
180 | {
181 | featureType: "administrative.country",
182 | elementType: "geometry.stroke",
183 | stylers: [
184 | {
185 | color: "#4b6878",
186 | },
187 | ],
188 | },
189 | {
190 | featureType: "administrative.land_parcel",
191 | elementType: "labels.text.fill",
192 | stylers: [
193 | {
194 | color: "#64779e",
195 | },
196 | ],
197 | },
198 | {
199 | featureType: "administrative.province",
200 | elementType: "geometry.stroke",
201 | stylers: [
202 | {
203 | color: "#4b6878",
204 | },
205 | ],
206 | },
207 | {
208 | featureType: "landscape.man_made",
209 | elementType: "geometry.stroke",
210 | stylers: [
211 | {
212 | color: "#334e87",
213 | },
214 | ],
215 | },
216 | {
217 | featureType: "landscape.natural",
218 | elementType: "geometry",
219 | stylers: [
220 | {
221 | color: "#023e58",
222 | },
223 | ],
224 | },
225 | {
226 | featureType: "poi",
227 | elementType: "geometry",
228 | stylers: [
229 | {
230 | color: "#283d6a",
231 | },
232 | ],
233 | },
234 | {
235 | featureType: "poi",
236 | elementType: "labels.text.fill",
237 | stylers: [
238 | {
239 | color: "#6f9ba5",
240 | },
241 | ],
242 | },
243 | {
244 | featureType: "poi",
245 | elementType: "labels.text.stroke",
246 | stylers: [
247 | {
248 | color: "#1d2c4d",
249 | },
250 | ],
251 | },
252 | {
253 | featureType: "poi.park",
254 | elementType: "geometry.fill",
255 | stylers: [
256 | {
257 | color: "#023e58",
258 | },
259 | ],
260 | },
261 | {
262 | featureType: "poi.park",
263 | elementType: "labels.text.fill",
264 | stylers: [
265 | {
266 | color: "#3C7680",
267 | },
268 | ],
269 | },
270 | {
271 | featureType: "road",
272 | elementType: "geometry",
273 | stylers: [
274 | {
275 | color: "#304a7d",
276 | },
277 | ],
278 | },
279 | {
280 | featureType: "road",
281 | elementType: "labels.text.fill",
282 | stylers: [
283 | {
284 | color: "#98a5be",
285 | },
286 | ],
287 | },
288 | {
289 | featureType: "road",
290 | elementType: "labels.text.stroke",
291 | stylers: [
292 | {
293 | color: "#1d2c4d",
294 | },
295 | ],
296 | },
297 | {
298 | featureType: "road.highway",
299 | elementType: "geometry",
300 | stylers: [
301 | {
302 | color: "#2c6675",
303 | },
304 | ],
305 | },
306 | {
307 | featureType: "road.highway",
308 | elementType: "geometry.stroke",
309 | stylers: [
310 | {
311 | color: "#255763",
312 | },
313 | ],
314 | },
315 | {
316 | featureType: "road.highway",
317 | elementType: "labels.text.fill",
318 | stylers: [
319 | {
320 | color: "#b0d5ce",
321 | },
322 | ],
323 | },
324 | {
325 | featureType: "road.highway",
326 | elementType: "labels.text.stroke",
327 | stylers: [
328 | {
329 | color: "#023e58",
330 | },
331 | ],
332 | },
333 | {
334 | featureType: "transit",
335 | elementType: "labels.text.fill",
336 | stylers: [
337 | {
338 | color: "#98a5be",
339 | },
340 | ],
341 | },
342 | {
343 | featureType: "transit",
344 | elementType: "labels.text.stroke",
345 | stylers: [
346 | {
347 | color: "#1d2c4d",
348 | },
349 | ],
350 | },
351 | {
352 | featureType: "transit.line",
353 | elementType: "geometry.fill",
354 | stylers: [
355 | {
356 | color: "#283d6a",
357 | },
358 | ],
359 | },
360 | {
361 | featureType: "transit.station",
362 | elementType: "geometry",
363 | stylers: [
364 | {
365 | color: "#3a4762",
366 | },
367 | ],
368 | },
369 | {
370 | featureType: "water",
371 | elementType: "geometry",
372 | stylers: [
373 | {
374 | color: "#0e1626",
375 | },
376 | ],
377 | },
378 | {
379 | featureType: "water",
380 | elementType: "labels.text.fill",
381 | stylers: [
382 | {
383 | color: "#4e6d70",
384 | },
385 | ],
386 | },
387 | ];
388 |
--------------------------------------------------------------------------------