Skip to content

Commit 90b9d1f

Browse files
author
newtom28
committed
Rename as mini-moving-map
1 parent 0865d85 commit 90b9d1f

File tree

11 files changed

+157
-77
lines changed

11 files changed

+157
-77
lines changed

.github/workflows/deploy.yaml

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
name: Deploy to GitHub Pages
2+
3+
on:
4+
push:
5+
branches: [main]
6+
workflow_dispatch:
7+
8+
permissions:
9+
contents: read
10+
pages: write
11+
id-token: write
12+
13+
concurrency:
14+
group: 'pages'
15+
cancel-in-progress: true
16+
17+
jobs:
18+
build:
19+
runs-on: ubuntu-latest
20+
steps:
21+
- name: Checkout
22+
uses: actions/checkout@v4
23+
24+
- name: Setup Node
25+
uses: actions/setup-node@v4
26+
with:
27+
node-version: 20
28+
cache: 'npm'
29+
30+
- name: Install deps
31+
run: npm ci
32+
33+
# --- Set VITE_BASE automatically for repo pages ---
34+
# For repo pages, base should be "/<repo-name>/"
35+
# For user/org pages (username.github.io), base should be "/"
36+
- name: Compute VITE_BASE
37+
id: base
38+
run: |
39+
REPO_NAME="${GITHUB_REPOSITORY#*/}"
40+
if [[ "$REPO_NAME" == *".github.io" ]]; then
41+
echo "VITE_BASE=/"
42+
else
43+
echo "VITE_BASE=/${REPO_NAME}/"
44+
fi >> $GITHUB_OUTPUT
45+
46+
- name: Build
47+
env:
48+
# You can override this in repo → Settings → Secrets and variables → Actions → Variables
49+
# by defining VITE_BASE; if not set, we use the computed default above.
50+
VITE_BASE: ${{ vars.VITE_BASE || steps.base.outputs.VITE_BASE }}
51+
run: |
52+
echo "Using VITE_BASE=$VITE_BASE"
53+
npm run build
54+
55+
- name: Upload artifact
56+
uses: actions/upload-pages-artifact@v3
57+
with:
58+
path: ./dist
59+
60+
deploy:
61+
needs: build
62+
runs-on: ubuntu-latest
63+
environment:
64+
name: github-pages
65+
url: ${{ steps.deployment.outputs.page_url }}
66+
steps:
67+
- name: Configure Pages
68+
uses: actions/configure-pages@v5
69+
70+
- name: Deploy
71+
id: deployment
72+
uses: actions/deploy-pages@v4

README.md

Lines changed: 52 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,33 @@
1-
# Map Explorer
1+
# Mini Moving Map
22

33
A minimalist Google Maps application with AI-powered landmark discovery and 3D exploration experience.
44

55
## Features
66

7+
- **Moving Map**: MSFS (Microsoft Flight Simulator) integration for aircraft marker tracking effect
78
- **Landmark Discovery**: Google Places nearby search + OpenAI landmark query + Wikipedia images
89
- **Location Search**: Google Places text search + OpenAI location query + User Geolocation
9-
- **Photorealistic 3D Maps**: Google Earth style 3D navigation + Cinematic flyover for each landmarks
10-
- **World ready**: LLM content and auto-update UI translation follows browser language setting
11-
- **Moving Map**: MSFS (Microsoft Flight Simulator) integration for aircraft marker tracking effect
10+
- **Photorealistic 3D Maps**: Google Earth-style 3D navigation + Cinematic flyovers for each landmarks
11+
- **World ready**: Automatically adapts LLM content and UI translation to browser's language setting
1212

1313
### Technical Highlights
1414

