LineString compression as Vec<u8>

I've made a simple lib crate to compress linestrings of georust (LineString). It transforms float point coordinates into integers and then stores them (without leading 0s) in one vector of u8s.

Here's a big question: how to set the precision of coordinates? E.g. for Google pseudo-Mercator 3857, only 2 decimal digits are needed, making 1cm precision. But for lon/lat one needs 7 digits for an equal precision.

Since LineStrings are CRS-agnostic, you need to pass precision as a parameter somewhere. So I came up with a trait with 2 methods, try_compact2 & try_compact7, and two modules for Serde, which looks ugly. That's why I'm open to suggestions how to organize this parametrization.

  1. To compress/decompress a linestring:

    use cmpls::{ToCompLs, wktls, assert_ls_eq};
    let ls = wktls!(76.9615707 43.2746200,76.9616699 43.2747688,76.9620742 43.2753715,76.9627532 43.2764091,76.9629516 43.2765502,76.9630584 43.2765998);
    let cmp = ls.try_compact7()?; // compress
    assert_ls_eq!(ls, cmp.linestring()); // decompresses with .linestring()
  2. To compact LineStrings only in serde serialization:

    use cmpls::{compls_p2, compls_p7};
    #[derive(Serialize, Deserialize)]
    struct MyStruct {
    	id: usize,
    	#[serde(with="compls_p7")] // geometry in lat/lon need 7 digits of precision, hence compls_p7
    	geometry_4326: LineString,
    	#[serde(with="compls_p2")]  // geometries in metre-based CRS need just 2 digits, use compls_p2
    	geometry_3857: LineString

Please, look at the code, and tell if anything needs a fix. (The test actually passes, btw.)

I would remove the String from CompLsError and make it a non-exaustive enum. You can use thiserror to reduce the boilerplate.

It seems like you are using signed LEB128 for the integer encoding although I am not exactly sure if you treat the sign equivalently. I think you should at least internally document that. The format is simple enough so you can implement it yourself but you might want to use an external crate like leb128 or varint-simd if you require the speed.

// >>1 is integer division by 2
These kinds of optimizations are not neccessary on a non-ancient compiler. Especially if they require a comment to explain.

Use rustfmt.

1 Like


I was unaware how it's called. I picked up the idea from Google Polyline and changed it to pack 7 bits per byte.

I checked the code in leb128-rs and it seems possible to use it, thanks!