Defend the Parks is a mobile app for exploring National Parks and other locations maintained by the National Park Service while learning about Indigenous languages, territories, treaties, placenames, nearby sovereignty records, and public Native Land resources. The app includes 'Where Are We? Mode' for GPS-based land context on where you are currently located, an in-app compass, and 'Journey Mode' for travel-aware updates when a user moves into an area that returns new Native Land API information.
Left: mobile / Expo app flow. Right: desktop web flow from the Cloudflare Pages deployment.
Open Table of Contents
- About the Project
- What the App Provides
- Platform Implementation and User Experience
- How to Use Defend the Parks
- Feature: Where Are We? Mode
- Feature: Journey Mode
- Feature: In-App Compass
- How It Was Built
- Architecture Flow
- Data Sources and API Strategy
- Native Land Context Approach
- UI, Styling, and Asset Credits
- Setup, API Keys, and Local Development
- Testing, Grading, and Platform Notes
- Deployment and Early Access
- Final Project Requirement Map
- Documentation Map
- License
- Screenshots and Credits
Defend the Parks by mp3li was built as a final project for DATA 144 - Data Structures during my last quarter working toward my Associate Degree in Software Development and Business Analytics.
The app is built to feel useful first. It is not only a list of parks, and it is not only a class demo. It is a mobile field-style guide for asking better questions about place: What National Park Service locations are near or connected to a state? What does the National Park Service publish about that place? What Indigenous languages, territories, treaties, placenames, and public Native Land resources are connected to the land? What changes when I physically move somewhere else?
Under the hood, those workflows are supported by structured app state, searchable lists, saved records, API integration, local persistence, GPS-based lookup, background-task source code, and responsive mobile/web interface behavior. The project stays practical while demonstrating the final-project requirements through a real user experience.
The app intentionally avoids presenting Native Land data as fixed legal borders. Indigenous sovereignty, language, relationship to land, and public data cannot be flattened into one clean boundary. Defend the Parks presents records as educational context, centers languages when available, and links users back to public source material for further research.
- Fixed app shell: persistent app title, compass Jump To menu, history-aware Back strip, Return to Homepage action, mobile bottom tab navigation, and web header navigation.
- Homepage welcome section: app purpose, National Park Service/Native Land API explanation, and user orientation.
- National Parks Picture Gallery: alphabetized NPS image gallery, the only expandable section on web, paged web loading, full-size image modal, and visible image credits.
- Featured Park of the Day: date, selected park or NPS location name, NPS image, image credit, save button, and the same detailed content system used on profile pages.
- About Native Land Records section: explanation of what Native Land records are being used for and why the app centers languages while still including territories and treaties.
- Placename Records: placename records returned by Native Land when available.
- Overview: National Park Service description for the selected park or NPS-maintained location.
- Get Involved & Defend This Park: stewardship copy, official NPS links, volunteer links, donation links, and contact actions where available.
- Languages Connected to This Location: Native Land language records listed before territory records when available.
- Territories in This Location: Native Land territory records returned for the selected park, NPS location, or GPS coordinate.
- Treaties Connected to This Location: Native Land treaty records with careful wording and research-starting context.
- Native Land Public Resources and Map Tools: links to public Native Land map, listing, placename, risk, renewal, territory, language, and treaty tools.
- Activities: NPS activity records returned for a park or NPS location.
- Topics: NPS topic records returned for a park or NPS location.
- Weather: NPS weather guidance when provided.
- Visiting the Park & Park Website: location summary, operating hours, visitor center hours, seasonal or holiday exceptions, address, contact information, website, and directions.
- Entrance Fees: NPS entrance-fee records when returned.
- Entrance Passes: NPS pass records when returned.
- Quick Actions: official website, directions, phone, email, and related action buttons when data is available.
- Search by State: state search, state result pages, returned count, and rows for parks and other NPS-maintained locations.
- Park and NPS-location profiles: full profile pages for parks, historic sites, trails, memorials, battlefields, monuments, and other NPS designations returned by the API.
- Where Are We? Mode: foreground GPS lookup, coordinate-acquired button state, in-app compass, Location Context, placenames, languages, territories, treaties, Native Land public resources, Nearby Sovereignties, and source links for the user's current location.
- Journey Mode: travel-aware GPS flow with Begin/Stop Journey Mode action, in-app compass, How Journey Mode Works explanation, current location results, persisted last-update state, web tab-open polling, and background-task/notification source.
- Saved Parks: saved count, saved park list, open actions, remove actions, and persisted saved state.
- Broadcast/system-event coverage: AppState listener, deep-link handling, and Journey Mode context-change state for the coursework broadcast/system-event requirement.
Defend the Parks is implemented as an Expo React Native app first, with a deployed Cloudflare Pages web build added so the project can be reviewed from a browser while preserving the mobile app experience.
The Expo/iPhone workflow was used to keep the mobile app experience intact for final-project testing:
- fixed app title and Jump To compass stay available above scrollable content;
- mobile bottom navigation remains available on phone-sized screens;
- Where Are We? uses foreground location permission, coordinate lookup, Native Land API records, and heading data when the runtime provides it;
- Journey Mode source includes the native background-task and notification path through
expo-task-manager,expo-location, andexpo-notifications; - source locations for the native Journey Mode path are
learning-react-native-app/tasks/journey-mode-task.ts,learning-react-native-app/services/journey-mode.ts,learning-react-native-app/app/(tabs)/journey-mode.tsx, andlearning-react-native-app/components/journey-mode-panel.tsx; - Expo Go can test the visible Journey Mode flow and foreground coordinate behavior, but may not fully run native background location and notification behavior.
The native app-store-style Journey Mode path is implemented in source. It was not distributed as an installable iOS build for grading because my available testing devices are in the Apple ecosystem and a paid Apple Developer account is a financial barrier. That is why the project also includes the Cloudflare Pages deployment as an accessible way to demonstrate the GPS/API/Journey Mode workflow without requiring an app-store build.
The desktop web build was adjusted so it feels intentional instead of like a stretched phone screen:
- desktop web hides the bottom tab bar and uses header navigation with text and icons;
- content uses readable-width constraints so the background image remains visible;
- sections are expanded by default so desktop users do not have to open every long content block;
- the National Parks Picture Gallery remains the only expandable/paged section because rendering every image at once would be noisy and slower;
- web-specific landscape backgrounds are used for Home/Search/State/Park/Where/Saved and Journey Mode;
- NPS requests use a Cloudflare Pages Function proxy so park data loads reliably from deployed browsers;
- early access is validated through a Cloudflare Pages Function and Cloudflare secret, not a hardcoded code in the repo;
- web navigation icons render as inline SVG paths instead of browser icon-font glyphs, preventing missing-font boxes.
Mobile web keeps the mobile app feel while still running from the deployed site:
- phone-width browsers use the mobile-style upper header and bottom tab navigation;
- mobile web keeps mobile image sizing for the gallery and state-result images instead of desktop two-column sizing;
- access-code modal sizing and text colors are adjusted for mobile browsers;
- mobile web uses the same early-access gate and Cloudflare-backed NPS data route as desktop web;
- Where Are We? and Journey Mode can use browser geolocation with permission;
- Journey Mode on web checks for updated coordinates every 5 minutes only while the browser tab remains open.
Journey Mode intentionally has different runtime behavior depending on where it runs:
| Runtime | What can be tested |
|---|---|
| Expo Go on iPhone | visible Journey Mode UI, Begin/Stop flow, foreground coordinates, current Native Land result rendering, saved state, and last-update display |
| Future native app-store build | source-supported background location task, changed-context comparison, and local notification scheduling when the operating system permits it |
| Desktop web/mobile web | foreground browser geolocation, Journey Mode start/stop flow, current-location results, and 5-minute tab-open polling |
The web build is an alternative review path for the assignment requirements, not a claim that browser tabs are equivalent to native background location after an app is closed or backgrounded.
The Homepage is the main guided entry point. It introduces the purpose of the app, explains that Defend the Parks uses National Park Service data and Native Land API data, opens the National Parks Picture Gallery, and shows the Featured Park of the Day with the same meaningful detail structure used on park pages. From the featured park, users can review Indigenous language, territory, treaty, placename, source, and map-tool records; read the National Park Service overview; open stewardship links; check activities, topics, weather, visitor information, fees, directions, and official park links; and save the park locally. Every major page also has the fixed compass Jump To menu, so long screens have a page-specific table of contents instead of forcing users to scroll blindly.
Left: mobile / Expo Homepage flow. Right: desktop web Homepage flow with expanded sections and landscape layout.
Search by State is for browsing National Park Service records geographically. Users search the state list, open a state page, review how many NPS-maintained locations were returned, and tap into a full profile page when they want the complete set of park data and Native Land context.
Left: mobile / Expo Search by State flow. Right: desktop web Search by State and state results.
Where Are We? is the current-location mode. After the user gives permission, the app gets exact coordinates, changes the button state so the user can see the coordinates were acquired, starts heading data for the in-app compass when available, calls Native Land API with the coordinates, and displays returned placenames, languages, territories, treaties, source links, public resources, and Nearby Sovereignties.
Left: mobile / Expo Where Are We? flow. Right: desktop web browser geolocation and expanded Native Land results.
Journey Mode is for road trips, moving through different areas, or any situation where the user wants updated context as they travel. The mode explains the permission model, starts from a deliberate Begin Journey Mode action, changes to Stop Journey Mode while active, and uses the same Native Land result structure as Where Are We? so users do not have to learn a second interface.
Left: mobile / Expo Journey Mode flow. Right: desktop web Journey Mode with tab-open polling and current-location results.
Saved Parks keeps selected places available after the user leaves the screen. The saved list shows how many places are saved, displays each saved park name, and gives direct open and remove actions.
Left: mobile / Expo Saved Parks list. Right: desktop web Saved Parks list in the Cloudflare Pages layout.
Where Are We? is the app's foreground GPS feature. It is designed for the moment a user wants to know what Native Land API records are associated with their current coordinates.
The mode:
- requests foreground location permission only when the user chooses to get coordinates;
- displays a loading compass while location data is being requested;
- shows coordinates directly in the button after a successful lookup;
- uses heading data to power an in-app compass when the device provides it;
- calls Native Land API with latitude and longitude;
- displays returned placenames, languages, territories, treaties, public resources, nearby records, and sources;
- keeps language and treaty wording consistent with park pages and Homepage sections.
The result is not a map replacement and not a legal boundary tool. It is a respectful coordinate-based learning mode that points users toward the records and sources available for where they are.
Journey Mode is the app's travel-aware GPS feature. It is built for movement: road trips, field visits, commutes, or any situation where a user crosses into an area that returns different Native Land API records.
The source code includes:
- Journey Mode start/stop flow;
- stored baseline context;
- background task registration with
expo-task-manager; - background location source with
expo-location; - local notification source with
expo-notifications; - comparison logic for changed Native Land context;
- persisted last-update state;
- desktop/mobile web polling every 5 minutes while the browser tab is open;
- shared result rendering with Where Are We?.
In a native-capable runtime that grants the needed permissions, Journey Mode can start background location updates, compare the new Native Land context to the saved baseline, and schedule a local notification when the returned context changes. This is not remote server push notification infrastructure; it is local notification scheduling from the app through expo-notifications.
Expo Go can show and test the foreground portions of this flow, but full background location and notification behavior is platform/build dependent. Because my available testing devices are in the Apple ecosystem and a paid Apple Developer account is a financial barrier, the final submission documents the native implementation locations and also deploys the app on Cloudflare Pages as an accessible web build. The web build demonstrates the GPS/API workflow and Journey Mode result updates while the tab remains open, but it does not claim to replace native app-store background location or local notifications after the app is closed or backgrounded.
The in-app compass appears in Where Are We? Mode and Journey Mode. It is built from React Native views, not a static image: a dark circular face, orange border, faint ivory crosshairs, orange needle, and ivory tail. The same visual language is also used for the fixed header's Jump To compass icon.
The compass uses expo-location heading updates through Location.watchHeadingAsync on native runtimes and a web-only DeviceOrientationEvent fallback when a browser provides tilting/orientation data. When heading data is available, the app rotates the compass by the inverse of the reported heading so the needle points north. If compass tilting is unavailable, the app says so clearly while still allowing coordinate lookups to work.
The compass does not need NPS API or Native Land API network data to calculate heading. It can work without API responses if the app is already running and the device/browser still provides heading or tilting sensor data, but support depends on platform permissions, hardware sensors, and browser/device behavior.
| Area | Implementation |
|---|---|
| Framework | Expo React Native with Expo Router |
| Language | TypeScript |
| Navigation | File-based tabs and dynamic routes |
| Styling | Shared theme constants, themed components, custom fonts, glass-style section surfaces |
| Fonts | League Spartan Bold for the app title, Aileron Regular and Italic for app text |
| Data | National Park Service API and Native Land API |
| GPS and compass | expo-location foreground location and heading data, plus web orientation fallback when available |
| Background task source | expo-task-manager |
| Notifications | expo-notifications local notification source |
| Local persistence | AsyncStorage and app context state |
| System events | React Native AppState, deep-link handling, and Journey Mode context-change state |
| Web deployment | Cloudflare Pages static export plus Pages Functions for NPS proxying and early-access validation |
User
-> Expo Router screens
-> shared themed UI components
-> service layer
-> National Park Service API
-> Native Land API
-> normalized records
-> Home, Search, park pages, Where Are We?, Journey Mode, and Saved Parks
Where Are We?
-> user taps Get My Coordinates
-> app requests foreground location permission
-> app gets latitude, longitude, and heading when available
-> app requests Native Land coordinate data
-> app displays records, nearby sovereignties, resource links, and source links
Journey Mode
-> user taps Begin Journey Mode
-> app requests needed location/notification permissions
-> app stores the current Native Land context as a baseline
-> native source can register a background task and schedule local notifications
-> web build checks every 5 minutes while the tab remains open
-> app compares returned context and refreshes current-location results
Defend the Parks uses two primary external data sources:
| Source | Used for |
|---|---|
| National Park Service API | Park and NPS-location records, names, designations, descriptions, images, image credits, activities, topics, weather notes, operating hours, entrance fees, passes, contacts, addresses, directions, and official URLs |
| Native Land API | Coordinate-based and place-based Indigenous language, territory, treaty, source, and available placename records |
The Expo/mobile source calls the service layer directly for local coursework testing. The deployed Cloudflare Pages web app uses a same-origin Pages Function for National Park Service requests so the browser does not call NPS directly from the static bundle. Native Land coordinate lookups remain part of the client-side educational workflow.
Secrets and access control are intentionally not documented with real values. The web early-access prompt validates against a Cloudflare Pages Function and a Cloudflare environment secret, not a hardcoded code in the repo.
Native Land records can include overlapping, changing, and layered relationships to land. Defend the Parks handles that carefully:
- Records are described as returned Native Land records, not legal boundary determinations.
- Language records are shown before territories when available.
- Territories and treaties remain visible when returned.
- Placename records are their own section.
- Treaty sections identify returned agreements and link users toward further research without interpreting treaty terms.
- Native Land public resources and map tools are linked separately because they are broader tools, not always exact coordinate-returned records.
- Nearby Sovereignties are approximate and are based on sampled nearby points.
The app uses a night-forest palette:
| Token | Hex |
|---|---|
| Campfire Orange | #aa5215 |
| Cedar Brown | #66310c |
| Pine Shadow | #151e08 |
| Deep Pine | #0b1406 |
| Night Black | #04040c |
Typography:
| Font | App use | Repo location |
|---|---|---|
| League Spartan Bold | App title and README title image | learning-react-native-app/assets/fonts/LeagueSpartan-Bold.otf |
| Aileron Regular | Main body text | learning-react-native-app/assets/fonts/Aileron-Regular.otf |
| Aileron Italic | Emphasis/accent text | learning-react-native-app/assets/fonts/Aileron-Italic.otf |
Font license files should be kept beside the font files in learning-react-native-app/assets/fonts/ for final submission once they are added.
Visual assets currently used by the app:
| Asset | App use |
|---|---|
maria-orlova-3UWc-EMf0zA-unsplash.jpg |
Main app background, photo by Maria Orlova on Unsplash |
evan-wise-2wvXI4mjYJ8-unsplash.jpg |
Where Are We? background, photo by Evan Wise on Unsplash |
evan-wise-mNSSpeJsnQA-unsplash.jpg |
Journey Mode background, photo by Evan Wise on Unsplash |
denise-jans-XCJt9Z3_0Ks-unsplash.jpg |
Web background for Home, Where Are We?, Search, state pages, park pages, and Saved Parks; photo by Denise Jans on Unsplash |
kyle-loftus-IG1m3RomhPI-unsplash.jpg |
Web background for Journey Mode; photo by Kyle Loftus on Unsplash |
| National Park Service API images | Park, gallery, featured-location, and profile images with NPS-provided credits shown in the app |
The Expo project lives in learning-react-native-app, so setup commands should run from that folder:
cd learning-react-native-app
npm install
npx expo start --clearCreate learning-react-native-app/.env with the required National Park Service and Native Land API variables used by the service layer. The variable names are defined in the API service files and should not be committed with real values.
Useful checks:
npm run lint
npx tsc --noEmitTesting so far has been focused on iPhone through the Expo workflow. That verifies the app shell, navigation, styling, NPS API display, Native Land API display, foreground coordinate lookup, heading/compass behavior when available, Saved Parks, Jump To, and manual Where Are We? results.
The Journey Mode source satisfies the implementation side of the background-location/notification requirement, but Expo Go can limit native background location and notification behavior on iPhone. This limitation is tied to platform/runtime constraints and, for this project, the financial barrier of Apple Developer account access for native iOS build distribution. For grading, the implementation is documented directly in source, and the Cloudflare Pages deployment provides an alternate accessible way to test the app's GPS permission flow, external API display, Journey Mode start/stop flow, current-location result rendering, and tab-open polling behavior.
The app is deployed on Cloudflare Pages:
https://defendtheparks.mp3li.online
Deployment uses:
- GitHub-connected Cloudflare Pages deployment from
main; - Expo web export through
npm run build:web; learning-react-native-app/distas the build output;- Cloudflare Pages Functions for selected server-side behavior;
- a same-origin NPS proxy at
functions/api/nps/[[path]].js; - server-side early-access validation at
functions/api/access-code.js; - Cloudflare environment variables/secrets for required private values.
The public web app is early-access only. This README intentionally documents that an early-access code exists, but does not publish the code.
Future iterations may add a more complete backend running on a local iMac or hosted server. That backend would not be required for the current mobile app to function, but it would be the better architecture for broader API-key protection, response caching, request normalization, analytics, and public deployment polish.
Where Are We? and the foreground part of Journey Mode can work in a web preview when the browser grants geolocation permission. What is different is native background behavior: a browser tab can show current Journey Mode results while it is open, but it should not be described as equivalent to native mobile background tracking and local notifications after the app is backgrounded or closed.
The final project proof is documented in docs/FINAL_PROJECT_REQUIREMENTS.md.
High-level requirement coverage:
| TXT requirement | Implementation proof |
|---|---|
| Access the device's location | Where Are We? and Journey Mode request location permission and read GPS/network coordinates through expo-location. |
| Process location data responsibly | The app explains why location is needed, only requests it after user action, displays coordinates to the user, treats Native Land records as contextual rather than legal boundaries, and keeps Journey Mode opt-in. |
| Use a background service such as location updates, notifications, or scheduled tasks | Journey Mode includes background location task registration through expo-task-manager/expo-location, local notification scheduling through expo-notifications, and web tab-open polling for browser review. |
| Listen for at least one broadcast event, system event, or custom event | The app listens to React Native AppState, handles deep links, and stores Journey Mode context-change events. |
| Consume data from an external API | The app consumes the National Park Service API and Native Land API. |
| Display retrieved data in the interface | NPS and Native Land records are displayed on Home, Search, state pages, park pages, Where Are We?, Nearby Sovereignties, Journey Mode, and Saved Parks where applicable. |
| Include at least one animated UI element or screen transition | Where Are We? includes an animated spinning compass while getting coordinates; the app also uses fade modal transitions and heading-driven compass rotation. |
| Clearly structured layout and navigation | The app includes fixed title/header, mobile bottom tabs, desktop web header nav, Back strip, Return to Homepage, Jump To menu, and sectioned content. |
| User interaction through buttons, inputs, or gestures | Users can search states, open parks, save/remove parks, open gallery images, use Jump To, get coordinates, begin/stop Journey Mode, and refresh location results. |
| Deliver source code by GitHub link and brief README | The repo includes source code, this README, requirement proof, testing guide, technical notes, style rules, and a source-available license. |
| Document | Purpose |
|---|---|
docs/FINAL_PROJECT_REQUIREMENTS.md |
Instructor-facing requirement proof and grading checklist |
docs/TECHNICAL_NOTES.md |
Location, API, Journey Mode, notification, platform, and future deployment notes |
docs/STYLE_RULES.md |
App palette, typography, layout, copy tone, section styling, and asset rules |
docs/TESTING_GUIDE.md |
Manual test path for Expo/iPhone and grading review |
Defend the Parks by mp3li is source-available for educational review, instructor evaluation, and early-access review only. It is not open source. Viewing and running the code locally for review is allowed with your own API keys, but copying, forking, redistributing, modifying, publishing, selling, or reusing the project without prior written permission from mp3li is not allowed. See LICENSE.
The README preview GIFs are built as side-by-side comparisons from selected mobile/Expo screenshots and matching desktop web screenshots. Feature GIFs are grouped by Homepage, Search, Where Are We?, Journey Mode, Saved Parks, and Jump To flows. Static README screenshots are copied into docs/readme-assets/screenshots/ with descriptive filenames, with desktop web screenshots under docs/readme-assets/screenshots/web/.
Park images and photo credits shown inside the app come from National Park Service API records. App background images are credited above for Maria Orlova, Evan Wise, Denise Jans, and Kyle Loftus on Unsplash.












































