fromBytes static method

VectorTileVtzero fromBytes({
  1. required Uint8List bytes,
})
override

Create from bytes using vtzero decoder

Implementation

static VectorTileVtzero fromBytes({required Uint8List bytes}) {
  final vtzTile = VtzTile.fromBytes(bytes);
  final layers = <vt.VectorTileLayer>[];

  try {
    final vtzLayers = vtzTile.getLayers();

    for (final vtzLayer in vtzLayers) {
      final features = <vt.VectorTileFeature>[];
      final keys = <String>[];
      final values = <vt.VectorTileValue>[];

      final vtzFeatures = vtzLayer.getFeatures();

      for (final vtzFeature in vtzFeatures) {
        // Get properties and build keys/values tables
        final props = vtzFeature.getProperties();
        final tags = <int>[];

        props.forEach((key, value) {
          // Add key to keys table if not exists
          int keyIndex = keys.indexOf(key);
          if (keyIndex == -1) {
            keyIndex = keys.length;
            keys.add(key);
          }

          // Add value to values table if not exists
          final vtValue = _convertValue(value);
          int valueIndex = values.indexWhere((v) => _valuesEqual(v, vtValue));
          if (valueIndex == -1) {
            valueIndex = values.length;
            values.add(vtValue);
          }

          // Add key-value pair as tags
          tags.add(keyIndex);
          tags.add(valueIndex);
        });

        // Convert geometry type
        vt.VectorTileGeomType? vtType;
        switch (vtzFeature.geometryType) {
          case VtzGeometryType.point:
            vtType = vt.VectorTileGeomType.POINT;
            break;
          case VtzGeometryType.linestring:
            vtType = vt.VectorTileGeomType.LINESTRING;
            break;
          case VtzGeometryType.polygon:
            vtType = vt.VectorTileGeomType.POLYGON;
            break;
          default:
            vtType = vt.VectorTileGeomType.UNKNOWN;
        }

        // Create closure that captures feature for lazy geometry decoding
        final layerExtent = vtzLayer.extent;
        final geomType = vtzFeature.geometryType;

        // Create optimized feature with geometry decoder closure
        features.add(
          VectorTileFeatureVtzero(
            geometryDecoder: (int x, int y, int z) {
              return vtzFeature.toGeoJson(
                extent: layerExtent,
                tileX: x,
                tileY: y,
                tileZ: z,
              );
            },
            geometryTypeVtz: geomType,
            extent: vtzLayer.extent,
            id: vtzFeature.id != null ? Int64(vtzFeature.id!) : Int64.ZERO,
            tags: tags,
            type: vtType,
            geometryList: [], // Not needed - we use native toGeoJson
            keys: keys,
            values: values,
          ),
        );
      }

      layers.add(
        VectorTileLayerVtzero(
          name: vtzLayer.name,
          extent: vtzLayer.extent,
          version: vtzLayer.version,
          keys: keys,
          values: values,
          features: features,
        ),
      );

      // DON'T dispose features or layers - they're kept alive via closures
      // vtzLayer.dispose();
    }

    // DON'T dispose tile - features still reference it via closures
    // Note: The native objects stay in memory until VectorTileVtzero is garbage collected
    // vtzTile.dispose();
    return VectorTileVtzero(layers: layers);
  } catch (e) {
    // Only dispose tile on error - features/layers are already consumed
    vtzTile.dispose();
    rethrow;
  }
}