Cách chuyển đổi GeoJSON sang JSON (Loại bỏ Hình học, Giữ lại Thuộc tính)
GeoJSON thực sự chứa gì (và tại sao nó lại thừa thãi so với nhu cầu của bạn)
GeoJSON thực chất cũng chỉ là JSON, nhưng với một cấu trúc rất cụ thể và thường khá cồng kềnh. Mỗi file thường gói dữ liệu của bạn trong một `FeatureCollection` chứa một mảng các đối tượng `Feature`. Mỗi `Feature` lại có hai phần chính: `geometry` và `properties`. Khối `geometry` là nơi chứa tất cả dữ liệu không gian—các mảng tọa độ, các loại hình dạng như Point hoặc Polygon, và các vòng tọa độ phức tạp có thể kéo dài hàng nghìn dòng. Khối `properties` mới là thứ bạn thường quan tâm: dữ liệu thuộc tính thực tế như tên, ID, số dân, hoặc dấu thời gian gắn với mỗi hình dạng. Cấu trúc đó trở thành một vấn đề khi bạn cần chuyển dữ liệu cho một hệ thống không hiểu 'bản đồ'. Hãy tưởng tượng bạn đã xuất một bộ dữ liệu về ranh giới của tất cả 4.200 quận của Hoa Kỳ từ một công cụ GIS. File GeoJSON kết quả có thể dễ dàng lên tới 18 MB, với mỗi đa giác quận được xác định bởi hàng nghìn cặp tọa độ. Nếu mục tiêu của bạn chỉ là sử dụng tên quận, mã FIPS và dân số trong một báo cáo hoặc API, thì bạn đang phải kéo theo 95% dữ liệu thừa. Phần hình học đó chỉ là nhiễu. Tệ hơn nữa, một số trình phân tích cú pháp sẽ thẳng thừng từ chối một file vì chúng không nhận dạng được các khóa không gian địa lý. Đối với những mục đích sử dụng phi không gian này, việc loại bỏ phần hình học không phải là làm mất dữ liệu; đó chỉ đơn giản là chuẩn bị dữ liệu một cách thông minh.
Sự khác biệt về cấu trúc: GeoJSON và JSON thuần túy
Hãy đi vào chi tiết về việc chuyển đổi này. Đây là một `Feature` GeoJSON tối giản mà bạn có thể bắt đầu: { "type": "Feature", "geometry": { "type": "Point", "coordinates": [-87.6298, 41.8781] }, "properties": { "city": "Chicago", "population": 2696555, "timezone": "America/Chicago" } } Sau khi loại bỏ phần hình học, mục tiêu của bạn là đối tượng JSON gọn gàng, đơn giản được lồng bên trong khóa `properties`: { "city": "Chicago", "population": 2696555, "timezone": "America/Chicago" } Khi đầu vào của bạn là một `FeatureCollection` (một file có nhiều feature), mục tiêu là tạo ra một mảng JSON duy nhất chỉ chứa các đối tượng thuộc tính từ mỗi feature: [ { "city": "Chicago", "population": 2696555, "timezone": "America/Chicago" }, { "city": "Houston", "population": 2304580, "timezone": "America/Chicago" } ] Đây là định dạng mà hầu hết các API, công cụ nhập bảng tính và trình tải cơ sở dữ liệu được xây dựng để hiểu. Bất kỳ ai đã từng gỡ lỗi một lệnh gọi API thất bại đều hiểu sự bực bội khi một trình phân tích cú pháp 'nghẹn' vì các khóa cấp cao không mong muốn như `geometry` hoặc `type`. Kết quả cuối cùng là một mảng JSON sạch sẽ, sẵn sàng hoạt động với hầu hết mọi công cụ. Một chi tiết quan trọng: điều gì sẽ xảy ra nếu một feature có giá trị `null` cho `properties` của nó? Điều này hoàn toàn hợp lệ trong GeoJSON. Một công cụ chuyển đổi tốt sẽ tạo ra một đối tượng rỗng `{}` cho feature đó thay vì bị treo hoặc, tệ hơn, âm thầm bỏ qua nó. Những công cụ chuyển đổi cẩu thả thường xuyên thất bại ở bài kiểm tra này.
Chuyển đổi GeoJSON sang JSON với CocoConvert
Để có một giải pháp nhanh chóng, không cần code, công cụ chuyển đổi GeoJSON sang JSON của CocoConvert tại /convert/geojson-to-json được thiết kế chính xác cho công việc này. Quá trình rất đơn giản. Bạn tải lên file `.geojson` hoặc `.json` của mình (nó chấp nhận cả hai phần mở rộng, vì GeoJSON thường sử dụng `.json`), chọn các tùy chọn của bạn và download kết quả đã được tinh gọn. Công cụ này hoạt động rất triệt để theo mặc định. Nó tìm mảng `features`, lấy đối tượng `properties` từ mỗi feature, và ghép chúng lại thành một mảng JSON sạch sẽ. Khóa `geometry`, khóa `type`, và lớp vỏ `FeatureCollection` đều bị bỏ lại. Nếu file của bạn chỉ chứa một `Feature` duy nhất thay vì một collection, đầu ra sẽ là một đối tượng JSON duy nhất, không phải một mảng. CocoConvert xử lý các file lên tới 50 MB ở gói miễn phí, đủ cho hầu hết các bộ dữ liệu có dưới 10.000 feature. Nhưng nếu bạn có một file khổng lồ như mạng lưới đường bộ toàn tiểu bang (có thể dễ dàng lên tới 200 MB hoặc hơn), bạn sẽ cần sử dụng một công cụ dòng lệnh, mà chúng tôi sẽ đề cập tiếp theo. Về quyền riêng tư, công cụ này xử lý file trên máy chủ nhưng không lưu trữ dữ liệu của bạn sau phiên làm việc, một chi tiết quan trọng nếu các thuộc tính của bạn bao gồm thông tin nhạy cảm như định danh cá nhân. Theo mặc định, file được download sẽ có phần mở rộng là `.json`. Bảng cài đặt cho phép bạn kiểm soát nhiều hơn, cho phép bạn chỉ định tên file tùy chỉnh hoặc nhận đầu ra được thụt lề, dễ đọc thay vì một dòng được thu gọn.
Các lựa chọn thay thế dùng dòng lệnh cho file lớn hoặc tự động hóa
Khi bạn xử lý các file trên 50 MB hoặc cần tự động hóa việc chuyển đổi này trong một script, dòng lệnh là người bạn của bạn. Bạn có hai vũ khí chính để lựa chọn: `jq` và module `json` tích hợp sẵn của Python. Đối với việc chuyển đổi JSON thuần túy, không gì có thể sánh bằng `jq` về tốc độ và sự đơn giản: jq '[.features[].properties]' input.geojson > output.json Lệnh một dòng này thực hiện chính xác những gì công cụ web làm: nó lặp qua từng feature, lấy ra đối tượng `properties`, và gói kết quả vào một mảng JSON. `jq` có thể dễ dàng cài đặt trên bất kỳ hệ điều hành chính nào (như `brew install jq` trên macOS hoặc `apt install jq` trên Debian/Ubuntu) và có thể xử lý các file dung lượng gigabyte mà không hề nao núng vì nó truyền dữ liệu theo luồng thay vì tải tất cả vào bộ nhớ. Nếu `id` của feature quan trọng (trong GeoJSON, nó nằm cùng cấp với `properties`, không phải bên trong), bạn có thể hợp nhất nó vào: jq '[.features[] | {id: .id} + .properties]' input.geojson > output.json Khi bạn cần nhiều logic hơn, Python là câu trả lời: import json with open('input.geojson') as f: gj = json.load(f) result = [feature['properties'] for feature in gj['features']] with open('output.json', 'w') as f: json.dump(result, f, indent=2) Đoạn script ngắn này cho bạn toàn quyền kiểm soát để lọc các feature, đổi tên các khóa, hoặc xử lý các trường hợp ngoại lệ kỳ lạ trước khi ghi ra kết quả. Hay nhất là module `json` là một phần của thư viện chuẩn Python, vì vậy không cần cài đặt thêm gì cả. Để tôi nói thẳng đề xuất của mình: Hãy dùng CocoConvert khi bạn có một file và muốn có kết quả trong 30 giây mà không cần động đến code. Hãy dùng `jq` hoặc Python khi bạn đang tự động hóa một quy trình dữ liệu hoặc xử lý hàng trăm file.
Xử lý các trường hợp ngoại lệ làm hỏng các công cụ chuyển đổi 'ngây thơ'
GeoJSON trong thực tế thường lộn xộn hơn các ví dụ sạch sẽ trong đặc tả. Dưới đây là những cạm bẫy phổ biến có thể làm hỏng một công cụ chuyển đổi 'ngây thơ'. **Hình học null:** Bạn sẽ thường xuyên gặp các feature có `"geometry": null`. Chúng hoàn toàn hợp lệ, thường đại diện cho các bản ghi đơn giản là thiếu dữ liệu vị trí. Một công cụ chuyển đổi mạnh mẽ vẫn phải trích xuất các thuộc tính của chúng, chứ không phải loại bỏ toàn bộ feature. Các phương pháp dùng `jq` và Python được trình bày ở trên xử lý đúng trường hợp này. **Thuộc tính lồng nhau:** Bản thân đối tượng `properties` có thể chứa các đối tượng JSON lồng nhau, như `"properties": {"address": {"street": "Main St"}}`. Việc loại bỏ hình học không làm phẳng các cấu trúc lồng nhau này; chúng được giữ nguyên. Nếu bạn cần một cấu trúc hoàn toàn phẳng (ví dụ, cho một file CSV), đó là một phép biến đổi riêng biệt bạn cần thực hiện. **Các khóa không nhất quán:** Việc một số feature trong một collection có khóa `"name"` trong khi những feature khác lại không là chuyện thường thấy. Mảng JSON kết quả sẽ đơn giản có các đối tượng với các hình dạng khác nhau. Điều này là JSON hợp lệ, nhưng nó có thể gây khó khăn cho các hệ thống có kiểu dữ liệu chặt chẽ. CocoConvert trích xuất trung thực những gì có ở đó; nó sẽ không cố gắng chuẩn hóa lược đồ cho bạn. **`GeometryCollection`:** Một số file sử dụng loại `GeometryCollection`, có cấu trúc khác với `FeatureCollection` tiêu chuẩn. Nhiều công cụ, bao gồm cả CocoConvert, mong đợi một `FeatureCollection` và có thể thất bại nếu chúng gặp một `GeometryCollection` ở cấp cao nhất. **Vấn đề về mã hóa:** Đây là một cơn đau đầu kinh điển của dữ liệu GIS. Đặc tả GeoJSON quy định bắt buộc phải mã hóa UTF-8, chấm hết. Nhưng các file được xuất từ phần mềm cũ đôi khi có thể chứa các ký tự Latin-1 hoặc Windows-1252. Điều này sẽ gây ra lỗi phân tích cú pháp. Bạn phải sửa mã hóa ở khâu đầu vào bằng một công cụ như `iconv` trước khi cố gắng chuyển đổi.
Khi nào bạn nên giữ lại Geometry (và chuyển đổi theo cách khác)
Mặc dù việc loại bỏ hình học rất tuyệt vời cho các quy trình chỉ xử lý dữ liệu, đôi khi đó lại hoàn toàn là một nước đi sai lầm. Có rất nhiều lúc bạn cần giữ lại dữ liệu không gian, chỉ là ở một dạng khác. Nếu bạn đang cung cấp dữ liệu cho một thư viện bản đồ web như Leaflet hoặc MapboxGL, hãy dừng lại. Đừng chuyển đổi bất cứ thứ gì. Cả hai thư viện này đều sử dụng GeoJSON một cách tự nhiên, vì vậy việc chuyển đổi sang JSON thuần túy sẽ loại bỏ chính những tọa độ mà chúng cần để vẽ bản đồ của bạn. Đôi khi bạn cần các tọa độ, nhưng ở một hình dạng khác—như một mảng phẳng các đối tượng `{lat, lng, name}` cho một biểu đồ tùy chỉnh. Đó là một nhiệm vụ định hình lại, không phải là loại bỏ hình học. `jq` là công cụ hoàn hảo cho việc này: jq '[.features[] | {lat: .geometry.coordinates[1], lng: .geometry.coordinates[0], name: .properties.name}]' input.geojson > output.json Hãy chú ý kỹ đến thứ tự tọa độ ở đây. GeoJSON tuân thủ nghiêm ngặt `[kinh độ, vĩ độ]`, hay (x, y). Điều này ngược lại với những gì nhiều người và nhiều hệ thống mong đợi. Mắc lỗi này là sai lầm phổ biến nhất khi xử lý tọa độ GeoJSON theo cách thủ công, và nó gây ra hiệu ứng vừa tức cười vừa bực bội là đặt dữ liệu của bạn sang nhầm bán cầu. Nếu mục tiêu của bạn là chuyển đổi GeoJSON sang một định dạng không gian địa lý khác như TopoJSON, Shapefile, hoặc KML, bạn cần một công cụ khác. CocoConvert không thực hiện các chuyển đổi bảo toàn hình học này. Đối với việc đó, bạn nên sử dụng một công cụ chuyên dụng như `ogr2ogr` (từ GDAL), một công cụ dòng lệnh mạnh mẽ, hoặc công cụ web tuyệt vời Mapshaper. Chúng là những công cụ phù hợp cho công việc đó. Công cụ GeoJSON sang JSON của chúng tôi tại /convert/geojson-to-json tập trung cao độ vào một nhiệm vụ duy nhất: trích xuất các thuộc tính. Nó làm một việc đó, và làm rất tốt.
Kiểm tra kết quả đầu ra trước khi sử dụng ở các bước tiếp theo
Trước khi bạn đưa file JSON mới toanh của mình vào một hệ thống phía sau, hãy dành hai phút để kiểm tra nó. Bước đơn giản này có thể giúp bạn tiết kiệm hàng giờ gỡ lỗi những thất bại khó hiểu sau này. Hãy mở file đó ra trong một trình soạn thảo văn bản. Cấp cao nhất có phải là một mảng (bắt đầu bằng `[`) không? Mỗi phần tử có phải là một đối tượng (bắt đầu bằng `{`) không? Quan trọng nhất, bạn có thấy bất kỳ khóa `geometry` hoặc `type` nào không? Lẽ ra là không. Tìm kiếm nhanh chuỗi 'coordinates' sẽ không cho ra kết quả nào. Tiếp theo, hãy kiểm tra số lượng bản ghi. Nếu GeoJSON đầu vào của bạn có 847 feature, mảng JSON đầu ra của bạn phải có chính xác 847 đối tượng. Nếu các con số không khớp, công cụ chuyển đổi đã làm rơi mất feature, có thể do đầu vào bị lỗi hoặc xử lý giá trị null không chính xác. Bây giờ, hãy kiểm tra ngẫu nhiên chính dữ liệu. So sánh các giá trị thuộc tính cho bản ghi đầu tiên, cuối cùng và một bản ghi ngẫu nhiên ở giữa trong file JSON mới của bạn với file GeoJSON gốc. Nếu tên, ID và các con số đều khớp, bạn có thể tự tin rằng việc chuyển đổi đã diễn ra suôn sẻ. Đối với các quy trình tự động, hãy sử dụng JSONSchema. Các công cụ như `ajv` cho Node.js hoặc `jsonschema` cho Python cho phép bạn xác minh theo lập trình rằng mọi đối tượng trong mảng của bạn đều có các khóa và kiểu dữ liệu bạn mong đợi. Điều này là cần thiết cho bất kỳ quy trình nào chạy thường xuyên trên dữ liệu thay đổi. Và một điều cuối cùng: nếu dữ liệu đang hướng đến một cơ sở dữ liệu, hãy chạy một truy vấn `COUNT` trên bảng sau khi nhập. Số hàng có khớp với những gì bạn mong đợi không? Việc kiểm tra 30 giây này là lời xác nhận cuối cùng rằng toàn bộ chuỗi—chuyển đổi và nhập—đã hoạt động hoàn hảo.