1515
- **Frontend:** Map integration, landmark visualization, location navigation, browser-side caching and test runner
1616
- **Backend:** Flask API connecting to MSFS via SimConnect proxy to fetch real-time aircraft telemetry
17-
- **LLM:** OpenAI selects landmarks from Google Places search results, generates descriptions, look up Wiki images, and adapts language based on location's country.
18-
- **Caching:** Proximity-based keys with coordinate rounding + location matching + search radius + TTL expiration
19-
- **Multi-Sources:** Google Places API (nearby search) + Wikipedia API (images) + OpenAI API (landmark info)
20-
- **User interaction:** location geocoding → multi-tier cache lookup → landmark discovery (Google Places + OpenAI) → multi-pass searches
21-
- **Auto Translation:** Support JSON resource, string changes detection, local TM, secondary locale for multi-lingual users
17+
- **LLM:** OpenAI selects landmarks from Google results, generates descriptions, adapts language to location
18+
- **Caching:** Proximity-based keys with coordinate rounding + location matching + TTL expiration
19+
- **Auto Translation:** Support JSON resource, string changes detection, local TM (Translation Memory), secondary locale for multi-lingual users
2220
- **Configuration:** Map defaults, test mode mock data on `config.json`, LLM prompt templates on `prompts.js`
2321

2422
### External Services
2523

2624
- **Google Maps API**: Core mapping functionality with 3D support
2725
- **Google Places API**: Location search and nearby places discovery
2826
- **OpenAI API**: LLM generated translations and landmark information
29-
- **Wikipedia API**: Landmark and location images
27+
- **Wikipedia API**: Landmark and location photos
3028

3129
### Vanilla JavaScript Frontend with Vite
30+
3231
```
3332
src/
3433
├── app.js # Init Google Maps application
@@ -44,78 +43,85 @@ src/
4443
└── test_runner.js # Client-side testing
4544
```
4645

47-
## Get Started
46+
## Getting Started
4847

