0
0
Lập trình
Sơn Tùng Lê
Sơn Tùng Lê103931498422911686980

Xây Dựng Bản Đồ Tương Tác Trek Annapurna Với React & Leaflet

Đăng vào 8 tháng trước

• 7 phút đọc

Xây Dựng Bản Đồ Tương Tác Trek Annapurna Với React & Leaflet

Năm ngoái, tôi đã có một cuộc phiêu lưu đáng nhớ: trekking qua Annapurna Circuit ở Nepal. Kinh nghiệm này đã thay đổi cuộc đời tôi, nhưng với tư cách là một lập trình viên, tôi không chỉ muốn chia sẻ những bức ảnh trong một album thông thường. Tôi muốn kể câu chuyện về hành trình - lộ trình, độ cao, những nơi tôi đã ngủ - theo cách tương tác. Vì vậy, tôi đã xây dựng một ứng dụng web tùy chỉnh để bản đồ hóa nó.

Trong bài viết này, tôi sẽ hướng dẫn bạn cách tôi tạo ra một bản đồ tương tác cho chuyến trekking của mình bằng React và Leaflet, một thư viện JavaScript mã nguồn mở mạnh mẽ cho các bản đồ tương tác thân thiện với di động.

Nội Dung Bài Viết

Giới thiệu về công nghệ

  • Frontend: React (sử dụng Vite để xây dựng nhanh)
  • Bản đồ: Leaflet.js
  • React-Leaflet: Liên kết cụ thể với React cho Leaflet, giúp làm việc dễ dàng hơn theo cách dựa trên thành phần.
  • Trực quan hóa dữ liệu: GeoJSON cho lộ trình trekking và một biểu đồ hồ sơ độ cao tùy chỉnh (sử dụng Chart.js, tuy nhiên tôi sẽ không đi sâu vào điều này ở đây).
  • Biểu tượng: React Icons cho các đánh dấu trên bản đồ.

Bước 1: Thiết lập dự án

Đầu tiên, tôi đã tạo một dự án React mới và cài đặt các phụ thuộc cần thiết.

Copy
npm create vite@latest annapurna-trek-map -- --template react
cd annapurna-trek-map
npm install leaflet react-leaflet
npm install react-icons
npm install

Bước 2: Thành phần bản đồ chính

Tôi đã tạo một thành phần TrekMap.jsx cơ bản để chứa bản đồ của chúng ta. React-Leaflet làm điều này cực kỳ đơn giản bằng cách cung cấp các thành phần như MapContainer, TileLayer, và Marker.

javascript Copy
// TrekMap.jsx
import { MapContainer, TileLayer, Marker, Popup } from 'react-leaflet';
import 'leaflet/dist/leaflet.css';

const TrekMap = () => {
  // Vị trí ban đầu tập trung vào Nepal
  const position = [28.3949, 84.1240]

  return (
    <div className="map-container">
      <MapContainer
        center={position}
        zoom={7}
        style={{ height: '500px', width: '100%' }}
      >
        {/* Thêm một TileLayer (kiểu bản đồ) */}
        <TileLayer
          attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
          url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
        />
        {/* Chúng ta sẽ thêm các đánh dấu và Lộ trình ở đây */}
      </MapContainer>
    </div>
  );
};

export default TrekMap;

Điểm chính: Bạn phải nhập tệp CSS của Leaflet để bản đồ hiển thị đúng cách!

Bước 3: Vẽ lộ trình GPX

Tôi đã ghi lại toàn bộ hành trình của mình bằng một chiếc đồng hồ GPS, điều này đã cho tôi một tệp GPX. Tôi đã chuyển đổi tệp GPX này thành định dạng GeoJSON (sử dụng một công cụ như GPSVisualizer) vì GeoJSON dễ làm việc hơn trong các ứng dụng web.

Tôi đã tạo một thành phần RouteLine.jsx để hiển thị lộ trình này trên bản đồ.

javascript Copy
// RouteLine.jsx
import { Polyline, Popup } from 'react-leaflet';

const RouteLine = ({ data }) => {
  // Chuyển đổi tọa độ GeoJSON sang mảng LatLng cho Polyline
  // GeoJSON là [kinh độ, vĩ độ], Leaflet muốn [vĩ độ, kinh độ]
  const polyline = data.geometry.coordinates.map(coord => [coord[1], coord[0]]);

  return (
    <Polyline
      pathOptions={{ color: 'blue', weight: 5 }}
      positions={polyline}
    >
      <Popup>Đường trekking Annapurna Circuit của tôi</Popup>
    </Polyline>
  );
};

export default RouteLine;

Tôi đã nhập thành phần này và dữ liệu GeoJSON của tôi vào thành phần chính TrekMap.

javascript Copy
// Trong TrekMap.jsx
import RouteLine from './RouteLine';
import trekGeoJSON from './assets/annapurna_route.json'; // nhập GeoJSON

// ... bên trong thẻ <MapContainer> ...
<RouteLine data={trekGeoJSON} />

Bước 4: Thêm các điểm quan trọng

