GIS 5 — Trực quan hoá bản đồ & Bản đồ nhiệt

13 thg 7, 2026 3 lượt xem
#visualization
#mapping
#gis
#choropleth
#leaflet

GIS 5 — Trực quan hoá bản đồ & Bản đồ nhiệt

Bốn bài trước cho bạn dữ liệu không gian: tổng quan, PostGIS, truy vấn không gian, định dạng dữ liệu. Nhưng một bảng kết quả SELECT district, SUM(balance) — dù đúng đến đâu — không giúp ban lãnh đạo nhìn ra điều gì. Bộ não người đọc bản đồ nhanh hơn đọc bảng gấp nhiều lần: một mảng màu đậm ở góc bản đồ nói "chỗ này nhiều" ngay lập tức, không cần dò từng dòng số. Bài này về bước cuối của chuỗi giá trị GIS: biến toạ độ và số liệu thành bản đồ để ra quyết định.

Trực quan hoá bản đồ là một nhánh của trực quan hoá dữ liệu nói chung — nên mọi nguyên tắc ở chọn biểu đồthiết kế dashboard vẫn áp dụng, cộng thêm những đặc thù riêng của không gian: hệ toạ độ, tile nền, và cái bẫy cổ điển của choropleth theo số tuyệt đối.

Bản đồ chuyên đề: chọn loại theo dữ liệu

"Bản đồ chuyên đề" (thematic map) là bản đồ trong đó màu sắc, kích thước hay mật độ mã hoá một giá trị dữ liệu, chứ không chỉ vẽ địa hình. Cũng như chọn biểu đồ, việc đầu tiên không phải "chọn màu gì" mà là loại bản đồ nào hợp với dữ liệu. Năm loại nền tảng:

  • Choropleth (bản đồ sắc độ theo vùng). Tô màu mỗi vùng hành chính (tỉnh/quận) theo giá trị: đậm = cao, nhạt = thấp. Hợp cho dữ liệu theo vùng đã tổng hợp: dư nợ theo tỉnh, tỉ lệ nợ xấu theo quận. Cảnh báo sống còn: choropleth phải tô theo giá trị đã chuẩn hoá (tỉ lệ, mật độ, bình quân đầu người), không theo số tuyệt đối — nếu không, bản đồ chỉ đang vẽ lại dân số hoặc diện tích. TP.HCM luôn "đậm nhất" đơn giản vì đông dân, không nói lên gì về hiệu quả kinh doanh trên mỗi khách.
  • Symbol / bubble map (bản đồ ký hiệu tỉ lệ). Đặt một điểm tròn tại vị trí, kích thước tỉ lệ với giá trị: dư nợ theo chi nhánh, số ATM theo điểm. Khác choropleth ở chỗ dữ liệu gắn với điểm cụ thể chứ không phải vùng — và bong bóng ít bị bóp méo bởi diện tích vùng hơn, nên đôi khi trung thực hơn choropleth cho số tuyệt đối.
  • Heatmap (bản đồ nhiệt mật độ). Làm mờ và cộng dồn mật độ điểm thành một lớp gradient nóng-lạnh: nơi tập trung giao dịch, khách hàng, hay điểm cảnh báo gian lận. Trả lời câu "chỗ nào dày?" — không cần ranh giới hành chính, hợp khi có rất nhiều điểm thô.
  • Cluster marker (gom cụm điểm). Khi có hàng nghìn điểm, vẽ hết ra sẽ thành một đám đen vô nghĩa. Cluster gom các điểm gần nhau thành một huy hiệu "×147", và tách ra khi zoom vào. Đây là cách trình bày danh sách điểm (mọi chi nhánh, mọi ATM) mà vẫn đọc được ở mọi mức zoom.
  • Flow map (bản đồ dòng chảy). Vẽ đường nối giữa hai vùng, độ dày = lưu lượng: dòng chuyển tiền giữa các tỉnh, luồng khách di chuyển giữa địa bàn. Mã hoá quan hệ có hướng giữa các nơi, không phải giá trị tại một nơi.

