reafactor(important): change to new map backround base goong.io
This commit is contained in:
@@ -0,0 +1,425 @@
|
||||
# Goong APIs In Use
|
||||
|
||||
Mục tiêu của tài liệu này:
|
||||
|
||||
- mô tả **chính xác** frontend hiện tại đang dùng gì từ Goong
|
||||
- mô tả **backend cần proxy gì** để giấu `api_key`
|
||||
- mô tả **response nào phải rewrite**
|
||||
- tránh liệt kê thừa các API Goong mà app hiện tại không đụng tới
|
||||
|
||||
Phạm vi kiểm tra:
|
||||
|
||||
- [config.ts](/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/uhm/api/config.ts:1)
|
||||
- [tiles.ts](/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/uhm/api/tiles.ts:1)
|
||||
- [useMapLayers.ts](/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/uhm/components/map/useMapLayers.ts:1)
|
||||
- style JSON đã tải về:
|
||||
- [goong_map_web.json](/home/amoratran/wsp/ultimate-history-map/FrontEndUser/tmp/goong-styles/goong_map_web.json)
|
||||
- [goong_satellite.json](/home/amoratran/wsp/ultimate-history-map/FrontEndUser/tmp/goong-styles/goong_satellite.json)
|
||||
|
||||
## 1. Tóm tắt kỹ thuật
|
||||
|
||||
Frontend hiện tại **không** `map.setStyle(goongStyleJson)` trực tiếp.
|
||||
|
||||
Thay vào đó:
|
||||
|
||||
1. app tự `fetch()` 2 style JSON của Goong
|
||||
2. app parse style JSON để lấy:
|
||||
- `raster source` từ `goong_satellite.json`
|
||||
- `sources + layers` cần thiết từ `goong_map_web.json`
|
||||
3. app `map.addSource(...)` và `map.addLayer(...)` thủ công
|
||||
4. từ thời điểm đó, **MapLibre tự request tiếp** các `source.url`
|
||||
5. rồi từ các source manifest đó, **MapLibre lại tự request tiếp** các tile URLs nằm trong `tiles[]`
|
||||
|
||||
Hệ quả:
|
||||
|
||||
- nếu BE chỉ proxy `assets/*.json` thì **chưa đủ**
|
||||
- nếu BE chỉ proxy `sources/*.json` mà **không rewrite `tiles[]`** thì **vẫn lộ key ở request tile**
|
||||
|
||||
## 2. Luồng request thật hiện tại
|
||||
|
||||
### 2.1. App fetch trực tiếp style JSON
|
||||
|
||||
Frontend gọi trực tiếp:
|
||||
|
||||
1. `https://tiles.goong.io/assets/goong_satellite.json?api_key=...`
|
||||
2. `https://tiles.goong.io/assets/goong_map_web.json?api_key=...`
|
||||
|
||||
Nguồn trong code:
|
||||
|
||||
- `GOONG_SATELLITE_STYLE_URL` ở [config.ts](/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/uhm/api/config.ts:15)
|
||||
- `GOONG_VECTOR_OVERLAY_STYLE_URL` ở [config.ts](/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/uhm/api/config.ts:19)
|
||||
- `loadGoongStyleDocument(...)` ở [tiles.ts](/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/uhm/api/tiles.ts:211)
|
||||
|
||||
Mục đích:
|
||||
|
||||
- `goong_satellite.json`
|
||||
- app lấy ra raster source đầu tiên
|
||||
- dùng làm nền satellite
|
||||
- `goong_map_web.json`
|
||||
- app lấy ra các layer/source phục vụ:
|
||||
- `Country Borders`
|
||||
- `Province Borders`
|
||||
- `District Borders`
|
||||
- `Country Labels`
|
||||
- `Rivers`
|
||||
|
||||
### 2.2. MapLibre fetch source manifests
|
||||
|
||||
Sau khi app clone source spec từ style JSON và `addSource(...)`, MapLibre tự bắn tiếp các request `source.url`.
|
||||
|
||||
Các source URL đang xuất hiện trong style JSON:
|
||||
|
||||
#### Trong `goong_satellite.json`
|
||||
|
||||
- `https://tiles.goong.io/sources/satellite.json?api_key=...`
|
||||
- `https://tiles.goong.io/sources/base.json?api_key=...`
|
||||
- `https://tiles.goong.io/sources/goong.json?api_key=...`
|
||||
|
||||
#### Trong `goong_map_web.json`
|
||||
|
||||
- `https://tiles.goong.io/sources/base.json?api_key=...`
|
||||
- `https://tiles.goong.io/sources/goong.json?api_key=...`
|
||||
|
||||
Ý nghĩa:
|
||||
|
||||
- `sources/satellite.json`
|
||||
- raster source manifest cho nền satellite
|
||||
- `sources/base.json`
|
||||
- vector source manifest cho các lớp `boundary`, `worldcountriespoints`, `worldnationalcapitals`
|
||||
- `sources/goong.json`
|
||||
- vector source manifest cho các lớp `riversandlakes`, `vietnam_administrator`
|
||||
|
||||
### 2.3. MapLibre fetch tile URLs nằm trong source manifests
|
||||
|
||||
Đây là phần dễ bị bỏ sót nhất.
|
||||
|
||||
Khi MapLibre đã tải `sources/satellite.json`, `sources/base.json`, `sources/goong.json`, nó sẽ tiếp tục request các URL nằm trong field:
|
||||
|
||||
- `tiles[]`
|
||||
|
||||
Tức là runtime thật của frontend hiện tại là:
|
||||
|
||||
1. fetch style JSON
|
||||
2. fetch source manifest
|
||||
3. fetch tile URL bên trong source manifest
|
||||
|
||||
Nếu backend muốn che key hoàn toàn, thì **bước 3 bắt buộc phải được proxy hoặc rewrite về domain backend**.
|
||||
|
||||
## 3. Những upstream Goong resource đang dùng thật
|
||||
|
||||
Tính theo runtime hiện tại, upstream Goong đang được dùng thật là:
|
||||
|
||||
### 3.1. Style JSON
|
||||
|
||||
- `assets/goong_satellite.json`
|
||||
- `assets/goong_map_web.json`
|
||||
|
||||
### 3.2. Source manifests
|
||||
|
||||
- `sources/satellite.json`
|
||||
- `sources/base.json`
|
||||
- `sources/goong.json`
|
||||
|
||||
### 3.3. Tile endpoints bên trong source manifests
|
||||
|
||||
- raster tile URLs nằm trong `sources/satellite.json`
|
||||
- vector tile URLs nằm trong `sources/base.json`
|
||||
- vector tile URLs nằm trong `sources/goong.json`
|
||||
|
||||
Lưu ý:
|
||||
|
||||
- tile URL pattern chính xác phải đọc từ source manifest upstream ở runtime
|
||||
- backend không nên hardcode khi chưa xác minh nội dung `tiles[]`
|
||||
|
||||
## 4. Những thứ frontend hiện tại dùng thêm hoặc KHÔNG dùng
|
||||
|
||||
### 4.1. Goong glyphs / fonts
|
||||
|
||||
Style JSON của Goong có field:
|
||||
|
||||
- `glyphs: https://tiles.goong.io/fonts/{fontstack}/{range}.pbf?api_key=...`
|
||||
|
||||
Flow hiện tại **có dùng glyphs của Goong qua proxy**.
|
||||
|
||||
Map đang trỏ `glyphs` vào:
|
||||
|
||||
- `/proxy/{encoded-https://tiles.goong.io/fonts/{fontstack}/{range}.pbf}`
|
||||
|
||||
Nguồn trong code:
|
||||
|
||||
- [useMapLayers.ts](/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/uhm/components/map/useMapLayers.ts:17)
|
||||
- [config.ts](/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/uhm/api/config.ts:12)
|
||||
|
||||
Kết luận:
|
||||
|
||||
- **backend proxy Goong fonts/glyphs là bắt buộc cho flow hiện tại**
|
||||
|
||||
### 4.2. Goong sprite
|
||||
|
||||
Style JSON của Goong có:
|
||||
|
||||
- `sprite: https://tiles.goong.io/sprite`
|
||||
|
||||
Nhưng flow hiện tại **không phụ thuộc sprite** vì:
|
||||
|
||||
- app không nạp toàn bộ Goong style vào map
|
||||
- app chỉ nhặt `sources` và `layers`
|
||||
- khi clone overlay labels, code còn chủ động loại bớt icon fields
|
||||
|
||||
Nguồn trong code:
|
||||
|
||||
- `cloneOverlayLayer(...)` ở [tiles.ts](/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/uhm/api/tiles.ts:411)
|
||||
|
||||
Kết luận:
|
||||
|
||||
- **không cần backend proxy Goong sprite cho flow hiện tại**
|
||||
|
||||
### 4.3. Các REST API khác của Goong
|
||||
|
||||
Không dùng:
|
||||
|
||||
- geocoding
|
||||
- autocomplete
|
||||
- directions
|
||||
- distance matrix
|
||||
- place details
|
||||
- static map
|
||||
|
||||
## 5. Backend cần làm gì
|
||||
|
||||
### 5.1. Mục tiêu backend
|
||||
|
||||
Backend phải đảm bảo:
|
||||
|
||||
1. browser không gọi Goong trực tiếp
|
||||
2. browser không nhìn thấy `api_key`
|
||||
3. frontend vẫn nhận được dữ liệu theo format mà MapLibre/app hiện tại cần
|
||||
|
||||
### 5.2. Hai kiểu triển khai
|
||||
|
||||
Có 2 cách:
|
||||
|
||||
#### Cách A: Transparent proxy
|
||||
|
||||
BE trả về gần như đúng response của Goong, chỉ rewrite URL.
|
||||
|
||||
Ưu điểm:
|
||||
|
||||
- gần với Goong
|
||||
- ít phải đổi frontend hơn
|
||||
|
||||
Nhược điểm:
|
||||
|
||||
- BE phải rewrite nhiều chỗ
|
||||
|
||||
#### Cách B: Normalize thành API nội bộ
|
||||
|
||||
BE không trả nguyên style/source của Goong mà trả dữ liệu đã xử lý sẵn cho FE.
|
||||
|
||||
Ưu điểm:
|
||||
|
||||
- hợp đồng BE-FE rõ hơn
|
||||
- ít phụ thuộc format Goong hơn
|
||||
|
||||
Nhược điểm:
|
||||
|
||||
- cần sửa frontend nhiều hơn
|
||||
|
||||
Với frontend hiện tại, **Cách A** là hợp lý nhất.
|
||||
|
||||
## 6. Contract backend được khuyến nghị
|
||||
|
||||
### 6.1. Proxy style JSON
|
||||
|
||||
#### `GET /proxy/goong/assets/goong_satellite.json`
|
||||
|
||||
Upstream:
|
||||
|
||||
- `https://tiles.goong.io/assets/goong_satellite.json?api_key=<server-side-key>`
|
||||
|
||||
Backend phải:
|
||||
|
||||
- fetch upstream bằng key server-side
|
||||
- parse JSON
|
||||
- rewrite `sources.*.url` về domain backend
|
||||
- có thể giữ nguyên các field khác
|
||||
|
||||
Response:
|
||||
|
||||
- `Content-Type: application/json`
|
||||
- body: style JSON đã rewrite
|
||||
|
||||
#### `GET /proxy/goong/assets/goong_map_web.json`
|
||||
|
||||
Upstream:
|
||||
|
||||
- `https://tiles.goong.io/assets/goong_map_web.json?api_key=<server-side-key>`
|
||||
|
||||
Backend phải:
|
||||
|
||||
- fetch upstream bằng key server-side
|
||||
- parse JSON
|
||||
- rewrite `sources.*.url` về domain backend
|
||||
- có thể giữ nguyên các field khác
|
||||
|
||||
Response:
|
||||
|
||||
- `Content-Type: application/json`
|
||||
- body: style JSON đã rewrite
|
||||
|
||||
### 6.2. Proxy source manifests
|
||||
|
||||
#### `GET /proxy/goong/sources/satellite.json`
|
||||
|
||||
Upstream:
|
||||
|
||||
- `https://tiles.goong.io/sources/satellite.json?api_key=<server-side-key>`
|
||||
|
||||
Backend phải:
|
||||
|
||||
- fetch upstream
|
||||
- parse JSON
|
||||
- rewrite mọi URL trong `tiles[]` về domain backend
|
||||
- giữ nguyên metadata quan trọng:
|
||||
- `tileSize`
|
||||
- `minzoom`
|
||||
- `maxzoom`
|
||||
- `bounds`
|
||||
- `scheme`
|
||||
- `attribution`
|
||||
|
||||
Response:
|
||||
|
||||
- `Content-Type: application/json`
|
||||
- body: source manifest đã rewrite
|
||||
|
||||
#### `GET /proxy/goong/sources/base.json`
|
||||
|
||||
Upstream:
|
||||
|
||||
- `https://tiles.goong.io/sources/base.json?api_key=<server-side-key>`
|
||||
|
||||
Backend phải:
|
||||
|
||||
- fetch upstream
|
||||
- parse JSON
|
||||
- rewrite mọi URL trong `tiles[]` về domain backend
|
||||
- giữ nguyên metadata tilejson khác
|
||||
|
||||
#### `GET /proxy/goong/sources/goong.json`
|
||||
|
||||
Upstream:
|
||||
|
||||
- `https://tiles.goong.io/sources/goong.json?api_key=<server-side-key>`
|
||||
|
||||
Backend phải:
|
||||
|
||||
- fetch upstream
|
||||
- parse JSON
|
||||
- rewrite mọi URL trong `tiles[]` về domain backend
|
||||
- giữ nguyên metadata tilejson khác
|
||||
|
||||
### 6.3. Proxy tile endpoints
|
||||
|
||||
Backend bắt buộc phải có route để trả tile thật.
|
||||
|
||||
Có thể làm generic, ví dụ:
|
||||
|
||||
- `GET /proxy/goong/tiles/*`
|
||||
|
||||
hoặc explicit hơn theo source:
|
||||
|
||||
- `GET /proxy/goong/tiles/satellite/...`
|
||||
- `GET /proxy/goong/tiles/base/...`
|
||||
- `GET /proxy/goong/tiles/goong/...`
|
||||
|
||||
Yêu cầu:
|
||||
|
||||
- request browser -> backend
|
||||
- backend -> upstream Goong bằng key server-side
|
||||
- stream response về browser
|
||||
- pass through hoặc preserve:
|
||||
- `Content-Type`
|
||||
- `Cache-Control`
|
||||
- `ETag`
|
||||
- `Last-Modified`
|
||||
|
||||
Response type có thể là:
|
||||
|
||||
- raster image
|
||||
- vector tile protobuf
|
||||
|
||||
## 7. Runtime dependency map cho BE
|
||||
|
||||
### 7.1. Satellite background
|
||||
|
||||
Luồng:
|
||||
|
||||
1. FE đọc `goong_satellite.json`
|
||||
2. FE lấy `sources.satellite`
|
||||
3. MapLibre gọi `sources/satellite.json`
|
||||
4. MapLibre gọi raster tile URLs trong `tiles[]`
|
||||
|
||||
BE cần cover:
|
||||
|
||||
- style JSON
|
||||
- source manifest
|
||||
- raster tile URLs
|
||||
|
||||
### 7.2. Overlay borders / labels / rivers
|
||||
|
||||
Luồng:
|
||||
|
||||
1. FE đọc `goong_map_web.json`
|
||||
2. FE lấy selected layers + selected sources
|
||||
3. MapLibre gọi `sources/base.json`
|
||||
4. MapLibre gọi `sources/goong.json`
|
||||
5. MapLibre gọi vector tile URLs của 2 source manifest này
|
||||
|
||||
BE cần cover:
|
||||
|
||||
- style JSON
|
||||
- 2 source manifests
|
||||
- vector tile URLs tương ứng
|
||||
|
||||
## 8. Danh sách tối thiểu BE phải cover
|
||||
|
||||
Nếu chỉ làm đúng những gì frontend hiện tại dùng, checklist tối thiểu là:
|
||||
|
||||
1. proxy `assets/goong_satellite.json`
|
||||
2. proxy `assets/goong_map_web.json`
|
||||
3. proxy `sources/satellite.json`
|
||||
4. proxy `sources/base.json`
|
||||
5. proxy `sources/goong.json`
|
||||
6. proxy toàn bộ tile URL được khai báo trong `sources/satellite.json`
|
||||
7. proxy toàn bộ tile URL được khai báo trong `sources/base.json`
|
||||
8. proxy toàn bộ tile URL được khai báo trong `sources/goong.json`
|
||||
|
||||
## 9. Những gì BE chưa cần làm ngay
|
||||
|
||||
Cho flow hiện tại, BE **chưa cần**:
|
||||
|
||||
- proxy Goong `glyphs`
|
||||
- proxy Goong `sprite`
|
||||
- proxy geocoding / directions / autocomplete
|
||||
|
||||
Điều này chỉ đúng khi frontend vẫn giữ kiến trúc hiện tại.
|
||||
|
||||
Nếu sau này frontend chuyển sang `map.setStyle(goongStyleJson)` trực tiếp, hãy đánh giá lại:
|
||||
|
||||
- `glyphs`
|
||||
- `sprite`
|
||||
|
||||
vì khi đó chúng có thể trở thành dependency bắt buộc.
|
||||
|
||||
## 10. Gợi ý ngắn cho team BE
|
||||
|
||||
Nếu muốn làm ít rủi ro nhất:
|
||||
|
||||
1. làm proxy `assets/*.json`
|
||||
2. rewrite `sources.*.url`
|
||||
3. làm proxy `sources/*.json`
|
||||
4. rewrite `tiles[]`
|
||||
5. làm proxy generic cho tile
|
||||
|
||||
Nếu làm thiếu bước 4 hoặc 5 thì key vẫn có thể lộ ở request tile.
|
||||
@@ -0,0 +1,129 @@
|
||||
# Goong Map Web Structure
|
||||
|
||||
Nguồn JSON gốc được tải về tại:
|
||||
|
||||
- `FrontEndUser/tmp/goong-styles/goong_map_web.json`
|
||||
|
||||
File này là style vector/label đầy đủ hơn, phù hợp để dò:
|
||||
|
||||
- water và water labels
|
||||
- boundary theo cấp
|
||||
- place labels cho lịch sử
|
||||
|
||||
## Mermaid overview
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
ROOT[goong_map_web.json]
|
||||
|
||||
ROOT --> S1[source: base]
|
||||
ROOT --> S2[source: composite]
|
||||
|
||||
S1 --> B1[source-layer: boundary]
|
||||
S1 --> B2[source-layer: worldcountriespoints]
|
||||
S1 --> B3[source-layer: worldnationalcapitals]
|
||||
|
||||
S2 --> C1[source-layer: riversandlakes]
|
||||
S2 --> C2[source-layer: rivernames]
|
||||
S2 --> C3[source-layer: lakenames]
|
||||
S2 --> C4[source-layer: vietnam_administrator]
|
||||
S2 --> C5[source-layer: streets_label]
|
||||
|
||||
B1 --> BL0[boundary-land-type-0 / type-0-bg]
|
||||
B1 --> BL1[boundary-land-type-1 / type-1-bg]
|
||||
B1 --> BL2[boundary-land-type-2 / type-2-bg]
|
||||
|
||||
B2 --> PC1[place-country-1]
|
||||
B2 --> PC2[place-country-2]
|
||||
|
||||
B3 --> CAP0[place-city-capital]
|
||||
|
||||
C1 --> W1[water]
|
||||
C1 --> W2[water-shadow]
|
||||
|
||||
C2 --> RN0[river-name-0]
|
||||
C2 --> RN1[river-name-1]
|
||||
C2 --> RN2[river-name-2]
|
||||
|
||||
C3 --> LN0[lake-name_priority_0]
|
||||
C3 --> LN1[lake-name_priority_1]
|
||||
C3 --> LN2[lake-name_priority_2]
|
||||
|
||||
C4 --> VA0[place-city-capital-vietnam]
|
||||
C4 --> VA1[place-city1 / place-city2]
|
||||
C4 --> VA2[place-town1 / place-town2]
|
||||
C4 --> VA3[place-suburb / borough / neighbourhood]
|
||||
C4 --> VA4[place-village]
|
||||
|
||||
C5 --> RD0[highway-name-minor]
|
||||
C5 --> RD1[highway-name-medium]
|
||||
C5 --> RD2[highway-name-major]
|
||||
```
|
||||
|
||||
## Boundary layers
|
||||
|
||||
Các layer boundary nổi bật:
|
||||
|
||||
- `boundary-land-type-0-bg`
|
||||
- `boundary-land-type-0`
|
||||
- `boundary-land-type-1-bg`
|
||||
- `boundary-land-type-1`
|
||||
- `boundary-land-type-2-bg`
|
||||
- `boundary-land-type-2`
|
||||
|
||||
Minzoom quan sát được:
|
||||
|
||||
- `type-0`: từ zoom `1`
|
||||
- `type-1`: từ zoom `5`
|
||||
- `type-2-bg`: từ zoom `7`
|
||||
- `type-2`: từ zoom `13`
|
||||
|
||||
Suy luận thực dụng:
|
||||
|
||||
- `type-0` có khả năng là biên giới quốc gia
|
||||
- `type-1` có khả năng là cấp tỉnh/thành
|
||||
- `type-2` có khả năng là cấp sâu hơn
|
||||
|
||||
## Water layers
|
||||
|
||||
Water fill:
|
||||
|
||||
- `water`
|
||||
- `water-shadow`
|
||||
|
||||
Water labels:
|
||||
|
||||
- `river-name-0`
|
||||
- `river-name-1`
|
||||
- `river-name-2`
|
||||
- `lake-name_priority_0`
|
||||
- `lake-name_priority_1`
|
||||
- `lake-name_priority_2`
|
||||
|
||||
## Place labels
|
||||
|
||||
Những label đáng quan tâm cho historical use:
|
||||
|
||||
- `place-country-1`
|
||||
- `place-country-2`
|
||||
- `place-city-capital`
|
||||
- `place-city-capital-vietnam`
|
||||
- `place-city1`
|
||||
- `place-city2`
|
||||
- `place-town1`
|
||||
- `place-town2`
|
||||
|
||||
Những label dễ gây rối nếu bật nhiều:
|
||||
|
||||
- `highway-name-*`
|
||||
- `place-suburb*`
|
||||
- `place-neighbourhood*`
|
||||
- `place-village`
|
||||
|
||||
## Gợi ý mapping cho UI
|
||||
|
||||
- `Country Borders` -> `boundary-land-type-0` + `boundary-land-type-0-bg`
|
||||
- `Province Borders` -> `boundary-land-type-1` + `boundary-land-type-1-bg`
|
||||
- `District Borders` -> `boundary-land-type-2` + `boundary-land-type-2-bg`
|
||||
- `Country Labels` -> `place-country-*`, `place-city-capital*`, `place-city*`, `place-town*`
|
||||
- `Rivers` -> `water`, `water-shadow`, `river-name-*`, `lake-name_*`
|
||||
@@ -0,0 +1,384 @@
|
||||
# Goong Proxy Backend Guide
|
||||
|
||||
Tài liệu này mô tả:
|
||||
|
||||
- luồng request thật của frontend hiện tại
|
||||
- backend cần proxy chỗ nào
|
||||
- backend cần rewrite chỗ nào
|
||||
- trade-off hiệu suất nếu proxy/rewrite toàn bộ Goong
|
||||
- khuyến nghị triển khai thực dụng cho team BE
|
||||
|
||||
Tài liệu liên quan:
|
||||
|
||||
- [goong_apis_in_use.md](/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/uhm/doc/goong_apis_in_use.md)
|
||||
- [goong_map_web_structure.md](/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/uhm/doc/goong_map_web_structure.md)
|
||||
- [goong_satellite_structure.md](/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/uhm/doc/goong_satellite_structure.md)
|
||||
|
||||
Code liên quan:
|
||||
|
||||
- [config.ts](/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/uhm/api/config.ts:1)
|
||||
- [tiles.ts](/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/uhm/api/tiles.ts:1)
|
||||
- [useMapLayers.ts](/home/amoratran/wsp/ultimate-history-map/FrontEndUser/src/uhm/components/map/useMapLayers.ts:1)
|
||||
|
||||
## 1. Bối cảnh hiện tại
|
||||
|
||||
Frontend hiện tại không `setStyle(goongStyle)` trực tiếp cho MapLibre.
|
||||
|
||||
Thay vào đó:
|
||||
|
||||
1. FE tự `fetch()` style JSON của Goong
|
||||
2. FE parse style JSON
|
||||
3. FE lấy ra:
|
||||
- raster source cho satellite
|
||||
- selected vector sources/layers cho borders, labels, rivers
|
||||
4. FE `addSource()` và `addLayer()` thủ công
|
||||
5. MapLibre tự request tiếp `source.url`
|
||||
6. Từ source manifest, MapLibre tự request tiếp các tile URLs trong `tiles[]`
|
||||
|
||||
Điểm quan trọng:
|
||||
|
||||
- browser có thể không chỉ gọi `assets/*.json`
|
||||
- browser sẽ đi sâu thêm ít nhất 2 tầng:
|
||||
- `sources/*.json`
|
||||
- tile URLs trong `tiles[]`
|
||||
|
||||
## 2. Luồng request hiện tại
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant FE as Frontend
|
||||
participant GL as MapLibre
|
||||
participant GO as Goong
|
||||
|
||||
FE->>GO: GET assets/goong_satellite.json?api_key=...
|
||||
FE->>GO: GET assets/goong_map_web.json?api_key=...
|
||||
|
||||
FE->>GL: addSource(raster/vector) + addLayer(...)
|
||||
|
||||
GL->>GO: GET sources/satellite.json?api_key=...
|
||||
GL->>GO: GET sources/base.json?api_key=...
|
||||
GL->>GO: GET sources/goong.json?api_key=...
|
||||
|
||||
GL->>GO: GET raster tile URLs from satellite tiles[]
|
||||
GL->>GO: GET vector tile URLs from base tiles[]
|
||||
GL->>GO: GET vector tile URLs from goong tiles[]
|
||||
```
|
||||
|
||||
## 3. Mục tiêu của backend proxy
|
||||
|
||||
Nếu mục tiêu là:
|
||||
|
||||
- không lộ `api_key` ở browser
|
||||
- vẫn giữ frontend hiện tại gần như nguyên
|
||||
|
||||
thì backend phải đảm bảo:
|
||||
|
||||
1. browser chỉ gọi domain BE
|
||||
2. BE gọi Goong bằng key server-side
|
||||
3. mọi URL Goong lồng bên trong JSON đều được rewrite về domain BE
|
||||
|
||||
Nếu thiếu bước 3:
|
||||
|
||||
- `api_key` vẫn có thể lộ ở request tầng sau
|
||||
|
||||
## 4. Những gì cần rewrite
|
||||
|
||||
### 4.1. Style JSON
|
||||
|
||||
Trong `goong_satellite.json` và `goong_map_web.json`, BE cần rewrite:
|
||||
|
||||
- `sources.*.url`
|
||||
|
||||
Ví dụ:
|
||||
|
||||
- từ `https://tiles.goong.io/sources/base.json?api_key=...`
|
||||
- thành `/proxy/goong/sources/base.json`
|
||||
|
||||
### 4.2. Source manifests
|
||||
|
||||
Trong `sources/satellite.json`, `sources/base.json`, `sources/goong.json`, BE cần rewrite:
|
||||
|
||||
- mọi phần tử trong `tiles[]`
|
||||
|
||||
Ví dụ:
|
||||
|
||||
- từ `https://.../{z}/{x}/{y}...api_key=...`
|
||||
- thành `/proxy/goong/tiles/...`
|
||||
|
||||
### 4.3. Những field còn phải để ý cho flow hiện tại
|
||||
|
||||
Với kiến trúc frontend hiện tại:
|
||||
|
||||
- `glyphs` đang được FE dùng qua proxy
|
||||
- `sprite` hiện chưa dùng
|
||||
|
||||
Nghĩa là:
|
||||
|
||||
- BE **phải** proxy được `fonts/{fontstack}/{range}.pbf`
|
||||
- BE hiện **chưa cần** proxy `sprite`
|
||||
|
||||
Nếu sau này FE chuyển sang `map.setStyle(goongStyleJson)` trực tiếp thì phải đánh giá lại `sprite` ngay.
|
||||
|
||||
## 5. Backend endpoint được khuyến nghị
|
||||
|
||||
### 5.1. Style endpoints
|
||||
|
||||
- `GET /proxy/goong/assets/goong_satellite.json`
|
||||
- `GET /proxy/goong/assets/goong_map_web.json`
|
||||
|
||||
Nhiệm vụ:
|
||||
|
||||
- gọi upstream Goong bằng key server-side
|
||||
- parse JSON
|
||||
- rewrite `sources.*.url`
|
||||
- trả JSON đã rewrite
|
||||
|
||||
### 5.2. Source endpoints
|
||||
|
||||
- `GET /proxy/goong/sources/satellite.json`
|
||||
- `GET /proxy/goong/sources/base.json`
|
||||
- `GET /proxy/goong/sources/goong.json`
|
||||
|
||||
Nhiệm vụ:
|
||||
|
||||
- gọi upstream Goong bằng key server-side
|
||||
- parse JSON
|
||||
- rewrite `tiles[]`
|
||||
- giữ nguyên:
|
||||
- `bounds`
|
||||
- `minzoom`
|
||||
- `maxzoom`
|
||||
- `scheme`
|
||||
- `tileSize`
|
||||
- `attribution`
|
||||
|
||||
### 5.3. Tile endpoint
|
||||
|
||||
Gợi ý route generic:
|
||||
|
||||
- `GET /proxy/goong/tiles/*`
|
||||
|
||||
Nhiệm vụ:
|
||||
|
||||
- nhận tile request từ browser
|
||||
- map sang upstream tile URL tương ứng
|
||||
- gọi Goong bằng key server-side nếu upstream yêu cầu
|
||||
- stream response về browser
|
||||
|
||||
Điểm quan trọng:
|
||||
|
||||
- tile response không nên parse lại
|
||||
- tile response nên stream/pass-through
|
||||
- giữ cache headers càng nhiều càng tốt
|
||||
|
||||
## 6. Luồng request sau khi proxy
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant FE as Frontend
|
||||
participant GL as MapLibre
|
||||
participant BE as Backend Proxy
|
||||
participant GO as Goong
|
||||
|
||||
FE->>BE: GET /proxy/goong/assets/goong_satellite.json
|
||||
FE->>BE: GET /proxy/goong/assets/goong_map_web.json
|
||||
|
||||
BE->>GO: fetch upstream style JSON
|
||||
GO-->>BE: style JSON
|
||||
BE-->>FE: rewritten style JSON
|
||||
|
||||
FE->>GL: addSource(raster/vector) + addLayer(...)
|
||||
|
||||
GL->>BE: GET /proxy/goong/sources/satellite.json
|
||||
GL->>BE: GET /proxy/goong/sources/base.json
|
||||
GL->>BE: GET /proxy/goong/sources/goong.json
|
||||
|
||||
BE->>GO: fetch upstream source manifests
|
||||
GO-->>BE: source manifests
|
||||
BE-->>GL: rewritten source manifests
|
||||
|
||||
GL->>BE: GET /proxy/goong/tiles/...
|
||||
BE->>GO: fetch upstream tile
|
||||
GO-->>BE: tile bytes
|
||||
BE-->>GL: tile bytes
|
||||
```
|
||||
|
||||
## 7. Trade-off hiệu suất
|
||||
|
||||
### 7.1. Rewrite JSON có chậm không?
|
||||
|
||||
Có overhead, nhưng **rất nhỏ** so với tile traffic.
|
||||
|
||||
JSON cần rewrite hiện tại chỉ gồm:
|
||||
|
||||
- 2 style JSON
|
||||
- 3 source manifests
|
||||
|
||||
Những file này nhỏ, số lượng ít, và có thể cache rất mạnh.
|
||||
|
||||
Kết luận:
|
||||
|
||||
- rewrite JSON không phải bottleneck chính
|
||||
|
||||
### 7.2. Tile proxy mới là chỗ đắt
|
||||
|
||||
Chi phí hiệu suất chính nằm ở:
|
||||
|
||||
- mọi tile phải đi qua backend
|
||||
- backend phải giữ thêm một hop mạng
|
||||
- mất lợi thế gọi trực tiếp CDN của Goong từ browser
|
||||
|
||||
Các ảnh hưởng có thể thấy:
|
||||
|
||||
- tăng latency
|
||||
- tăng bandwidth qua BE
|
||||
- tăng CPU/memory nếu BE buffer response thay vì stream
|
||||
- tăng load connection pool tới Goong
|
||||
|
||||
### 7.3. Nếu không rewrite tile URL
|
||||
|
||||
Nếu BE chỉ rewrite style/source JSON nhưng không rewrite `tiles[]`:
|
||||
|
||||
- browser vẫn gọi Goong trực tiếp ở bước tile
|
||||
- `api_key` vẫn có thể lộ
|
||||
|
||||
Tức là:
|
||||
|
||||
- hiệu suất tốt hơn
|
||||
- nhưng mục tiêu bảo mật key không đạt
|
||||
|
||||
## 8. Cách giảm thiểu impact hiệu suất
|
||||
|
||||
### 8.1. Cache rewritten JSON ở BE
|
||||
|
||||
Khuyến nghị:
|
||||
|
||||
- cache in-memory hoặc Redis cho:
|
||||
- `goong_satellite.json`
|
||||
- `goong_map_web.json`
|
||||
- `sources/satellite.json`
|
||||
- `sources/base.json`
|
||||
- `sources/goong.json`
|
||||
|
||||
TTL có thể dài vì:
|
||||
|
||||
- style/source manifest không đổi liên tục
|
||||
|
||||
Tối ưu:
|
||||
|
||||
- chỉ rewrite một lần rồi reuse
|
||||
|
||||
### 8.2. Stream tile response
|
||||
|
||||
Cho tile route:
|
||||
|
||||
- không parse body
|
||||
- không buffer toàn bộ file vào memory nếu không cần
|
||||
- stream thẳng upstream -> client
|
||||
|
||||
### 8.3. Preserve cache headers
|
||||
|
||||
Với tile route, BE nên pass-through hoặc preserve:
|
||||
|
||||
- `Cache-Control`
|
||||
- `ETag`
|
||||
- `Last-Modified`
|
||||
- `Content-Type`
|
||||
|
||||
Nếu BE/ngược phía CDN có cache tốt, impact sẽ giảm rất nhiều.
|
||||
|
||||
### 8.4. Dùng CDN/reverse proxy trước BE nếu có thể
|
||||
|
||||
Nếu production có CDN/nginx/edge cache:
|
||||
|
||||
- cache mạnh cho:
|
||||
- rewritten style JSON
|
||||
- rewritten source manifests
|
||||
- tile responses
|
||||
|
||||
Điều này quan trọng hơn tối ưu code rewrite.
|
||||
|
||||
### 8.5. Đừng rewrite tile mỗi request theo kiểu string building phức tạp
|
||||
|
||||
Nên:
|
||||
|
||||
- rewrite `tiles[]` một lần ở source manifest
|
||||
- tile route chỉ resolve path đơn giản và forward
|
||||
|
||||
Không nên:
|
||||
|
||||
- parse lại manifest ở mỗi tile request
|
||||
|
||||
## 9. Recommendation thực dụng
|
||||
|
||||
Nếu team BE muốn giải pháp cân bằng giữa bảo mật và hiệu suất:
|
||||
|
||||
### Option A. Full proxy, full rewrite
|
||||
|
||||
BE cover:
|
||||
|
||||
1. style JSON
|
||||
2. source manifests
|
||||
3. tiles
|
||||
|
||||
Ưu điểm:
|
||||
|
||||
- key không lộ ra browser
|
||||
- FE không cần biết upstream Goong
|
||||
|
||||
Nhược điểm:
|
||||
|
||||
- BE chịu toàn bộ traffic tile
|
||||
|
||||
### Option B. Hybrid
|
||||
|
||||
BE cover:
|
||||
|
||||
1. style JSON
|
||||
2. source manifests
|
||||
|
||||
Nhưng không rewrite `tiles[]`
|
||||
|
||||
Ưu điểm:
|
||||
|
||||
- BE nhẹ hơn
|
||||
|
||||
Nhược điểm:
|
||||
|
||||
- key vẫn lộ ở tile request
|
||||
|
||||
Kết luận:
|
||||
|
||||
- nếu ưu tiên bảo mật key thật sự: dùng **Option A**
|
||||
- nếu ưu tiên hiệu suất hơn và chấp nhận domain restrictions của Goong: dùng **Option B**
|
||||
|
||||
## 10. Recommendation cho codebase hiện tại
|
||||
|
||||
Với frontend hiện tại, hướng hợp lý nhất là:
|
||||
|
||||
1. giữ nguyên FE logic parse style/source như hiện nay
|
||||
2. chuyển các URL Goong ở `config.ts` sang endpoint nội bộ BE
|
||||
3. để BE rewrite:
|
||||
- `sources.*.url`
|
||||
- `tiles[]`
|
||||
4. để BE stream tile response
|
||||
5. cache rewritten JSON ở BE
|
||||
|
||||
Nói ngắn:
|
||||
|
||||
- rewrite JSON: nên làm
|
||||
- rewrite tile URLs: bắt buộc nếu muốn giấu key
|
||||
- proxy tile: phần tốn hiệu suất nhất
|
||||
- muốn bù hiệu suất: phải dùng cache/stream/CDN tốt
|
||||
|
||||
## 11. Checklist cho team BE
|
||||
|
||||
1. Tạo route proxy cho 2 style JSON
|
||||
2. Tạo route proxy cho 3 source manifests
|
||||
3. Rewrite `sources.*.url` trong style JSON
|
||||
4. Rewrite `tiles[]` trong source manifests
|
||||
5. Tạo route proxy tile generic
|
||||
6. Stream tile response
|
||||
7. Preserve cache headers
|
||||
8. Cache rewritten JSON
|
||||
9. Kiểm tra browser không còn request trực tiếp `tiles.goong.io`
|
||||
@@ -0,0 +1,99 @@
|
||||
# Goong Satellite Structure
|
||||
|
||||
Nguồn JSON gốc được tải về tại:
|
||||
|
||||
- `FrontEndUser/tmp/goong-styles/goong_satellite.json`
|
||||
|
||||
File này là style satellite. Nó vẫn có boundary và labels, nhưng ít lớp nước hơn `goong_map_web.json`.
|
||||
|
||||
## Mermaid overview
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
ROOT[goong_satellite.json]
|
||||
|
||||
ROOT --> S0[source: satellite]
|
||||
ROOT --> S1[source: base]
|
||||
ROOT --> S2[source: composite]
|
||||
|
||||
S1 --> B1[source-layer: boundary]
|
||||
S1 --> B2[source-layer: worldcountriespoints]
|
||||
S1 --> B3[source-layer: worldnationalcapitals]
|
||||
|
||||
S2 --> C1[source-layer: vietnam_administrator]
|
||||
S2 --> C2[source-layer: streets_label]
|
||||
|
||||
B1 --> BL0[boundary-land-type-0 / type-0-bg]
|
||||
B1 --> BL1[boundary-land-type-1 / type-1-bg]
|
||||
B1 --> BL2[boundary-land-type-2 / type-2-bg]
|
||||
|
||||
B2 --> PC1[place-country-1]
|
||||
B2 --> PC2[place-country-2]
|
||||
|
||||
B3 --> CAP0[place-city-capital]
|
||||
|
||||
C1 --> VA0[place-city-capital-vietnam]
|
||||
C1 --> VA1[place-city1 / place-city2]
|
||||
C1 --> VA2[place-town1 / place-town2]
|
||||
C1 --> VA3[place-suburb / borough / neighbourhood]
|
||||
C1 --> VA4[place-village]
|
||||
|
||||
C2 --> RD0[highway-name-minor]
|
||||
C2 --> RD1[highway-name-medium]
|
||||
C2 --> RD2[highway-name-major]
|
||||
```
|
||||
|
||||
## Boundary layers
|
||||
|
||||
Các layer boundary nổi bật:
|
||||
|
||||
- `boundary-land-type-0-bg`
|
||||
- `boundary-land-type-0`
|
||||
- `boundary-land-type-1-bg`
|
||||
- `boundary-land-type-1`
|
||||
- `boundary-land-type-2-bg`
|
||||
- `boundary-land-type-2`
|
||||
|
||||
Minzoom quan sát được:
|
||||
|
||||
- `type-0`: từ zoom `1`
|
||||
- `type-1`: từ zoom `5`
|
||||
- `type-2-bg`: từ zoom `7`
|
||||
- `type-2`: từ zoom `7`
|
||||
|
||||
## Place labels
|
||||
|
||||
Labels hữu ích:
|
||||
|
||||
- `place-country-1`
|
||||
- `place-country-2`
|
||||
- `place-city-capital`
|
||||
- `place-city-capital-vietnam`
|
||||
- `place-city1`
|
||||
- `place-city2`
|
||||
- `place-town1`
|
||||
- `place-town2`
|
||||
|
||||
Labels dễ gây rối:
|
||||
|
||||
- `highway-name-*`
|
||||
- `place-suburb*`
|
||||
- `place-neighbourhood*`
|
||||
- `place-village`
|
||||
|
||||
## Khác biệt thực dụng so với goong_map_web
|
||||
|
||||
- Có `source: satellite`
|
||||
- Boundary vẫn hiện diện rõ
|
||||
- Labels hành chính vẫn có
|
||||
- Không lộ ra nhóm water chi tiết rõ như `goong_map_web`
|
||||
- Phù hợp làm raster/satellite nền hơn là style để dò water layers
|
||||
|
||||
## Gợi ý dùng thực tế
|
||||
|
||||
- Dùng `goong_satellite.json` cho nền satellite
|
||||
- Dùng `goong_map_web.json` để dò:
|
||||
- water
|
||||
- water labels
|
||||
- boundary theo cấp
|
||||
- labels hành chính
|
||||
Reference in New Issue
Block a user