49-
1. **Install prerequisites**
50-
- Install Node.js (https://nodejs.org/).
51-
- Clone this repository and install dependencies (see `package.json`)
48+
**Prerequisites**
49+
50+
- Install Node.js (https://nodejs.org/).
51+
- Clone this repository and install dependencies (see `package.json`)
5252

5353
```bash
5454
git clone <repository-url>
5555
cd <my-project>
5656
npm install
5757
```
5858

59-
2. **Create environment file**
59+
**Create environment file**
6060

61-
Create your own `.env` file and add your API keys for local development. Vite exposes variables prefixed with `VITE_` to the browser app via `index.html`.
61+
Create your own `.env` file and add your API keys for local development. Vite exposes variables prefixed with `VITE_` to the browser app via `index.html`.
6262

6363
```bash
6464
VITE_GOOGLE_MAPS_API_KEY=your_google_maps_api_key
6565
VITE_OPENAI_API_KEY=your_openai_api_key
6666
```
6767

68-
3. **Generate API keys**
68+
### "Bring your own key" approach
69+
70+
As an open-source project targeting technical users, this application is designed to run on user's keys. The user is responsible for:
71+
72+
1. Creating their own Google Cloud project and API key (required).
73+
2. Securing their key by restricting it to their own domains (`localhost` for testing, their deployment domain for production).
74+
3. The costs associated with their usage.
6975

70-
- **Google Maps API Key**
71-
1. Visit the [Google Cloud Console](https://console.cloud.google.com/).
72-
2. Create or select a project.
73-
3. Enable “Maps JavaScript API” and “Places API (New)” in the API library.
74-
4. Create an API key under **APIs & Services → Credentials**.
75-
5. For local development restrict referrers to `localhost`.
76+
**Generate API keys**
7677

77-
- **OpenAI API Key**
78-
1. Visit the [OpenAI dashboard](https://platform.openai.com/account/api-keys).
79-
2. Create a new secret key and copy it for later use; it won't be shown again.
78+
- **Google Maps API Key**
8079

81-
4. **Start the development server**
80+
1. Visit the [Google Cloud Console](https://console.cloud.google.com/) → Create or select a project → Go to [Google Maps Platform](https://console.cloud.google.com/google/maps-apis) ([See Also](https://developers.google.com/maps/documentation/javascript/get-api-key)).
81+
2. Enable “Map Tiles API”, “Maps JavaScript API”, “Geocoding API” and “Places API (New)” under **APIs & Services**.
82+
3. Create an API key under **Keys & Credentials**. For local development restrict **HTTP referrer** to `localhost`.
83+
84+
- **OpenAI API Key**
85+
1. Visit the [OpenAI dashboard](https://platform.openai.com/api-keys).
86+
2. Create a new secret key and copy it for later use; it won't be shown again.
87+
88+
**Start the development server**
8289

8390
```bash
8491
npm run dev
8592
```
8693

87-
5. **Enter API keys in the app**
94+
**Enter API keys in the app**
95+
96+
- Open `http://localhost:5001` in your browser.
97+
- Click the gear icon (**⚙️ Settings UI**) in the bottom‑left corner.
98+
- Fill in `GOOGLE_MAPS_API_KEY` and `OPENAI_API_KEY`, then close to save.
99+
- Settings are stored in `localStorage` under `APP_SETTINGS`.
100+
- In Chrome, view them under DevTools → Application → Local Storage; landmark caches use keys starting with `landmark_`.
88101

89-
- Open `http://localhost:5173` in your browser.
90-
- Click the gear icon (**⚙️ Settings**) in the bottom‑left corner.
91-
- Fill in `GOOGLE_MAPS_API_KEY` and `OPENAI_API_KEY`, then close to save.
92-
- Settings are stored in `localStorage` under `APP_SETTINGS`; landmark caches use keys starting with `landmark_`.
93-
- In Chrome, view them under DevTools → Application → Local Storage.
102+
**Start server proxy for MSFS "moving map**
94103

95-
6. **Start server proxy for MSFS "moving map**
104+
- Install Python with UV and dependencies (see `pyproject.toml`)
96105

97106
```bash
107+
uv pip install .
98108
python server.py
99109
```
100110

101111
## Usage
102112

103-
- Pan and zoom the Google map. Use the **🔍 search box** to jump to a city or location.
104-
- Click **🏛️ Landmarks** to fetch nearby points of interest around the map center.
105-
- Select a landmark card to read the AI-generated description and see a photo.
106-
- For 3D views, click **[3D]** in the card to explore a photorealistic flyover.
107-
- Use **📍 My Location** to center the map on your current position.
113+
- Pan and zoom the Google map. Use **🔍 Location Search** to search a city or place.
114+
- Click **🏛️ Landmarks** to discover nearby points of interest around the map center.
115+
- Select a landmark card to read the AI-generated description and see a Wiki photo.
116+
- Click **[3D]** on landmark cards to explore with a photorealistic 3D map view.
117+
- Use **📍 My Location** to center the map at current geolocation per browser detection.
108118
- Open the gear icon (**⚙️ Settings**) to update API keys or clear stored values.
109-
- Click **🌐 Locale** to toggle preferred locales per browser language setting.
110-
- When connected to MSFS, click **aircraft ✈️** icon to sync aircraft position.
119+
- Use **🌐 Locale** to toggle between multiple preferred locales per browser setting.
120+
- Click **aircraft ✈️** icon to sync aircraft position when connected to MSFS.
111121

112122
### Testing
113123

114124
- Frontend Test Runner - standalone test script running direct function testing
115125
- Built-in test mode with mock data from config.json, skipping API calls
116126
- Runnable on both browser console and Node.js CLI via `npm test`
117-
- Append `?test=true` to the URL to run tests on-browser
118-
119-
### MIT License - see [LICENSE](LICENSE) for details.
120-
121-
Please respect Google Maps and OpenAI terms of service.
127+
- Append `?test=true` to the URL to auto-run tests on-browser

index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<head>
44
<meta charset="UTF-8" />
55
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6-
<title>Map Explorer</title>
6+
<title>Mini Moving Map</title>
77
<link rel="icon" type="image/svg+xml" href="/globe.svg" />
88
<link rel="stylesheet" href="/src/style.css" />
99

public/locales/en.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
"location_not_found": "Location not found",
5959
"geolocation_not_supported": "Geolocation not supported",
6060
"unable_to_get_geolocation": "Unable to get Geolocation",
61-
"moving_map_server_unavailable": "Moving Map server not available"
61+
"moving_map_server_unavailable": "Moving Map server not available",
62+
"invalid_search_query": "Please enter a valid search query"
6263
}
6364
}

server/simconnect.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ def update_aircraft_data():
6161
center_long = -122.3754
6262
radius = 0.01
6363
t = 0.0
64-
print("Starting Circular flight test...")
64+
print("Starting Circular flight demo/test mode...")
6565

6666
while True:
6767
try:

src/backend.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
} from './gmap.js';
1111
import { setCachedLandmarks, findByLocation } from './cache.js';
1212
import { same_landmarks } from './utils.js';
13-
import { getCountryLanguage } from './lion.js';
13+
import { i18n, getCountryLanguage } from './lion.js';
1414

1515
class LandmarkService {
1616
constructor() {
@@ -99,7 +99,7 @@ class LandmarkService {
9999
return {
100100
location: '',
101101
landmarks: [],
102-
error: 'Please enter a valid search query',
102+
error: i18n.t('errors.invalid_search_query'),
103103
};
104104

105105
// Get location details from coordinates

src/cache.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,6 @@ import { same_landmarks, distance_km } from './utils.js';
66
*/
77

88
const CACHE_PREFIX = 'landmarks_';
9-
const CACHE_TTL_HOURS = window.APP_CONFIG?.CACHE_TTL_HOURS ?? 48;
10-
const CACHE_TTL_MS = CACHE_TTL_HOURS * 60 * 60 * 1000;
11-
129
const location_index = {};
1310

1411
/**
@@ -115,6 +112,9 @@ export function clearLandmarkCache() {
115112

116113
export function enableLandmarkCache() {
117114
try {
115+
const CACHE_TTL_HOURS = window.APP_CONFIG?.CACHE_TTL_HOURS ?? 48;
116+
const CACHE_TTL_MS = CACHE_TTL_HOURS * 60 * 60 * 1000;
117+
118118
const keys = Object.keys(localStorage);
119119
const cacheKeys = keys.filter((key) => key.startsWith(CACHE_PREFIX));
120120
let totalSize = 0;

src/lion.js

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,7 @@ class I18n {
296296
export const i18n = new I18n();
297297

298298
export async function initi18n() {
299-
testI18n();
299+
// testI18n();
300300
i18n.TM = JSON.parse(localStorage.getItem(LOCAL_TM)) || {};
301301
const source_file = await i18n.loadLocale();
302302
if (source_file) i18n.updateTM(source_file);
@@ -333,7 +333,7 @@ function isValidLocale(str) {
333333
return regex.test(str);
334334
}
335335

336-
function testI18n() {
336+
export function testI18n() {
337337
const mock = new I18n();
338338
const mockSrc = {
339339
hello: 'Hello, {name}!',
@@ -345,16 +345,16 @@ function testI18n() {
345345
};
346346
mock.translations[FALLBACK_LANGUAGE] = mockSrc;
347347

348-
let tgt = mock.t('hello', { name: 'Tom' });
349-
console.log('lion test 1: ', tgt, mock.lang);
350-
console.assert(tgt === 'Hello, Tom!', 'Placeholder failed');
348+
let tgt = mock.t('hello', { name: 'World' });
349+
console.log('L10n test 1: ', tgt, mock.lang);
350+
console.assert(tgt === 'Hello, World!', 'Placeholder failed');
351351

352352
mock.updateTM(mockSrc, mockTgt, 'zh');
353353
console.assert(
354354
mock.lookupTM('zh') === null,
355355
'lookupTM should return null when locale is ready'
356356
);
357-
console.debug('TM records:', mock.TM);
357+
//console.debug('TM records:', mock.TM);
358358
let missing = mock.lookupTM('fr');
359359
console.assert(
360360
missing && missing.hello === 'Hello, {name}!',
@@ -367,16 +367,16 @@ function testI18n() {
367367
);
368368
mock.translations['zh'] = exported;
369369

370+
/*
370371
console.debug(
371-
'lion test 2: ',
372+
'L10n test 2: ',
372373
(tgt = mock.t('nested.example', { value: 42 }, 'zh'))
373374
);
374375
console.assert(tgt === '答案是 42。', 'Nested failed');
375-
console.debug('res-bundles: ', mock.translations);
376-
console.debug('lion test 3: ', (tgt = mock.t('missing.key')));
376+
//console.debug('res-bundles: ', mock.translations);
377+
console.debug('L10n test 3: ', (tgt = mock.t('missing.key')));
377378
console.assert(tgt === 'missing.key', 'Missing key failed');
378379
379-
/*
380380
const original_json = {
381381
app: {
382382
loading_text: 'Loading map…',
@@ -432,6 +432,7 @@ function testI18n() {
432432
exported = mock.exportTM('zh', partial_json);
433433
console.log('Exported:', JSON.stringify(exported, null, 2));
434434
*/
435+
return true;
435436
}
436437

437438
function getLanguageSetting() {

0 commit comments

Comments
 (0)