Nguyên tắc gốc giống hệt chọn biểu đồ: để bản chất dữ liệu quyết định hình thức, không chọn loại bản đồ vì nó "đẹp" hay "ngầu".

Nguồn dữ liệu: gộp theo địa bàn

Mọi choropleth theo tỉnh đều bắt đầu từ một truy vấn gộp theo địa bàn. Trong thế giới GIS đầy đủ, "địa bàn" được suy ra từ toạ độ bằng ST_Contains (xem bài truy vấn không gian). Nhưng khi cột city đã được gán sẵn dạng chuỗi, việc gộp chỉ là một GROUP BY bình thường — và đây là câu chạy được thật trên sandbox, làm dữ liệu nguồn cho lớp bản đồ theo địa bàn:

-- ▶ Chạy được
SELECT c.city,
       COUNT(DISTINCT c.id)         AS num_customers,
       COUNT(a.id)                  AS num_accounts,
       ROUND(SUM(a.balance)::numeric, 2)                      AS total_balance,
       ROUND((SUM(a.balance) / NULLIF(COUNT(DISTINCT c.id), 0))::numeric, 2) AS balance_per_customer
FROM customers c
LEFT JOIN accounts a ON a.customer_id = c.id
GROUP BY c.city
ORDER BY total_balance DESC;

Chú ý cột balance_per_customer (số dư bình quân đầu khách): đây chính là con số nên đưa lên choropleth, không phải total_balance. total_balancesố tuyệt đối — tô nó lên bản đồ sẽ chỉ đang vẽ lại "tỉnh nào đông khách", không phải "tỉnh nào giá trị cao trên mỗi khách". Một câu SQL, hai cột, hai bản đồ khác hẳn nhau về ý nghĩa. Đó là toàn bộ tinh thần của phần "chuẩn hoá" bên dưới.

Lưu ý sandbox: như cả series đã nhấn, sandbox chạy PostgreSQL thuần, không có PostGIS. Câu SQL trên là non-spatial (chỉ GROUP BY city) nên chạy thật; mọi đoạn ST_* trong bài là minh hoạ cú pháp, không đánh dấu chạy được.

Từ PostGIS ra web map: luồng dữ liệu

Một web map tương tác không đọc trực tiếp bảng PostGIS — nó cần dữ liệu ở định dạng mà trình duyệt hiểu, phổ biến nhất là GeoJSON (xem định dạng dữ liệu). PostGIS có hàm biến hình học thành GeoJSON ngay trong SQL: ST_AsGeoJSON(geom).

-- MINH HOẠ (PostGIS, không chạy trên sandbox)
-- Xuất mỗi tỉnh kèm giá trị đã chuẩn hoá thành 1 GeoJSON Feature
SELECT jsonb_build_object(
  'type', 'Feature',
  'geometry', ST_AsGeoJSON(ST_Simplify(d.geom, 0.005))::jsonb,
  'properties', jsonb_build_object(
     'name', d.name,
     'balance_per_customer', s.balance_per_customer)
) AS feature
FROM districts d
JOIN province_stats s ON s.city = d.name;

Điểm đáng chú ý: ST_Simplify giảm số đỉnh của polygon trước khi xuất. Ranh giới tỉnh gốc có thể hàng chục nghìn điểm — quá nặng để tải và vẽ mượt trên web. Đơn giản hoá hình học là bước tối ưu bắt buộc cho web map. Toàn bộ đường ống:

Với dữ liệu lớn hoặc zoom nhiều mức, thay GeoJSON tĩnh bằng vector tiles — dữ liệu được cắt thành các ô (tile) theo mức zoom, trình duyệt chỉ tải phần đang nhìn (level of detail). Đây là cùng ý tưởng "chỉ nạp cái đang cần" như tile bản đồ nền.

Tile & bản đồ nền

Mọi bản đồ web đều có hai lớp: nền (basemap)dữ liệu của bạn đè lên trên. Nền thường là tile ảnh hoặc vector tiles lấy từ nhà cung cấp:

  • OpenStreetMap (OSM) — nền mở, miễn phí, phổ biến nhất; đường/tên/địa danh do cộng đồng đóng góp.
  • Vector tiles (Mapbox, MapTiler, OSM vector) — nền dưới dạng vector thay vì ảnh raster: sắc nét ở mọi mức zoom, xoay/đổi màu được, nhẹ hơn.

