Why do it?

Using the LuciadCPillar API, you can decode OGC 3D tiles and HSPC data from either a file path or a URL. There may be cases where you want to control how the data is loaded. For example, you want to:

  • Add authentication when loading data from an OGC 3D Tiles server

  • Add an unzip step

  • Use a third-party library to load the data, instead of loading it from the file system

How to do it?

You customize data loading by creating a custom IDataLoaderIDataLoaderIDataLoader and passing it to the Ogc3DTilesModelDecoderOgc3DTilesModelDecoderOgc3DTilesModelDecoder or HspcModelDecoderHspcModelDecoderHspcModelDecoder options when decoding data.

Program: Custom data loader shows an example of a custom data loader that adds a base path to the file path and reads the file from the file system.

Program: Custom data loader
class CustomDataLoader final : public IDataLoader {
public:
  std::string _basePath;
  explicit CustomDataLoader(std::string basePath) : _basePath(std::move(basePath)) {
  }

  luciad::expected<DataEntity, ErrorInfo> load(const std::string& path, const CancellationToken& /*token*/) override {
    std::cout << "Custom data loader: '" << path << "'" << std::endl;

    auto fullPath = _basePath.append(path);
    std::ifstream file(fullPath, std::ios::binary);

    if (!file.is_open()) {
      std::string message = std::string{"Opening file failed: '"} + path + "'";
      ErrorCode errorCode = ErrorCode::Unsupported;
      return luciad::make_unexpected(ErrorInfo(errorCode, message));
    }

    // Prepare the buffer
    // Isolate size
    file.seekg(0, std::ifstream::end);
    auto size = file.tellg();
    file.seekg(0, std::ifstream::beg);

    // Load the data into a memory buffer.
    MemoryBuffer<uint8_t> data(size);
    file.read((char*)data.data(), data.sizeInBytes());

    // Success return
    DataEntity result{data, ""};

    return {result};
  }
};
private class CustomDataLoader : IDataLoader
{
    public DataEntity Load(string path, CancellationToken token)
    {
        using (var fs = File.Open(path, FileMode.Open, FileAccess.Read))
        {
            byte[] data = new byte[fs.Length];
            int numBytesToRead = (int)fs.Length;
            int offset = 0;
            while (numBytesToRead > 0)
            {
                if (token.IsCanceled)
                {
                    throw new OperationCanceledException();
                }

                int readLen = fs.Read(data, offset, numBytesToRead);

                offset += readLen;
                numBytesToRead -= readLen;
            }

            ByteBuffer byteBuffer = new ByteBuffer(data);
            return new DataEntity(byteBuffer, "");
        }
    }
}
private static class CustomDataLoader implements IDataLoader {

  private final String _basePath;

  CustomDataLoader(String basePath) {
    _basePath = basePath;
  }

  @Override
  public @NotNull DataEntity load(@NotNull String path, @NotNull CancellationToken token) throws IOException {
    String fullPath = _basePath + path;
    File file = new File(fullPath);
    if (!file.exists()) {
      throw new IOException("File does not exist.");
    }
    int size = (int) file.length();
    byte[] bytes = new byte[size];
    try (BufferedInputStream buf = new BufferedInputStream(new FileInputStream(file))) {
      buf.read(bytes, 0, bytes.length);
    }

    ByteBuffer byteBuffer = new ByteBuffer(bytes);

    return new DataEntity(byteBuffer, "");
  }
}

Program: Custom data loader usage shows an example of how to use the custom data loader

Program: Custom data loader usage
auto customDataLoader = std::make_shared<CustomDataLoader>("");
auto options = Ogc3DTilesModelDecoder::Options::newBuilder().dataLoader(customDataLoader).build();
auto model = Ogc3DTilesModelDecoder::decode(path.string(), options);
var customDataLoader = new CustomDataLoader();
var options = Ogc3DTilesModelDecoder.Options.NewBuilder()
    .DataLoader(customDataLoader)
    .Build();
var model = Ogc3DTilesModelDecoder.Decode(path, options);
var customDataLoader = new CustomDataLoader("");
var options = Ogc3DTilesModelDecoder.Options.newBuilder()
                                            .dataLoader(customDataLoader)
                                            .build();
var model = Ogc3DTilesModelDecoder.decode(path, options);