add all
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- .dockerfile +19 -0
- .gitignore +35 -0
- README.md +96 -11
- app.py +95 -0
- prepare_solid_data.py +113 -0
- requirements.txt +8 -0
- retrieval_app.py +128 -0
- solid_data.py +240 -0
- solid_data/GloAspectClE_5min/GloAspectClE_5min.npz +3 -0
- solid_data/GloAspectClE_5min/heatmap.png +0 -0
- solid_data/GloAspectClE_5min/metadata.json +1 -0
- solid_data/GloAspectClN_5min/GloAspectClN_5min.npz +3 -0
- solid_data/GloAspectClN_5min/heatmap.png +0 -0
- solid_data/GloAspectClN_5min/metadata.json +1 -0
- solid_data/GloAspectClS_5min/GloAspectClS_5min.npz +3 -0
- solid_data/GloAspectClS_5min/heatmap.png +0 -0
- solid_data/GloAspectClS_5min/metadata.json +1 -0
- solid_data/GloAspectClU_5min/GloAspectClU_5min.npz +3 -0
- solid_data/GloAspectClU_5min/heatmap.png +0 -0
- solid_data/GloAspectClU_5min/metadata.json +1 -0
- solid_data/GloAspectClW_5min/GloAspectClW_5min.npz +3 -0
- solid_data/GloAspectClW_5min/heatmap.png +0 -0
- solid_data/GloAspectClW_5min/metadata.json +1 -0
- solid_data/GloElev_5min/GloElev_5min.npz +3 -0
- solid_data/GloElev_5min/heatmap.png +0 -0
- solid_data/GloElev_5min/metadata.json +1 -0
- solid_data/GloLand_5min/GloLand_5min.npz +3 -0
- solid_data/GloLand_5min/heatmap.png +0 -0
- solid_data/GloLand_5min/metadata.json +1 -0
- solid_data/GloSlopesCl1_5min/GloSlopesCl1_5min.npz +3 -0
- solid_data/GloSlopesCl1_5min/heatmap.png +0 -0
- solid_data/GloSlopesCl1_5min/metadata.json +1 -0
- solid_data/GloSlopesCl2_5min/GloSlopesCl2_5min.npz +3 -0
- solid_data/GloSlopesCl2_5min/heatmap.png +0 -0
- solid_data/GloSlopesCl2_5min/metadata.json +1 -0
- solid_data/GloSlopesCl3_5min/GloSlopesCl3_5min.npz +3 -0
- solid_data/GloSlopesCl3_5min/heatmap.png +0 -0
- solid_data/GloSlopesCl3_5min/metadata.json +1 -0
- solid_data/GloSlopesCl4_5min/GloSlopesCl4_5min.npz +3 -0
- solid_data/GloSlopesCl4_5min/heatmap.png +0 -0
- solid_data/GloSlopesCl4_5min/metadata.json +1 -0
- solid_data/GloSlopesCl5_5min/GloSlopesCl5_5min.npz +3 -0
- solid_data/GloSlopesCl5_5min/heatmap.png +0 -0
- solid_data/GloSlopesCl5_5min/metadata.json +1 -0
- solid_data/GloSlopesCl6_5min/GloSlopesCl6_5min.npz +3 -0
- solid_data/GloSlopesCl6_5min/heatmap.png +0 -0
- solid_data/GloSlopesCl6_5min/metadata.json +1 -0
- solid_data/GloSlopesCl7_5min/GloSlopesCl7_5min.npz +3 -0
- solid_data/GloSlopesCl7_5min/heatmap.png +0 -0
- solid_data/GloSlopesCl7_5min/metadata.json +1 -0
.dockerfile
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
FROM python:3.9-slim
|
| 2 |
+
|
| 3 |
+
WORKDIR /app
|
| 4 |
+
|
| 5 |
+
# Cài đặt các gói phụ thuộc
|
| 6 |
+
COPY requirements.txt .
|
| 7 |
+
RUN pip install --no-cache-dir -r requirements.txt
|
| 8 |
+
|
| 9 |
+
# Sao chép mã nguồn vào container
|
| 10 |
+
COPY . .
|
| 11 |
+
|
| 12 |
+
# Cài đặt biến môi trường
|
| 13 |
+
ENV SOLID_DATA_PATH="/app/solid_data"
|
| 14 |
+
|
| 15 |
+
# Mở cổng cho ứng dụng
|
| 16 |
+
EXPOSE 7860
|
| 17 |
+
|
| 18 |
+
# Khởi chạy ứng dụng
|
| 19 |
+
CMD ["python", "app.py"]
|
.gitignore
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Byte-compiled / optimized / DLL files
|
| 2 |
+
__pycache__/
|
| 3 |
+
*.py[cod]
|
| 4 |
+
*$py.class
|
| 5 |
+
|
| 6 |
+
# Distribution / packaging
|
| 7 |
+
dist/
|
| 8 |
+
build/
|
| 9 |
+
*.egg-info/
|
| 10 |
+
|
| 11 |
+
# Virtual environments
|
| 12 |
+
venv/
|
| 13 |
+
env/
|
| 14 |
+
ENV/
|
| 15 |
+
|
| 16 |
+
# IDE specific files
|
| 17 |
+
.idea/
|
| 18 |
+
.vscode/
|
| 19 |
+
*.swp
|
| 20 |
+
*.swo
|
| 21 |
+
|
| 22 |
+
# Local development settings
|
| 23 |
+
.env
|
| 24 |
+
.env.local
|
| 25 |
+
|
| 26 |
+
# Logs
|
| 27 |
+
logs/
|
| 28 |
+
*.log
|
| 29 |
+
|
| 30 |
+
# Data files (uncomment if you don't want to commit data files)
|
| 31 |
+
# solid_data/
|
| 32 |
+
# *.npz
|
| 33 |
+
|
| 34 |
+
# Jupyter notebook checkpoints
|
| 35 |
+
.ipynb_checkpoints/
|
README.md
CHANGED
|
@@ -1,11 +1,96 @@
|
|
| 1 |
-
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Solid Data API
|
| 2 |
+
|
| 3 |
+
API để tra cứu dữ liệu dựa trên tọa độ latitude và longitude.
|
| 4 |
+
|
| 5 |
+
## Giới thiệu
|
| 6 |
+
|
| 7 |
+
API này cho phép người dùng truy vấn dữ liệu từ các tệp SolidData dựa trên tọa độ vị trí (latitude và longitude). API sẽ trả về chuỗi giá trị từ tất cả các lớp dữ liệu tại vị trí đó.
|
| 8 |
+
|
| 9 |
+
## Cài đặt
|
| 10 |
+
|
| 11 |
+
### Yêu cầu
|
| 12 |
+
|
| 13 |
+
- Python 3.8 trở lên
|
| 14 |
+
- Các thư viện được liệt kê trong `requirements.txt`
|
| 15 |
+
|
| 16 |
+
### Cách cài đặt
|
| 17 |
+
|
| 18 |
+
1. Clone repository
|
| 19 |
+
```bash
|
| 20 |
+
git clone https://huggingface.co/spaces/l1aF2027/Solid-Data
|
| 21 |
+
cd Solid-Data
|
| 22 |
+
```
|
| 23 |
+
|
| 24 |
+
2. Cài đặt các thư viện phụ thuộc
|
| 25 |
+
```bash
|
| 26 |
+
pip install -r requirements.txt
|
| 27 |
+
```
|
| 28 |
+
|
| 29 |
+
3. Đặt dữ liệu SolidData vào thư mục `solid_data/`
|
| 30 |
+
|
| 31 |
+
4. Chạy ứng dụng
|
| 32 |
+
```bash
|
| 33 |
+
python app.py
|
| 34 |
+
```
|
| 35 |
+
|
| 36 |
+
## Cách sử dụng API
|
| 37 |
+
|
| 38 |
+
### Endpoint
|
| 39 |
+
|
| 40 |
+
- `POST /get_data`: Trả về giá trị dữ liệu tại tọa độ được chỉ định
|
| 41 |
+
|
| 42 |
+
### Cấu trúc request
|
| 43 |
+
|
| 44 |
+
```json
|
| 45 |
+
{
|
| 46 |
+
"latitude": 10.123,
|
| 47 |
+
"longitude": 106.456
|
| 48 |
+
}
|
| 49 |
+
```
|
| 50 |
+
|
| 51 |
+
### Cấu trúc response
|
| 52 |
+
|
| 53 |
+
```json
|
| 54 |
+
{
|
| 55 |
+
"values": "[0.0, 1.0, 0.0, 0.0, ...]",
|
| 56 |
+
"status": "success"
|
| 57 |
+
}
|
| 58 |
+
```
|
| 59 |
+
|
| 60 |
+
### Ví dụ sử dụng với curl
|
| 61 |
+
|
| 62 |
+
```bash
|
| 63 |
+
curl -X 'POST' \
|
| 64 |
+
'https://[your-username]-solid-data-api.hf.space/get_data' \
|
| 65 |
+
-H 'accept: application/json' \
|
| 66 |
+
-H 'Content-Type: application/json' \
|
| 67 |
+
-d '{"latitude": 10.123, "longitude": 106.456}'
|
| 68 |
+
```
|
| 69 |
+
|
| 70 |
+
### Endpoints khác
|
| 71 |
+
|
| 72 |
+
- `GET /health`: Kiểm tra trạng thái hoạt động của API
|
| 73 |
+
- `GET /data_info`: Lấy thông tin về các tệp dữ liệu đã được tải
|
| 74 |
+
|
| 75 |
+
## Triển khai trên Hugging Face Spaces
|
| 76 |
+
|
| 77 |
+
API này được thiết kế để dễ dàng triển khai trên Hugging Face Spaces. Các tệp cấu hình cần thiết đã được bao gồm:
|
| 78 |
+
- `Dockerfile`: Cấu hình Docker để xây dựng container
|
| 79 |
+
- `requirements.txt`: Danh sách các thư viện Python cần thiết
|
| 80 |
+
|
| 81 |
+
## Cấu trúc thư mục
|
| 82 |
+
|
| 83 |
+
```
|
| 84 |
+
.
|
| 85 |
+
├── app.py # Mã nguồn chính của API
|
| 86 |
+
├── retrieval_app.py # Module để tải và truy xuất dữ liệu
|
| 87 |
+
├── solid_data/ # Thư mục chứa dữ liệu (các tệp .npz)
|
| 88 |
+
├── requirements.txt # Danh sách các thư viện cần thiết
|
| 89 |
+
├── Dockerfile # Cấu hình Docker để triển khai
|
| 90 |
+
└── README.md # Tệp hướng dẫn này
|
| 91 |
+
```
|
| 92 |
+
|
| 93 |
+
## Lưu ý
|
| 94 |
+
|
| 95 |
+
- Đảm bảo rằng tệp `solid_data.py` chứa lớp `SolidData` đã được đặt trong thư mục làm việc
|
| 96 |
+
- Thư mục `solid_data/` phải chứa các tệp .npz có cấu trúc phù hợp
|
app.py
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from fastapi import FastAPI, HTTPException
|
| 2 |
+
import uvicorn
|
| 3 |
+
from pydantic import BaseModel
|
| 4 |
+
from typing import List, Optional
|
| 5 |
+
import numpy as np
|
| 6 |
+
import os
|
| 7 |
+
from tqdm.auto import tqdm
|
| 8 |
+
import pandas as pd
|
| 9 |
+
from solid_data import SolidData
|
| 10 |
+
from retrieval_app import RetrievalSolidData
|
| 11 |
+
|
| 12 |
+
# Khởi tạo FastAPI app
|
| 13 |
+
app = FastAPI(
|
| 14 |
+
title="Solid Data API",
|
| 15 |
+
description="API để tra cứu dữ liệu dựa trên tọa độ latitude và longitude",
|
| 16 |
+
version="1.0.0"
|
| 17 |
+
)
|
| 18 |
+
|
| 19 |
+
# Model dữ liệu đầu vào
|
| 20 |
+
class CoordinateRequest(BaseModel):
|
| 21 |
+
latitude: float
|
| 22 |
+
longitude: float
|
| 23 |
+
|
| 24 |
+
# Model dữ liệu đầu ra
|
| 25 |
+
class DataResponse(BaseModel):
|
| 26 |
+
values: str
|
| 27 |
+
status: str = "success"
|
| 28 |
+
|
| 29 |
+
# Biến global để lưu đối tượng RetrievalSolidData
|
| 30 |
+
solid_data_app = None
|
| 31 |
+
|
| 32 |
+
# Hàm khởi tạo dữ liệu - sẽ được gọi khi server khởi động
|
| 33 |
+
@app.on_event("startup")
|
| 34 |
+
async def startup_event():
|
| 35 |
+
global solid_data_app
|
| 36 |
+
solid_data_path = os.environ.get("SOLID_DATA_PATH", "./solid_data")
|
| 37 |
+
print(f"Khởi tạo dữ liệu từ thư mục: {solid_data_path}")
|
| 38 |
+
solid_data_app = RetrievalSolidData(solid_data_path)
|
| 39 |
+
print("Khởi tạo dữ liệu hoàn tất")
|
| 40 |
+
|
| 41 |
+
# API endpoint để lấy dữ liệu dựa trên tọa độ
|
| 42 |
+
@app.post("/get_data", response_model=DataResponse)
|
| 43 |
+
async def get_data(request: CoordinateRequest):
|
| 44 |
+
global solid_data_app
|
| 45 |
+
|
| 46 |
+
if solid_data_app is None:
|
| 47 |
+
raise HTTPException(status_code=500, detail="Hệ thống dữ liệu chưa được khởi tạo")
|
| 48 |
+
|
| 49 |
+
try:
|
| 50 |
+
# Lấy dữ liệu từ RetrievalSolidData
|
| 51 |
+
df_data = solid_data_app.get_data(request.longitude, request.latitude)
|
| 52 |
+
|
| 53 |
+
# Sắp xếp dữ liệu theo thứ tự như yêu cầu
|
| 54 |
+
new_order = ['Elevation', 'SlopesCl1', 'SlopesCl2', 'SlopesCl3', 'SlopesCl4', 'SlopesCl5', 'SlopesCl6',
|
| 55 |
+
'SlopesCl7', 'SlopesCl8', 'AspectClN', 'AspectClE', 'AspectClS', 'AspectClW', 'AspectClU',
|
| 56 |
+
'WAT', 'NVG', 'URB', 'GRS', 'FOR', 'CULTRF', 'CULTIR', 'CULT', 'sq1', 'sq2', 'sq3', 'sq4',
|
| 57 |
+
'sq5', 'sq6', 'sq7', 'LandMask']
|
| 58 |
+
|
| 59 |
+
df_data['name'] = pd.Categorical(df_data['name'], categories=new_order, ordered=True)
|
| 60 |
+
df_data = df_data.sort_values('name').reset_index(drop=True)
|
| 61 |
+
|
| 62 |
+
# Chuyển cột value thành một chuỗi dạng [x1, x2, ...]
|
| 63 |
+
values_list = df_data['value'].tolist()
|
| 64 |
+
values_str = str(values_list).replace("'", "")
|
| 65 |
+
|
| 66 |
+
return DataResponse(values=values_str)
|
| 67 |
+
|
| 68 |
+
except ValueError as e:
|
| 69 |
+
raise HTTPException(status_code=400, detail=f"Lỗi dữ liệu: {str(e)}")
|
| 70 |
+
except Exception as e:
|
| 71 |
+
raise HTTPException(status_code=500, detail=f"Lỗi hệ thống: {str(e)}")
|
| 72 |
+
|
| 73 |
+
# API endpoint để kiểm tra trạng thái
|
| 74 |
+
@app.get("/health")
|
| 75 |
+
async def health_check():
|
| 76 |
+
return {"status": "ok"}
|
| 77 |
+
|
| 78 |
+
# API endpoint để lấy thông tin về dữ liệu
|
| 79 |
+
@app.get("/data_info")
|
| 80 |
+
async def get_data_info():
|
| 81 |
+
global solid_data_app
|
| 82 |
+
|
| 83 |
+
if solid_data_app is None:
|
| 84 |
+
raise HTTPException(status_code=500, detail="Hệ thống dữ liệu chưa được khởi tạo")
|
| 85 |
+
|
| 86 |
+
try:
|
| 87 |
+
info_df = solid_data_app.get_data_info()
|
| 88 |
+
return {"data_info": info_df.to_dict(orient="records")}
|
| 89 |
+
except Exception as e:
|
| 90 |
+
raise HTTPException(status_code=500, detail=f"Lỗi hệ thống: {str(e)}")
|
| 91 |
+
|
| 92 |
+
# Chạy ứng dụng nếu file này được thực thi trực tiếp
|
| 93 |
+
if __name__ == "__main__":
|
| 94 |
+
port = int(os.environ.get("PORT", 7860)) # Hugging Face Spaces sử dụng cổng 7860
|
| 95 |
+
uvicorn.run("app:app", host="0.0.0.0", port=port, reload=True)
|
prepare_solid_data.py
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json
|
| 2 |
+
import os
|
| 3 |
+
from solid_data import SolidData
|
| 4 |
+
|
| 5 |
+
class ProcessingSolidData:
|
| 6 |
+
def __init__(self, input_root="data", output_root="solid_data", data_info=None):
|
| 7 |
+
"""
|
| 8 |
+
Khởi tạo đối tượng GenSolidData với thông tin thư mục và ánh xạ.
|
| 9 |
+
- input_root: Thư mục chứa các tệp ASC (ví dụ: 'data').
|
| 10 |
+
- output_root: Thư mục lưu kết quả (ví dụ: 'solid_data').
|
| 11 |
+
- data_info: Dictionary chứa filename, name và dtype dữ liệu.
|
| 12 |
+
"""
|
| 13 |
+
self.input_root = input_root
|
| 14 |
+
self.output_root = output_root
|
| 15 |
+
# Kiểm tra nếu data_info không được cung cấp, tạo dictionary trống
|
| 16 |
+
self.data_info = data_info if data_info else {}
|
| 17 |
+
|
| 18 |
+
def load_data_info(self, filename="data_info.json"):
|
| 19 |
+
"""
|
| 20 |
+
Đọc file JSON và trả về dictionary ánh xạ từ filename sang name và dtype.
|
| 21 |
+
- filename: Đường dẫn đến file JSON chứa bộ ánh xạ.
|
| 22 |
+
"""
|
| 23 |
+
with open(filename, "r") as f:
|
| 24 |
+
self.data_info = json.load(f)
|
| 25 |
+
return self.data_info
|
| 26 |
+
|
| 27 |
+
def process_asc_files(self):
|
| 28 |
+
"""
|
| 29 |
+
Duyệt qua tất cả tệp .asc trong input_root, đọc và lưu vào output_root với cấu trúc tương ứng.
|
| 30 |
+
"""
|
| 31 |
+
# Duyệt qua tất cả thư mục và tệp trong input_root
|
| 32 |
+
for root, dirs, files in os.walk(self.input_root):
|
| 33 |
+
for file in files:
|
| 34 |
+
if file.endswith(".asc"): # Chỉ xử lý tệp .asc
|
| 35 |
+
# Đường dẫn đầy đủ của tệp .asc
|
| 36 |
+
asc_path = os.path.join(root, file)
|
| 37 |
+
|
| 38 |
+
# Tên tệp không có đuôi (sẽ dùng làm name và tên thư mục lưu)
|
| 39 |
+
file_name = os.path.splitext(file)[0]
|
| 40 |
+
|
| 41 |
+
# Ánh xạ file_name sang name và dtype nếu có trong dictionary
|
| 42 |
+
data_info = self.data_info.get(file_name, {"name": file_name, "dtype": "float", "description": "Unknown"})
|
| 43 |
+
name = data_info["name"]
|
| 44 |
+
dtype = data_info["dtype"]
|
| 45 |
+
description = data_info["description"]
|
| 46 |
+
|
| 47 |
+
# Đường dẫn tương đối từ input_root
|
| 48 |
+
relative_path = os.path.relpath(root, self.input_root)
|
| 49 |
+
if relative_path == ".":
|
| 50 |
+
output_dir = os.path.join(self.output_root, file_name)
|
| 51 |
+
else:
|
| 52 |
+
output_dir = os.path.join(self.output_root, relative_path, file_name)
|
| 53 |
+
|
| 54 |
+
print(f"Đang xử lý: {asc_path} -> {output_dir}")
|
| 55 |
+
|
| 56 |
+
try:
|
| 57 |
+
# Đọc tệp .asc và tạo SolidData với name từ ánh xạ
|
| 58 |
+
data = SolidData.read_asc(asc_path, name=name, dtype=dtype, description=description)
|
| 59 |
+
|
| 60 |
+
try:
|
| 61 |
+
data.plot(os.path.join(output_dir, "heatmap.png"))
|
| 62 |
+
except Exception as e:
|
| 63 |
+
print(f"Lỗi khi xử lý {asc_path}: {str(e)}")
|
| 64 |
+
|
| 65 |
+
# Lưu ma trận và metadata vào output_dir
|
| 66 |
+
data.save_dok_matrix(output_dir)
|
| 67 |
+
|
| 68 |
+
print(f"Đã lưu thành công: {output_dir}")
|
| 69 |
+
except Exception as e:
|
| 70 |
+
print(f"Lỗi khi xử lý {asc_path}: {str(e)}")
|
| 71 |
+
|
| 72 |
+
|
| 73 |
+
def generate_data_info_json(self, output_file="data_info.json"):
|
| 74 |
+
"""
|
| 75 |
+
Tạo file JSON ánh xạ filename sang name và dtype cho tất cả các tệp .asc trong thư mục input_root.
|
| 76 |
+
- output_file: Đường dẫn đến file JSON sẽ được tạo.
|
| 77 |
+
"""
|
| 78 |
+
data_info = {}
|
| 79 |
+
|
| 80 |
+
# Duyệt qua tất cả tệp .asc trong input_root
|
| 81 |
+
for root, dirs, files in os.walk(self.input_root):
|
| 82 |
+
for file in files:
|
| 83 |
+
if file.endswith(".asc"): # Chỉ xử lý tệp .asc
|
| 84 |
+
file_name = os.path.splitext(file)[0]
|
| 85 |
+
# Mặc định name và dtype
|
| 86 |
+
name = file_name # Dùng file_name làm name mặc định
|
| 87 |
+
dtype = "float" # Kiểu dữ liệu mặc định
|
| 88 |
+
|
| 89 |
+
# Thêm vào dictionary
|
| 90 |
+
data_info[file_name] = {"name": name, "dtype": dtype, "description": "Unknown"}
|
| 91 |
+
|
| 92 |
+
# Lưu dictionary vào file JSON
|
| 93 |
+
with open(output_file, "w") as f:
|
| 94 |
+
json.dump(data_info, f, indent=4)
|
| 95 |
+
|
| 96 |
+
print(f"File JSON đã được tạo: {output_file}")
|
| 97 |
+
|
| 98 |
+
|
| 99 |
+
if __name__ == "__main__":
|
| 100 |
+
# Khởi tạo đối tượng GenSolidData
|
| 101 |
+
input_dir = "./data"
|
| 102 |
+
output_dir = "./solid_data"
|
| 103 |
+
|
| 104 |
+
gen_solid_data = ProcessingSolidData(input_root=input_dir, output_root=output_dir)
|
| 105 |
+
|
| 106 |
+
# Tạo file JSON chứa ánh xạ từ filename sang name và dtype
|
| 107 |
+
# gen_solid_data.generate_data_info_json("data_info.json")
|
| 108 |
+
|
| 109 |
+
# Đọc bộ ánh xạ từ file JSON
|
| 110 |
+
gen_solid_data.load_data_info("data_info.json")
|
| 111 |
+
|
| 112 |
+
# Gọi phương thức để xử lý các tệp ASC
|
| 113 |
+
gen_solid_data.process_asc_files()
|
requirements.txt
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
fastapi==0.103.1
|
| 2 |
+
uvicorn==0.23.2
|
| 3 |
+
pydantic==2.3.0
|
| 4 |
+
numpy==1.24.3
|
| 5 |
+
pandas==2.0.3
|
| 6 |
+
scipy==1.10.1
|
| 7 |
+
tqdm==4.66.1
|
| 8 |
+
python-multipart==0.0.6
|
retrieval_app.py
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
from tqdm.auto import tqdm
|
| 3 |
+
import pandas as pd
|
| 4 |
+
from solid_data import SolidData
|
| 5 |
+
|
| 6 |
+
class RetrievalSolidData:
|
| 7 |
+
def __init__(self, directory):
|
| 8 |
+
"""
|
| 9 |
+
Khởi tạo đối tượng với thư mục chứa các tệp dữ liệu Solid.
|
| 10 |
+
- directory: Thư mục chứa dữ liệu Solid.
|
| 11 |
+
"""
|
| 12 |
+
self.directory = directory
|
| 13 |
+
self.solid_data_dict = self.load_solid_data_from_directory()
|
| 14 |
+
|
| 15 |
+
def load_solid_data_from_directory(self):
|
| 16 |
+
"""
|
| 17 |
+
Duyệt qua thư mục, tìm các tệp .npz và sử dụng hàm load_dok_matrix
|
| 18 |
+
để tải và lưu các đối tượng SolidData vào một dictionary.
|
| 19 |
+
- Trả về: Dictionary với tên dữ liệu là key và đối tượng SolidData là value.
|
| 20 |
+
"""
|
| 21 |
+
solid_data_dict = {}
|
| 22 |
+
|
| 23 |
+
# Tạo danh sách tất cả file .npz cần xử lý
|
| 24 |
+
npz_files = []
|
| 25 |
+
for root, dirs, files in os.walk(self.directory):
|
| 26 |
+
for file in files:
|
| 27 |
+
if file.endswith(".npz"):
|
| 28 |
+
npz_files.append((root, file))
|
| 29 |
+
|
| 30 |
+
# Dùng tqdm để hiển thị tiến trình
|
| 31 |
+
for root, file in tqdm(npz_files, desc="Đang tải dữ liệu", unit="file"):
|
| 32 |
+
file_name = os.path.splitext(file)[0]
|
| 33 |
+
try:
|
| 34 |
+
print(f"Đang lấy dữ liệu từ {root}")
|
| 35 |
+
solid_data_dict[file_name] = SolidData.load_dok_matrix(root)
|
| 36 |
+
except Exception as e:
|
| 37 |
+
print(f"\nKhông thể tải dữ liệu từ {file}: {str(e)}")
|
| 38 |
+
|
| 39 |
+
return solid_data_dict
|
| 40 |
+
|
| 41 |
+
def get_data(self, lon, lat):
|
| 42 |
+
"""
|
| 43 |
+
Duyệt qua tất cả các SolidData trong dictionary và trả về giá trị tại vị trí lon, lat.
|
| 44 |
+
- lon, lat: Tọa độ lon và lat cần tra cứu.
|
| 45 |
+
- Trả về: Dictionary chứa tên dữ liệu là key và giá trị tại (lon, lat) là value.
|
| 46 |
+
"""
|
| 47 |
+
result_dict = {'name': [], 'value': []}
|
| 48 |
+
for file_name, solid_data in self.solid_data_dict.items():
|
| 49 |
+
try:
|
| 50 |
+
name = solid_data.name
|
| 51 |
+
value = solid_data[lon, lat]
|
| 52 |
+
result_dict['name'].append(name)
|
| 53 |
+
result_dict['value'].append(value)
|
| 54 |
+
except ValueError as e:
|
| 55 |
+
result_dict['name'].append(name)
|
| 56 |
+
result_dict['value'].append(f"Lỗi: {e}")
|
| 57 |
+
|
| 58 |
+
# Chuyển kết quả sang DataFrame
|
| 59 |
+
df = pd.DataFrame(result_dict)
|
| 60 |
+
return df
|
| 61 |
+
|
| 62 |
+
def get_data_info(self):
|
| 63 |
+
"""
|
| 64 |
+
Chuyển thông tin từ solid_data_dict thành DataFrame
|
| 65 |
+
|
| 66 |
+
Returns:
|
| 67 |
+
pd.DataFrame: DataFrame chứa các thuộc tính của các đối tượng SolidData
|
| 68 |
+
"""
|
| 69 |
+
data = []
|
| 70 |
+
|
| 71 |
+
for file_name, solid_data in self.solid_data_dict.items():
|
| 72 |
+
data.append({
|
| 73 |
+
'file_name': file_name,
|
| 74 |
+
'name': solid_data.name,
|
| 75 |
+
'rows': solid_data.rows,
|
| 76 |
+
'columns': solid_data.cols,
|
| 77 |
+
'x_corner': solid_data.x_corner,
|
| 78 |
+
'y_corner': solid_data.y_corner,
|
| 79 |
+
'cell_size': solid_data.cellsize,
|
| 80 |
+
'description': getattr(solid_data, 'description', 'Unknown') # Thêm trường description nếu có
|
| 81 |
+
})
|
| 82 |
+
|
| 83 |
+
# Tạo DataFrame từ danh sách dữ liệu
|
| 84 |
+
df = pd.DataFrame(data)
|
| 85 |
+
|
| 86 |
+
# Sắp xếp các cột (tuỳ chọn)
|
| 87 |
+
columns_order = ['name', 'rows', 'columns', 'x_corner', 'y_corner', 'cell_size', 'description']
|
| 88 |
+
df = df[columns_order]
|
| 89 |
+
|
| 90 |
+
return df
|
| 91 |
+
|
| 92 |
+
if __name__ == "__main__":
|
| 93 |
+
solid_data_path = "./solid_data"
|
| 94 |
+
app = RetrievalSolidData(solid_data_path)
|
| 95 |
+
|
| 96 |
+
while True:
|
| 97 |
+
print("\n" + "="*50)
|
| 98 |
+
print("NHẬP TỌA ĐỘ ĐỂ TRA CỨU DỮ LIỆU")
|
| 99 |
+
print("="*50)
|
| 100 |
+
|
| 101 |
+
try:
|
| 102 |
+
# Nhập tọa độ từ người dùng
|
| 103 |
+
lat = float(input("Nhập vĩ độ (latitude): "))
|
| 104 |
+
lon = float(input("Nhập kinh độ (longitude): "))
|
| 105 |
+
|
| 106 |
+
# Lấy dữ liệu
|
| 107 |
+
df_data = app.get_data(lon, lat)
|
| 108 |
+
new_order = ['Elevation', 'SlopesCl1', 'SlopesCl2', 'SlopesCl3', 'SlopesCl4', 'SlopesCl5', 'SlopesCl6', 'SlopesCl7', 'SlopesCl8', 'AspectClN', 'AspectClE', 'AspectClS', 'AspectClW', 'AspectClU', 'WAT', 'NVG', 'URB', 'GRS', 'FOR', 'CULTRF', 'CULTIR', 'CULT', 'sq1', 'sq2', 'sq3', 'sq4', 'sq5', 'sq6', 'sq7', 'LandMask']
|
| 109 |
+
df_data['name'] = pd.Categorical(df_data['name'], categories=new_order, ordered=True)
|
| 110 |
+
|
| 111 |
+
# Sắp xếp theo cột 'name'
|
| 112 |
+
df_data = df_data.sort_values('name').reset_index(drop=True)
|
| 113 |
+
|
| 114 |
+
# Hiển thị kết quả
|
| 115 |
+
print("\nKẾT QUẢ TRA CỨU:")
|
| 116 |
+
print("="*50)
|
| 117 |
+
print(f"Tọa độ: Lat={lat}, Lon={lon}")
|
| 118 |
+
print("\nGiá trị tại tọa độ:")
|
| 119 |
+
print(df_data)
|
| 120 |
+
print(df_data['value'])
|
| 121 |
+
except ValueError:
|
| 122 |
+
print("Lỗi: Vui lòng nhập số hợp lệ cho tọa độ!")
|
| 123 |
+
except KeyboardInterrupt:
|
| 124 |
+
print("\nKết thúc chương trình...")
|
| 125 |
+
break
|
| 126 |
+
except Exception as e:
|
| 127 |
+
print(f"Lỗi: {str(e)}")
|
| 128 |
+
|
solid_data.py
ADDED
|
@@ -0,0 +1,240 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import numpy as np
|
| 2 |
+
from scipy.sparse import dok_matrix, save_npz, load_npz
|
| 3 |
+
from tqdm.auto import tqdm
|
| 4 |
+
import json
|
| 5 |
+
import os
|
| 6 |
+
|
| 7 |
+
class LonLatMapper:
|
| 8 |
+
def __init__(self, x_corner, y_corner, cellsize, rows, cols):
|
| 9 |
+
"""
|
| 10 |
+
Khởi tạo bộ ánh xạ lon-lat sang x-y và ngược lại.
|
| 11 |
+
- x_corner: Tọa độ x (kinh độ) của góc dưới-trái ô đầu tiên.
|
| 12 |
+
- y_corner: Tọa độ y (vĩ độ) của góc dưới-trái ô đầu tiên.
|
| 13 |
+
- cellsize: Kích thước ô lưới (độ hoặc mét).
|
| 14 |
+
- rows: Số hàng của lưới.
|
| 15 |
+
- cols: Số cột của lưới.
|
| 16 |
+
"""
|
| 17 |
+
self.x_corner = x_corner
|
| 18 |
+
self.y_corner = y_corner
|
| 19 |
+
self.cellsize = cellsize
|
| 20 |
+
self.rows = rows
|
| 21 |
+
self.cols = cols
|
| 22 |
+
|
| 23 |
+
def lonlat2xy(self, lon, lat):
|
| 24 |
+
"""
|
| 25 |
+
Ánh xạ (lon, lat) sang chỉ số ô lưới (x, y).
|
| 26 |
+
- lon: Kinh độ (hoặc tọa độ x trong hệ chiếu).
|
| 27 |
+
- lat: Vĩ độ (hoặc tọa độ y trong hệ chiếu).
|
| 28 |
+
- Trả về: (x, y) - chỉ số cột và hàng.
|
| 29 |
+
"""
|
| 30 |
+
x = int(round((lon - self.x_corner) / self.cellsize))
|
| 31 |
+
y = abs(int(round((lat + self.y_corner) / self.cellsize)))
|
| 32 |
+
|
| 33 |
+
# Kiểm tra giới hạn
|
| 34 |
+
if not (0 <= x < self.cols and 0 <= y < self.rows):
|
| 35 |
+
raise ValueError(f"Chỉ số (x={x}, y={y}) ngoài phạm vi lưới ({self.cols}x{self.rows})")
|
| 36 |
+
|
| 37 |
+
return x, y
|
| 38 |
+
|
| 39 |
+
def xy2lonlat(self, x, y):
|
| 40 |
+
"""
|
| 41 |
+
Ánh xạ chỉ số ô lưới (x, y) sang (lon, lat).
|
| 42 |
+
- x: Chỉ số cột.
|
| 43 |
+
- y: Chỉ số hàng.
|
| 44 |
+
- Trả về: (lon, lat) - kinh độ và vĩ độ.
|
| 45 |
+
"""
|
| 46 |
+
# Kiểm tra giới hạn
|
| 47 |
+
if not (0 <= x < self.cols and 0 <= y < self.rows):
|
| 48 |
+
raise ValueError(f"Chỉ số (x={x}, y={y}) ngoài phạm vi lưới ({self.cols}x{self.rows})")
|
| 49 |
+
|
| 50 |
+
lon = self.x_corner + x * self.cellsize
|
| 51 |
+
lat = abs(self.y_corner) - y * self.cellsize
|
| 52 |
+
return lon, lat
|
| 53 |
+
|
| 54 |
+
def lonlat2xy_batch(self, lons, lats):
|
| 55 |
+
"""
|
| 56 |
+
Ánh xạ hàng loạt (lon, lat) sang (x, y).
|
| 57 |
+
- lons, lats: Mảng hoặc danh sách kinh độ và vĩ độ.
|
| 58 |
+
- Trả về: Danh sách (x, y).
|
| 59 |
+
"""
|
| 60 |
+
lons = np.asarray(lons)
|
| 61 |
+
lats = np.asarray(lats)
|
| 62 |
+
x = np.round((lons - self.x_corner) / self.cellsize).astype(int)
|
| 63 |
+
y = np.abs(np.round((lats + self.y_corner) / self.cellsize)).astype(int)
|
| 64 |
+
|
| 65 |
+
# Kiểm tra giới hạn
|
| 66 |
+
valid = (x >= 0) & (x < self.cols) & (y >= 0) & (y < self.rows)
|
| 67 |
+
if not valid.all():
|
| 68 |
+
raise ValueError("Một số tọa độ ngoài phạm vi lưới")
|
| 69 |
+
|
| 70 |
+
return list(zip(x, y))
|
| 71 |
+
|
| 72 |
+
def xy2lonlat_batch(self, xs, ys):
|
| 73 |
+
"""
|
| 74 |
+
Ánh xạ hàng loạt (x, y) sang (lon, lat).
|
| 75 |
+
- xs, ys: Mảng hoặc danh sách chỉ số cột và hàng.
|
| 76 |
+
- Trả về: Danh sách (lon, lat).
|
| 77 |
+
"""
|
| 78 |
+
xs = np.asarray(xs)
|
| 79 |
+
ys = np.asarray(ys)
|
| 80 |
+
|
| 81 |
+
# Kiểm tra giới hạn
|
| 82 |
+
valid = (xs >= 0) & (xs < self.cols) & (ys >= 0) & (ys < self.rows)
|
| 83 |
+
if not valid.all():
|
| 84 |
+
raise ValueError("Một số chỉ số ngoài phạm vi lưới")
|
| 85 |
+
|
| 86 |
+
lons = self.x_corner + xs * self.cellsize
|
| 87 |
+
lats = abs(self.y_corner) - ys * self.cellsize
|
| 88 |
+
return list(zip(lons, lats))
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
class SolidData():
|
| 92 |
+
def __init__(self, M, rows, cols, x_corner, y_corner, cellsize, name, description="Unknown"):
|
| 93 |
+
self.M, self.rows, self.cols, self.x_corner, self.y_corner, self.cellsize = M, rows, cols, x_corner, y_corner, cellsize
|
| 94 |
+
self.name = name
|
| 95 |
+
self.description = description
|
| 96 |
+
self.mapper = LonLatMapper(self.x_corner, self.y_corner, self.cellsize, self.rows, self.cols)
|
| 97 |
+
|
| 98 |
+
def __getitem__(self, key):
|
| 99 |
+
"""
|
| 100 |
+
Truy cập ma trận thưa hoặc metadata qua toán tử [].
|
| 101 |
+
- key là tuple (i,j): Trả về giá trị M[i,j] (hoặc 0 nếu không tồn tại). i, j là lon, lat
|
| 102 |
+
"""
|
| 103 |
+
if isinstance(key, tuple):
|
| 104 |
+
if self.M is None:
|
| 105 |
+
raise ValueError("Ma trận chưa được tải. Gọi load_data() trước.")
|
| 106 |
+
x, y = self.mapper.lonlat2xy(key[0], key[1])
|
| 107 |
+
return self.M[y, x] # Trả về giá trị tại (i,j), 0 nếu không có
|
| 108 |
+
else:
|
| 109 |
+
raise TypeError("Khóa phải là tuple (lon, lat)")
|
| 110 |
+
|
| 111 |
+
@staticmethod
|
| 112 |
+
def read_asc(file_path, name, dtype='int', description="Unknown"):
|
| 113 |
+
# Đọc metadata từ header
|
| 114 |
+
with open(file_path, "r") as file:
|
| 115 |
+
cols = int(file.readline().split()[1])
|
| 116 |
+
rows = int(file.readline().split()[1])
|
| 117 |
+
x_corner = float(file.readline().split()[1])
|
| 118 |
+
y_corner = float(file.readline().split()[1])
|
| 119 |
+
cellsize = float(file.readline().split()[1])
|
| 120 |
+
nodata_val = float(file.readline().split()[1]) # Giả sử nodata là số
|
| 121 |
+
|
| 122 |
+
# Đọc dữ liệu dưới dạng mảng NumPy (bỏ qua 6 dòng header)
|
| 123 |
+
data = np.loadtxt(file_path, skiprows=6, dtype=np.float64)
|
| 124 |
+
|
| 125 |
+
# Xử lý giá trị nodata và chuyển đổi sang ma trận thưa
|
| 126 |
+
mask = (data != nodata_val)
|
| 127 |
+
if dtype == 'int':
|
| 128 |
+
data = data.astype(np.int64)
|
| 129 |
+
elif dtype == 'float':
|
| 130 |
+
data = data.astype(np.float64)
|
| 131 |
+
|
| 132 |
+
# Tạo ma trận thưa (dok_matrix hoặc lil_matrix)
|
| 133 |
+
M = dok_matrix((rows, cols), dtype=data.dtype)
|
| 134 |
+
rows_idx, cols_idx = np.where(mask)
|
| 135 |
+
M[rows_idx, cols_idx] = data[rows_idx, cols_idx]
|
| 136 |
+
|
| 137 |
+
return SolidData(M, rows, cols, x_corner, y_corner, cellsize, name, description)
|
| 138 |
+
|
| 139 |
+
def save_dok_matrix(self, output_dir):
|
| 140 |
+
"""
|
| 141 |
+
Lưu ma trận dok_matrix và metadata vào thư mục output_dir.
|
| 142 |
+
- output_dir: Thư mục lưu tệp.
|
| 143 |
+
- Tệp ma trận: <tên thư mục>.npz.
|
| 144 |
+
- Tệp metadata: metadata.json.
|
| 145 |
+
"""
|
| 146 |
+
print("Bắt đầu lưu dữ liệu: ")
|
| 147 |
+
# Lấy tên thư mục từ output_dir
|
| 148 |
+
folder_name = os.path.basename(os.path.normpath(output_dir))
|
| 149 |
+
|
| 150 |
+
# Tạo thư mục nếu chưa tồn tại
|
| 151 |
+
os.makedirs(output_dir, exist_ok=True)
|
| 152 |
+
|
| 153 |
+
# Đường dẫn tệp
|
| 154 |
+
matrix_path = os.path.join(output_dir, f"{folder_name}.npz")
|
| 155 |
+
metadata_path = os.path.join(output_dir, "metadata.json")
|
| 156 |
+
|
| 157 |
+
# Chuyển dok_matrix sang csr_matrix để lưu
|
| 158 |
+
M_csr = self.M.tocsr()
|
| 159 |
+
save_npz(matrix_path, M_csr)
|
| 160 |
+
|
| 161 |
+
# Lưu metadata
|
| 162 |
+
metadata = {
|
| 163 |
+
'name' : self.name,
|
| 164 |
+
'rows' : self.rows,
|
| 165 |
+
'cols' : self.cols,
|
| 166 |
+
'x_corner': self.x_corner,
|
| 167 |
+
'y_corner': self.y_corner,
|
| 168 |
+
'cellsize': self.cellsize,
|
| 169 |
+
'description' : self.description
|
| 170 |
+
}
|
| 171 |
+
with open(metadata_path, 'w') as f:
|
| 172 |
+
json.dump(metadata, f)
|
| 173 |
+
|
| 174 |
+
@staticmethod
|
| 175 |
+
def load_dok_matrix(input_dir):
|
| 176 |
+
"""
|
| 177 |
+
Đọc ma trận dok_matrix và metadata từ thư mục input_dir.
|
| 178 |
+
- input_dir: Thư mục chứa tệp <tên thư mục>.npz và metadata.json.
|
| 179 |
+
- Trả về: (M, x_corner, y_corner, cellsize)
|
| 180 |
+
"""
|
| 181 |
+
# Lấy tên thư mục từ input_dir
|
| 182 |
+
folder_name = os.path.basename(os.path.normpath(input_dir))
|
| 183 |
+
|
| 184 |
+
# Đường dẫn tệp
|
| 185 |
+
matrix_path = os.path.join(input_dir, f"{folder_name}.npz")
|
| 186 |
+
metadata_path = os.path.join(input_dir, "metadata.json")
|
| 187 |
+
|
| 188 |
+
# Đọc ma trận
|
| 189 |
+
M_csr = load_npz(matrix_path)
|
| 190 |
+
M = M_csr.todok() # Chuyển lại dok_matrix
|
| 191 |
+
|
| 192 |
+
# Đọc metadata
|
| 193 |
+
with open(metadata_path, 'r') as f:
|
| 194 |
+
metadata = json.load(f)
|
| 195 |
+
|
| 196 |
+
rows = metadata['rows']
|
| 197 |
+
cols = metadata['cols']
|
| 198 |
+
x_corner = metadata['x_corner']
|
| 199 |
+
y_corner = metadata['y_corner']
|
| 200 |
+
cellsize = metadata['cellsize']
|
| 201 |
+
description = metadata['description']
|
| 202 |
+
name = metadata['name']
|
| 203 |
+
data = SolidData(M, rows, cols, x_corner, y_corner, cellsize, name, description)
|
| 204 |
+
return data
|
| 205 |
+
|
| 206 |
+
def sparse2dict(self):
|
| 207 |
+
sparse_dict = {
|
| 208 |
+
'lon': [],
|
| 209 |
+
'lat': [],
|
| 210 |
+
self.name: [],
|
| 211 |
+
}
|
| 212 |
+
|
| 213 |
+
llmapper = LonLatMapper(self.x_corner, self.y_corner, self.cellsize, self.rows, self.cols)
|
| 214 |
+
for i in tqdm(self.M.items()):
|
| 215 |
+
y, x = i[0]
|
| 216 |
+
lon, lat = llmapper.xy2lonlat(x, y)
|
| 217 |
+
sparse_dict['lon'].append(lon)
|
| 218 |
+
sparse_dict['lat'].append(lat)
|
| 219 |
+
sparse_dict[self.name].append(i[1])
|
| 220 |
+
return sparse_dict
|
| 221 |
+
|
| 222 |
+
def plot(self, output_file=None):
|
| 223 |
+
import matplotlib.pyplot as plt
|
| 224 |
+
import vaex
|
| 225 |
+
|
| 226 |
+
data_dict = self.sparse2dict()
|
| 227 |
+
df = vaex.from_dict(data_dict)
|
| 228 |
+
df.viz.heatmap(df.lon, df.lat, what=vaex.stat.mean(getattr(df, self.name)))
|
| 229 |
+
if output_file:
|
| 230 |
+
print("Bắt đầu lưu biểu đồ")
|
| 231 |
+
folder_name = os.path.dirname(output_file)
|
| 232 |
+
# Tạo thư mục nếu chưa tồn tại
|
| 233 |
+
os.makedirs(folder_name, exist_ok=True)
|
| 234 |
+
plt.savefig(output_file) # Lưu hình thành tệp
|
| 235 |
+
else:
|
| 236 |
+
print("Bắt đầu plot biểu đồ:")
|
| 237 |
+
plt.show()
|
| 238 |
+
plt.close()
|
| 239 |
+
|
| 240 |
+
|
solid_data/GloAspectClE_5min/GloAspectClE_5min.npz
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:c9267959d510d1c7333fb86e5a5316af45e4b680b360d088067be59ecdf73626
|
| 3 |
+
size 3923278
|
solid_data/GloAspectClE_5min/heatmap.png
ADDED
|
solid_data/GloAspectClE_5min/metadata.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
{"name": "AspectClE", "rows": 2160, "cols": 4320, "x_corner": -180.0, "y_corner": -90.0, "cellsize": 0.0833333333333333, "description": "Aspect class for East-facing slopes, representing the direction of the slope gradient in the Eastward direction."}
|
solid_data/GloAspectClN_5min/GloAspectClN_5min.npz
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:9d9d1ac7af0e133533656365849f3f2264a05da4030001fba19db59fe7743494
|
| 3 |
+
size 3892827
|
solid_data/GloAspectClN_5min/heatmap.png
ADDED
|
solid_data/GloAspectClN_5min/metadata.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
{"name": "AspectClN", "rows": 2160, "cols": 4320, "x_corner": -180.0, "y_corner": -90.0, "cellsize": 0.0833333333333333, "description": "Aspect class for North-facing slopes, representing the direction of the slope gradient towards the north."}
|
solid_data/GloAspectClS_5min/GloAspectClS_5min.npz
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:882415f2db533c7723bd146dbb0f3255ccb88205460894c2aa7a7e1a9f74143a
|
| 3 |
+
size 3890603
|
solid_data/GloAspectClS_5min/heatmap.png
ADDED
|
solid_data/GloAspectClS_5min/metadata.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
{"name": "AspectClS", "rows": 2160, "cols": 4320, "x_corner": -180.0, "y_corner": -90.0, "cellsize": 0.0833333333333333, "description": "Aspect class for South-facing slopes, representing the direction of the slope gradient towards the south."}
|
solid_data/GloAspectClU_5min/GloAspectClU_5min.npz
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:15ed47a363aee7a166f180c8255c4d561415beced7fa655392c9f674e70cba95
|
| 3 |
+
size 4319324
|
solid_data/GloAspectClU_5min/heatmap.png
ADDED
|
solid_data/GloAspectClU_5min/metadata.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
{"name": "AspectClU", "rows": 2160, "cols": 4320, "x_corner": -180.0, "y_corner": -90.0, "cellsize": 0.0833333333333333, "description": "Aspect class for undefined slope aspects where the slope gradient is either undefined or less than a certain threshold."}
|
solid_data/GloAspectClW_5min/GloAspectClW_5min.npz
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:a114071a642a3c9246dcd83a4bdf4ee6ce68db6e4cdf964c63e5719991bd8eb1
|
| 3 |
+
size 3926473
|
solid_data/GloAspectClW_5min/heatmap.png
ADDED
|
solid_data/GloAspectClW_5min/metadata.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
{"name": "AspectClW", "rows": 2160, "cols": 4320, "x_corner": -180.0, "y_corner": -90.0, "cellsize": 0.0833333333333333, "description": "Aspect class for West-facing slopes, representing the direction of the slope gradient towards the west."}
|
solid_data/GloElev_5min/GloElev_5min.npz
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:3522a95065d3c14222e0b50e1c184bd4a673f549d0a257486dfdcd2760f38b3c
|
| 3 |
+
size 4328335
|
solid_data/GloElev_5min/heatmap.png
ADDED
|
solid_data/GloElev_5min/metadata.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
{"name": "Elevation", "rows": 2160, "cols": 4320, "x_corner": -180.0, "y_corner": -90.0, "cellsize": 0.0833333333333333, "description": "Median elevation data for each grid cell, measured in meters above sea level."}
|
solid_data/GloLand_5min/GloLand_5min.npz
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:64c27c031dc15e6a3b0860bbd59e77b48298682d0d0c54625837346c326cdd6e
|
| 3 |
+
size 407106
|
solid_data/GloLand_5min/heatmap.png
ADDED
|
solid_data/GloLand_5min/metadata.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
{"name": "LandMask", "rows": 2160, "cols": 4320, "x_corner": -180.0, "y_corner": -90.0, "cellsize": 0.0833333333333333, "description": "Land mask indicating the fraction of 3 arc-second grid cells that fall into a 5-minute"}
|
solid_data/GloSlopesCl1_5min/GloSlopesCl1_5min.npz
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:fb0b1699145748c061e0ffd8d07ba33ef8755374844d3db8be05797108672999
|
| 3 |
+
size 3591579
|
solid_data/GloSlopesCl1_5min/heatmap.png
ADDED
|
solid_data/GloSlopesCl1_5min/metadata.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
{"name": "SlopesCl1", "rows": 2160, "cols": 4320, "x_corner": -180.0, "y_corner": -90.0, "cellsize": 0.0833333333333333, "description": "Percentage of grid cells with slope between 0% and 0.5%."}
|
solid_data/GloSlopesCl2_5min/GloSlopesCl2_5min.npz
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:9339c9b3ac6145f057d78f8b4e4e1446b4ffeeac255604090a1aeb2e5fc8c567
|
| 3 |
+
size 4184117
|
solid_data/GloSlopesCl2_5min/heatmap.png
ADDED
|
solid_data/GloSlopesCl2_5min/metadata.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
{"name": "SlopesCl2", "rows": 2160, "cols": 4320, "x_corner": -180.0, "y_corner": -90.0, "cellsize": 0.0833333333333333, "description": "Percentage of grid cells with slope between 0.5% and 2%."}
|
solid_data/GloSlopesCl3_5min/GloSlopesCl3_5min.npz
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:472747752d1f2025cbabe1229c814d522db3bfc1973608531d6eceb6838ff23b
|
| 3 |
+
size 4158399
|
solid_data/GloSlopesCl3_5min/heatmap.png
ADDED
|
solid_data/GloSlopesCl3_5min/metadata.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
{"name": "SlopesCl3", "rows": 2160, "cols": 4320, "x_corner": -180.0, "y_corner": -90.0, "cellsize": 0.0833333333333333, "description": "Percentage of grid cells with slope between 2% and 5%."}
|
solid_data/GloSlopesCl4_5min/GloSlopesCl4_5min.npz
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:7e1770f8040047f64557c373e12a8557797d131d8c283e2af03d97d6fb5300f1
|
| 3 |
+
size 3776388
|
solid_data/GloSlopesCl4_5min/heatmap.png
ADDED
|
solid_data/GloSlopesCl4_5min/metadata.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
{"name": "SlopesCl4", "rows": 2160, "cols": 4320, "x_corner": -180.0, "y_corner": -90.0, "cellsize": 0.0833333333333333, "description": "Percentage of grid cells with slope between 5% and 10%."}
|
solid_data/GloSlopesCl5_5min/GloSlopesCl5_5min.npz
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:308a95ee1fa2254e45293acde29af429170e311793498aa0a9714090f0fcae65
|
| 3 |
+
size 3069507
|
solid_data/GloSlopesCl5_5min/heatmap.png
ADDED
|
solid_data/GloSlopesCl5_5min/metadata.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
{"name": "SlopesCl5", "rows": 2160, "cols": 4320, "x_corner": -180.0, "y_corner": -90.0, "cellsize": 0.0833333333333333, "description": "Percentage of grid cells with slope between 10% and 15%."}
|
solid_data/GloSlopesCl6_5min/GloSlopesCl6_5min.npz
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:ae609a37a1ef48772cc1bdf50adfbdf61f2bb1089162ea747b968ea8255afb8a
|
| 3 |
+
size 2762316
|
solid_data/GloSlopesCl6_5min/heatmap.png
ADDED
|
solid_data/GloSlopesCl6_5min/metadata.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
{"name": "SlopesCl6", "rows": 2160, "cols": 4320, "x_corner": -180.0, "y_corner": -90.0, "cellsize": 0.0833333333333333, "description": "Percentage of grid cells with slope between 15% and 30%."}
|
solid_data/GloSlopesCl7_5min/GloSlopesCl7_5min.npz
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:33d4c1fb0000dac64a753eac2be9183e315f1db31afda7924a7276c39892bfa3
|
| 3 |
+
size 1890531
|
solid_data/GloSlopesCl7_5min/heatmap.png
ADDED
|
solid_data/GloSlopesCl7_5min/metadata.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
{"name": "SlopesCl7", "rows": 2160, "cols": 4320, "x_corner": -180.0, "y_corner": -90.0, "cellsize": 0.0833333333333333, "description": "Percentage of grid cells with slope between 30% and 45%."}
|