Bản đồ nền chia thế giới theo mức zoom: zoom nhỏ = cả nước ít chi tiết, zoom lớn = từng con phố. Bản thân dữ liệu của bạn cũng nên đổi theo zoom — đây là level of detail: ở mức tỉnh hiện choropleth theo tỉnh; zoom vào một tỉnh thì hiện quận; zoom sâu nữa mới hiện điểm chi nhánh. Cluster marker (ở trên) là cơ chế level-of-detail cho lớp điểm.

Công cụ

Không có một công cụ "đúng" duy nhất — chọn theo mục đích (phân tích một lần hay dashboard sống) và đối tượng (nhà phân tích hay ban lãnh đạo).

NhómCông cụDùng khi
Desktop GISQGISPhân tích không gian sâu, dựng và xuất bản đồ tĩnh (PDF/ảnh) chất lượng in ấn; miễn phí, mã nguồn mở
Thư viện webLeafletBản đồ tương tác nhẹ, đơn giản; marker, popup, choropleth cơ bản; dễ học nhất
Mapbox GL JSVector tiles, bản đồ mượt, tuỳ biến style sâu, 3D
deck.glTrực quan lượng lớn điểm/đường tăng tốc GPU: heatmap, arc/flow, hexbin hàng triệu điểm
Kepler.glCông cụ no-code trên nền deck.gl: kéo-thả CSV/GeoJSON ra bản đồ heatmap/cluster/arc để khám phá nhanh
Nền BI có bản đồPower BI (map, filled map, Azure Maps)Đưa bản đồ vào dashboard doanh nghiệp cho ban lãnh đạo — xem tổng quan Power BI
Superset (lớp deck.gl)BI mã nguồn mở, có sẵn các loại bản đồ deck.gl — xem tổng quan BI mã nguồn mở

Quy tắc thực dụng: khám phá nhanh dùng Kepler.gl; phân tích + xuất bản đồ in dùng QGIS; dashboard sống cho lãnh đạo dùng Power BI hoặc Superset (không bắt sếp mở IDE); sản phẩm/app riêng mới cần tự dựng bằng Leaflet/Mapbox GL/deck.gl.

Nguyên tắc thiết kế bản đồ tốt

Bản đồ dễ đánh lừa hơn biểu đồ thường vì trông có vẻ khách quan. Vài nguyên tắc — mở rộng từ thiết kế dashboard:

  • Chuẩn hoá trước khi tô. Nhắc lại vì quá quan trọng: choropleth tô tỉ lệ / mật độ / bình quân, không tô số tuyệt đối. Số tuyệt đối thì dùng bubble map.
  • Chọn đúng loại bảng màu.
    • Tuần tự (sequential) — một màu đậm dần: cho dữ liệu một chiều thấp→cao (dư nợ bình quân, mật độ khách).
    • Phân kỳ (diverging) — hai màu đối lập, nhạt ở giữa: cho dữ liệu có điểm giữa có nghĩa (tăng/giảm so với năm trước, trên/dưới trung bình).
    • Tránh dải cầu vồng (rainbow): mắt người không đọc được thứ tự trên nó, dễ tạo "ranh giới ảo".
  • Luôn có chú giải (legend). Bản đồ không có legend là mã hoá màu mà không đưa khoá giải mã — vô nghĩa. Ghi rõ đơn vịkhoảng phân lớp.
  • Cách chia lớp (classification) cũng là một lựa chọn. Chia khoảng đều, chia phân vị (quantile), hay Jenks — mỗi cách cho một bản đồ khác nhau từ cùng dữ liệu. Nêu rõ cách chia để người xem không bị dẫn dắt vô tình.
  • Đừng để diện tích bóp méo. Vùng to thu hút mắt hơn dù giá trị thấp; ý thức rằng choropleth thiên vị các vùng rộng.

