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 IDataLoader
IDataLoader
IDataLoader
and passing it to the
Ogc3DTilesModelDecoder
Ogc3DTilesModelDecoder
Ogc3DTilesModelDecoder
or
HspcModelDecoder
HspcModelDecoder
HspcModelDecoder
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.
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
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);