- #include "gdal_common.hpp"
- #include "gdal_majorobject.hpp"
- #include "gdal_driver.hpp"
- #include "gdal_dataset.hpp"
- #include "utils/string_list.hpp"
-
- namespace node_gdal {
-
- Nan::Persistent<FunctionTemplate> Driver::constructor;
- ObjectCache<GDALDriver, Driver> Driver::cache;
- #if GDAL_VERSION_MAJOR < 2
- ObjectCache<OGRSFDriver, Driver> Driver::cache_ogr;
- #endif
-
- void Driver::Initialize(Local<Object> target)
- {
- Nan::HandleScope scope;
-
- Local<FunctionTemplate> lcons = Nan::New<FunctionTemplate>(Driver::New);
- lcons->InstanceTemplate()->SetInternalFieldCount(1);
- lcons->SetClassName(Nan::New("Driver").ToLocalChecked());
-
- Nan::SetPrototypeMethod(lcons, "toString", toString);
- Nan::SetPrototypeMethod(lcons, "open", open);
- Nan::SetPrototypeMethod(lcons, "create", create);
- Nan::SetPrototypeMethod(lcons, "createCopy", createCopy);
- Nan::SetPrototypeMethod(lcons, "deleteDataset", deleteDataset);
- Nan::SetPrototypeMethod(lcons, "rename", rename);
- Nan::SetPrototypeMethod(lcons, "copyFiles", copyFiles);
- Nan::SetPrototypeMethod(lcons, "getMetadata", getMetadata);
-
- ATTR(lcons, "description", descriptionGetter, READ_ONLY_SETTER);
-
- Nan::Set(target, Nan::New("Driver").ToLocalChecked(), Nan::GetFunction(lcons).ToLocalChecked());
-
- constructor.Reset(lcons);
- }
-
-
- #if GDAL_VERSION_MAJOR < 2
- Driver::Driver(GDALDriver *driver)
- : Nan::ObjectWrap(), uses_ogr(false), this_gdaldriver(driver), this_ogrdriver(0)
- {
- LOG("Created GDAL Driver [%p]", driver);
- }
- Driver::Driver(OGRSFDriver *driver)
- : Nan::ObjectWrap(), uses_ogr(true), this_gdaldriver(0), this_ogrdriver(driver)
- {
- LOG("Created OGR Driver [%p]", driver);
- }
- Driver::Driver()
- : Nan::ObjectWrap(), uses_ogr(false), this_gdaldriver(0), this_ogrdriver(0)
- {
- }
- #else
- Driver::Driver(GDALDriver *driver)
- : Nan::ObjectWrap(), this_gdaldriver(driver)
- {
- LOG("Created GDAL Driver [%p]", driver);
- }
- Driver::Driver()
- : Nan::ObjectWrap(), this_gdaldriver(0)
- {
- }
- #endif
-
-
-
- Driver::~Driver()
- {
- dispose();
- }
-
- void Driver::dispose()
- {
- #if GDAL_VERSION_MAJOR < 2
- if(uses_ogr) {
- if(this_ogrdriver) {
- LOG("Disposing OGR Driver [%p]", this_ogrdriver);
- cache_ogr.erase(this_ogrdriver);
- LOG("Disposed OGR Driver [%p]", this_ogrdriver);
- this_ogrdriver = NULL;
- }
- return;
- }
- #endif
- if(this_gdaldriver) {
- LOG("Disposing GDAL Driver [%p]", this_gdaldriver);
- cache.erase(this_gdaldriver);
- LOG("Disposed GDAL Driver [%p]", this_gdaldriver);
- this_gdaldriver = NULL;
- }
- }
-
-
- /**
- * Format specific driver.
- *
- * An instance of this class is created for each supported format, and
- * manages information about the format.
- *
- * This roughly corresponds to a file format, though some drivers may
- * be gateways to many formats through a secondary multi-library.
- *
- * @class gdal.Driver
- */
- NAN_METHOD(Driver::New)
- {
- Nan::HandleScope scope;
-
- if (!info.IsConstructCall()) {
- Nan::ThrowError("Cannot call constructor as function, you need to use 'new' keyword");
- return;
- }
-
- if (info[0]->IsExternal()) {
- Local<External> ext = info[0].As<External>();
- void* ptr = ext->Value();
- Driver *f = static_cast<Driver *>(ptr);
- f->Wrap(info.This());
-
- info.GetReturnValue().Set(info.This());
- return;
- } else {
- Nan::ThrowError("Cannot create Driver directly");
- return;
- }
- }
-
- Local<Value> Driver::New(GDALDriver *driver)
- {
- Nan::EscapableHandleScope scope;
-
- if (!driver) {
- return scope.Escape(Nan::Null());
- }
- if (cache.has(driver)) {
- return scope.Escape(cache.get(driver));
- }
-
- Driver *wrapped = new Driver(driver);
- Local<Value> ext = Nan::New<External>(wrapped);
- Local<Object> obj = Nan::NewInstance(Nan::GetFunction(Nan::New(Driver::constructor)).ToLocalChecked(), 1, &ext).ToLocalChecked();
-
- //LOG("ADDING DRIVER TO CACHE [%p]", driver);
- cache.add(driver, obj);
- //LOG("DONE ADDING DRIVER TO CACHE [%p]", driver);
-
- return scope.Escape(obj);
- }
-
- #if GDAL_VERSION_MAJOR < 2
- Local<Value> Driver::New(OGRSFDriver *driver)
- {
- Nan::EscapableHandleScope scope;
-
- if (!driver) {
- return scope.Escape(Nan::Null());
- }
- if (cache_ogr.has(driver)) {
- return scope.Escape(cache_ogr.get(driver));
- }
-
- Driver *wrapped = new Driver(driver);
- Local<Value> ext = Nan::New<External>(wrapped);
- v8::Local<v8::Object> obj = Nan::NewInstance(Nan::GetFunction(Nan::New(Driver::constructor)).ToLocalChecked(), 1, &ext).ToLocalChecked();
-
- cache_ogr.add(driver, obj);
-
- return scope.Escape(obj);
- }
- #endif
-
- NAN_METHOD(Driver::toString)
- {
- Nan::HandleScope scope;
- info.GetReturnValue().Set(Nan::New("Driver").ToLocalChecked());
- }
-
- /**
- * @readOnly
- * @attribute description
- * @type String
- */
- NAN_GETTER(Driver::descriptionGetter)
- {
- Nan::HandleScope scope;
- Driver* driver = Nan::ObjectWrap::Unwrap<Driver>(info.This());
-
- #if GDAL_VERSION_MAJOR < 2
- if (driver->uses_ogr) {
- info.GetReturnValue().Set(SafeString::New(driver->getOGRSFDriver()->GetName()));
- return;
- }
- #endif
-
- info.GetReturnValue().Set(SafeString::New(driver->getGDALDriver()->GetDescription()));
- }
-
- /**
- * @throws Error
- * @method deleteDataset
- * @param {string} filename
- */
- NAN_METHOD(Driver::deleteDataset)
- {
- Nan::HandleScope scope;
-
- std::string name("");
- NODE_ARG_STR(0, "dataset name", name);
-
- Driver* driver = Nan::ObjectWrap::Unwrap<Driver>(info.This());
-
- #if GDAL_VERSION_MAJOR < 2
- if (driver->uses_ogr) {
- OGRErr err = driver->getOGRSFDriver()->DeleteDataSource(name.c_str());
- if(err) {
- NODE_THROW_OGRERR(err);
- return;
- }
- return;
- }
- #endif
-
- CPLErr err = driver->getGDALDriver()->Delete(name.c_str());
- if (err) {
- NODE_THROW_CPLERR(err);
- return;
- }
- return;
- }
-
- /**
- * Create a new dataset with this driver.
- *
- * @throws Error
- * @method create
- * @param {String} filename
- * @param {Integer} [x_size=0] raster width in pixels (ignored for vector datasets)
- * @param {Integer} [y_size=0] raster height in pixels (ignored for vector datasets)
- * @param {Integer} [band_count=0]
- * @param {Integer} [data_type=gdal.GDT_Byte] pixel data type (ignored for vector datasets) (see {{#crossLink "Constants (GDT)"}}data types{{/crossLink}})
- * @param {String[]|object} [creation_options] An array or object containing driver-specific dataset creation options
- * @return gdal.Dataset
- */
- NAN_METHOD(Driver::create)
- {
- Nan::HandleScope scope;
- Driver *driver = Nan::ObjectWrap::Unwrap<Driver>(info.This());
-
- std::string filename;
- unsigned int x_size = 0, y_size = 0, n_bands = 0;
- GDALDataType type = GDT_Byte;
- std::string type_name = "";
- StringList options;
-
- NODE_ARG_STR(0, "filename", filename);
-
- if(info.Length() < 3){
- if(info.Length() > 1 && options.parse(info[1])){
- return; //error parsing string list
- }
- } else {
- NODE_ARG_INT(1, "x size", x_size);
- NODE_ARG_INT(2, "y size", y_size);
- NODE_ARG_INT_OPT(3, "number of bands", n_bands);
- NODE_ARG_OPT_STR(4, "data type", type_name);
- if(info.Length() > 5 && options.parse(info[5])){
- return; //error parsing string list
- }
- if(!type_name.empty()) {
- type = GDALGetDataTypeByName(type_name.c_str());
- }
- }
-
-
- #if GDAL_VERSION_MAJOR < 2
- if(driver->uses_ogr){
- OGRSFDriver *raw = driver->getOGRSFDriver();
- OGRDataSource *ds = raw->CreateDataSource(filename.c_str(), options.get());
-
- if (!ds) {
- Nan::ThrowError("Error creating dataset");
- return;
- }
-
- info.GetReturnValue().Set(Dataset::New(ds));
- return;
- }
- #endif
-
- GDALDriver *raw = driver->getGDALDriver();
- GDALDataset* ds = raw->Create(filename.c_str(), x_size, y_size, n_bands, type, options.get());
-
- if (!ds) {
- Nan::ThrowError("Error creating dataset");
- return;
- }
-
- info.GetReturnValue().Set(Dataset::New(ds));
- }
-
- /**
- * Create a copy of a dataset.
- *
- * @throws Error
- * @method createCopy
- * @param {String} filename
- * @param {gdal.Dataset} src
- * @param {Boolean} [strict=false]
- * @param {String[]|object} [options=null] An array or object containing driver-specific dataset creation options
- * @return gdal.Dataset
- */
- NAN_METHOD(Driver::createCopy)
- {
- Nan::HandleScope scope;
- Driver *driver = Nan::ObjectWrap::Unwrap<Driver>(info.This());
-
- if(!driver->isAlive()){
- Nan::ThrowError("Driver object has already been destroyed");
- return;
- }
-
- std::string filename;
- Dataset* src_dataset;
- unsigned int strict = 0;
- StringList options;
-
- NODE_ARG_STR(0, "filename", filename);
-
- //NODE_ARG_STR(1, "source dataset", src_dataset)
- if(info.Length() < 2){
- Nan::ThrowError("source dataset must be provided");
- return;
- }
- if (IS_WRAPPED(info[1], Dataset)) {
- src_dataset = Nan::ObjectWrap::Unwrap<Dataset>(info[1].As<Object>());
- } else {
- Nan::ThrowError("source dataset must be a Dataset object");
- return;
- }
-
- if(!src_dataset->isAlive()){
- Nan::ThrowError("Dataset object has already been destroyed");
- return;
- }
-
- if(info.Length() > 2 && options.parse(info[2])){
- return; //error parsing string list
- }
-
- #if GDAL_VERSION_MAJOR < 2
- if (driver->uses_ogr != src_dataset->uses_ogr){
- Nan::ThrowError("Driver unable to copy dataset");
- return;
- }
- if (driver->uses_ogr) {
- OGRSFDriver *raw = driver->getOGRSFDriver();
- OGRDataSource *raw_ds = src_dataset->getDatasource();
-
- OGRDataSource *ds = raw->CopyDataSource(raw_ds, filename.c_str(), options.get());
-
- if (!ds) {
- Nan::ThrowError("Error copying dataset.");
- return;
- }
-
- info.GetReturnValue().Set(Dataset::New(ds));
- return;
- }
- #endif
-
- GDALDriver *raw = driver->getGDALDriver();
- GDALDataset *raw_ds = src_dataset->getDataset();
- GDALDataset *ds = raw->CreateCopy(filename.c_str(), raw_ds, strict, options.get(), NULL, NULL);
-
- if (!ds) {
- Nan::ThrowError("Error copying dataset");
- return;
- }
-
- info.GetReturnValue().Set(Dataset::New(ds));
- }
-
- /**
- * Copy the files of a dataset.
- *
- * @throws Error
- * @method copyFiles
- * @param {String} name_old New name for the dataset.
- * @param {String} name_new Old name of the dataset.
- */
- NAN_METHOD(Driver::copyFiles)
- {
- Nan::HandleScope scope;
- Driver *driver = Nan::ObjectWrap::Unwrap<Driver>(info.This());
- std::string old_name;
- std::string new_name;
-
- #if GDAL_VERSION_MAJOR < 2
- if(driver->uses_ogr) {
- Nan::ThrowError("Driver unable to copy files");
- return;
- }
- #endif
-
- NODE_ARG_STR(0, "new name", new_name);
- NODE_ARG_STR(1, "old name", old_name);
-
- CPLErr err = driver->getGDALDriver()->CopyFiles(new_name.c_str(), old_name.c_str());
- if (err) {
- NODE_THROW_CPLERR(err);
- return;
- }
-
- return;
- }
-
- /**
- * Renames the dataset.
- *
- * @throws Error
- * @method rename
- * @param {String} new_name New name for the dataset.
- * @param {String} old_name Old name of the dataset.
- */
- NAN_METHOD(Driver::rename)
- {
- Nan::HandleScope scope;
- Driver *driver = Nan::ObjectWrap::Unwrap<Driver>(info.This());
- std::string old_name;
- std::string new_name;
-
- #if GDAL_VERSION_MAJOR < 2
- if(driver->uses_ogr) {
- Nan::ThrowError("Driver unable to rename files");
- return;
- }
- #endif
-
- NODE_ARG_STR(0, "new name", new_name);
- NODE_ARG_STR(1, "old name", old_name);
-
- CPLErr err = driver->getGDALDriver()->Rename(new_name.c_str(), old_name.c_str());
- if (err) {
- NODE_THROW_CPLERR(err);
- return;
- }
-
- return;
- }
-
- /**
- * Returns metadata about the driver.
- *
- * @throws Error
- * @method getMetadata
- * @param {String} [domain]
- * @return Object
- */
- NAN_METHOD(Driver::getMetadata)
- {
- Nan::HandleScope scope;
- Driver *driver = Nan::ObjectWrap::Unwrap<Driver>(info.This());
-
- Local<Object> result;
-
- std::string domain("");
- NODE_ARG_OPT_STR(0, "domain", domain);
-
- #if GDAL_VERSION_MAJOR < 2
- if (driver->uses_ogr){
- result = Nan::New<Object>();
- Nan::Set(result, Nan::New("DCAP_VECTOR").ToLocalChecked(), Nan::New("YES").ToLocalChecked());
- info.GetReturnValue().Set(result);
- return;
- }
- #endif
-
- GDALDriver* raw = driver->getGDALDriver();
- result = MajorObject::getMetadata(raw, domain.empty() ? NULL : domain.c_str());
- #if GDAL_VERSION_MAJOR < 2
- Nan::Set(result, Nan::New("DCAP_RASTER").ToLocalChecked(), Nan::New("YES").ToLocalChecked());
- #endif
- info.GetReturnValue().Set(result);
- }
-
- /**
- * Opens a dataset.
- *
- * @throws Error
- * @method open
- * @param {String} path
- * @param {String} [mode=`"r"`] The mode to use to open the file: `"r"` or `"r+"`
- * @return {gdal.Dataset}
- */
- NAN_METHOD(Driver::open)
- {
- Nan::HandleScope scope;
- Driver *driver = Nan::ObjectWrap::Unwrap<Driver>(info.This());
-
- std::string path;
- std::string mode = "r";
- GDALAccess access = GA_ReadOnly;
-
- NODE_ARG_STR(0, "path", path);
- NODE_ARG_OPT_STR(1, "mode", mode);
-
- if (mode == "r+") {
- access = GA_Update;
- } else if (mode != "r") {
- Nan::ThrowError("Invalid open mode. Must be \"r\" or \"r+\"");
- return;
- }
-
- #if GDAL_VERSION_MAJOR < 2
- if (driver->uses_ogr){
- OGRSFDriver *raw = driver->getOGRSFDriver();
- OGRDataSource *ds = raw->Open(path.c_str(), static_cast<int>(access));
- if (!ds) {
- Nan::ThrowError("Error opening dataset");
- return;
- }
- info.GetReturnValue().Set(Dataset::New(ds));
- return;
- }
- #endif
-
- GDALDriver *raw = driver->getGDALDriver();
- GDALOpenInfo *open_info = new GDALOpenInfo(path.c_str(), access);
- GDALDataset *ds = raw->pfnOpen(open_info);
- delete open_info;
- if (!ds) {
- Nan::ThrowError("Error opening dataset");
- return;
- }
- info.GetReturnValue().Set(Dataset::New(ds));
- }
-
- } // namespace node_gdal
-
-