# geojson 压缩

利用 echarts 里面 geojson 压缩的方法可以大大缩小 geojson 文件的大小

300kb 压缩后 12kb 十倍以上

# 压缩

const fs = require("fs");

fs.readFile("目标文件", "utf8", (err, data) => {
  const res = encodeGeo(JSON.parse(data));
  fs.writeFileSync("结果文件", JSON.stringify(res));
});

// begin: 压缩算法
function encodePolygon(coordinate, encodeOffsets) {
  var result = "";
  var prevX = quantize(coordinate[0][0]);
  var prevY = quantize(coordinate[0][1]);
  // Store the origin offset
  encodeOffsets[0] = prevX;
  encodeOffsets[1] = prevY;
  for (var i = 0; i < coordinate.length; i++) {
    var point = coordinate[i];
    result += encode(point[0], prevX);
    result += encode(point[1], prevY);
    prevX = quantize(point[0]);
    prevY = quantize(point[1]);
  }
  return result;
}

function encode(val, prev) {
  // Quantization
  val = quantize(val);

  val = val - prev;
  if (((val << 1) ^ (val >> 15)) + 64 === 8232) {
    //WTF, 8232 will get syntax error in js code
    val--;
  }
  // ZigZag
  val = (val << 1) ^ (val >> 15);
  // add offset and get unicode
  return String.fromCharCode(val + 64);
}
function quantize(val) {
  return Math.ceil(val * 1024);
}

function encodeGeo(geo) {
  // 压缩标识
  geo.UTF8Encoding = true;
  geo.UTF8Scale = 1024;

  for (let f_i = 0; f_i < geo.features.length; f_i++) {
    var geometry = geo.features[f_i].geometry;
    if (geometry.type === "Polygon") {
      var coordinates = geometry.coordinates;
      geometry.encodeOffsets = Array(coordinates.length);
      for (let c = 0; c < coordinates.length; c++) {
        var offset = Array(2);
        var codeStr = encodePolygon(coordinates[c], offset);
        coordinates[c] = codeStr;
        geometry.encodeOffsets[c] = offset;
      }
    } else if (geometry.type === "MultiPolygon") {
      var coordinates = geometry.coordinates;
      geometry.encodeOffsets = Array(coordinates.length);
      for (let c = 0; c < coordinates.length; c++) {
        var coordinate = coordinates[c];
        geometry.encodeOffsets[c] = Array(coordinate.length);

        for (let c2 = 0; c2 < coordinate.length; c2++) {
          var offset = Array(2);
          var codeStr = encodePolygon(coordinate[c2], offset);
          coordinate[c2] = codeStr;
          geometry.encodeOffsets[c][c2] = offset;
        }
      }
    }
  }
  return geo;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77

# 解压

function decode(json) {
  if (!json.UTF8Encoding) {
    return json;
  }
  var features = json.features;

  for (var f = 0; f < features.length; f++) {
    var feature = features[f];
    var geometry = feature.geometry;
    var coordinates = geometry.coordinates;
    var encodeOffsets = geometry.encodeOffsets;

    for (var c = 0; c < coordinates.length; c++) {
      var coordinate = coordinates[c];

      if (geometry.type === "Polygon") {
        coordinates[c] = decodePolygon(coordinate, encodeOffsets[c]);
      } else if (geometry.type === "MultiPolygon") {
        for (var c2 = 0; c2 < coordinate.length; c2++) {
          var polygon = coordinate[c2];
          coordinate[c2] = decodePolygon(polygon, encodeOffsets[c][c2]);
        }
      }
    }
  }
  // Has been decoded
  json.UTF8Encoding = false;
  return json;
}

function decodePolygon(coordinate, encodeOffsets) {
  var result = [];
  var prevX = encodeOffsets[0];
  var prevY = encodeOffsets[1];

  for (var i = 0; i < coordinate.length; i += 2) {
    var x = coordinate.charCodeAt(i) - 64;
    var y = coordinate.charCodeAt(i + 1) - 64;
    // ZigZag decoding
    x = (x >> 1) ^ -(x & 1);
    y = (y >> 1) ^ -(y & 1);
    // Delta decoding
    x += prevX;
    y += prevY;

    prevX = x;
    prevY = y;
    // Dequantize
    result.push([x / 1024, y / 1024]);
  }

  return result;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53