Cạm bẫy thường gặp

  • Choropleth theo số tuyệt đối. Cạm bẫy số một, nhắc lần cuối: tô total_balance theo tỉnh chỉ đang vẽ lại bản đồ dân số. Chuẩn hoá, hoặc chuyển sang bubble map.
  • Quá nhiều điểm. Đổ 2 triệu giao dịch dạng marker riêng lẻ → màn hình đen kịt, trình duyệt treo. Dùng heatmap, cluster, hoặc hexbin (deck.gl) để cộng dồn.
  • Màu gây hiểu nhầm. Đỏ = xấu/nóng, xanh = tốt trong trực giác người xem; tô ngược (đỏ cho "tốt") gây hiểu sai. Bảng màu cầu vồng và dải quá nhiều bậc cũng làm người xem đọc sai thứ tự.
  • Bỏ vùng "0" thành trắng lẫn với "không có dữ liệu". Phân biệt rõ giá trị bằng 0thiếu dữ liệu (thường tô hoa văn/xám riêng), tránh người xem hiểu nhầm "trắng = không quan tâm".
  • SRID / phép chiếu sai. Bản đồ lệch, dữ liệu "rơi ra biển" thường do nhầm hệ toạ độ hoặc đảo lat/lng — xem lại truy vấn không gian.

Use case thực tế

Bối cảnh NCB — dashboard bản đồ dư nợ / huy động / khách theo tỉnh cho ban lãnh đạo.

Ban điều hành muốn một trang bản đồ Việt Nam, mở lên là thấy ngay bức tranh địa bàn của toàn hàng, cập nhật hằng đêm. Yêu cầu: dư nợ, huy động, số khách theo 63 tỉnh; lọc theo sản phẩm và thời gian; bấm vào một tỉnh để khoan sâu.

Các bước đội Data triển khai:

  1. Gộp số liệu theo địa bàn. Batch đêm chạy truy vấn gộp theo city/tỉnh (bản đầy đủ là spatial join ST_Contains gán khách/giao dịch vào tỉnh; nếu city đã chuẩn thì chỉ là GROUP BY như câu chạy được ở trên), materialize thành bảng province_stats(city, num_customers, total_balance, balance_per_customer, deposit, ...).
  2. Chuẩn bị hình học tỉnh. Ranh giới 63 tỉnh (SRID 4326) được ST_Simplify để nhẹ, rồi ST_AsGeoJSON xuất một lần thành file GeoJSON nền — hình học ít đổi nên tách khỏi số liệu, chỉ tải một lần.
  3. Chọn đúng loại bản đồ cho từng chỉ số. Choropleth cho chỉ số chuẩn hoá (dư nợ bình quân đầu khách, tỉ lệ CASA); bubble map cho số tuyệt đối (tổng dư nợ theo tỉnh — bong bóng ít méo theo diện tích hơn tô nền); heatmap điểm cho phân bố giao dịch thẻ trong đô thị lớn.
  4. Dựng trong nền BI. Bản đồ đặt trong Power BI (filled map + Azure Maps) hoặc Superset (lớp deck.gl) để ban lãnh đạo dùng ngay trong dashboard doanh nghiệp — không cần công cụ GIS chuyên dụng. Đội phân tích dùng thêm QGIS để xuất bản đồ tĩnh chèn vào báo cáo HĐQT hằng quý, và Kepler.gl để thử nhanh các góc nhìn trước khi chốt.
  5. Thiết kế đúng chuẩn. Bảng màu tuần tự cho dư nợ bình quân, phân kỳ cho tăng trưởng so với quý trước; legend ghi rõ đơn vị (tỉ đồng) và cách chia phân vị; tỉnh thiếu dữ liệu tô xám riêng.

Kết quả: một trang bản đồ duy nhất thay cho hàng chục dòng bảng, giúp ban lãnh đạo thấy ngay tỉnh nào giá trị cao trên mỗi khách (không phải chỉ đông khách), tỉnh nào tăng trưởng huy động, và tỉnh nào là "vùng trắng" đáng mở rộng — dữ liệu địa bàn từ các mẫu truy vấn không gian cuối cùng trở thành công cụ ra quyết định trực quan.

