001package edu.wisc.ssec.mcidasv; 002 003import ucar.unidata.util.FileManager; 004 005import javax.swing.*; 006import javax.swing.filechooser.FileNameExtensionFilter; 007import java.io.*; 008import java.nio.*; 009import java.util.*; 010import java.util.logging.Logger; 011 012public class ShapefileToGeoJSON { 013 private static final Logger logger = Logger.getLogger(ShapefileToGeoJSON.class.getName()); 014 015 public static boolean convert(String inputPath, String outputPath) { 016 String dbfPath = inputPath.substring(0, inputPath.lastIndexOf(".")) + ".dbf"; 017 File shpFile = new File(inputPath); 018 File dbfFile = new File(dbfPath); 019 020 if (!shpFile.exists()) return false; 021 022 try (DataInputStream shpStream = new DataInputStream(new FileInputStream(shpFile)); 023 FileWriter writer = new FileWriter(outputPath)) { 024 025 shpStream.skipBytes(100); 026 027 List<Map<String, Object>> attributes = dbfFile.exists() ? parseDbf(dbfFile) : new ArrayList<>(); 028 029 writer.write("{\"type\": \"FeatureCollection\", \"features\": ["); 030 int recordIdx = 0; 031 boolean firstFeature = true; 032 033 while (shpStream.available() > 0) { 034 int length = shpStream.readInt() * 2; 035 byte[] data = new byte[length]; 036 shpStream.readFully(data); 037 ByteBuffer bb = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN); 038 039 int type = bb.getInt(0); 040 if (type == 3 || type == 5) { 041 if (!firstFeature) writer.write(","); 042 firstFeature = false; 043 044 boolean isPolygon = (type == 5); 045 int numParts = bb.getInt(36); 046 int numPoints = bb.getInt(40); 047 int coordStart = 44 + (numParts * 4); 048 049 int[] parts = new int[numParts]; 050 for (int p = 0; p < numParts; p++) { 051 parts[p] = bb.getInt(44 + p * 4); 052 } 053 054 if (isPolygon) { 055 boolean isMulti = numParts > 1; 056 String geomType = isMulti ? "MultiPolygon" : "Polygon"; 057 writer.write("{\"type\": \"Feature\", \"geometry\": {\"type\": \"" + geomType + "\", \"coordinates\": ["); 058 059 for (int p = 0; p < numParts; p++) { 060 if (p > 0) writer.write(","); 061 int start = parts[p]; 062 int end = (p + 1 < numParts) ? parts[p + 1] : numPoints; 063 064 if (isMulti) writer.write("["); 065 writer.write("["); 066 for (int i = start; i < end; i++) { 067 double lon = bb.getDouble(coordStart + (i * 16)); 068 double lat = bb.getDouble(coordStart + (i * 16) + 8); 069 if (i > start) writer.write(","); 070 writer.write("[" + lon + "," + lat + "]"); 071 } 072 writer.write("]"); 073 if (isMulti) writer.write("]"); 074 } 075 } else { 076 writer.write("{\"type\": \"Feature\", \"geometry\": {\"type\": \"LineString\", \"coordinates\": ["); 077 for (int i = 0; i < numPoints; i++) { 078 double lon = bb.getDouble(coordStart + (i * 16)); 079 double lat = bb.getDouble(coordStart + (i * 16) + 8); 080 if (i > 0) writer.write(","); 081 writer.write("[" + lon + "," + lat + "]"); 082 } 083 } 084 085 writer.write("]}, \"properties\": "); 086 if (recordIdx < attributes.size()) { 087 writer.write(mapToJson(attributes.get(recordIdx))); 088 } else { 089 writer.write("{}"); 090 } 091 writer.write("}"); 092 } 093 recordIdx++; 094 } 095 writer.write("]}"); 096 return true; 097 } catch (Exception e) { 098 e.printStackTrace(); 099 return false; 100 } 101 } 102 103 private static List<Map<String, Object>> parseDbf(File file) throws Exception { 104 List<Map<String, Object>> rows = new ArrayList<>(); 105 try (FileInputStream fis = new FileInputStream(file)) { 106 byte[] header = new byte[32]; 107 fis.read(header); 108 ByteBuffer hb = ByteBuffer.wrap(header).order(ByteOrder.LITTLE_ENDIAN); 109 int numRecords = hb.getInt(4); 110 short headerLength = hb.getShort(8); 111 short recordLength = hb.getShort(10); 112 113 List<String> fieldNames = new ArrayList<>(); 114 List<Integer> fieldWidths = new ArrayList<>(); 115 116 int bytesRead = 32; 117 while (bytesRead < headerLength - 1) { 118 byte[] fieldBuf = new byte[32]; 119 fis.read(fieldBuf); 120 fieldNames.add(new String(fieldBuf, 0, 11).trim()); 121 fieldWidths.add(fieldBuf[16] & 0xFF); 122 bytesRead += 32; 123 } 124 fis.skip(1); 125 126 for (int i = 0; i < numRecords; i++) { 127 byte[] recBuf = new byte[recordLength]; 128 fis.read(recBuf); 129 Map<String, Object> row = new LinkedHashMap<>(); 130 int offset = 1; 131 for (int f = 0; f < fieldNames.size(); f++) { 132 int width = fieldWidths.get(f); 133 row.put(fieldNames.get(f), new String(recBuf, offset, width).trim()); 134 offset += width; 135 } 136 rows.add(row); 137 } 138 } 139 return rows; 140 } 141 142 private static String mapToJson(Map<String, Object> map) { 143 StringBuilder sb = new StringBuilder("{"); 144 map.forEach((k, v) -> sb.append("\"").append(k).append("\":\"").append(v).append("\",")); 145 if (sb.length() > 1) sb.setLength(sb.length() - 1); 146 return sb.append("}").toString(); 147 } 148 149 public static String getShapeFile() { 150 FileNameExtensionFilter filter = new FileNameExtensionFilter("Shape Files (*.shp)", "shp"); 151 return FileManager.getReadFile("Load File", filter); 152 } 153}