Lộ trình thì tốt, nhưng câu chuyện nằm ở những điểm dừng chân. Tôi đã tạo một mảng các đối tượng cho những địa điểm quan trọng như nhà trọ, đèo, và các địa danh.

javascript Copy
// points-of-interest.js
export const pointsOfInterest = [
  {
    id: 1,
    name: "Besisahar",
    position: [28.2308, 84.3784],
    type: "start",
    description: "Điểm bắt đầu chính thức của Annapurna Circuit."
  },
  {
    id: 2,
    name: "Manang",
    position: [28.6666, 84.0166],
    type: "teahouse",
    description: "Điểm dừng thích ứng. Cảnh đẹp ngắm nhìn Gangapurna."
  },
  {
    id: 3,
    name: "Thorong La Pass",
    position: [28.7936, 83.9407],
    type: "pass",
    description: "Điểm cao nhất của chuyến trekking ở độ cao 5.416 mét! Một cuộc leo núi khắc nghiệt và xứng đáng."
  },
  // ... nhiều điểm nữa
];

Tôi đã lặp qua mảng này để tạo các đánh dấu với biểu tượng tùy chỉnh.

javascript Copy
// Trong TrekMap.jsx
import { pointsOfInterest } from '../data/points-of-interest';
import { FaMountain, FaHome, FaFlagCheckered } from 'react-icons/fa';

// Hàm lấy biểu tượng dựa trên loại
const getIcon = (type) => {
  const iconConfig = { color: 'white', size: 20 };
  switch (type) {
    case 'pass':
      return <FaMountain style={{ ...iconConfig, color: 'red' }} />;
    case 'teahouse':
      return <FaHome style={{ ...iconConfig, color: 'blue' }} />;
    case 'start':
      return <FaFlagCheckered style={{ ...iconConfig, color: 'green' }} />;
    default:
      return null;
  }
};

// ... bên trong thẻ <MapContainer> ...
{pointsOfInterest.map((poi) => (
  <Marker key={poi.id} position={poi.position} icon={getIcon(poi.type)}>
    <Popup>
      <strong>{poi.name}</strong><br />
      {poi.description}
    </Popup>
  </Marker>
))}

Bước 5: Sản phẩm cuối cùng và những thách thức

Thành phần cuối cùng đã kết hợp tất cả lại: các ô bản đồ OpenStreetMap, đường màu xanh uốn lượn qua dãy Himalaya, và các đánh dấu tùy chỉnh cho từng địa điểm quan trọng.

Những thách thức mà tôi đã gặp phải:

  1. Lật tọa độ: Như đã đề cập trong mã, GeoJSON sử dụng [kinh độ, vĩ độ] trong khi Leaflet sử dụng [vĩ độ, kinh độ]. Đây là một "cái bẫy" cổ điển.
  2. Biểu tượng đánh dấu: Leaflet có một vấn đề đã biết với các biểu tượng mặc định trong React. Bạn cần giải quyết điều này bằng cách thiết lập thủ công biểu tượng (tôi đã sử dụng một thành phần Icon từ react-leaflet).
  3. Hiệu suất: Đường GPX có hàng ngàn điểm. Đối với phiên bản tương lai, tôi sẽ đơn giản hóa đường GeoJSON để cải thiện hiệu suất trên các thiết bị có công suất thấp hơn.

Lý do thực hiện

Dự án này không chỉ là một bản đồ; nó là cách để kết hợp hai đam mê của tôi: lập trình và phiêu lưu. Nó phục vụ như một câu chuyện tương tác, hấp dẫn cho một trong những trải nghiệm tuyệt vời nhất trong đời tôi. Thách thức kỹ thuật trong việc chuyển đổi dữ liệu GPS thô thành một câu chuyện hình ảnh đẹp là một trải nghiệm cực kỳ thỏa mãn.

Đối với bất kỳ lập trình viên nào đang tìm kiếm một dự án mới cho danh mục của mình, tôi rất khuyên bạn nên bản đồ hóa một hành trình cá nhân. Những kỹ năng bạn học được với dữ liệu không gian và trực quan hóa rất có thể được áp dụng rộng rãi.

Bạn muốn xem mã cuối cùng không? Bạn có thể kiểm tra kho lưu trữ GitHub ở đây.

Bạn có muốn trekking ở Nepal không? Những ngọn núi đang chờ đón bạn. Nếu bạn quyết định đáp lại lời kêu gọi đó, tôi rất khuyên bạn nên kết nối với đội ngũ chuyên gia tại Independent Trekking Guide Nepal để có một cuộc phiêu lưu chân thực và được tổ chức tốt.


Bạn đã xây dựng bất cứ điều gì với các thư viện bản đồ chưa? Hãy chia sẻ các dự án và thách thức của bạn trong phần bình luận dưới đây!

Gợi ý câu hỏi phỏng vấn
Không có dữ liệu

Không có dữ liệu

Bài viết được đề xuất
Bài viết cùng tác giả

Bình luận

Chưa có bình luận nào

Chưa có bình luận nào