Ghi nhớ

  • Trực quan hoá bản đồ là bước cuối biến toạ độ + số liệu thành bản đồ để quyết định; mọi nguyên tắc của chọn biểu đồthiết kế dashboard vẫn áp dụng, cộng đặc thù không gian.
  • Chọn loại bản đồ theo dữ liệu: choropleth (giá trị theo vùng, phải chuẩn hoá), bubble/symbol (giá trị theo điểm, hợp số tuyệt đối), heatmap (mật độ điểm), cluster (danh sách nhiều điểm), flow map (quan hệ giữa hai nơi).
  • Cạm bẫy số một: choropleth theo số tuyệt đối chỉ vẽ lại dân số/diện tích — luôn tô tỉ lệ / mật độ / bình quân, số tuyệt đối thì dùng bubble.
  • Luồng web map: PostGIS → ST_AsGeoJSON (+ ST_Simplify) → GeoJSON / vector tiles → Leaflet/Mapbox GL/deck.gl, đè lên tile nền OSM; dùng level of detail theo zoom.
  • Công cụ: QGIS (phân tích + xuất bản đồ in), Leaflet/Mapbox GL/deck.gl/Kepler.gl (web tương tác), Power BI & Superset cho dashboard lãnh đạo — xem Power BIBI mã nguồn mở.
  • Thiết kế: bảng màu tuần tự (một chiều) vs phân kỳ (có điểm giữa), luôn có legend + đơn vị, nêu cách chia lớp; tránh quá nhiều điểm, màu gây hiểu nhầm, và diện tích bóp méo.
  • Trên sandbox không có PostGIS: chỉ câu gộp theo city trên customers/accounts chạy thật — làm dữ liệu nguồn cho bản đồ theo địa bàn; SQL/PostGIS không gian là minh hoạ.

Bài viết liên quan

Các kỹ thuật phân tích không gian tạo giá trị kinh doanh cho ngân hàng: phân tích mật độ & hotspot, gom cụm ST_ClusterDBSCAN/KMeans, tự tương quan không gian (Moran's I), chọn địa điểm ATM/chi nhánh bằng service area, isochrone và overlay đa lớp. Nối tiếp bài truy vấn không gian, minh hoạ bằng SQL PostGIS và quy trình site selection.

13 thg 7, 2026 3

PostGIS biến PostgreSQL thành cơ sở dữ liệu không gian đầy đủ. Bài này đi từ cài đặt CREATE EXTENSION, kiểu geometry vs geography, SRID/EPSG:4326, cách nhập điểm bằng ST_MakePoint/ST_GeomFromText, các hàm cốt lõi (ST_Distance, ST_DWithin, ST_Contains, ST_Buffer, ST_Transform...), chỉ mục GIST bắt buộc cho hiệu năng, tới bài toán tìm ATM/chi nhánh trong bán kính quanh khách hàng NCB.

13 thg 7, 2026 3

Nhập môn dữ liệu không gian (spatial/geospatial): dữ liệu gắn vị trí trên Trái Đất, các loại hình học điểm/đường/vùng, hệ toạ độ CRS/SRID (WGS84, UTM/VN2000), vector vs raster, quan hệ và phép đo không gian. Đặt nền cho cả series GIS và giá trị của nó với ngân hàng NCB: mạng lưới chi nhánh/ATM, phân tích khách hàng theo địa bàn, rủi ro và gian lận theo vị trí.

13 thg 7, 2026 3

Bài kết series: tổng hợp năm nhóm ứng dụng dữ liệu không gian (location intelligence) trong ngân hàng — tối ưu mạng lưới chi nhánh/ATM, phân tích khách theo địa bàn, rủi ro tín dụng theo vùng, gian lận/AML theo vị trí, marketing địa điểm. Kèm kiến trúc dữ liệu không gian (geocoding, PostGIS, dashboard bản đồ), quyền riêng tư dữ liệu vị trí, và lộ trình áp dụng cho NCB từ chuẩn hoá địa chỉ đến phân tích nâng cao.

13 thg 7, 2026 2