diff --git a/Dockerfile b/Dockerfile index c126937f5..8dc3f290e 100755 --- a/Dockerfile +++ b/Dockerfile @@ -6,7 +6,7 @@ USER root LABEL maintainer="adaguc@knmi.nl" # Version should be same as in Definitions.h -LABEL version="2.18.0" +LABEL version="2.19.0" # Try to update image packages RUN apt-get -q -y update \ diff --git a/NEWS.md b/NEWS.md index af45ef042..dffe1d914 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,7 @@ +**Version 2.19.0 2024-02-14** +- Support Irregular grids based on 1D lat/lon variables +- Support Irregular grids based on 2D lat/lon variables + **Version 2.18.0 2024-02-14** - Support live update layer, displays a GetMap image with the current time per second for the last hour. diff --git a/adagucserverEC/CConvertLatLonGrid.cpp b/adagucserverEC/CConvertLatLonGrid.cpp new file mode 100644 index 000000000..161296a28 --- /dev/null +++ b/adagucserverEC/CConvertLatLonGrid.cpp @@ -0,0 +1,191 @@ +/****************************************************************************** + * + * Project: ADAGUC Server + * Purpose: ADAGUC OGC Server + * Author: Maarten Plieger, plieger "at" knmi.nl + * Date: 2024-01-26 + * + ****************************************************************************** + * + * Copyright 2024, Royal Netherlands Meteorological Institute (KNMI) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +#include "CConvertLatLonGrid.h" +#include "CFillTriangle.h" +#include "CImageWarper.h" + +#define CConvertLatLonGrid_DEBUG + +const char *CConvertLatLonGrid::className = "CConvertLatLonGrid"; + +static const char *const lonNamesToCheck[] = {"lon", "longitude"}; +static const char *const latNamesToCheck[] = {"lat", "latitude"}; + +CDF::Variable *CConvertLatLonGrid::getLon1D(CDFObject *cdfObject) { + for (auto lonName : lonNamesToCheck) { + CDF::Variable *lon1DVar = cdfObject->getVariableNE(lonName); + if (lon1DVar != nullptr && lon1DVar->dimensionlinks.size() == 1) { + return lon1DVar; + } + } + return nullptr; +} + +CDF::Variable *CConvertLatLonGrid::getLat1D(CDFObject *cdfObject) { + for (auto latName : latNamesToCheck) { + CDF::Variable *lon1DVar = cdfObject->getVariableNE(latName); + if (lon1DVar != nullptr && lon1DVar->dimensionlinks.size() == 1) { + return lon1DVar; + } + } + return nullptr; +} +CDF::Variable *CConvertLatLonGrid::getLon2D(CDFObject *cdfObject) { + for (auto lonName : lonNamesToCheck) { + CDF::Variable *lon1DVar = cdfObject->getVariableNE(lonName); + if (lon1DVar != nullptr && lon1DVar->dimensionlinks.size() == 2) { + return lon1DVar; + } + } + return nullptr; +} +CDF::Variable *CConvertLatLonGrid::getLat2D(CDFObject *cdfObject) { + for (auto latName : latNamesToCheck) { + CDF::Variable *lon1DVar = cdfObject->getVariableNE(latName); + if (lon1DVar != nullptr && lon1DVar->dimensionlinks.size() == 2) { + return lon1DVar; + } + } + return nullptr; +} + +bool CConvertLatLonGrid::checkIfIrregularLatLon(CDFObject *cdfObject) { + CDF::Variable *lon1DVar = getLon1D(cdfObject); + CDF::Variable *lat1DVar = getLat1D(cdfObject); + + if (lon1DVar != nullptr && lat1DVar != nullptr) { + if (lat1DVar->dimensionlinks.size() == 1 && lon1DVar->dimensionlinks.size() == 1) { + lon1DVar->readData(CDF_DOUBLE); + lat1DVar->readData(CDF_DOUBLE); + size_t width = lon1DVar->dimensionlinks[0]->getSize(); + size_t height = lat1DVar->dimensionlinks[0]->getSize(); + + double *dfdim_X = (double *)lon1DVar->data; + double *dfdim_Y = (double *)lat1DVar->data; + + double cellSizeXBorder = fabs(dfdim_X[0] - dfdim_X[0 + 1]); + double cellSizeXCenter = fabs(dfdim_X[width / 2] - dfdim_X[width / 2 + 1]); + double deviationX = ((cellSizeXBorder - cellSizeXCenter) / cellSizeXBorder); + + double cellSizeYBorder = fabs(dfdim_Y[0] - dfdim_Y[0 + 1]); + double cellSizeYCenter = fabs(dfdim_Y[height / 2] - dfdim_Y[height / 2 + 1]); + double deviationY = ((cellSizeYBorder - cellSizeYCenter) / cellSizeYBorder); + + // When the cellsize deviates more than 1% in the center than in the border, we call this grid irregular lat/lon + if (deviationY > 0.01 || deviationX > 0.01) { + CDBDebug("Note: Irregular grid encountered"); + return true; + } + } + } + return false; +} + +bool CConvertLatLonGrid::isLatLonGrid(CDFObject *cdfObject) { + + if (cdfObject->getAttributeNE("ConvertLatLonGridActive") != nullptr) { + return true; + } + + bool hasXYDimensions = cdfObject->getDimensionNE("x") != NULL && cdfObject->getDimensionNE("y") != NULL; + bool hasXYVariables = cdfObject->getVariableNE("x") != NULL && cdfObject->getVariableNE("y") != NULL; + + bool fixIrregular = checkIfIrregularLatLon(cdfObject); + + if (fixIrregular) { + // Convert + hasXYDimensions = true; + hasXYVariables = false; + + // When the latitude or longitude grids are not present in the cdfObject, add them to the cdfObject + CDF::Variable *lonGridVar = getLon2D(cdfObject); + if (lonGridVar == nullptr) { + CDBDebug("Adding 2D latlon grids"); + + // Rename 1D lat/lon variables to x and y + CDF::Variable *lon1DVar = getLon1D(cdfObject); + CDF::Variable *lat1DVar = getLat1D(cdfObject); + CDF::Dimension *lon1DDim = lon1DVar->dimensionlinks[0]; + CDF::Dimension *lat1DDim = lat1DVar->dimensionlinks[0]; + lon1DDim->setName("x"); + lat1DDim->setName("y"); + + // These variables should not be used, as the 2d lat/lon variables are used instead + lon1DVar->setName("unusedlon"); + lat1DVar->setName("unusedlat"); + lon1DVar->readData(CDF_DOUBLE); + lat1DVar->readData(CDF_DOUBLE); + + // Define the 2D latitude longitude grids + CDF::Variable *longitude = new CDF::Variable(); + longitude->setType(CDF_DOUBLE); + longitude->name.copy("longitude"); + longitude->dimensionlinks.push_back(lat1DDim); + longitude->dimensionlinks.push_back(lon1DDim); + longitude->setAttributeText("ADAGUCConvertLatLonGridConverter", "DONE"); + longitude->setAttributeText("ADAGUC_SKIP", "TRUE"); + + // longitude->setCustomReader(CDF::Variable::CustomMemoryReaderInstance); + cdfObject->addVariable(longitude); + longitude->allocateData(lon1DDim->length * lat1DDim->length); + + CDF::Variable *latitude = new CDF::Variable(); + latitude->setType(CDF_DOUBLE); + latitude->name.copy("latitude"); + latitude->dimensionlinks.push_back(lat1DDim); + latitude->dimensionlinks.push_back(lon1DDim); + latitude->setAttributeText("ADAGUCConvertLatLonGridConverter", "DONE"); + latitude->setAttributeText("ADAGUC_SKIP", "TRUE"); + // latitude->setCustomReader(CDF::Variable::CustomMemoryReaderInstance); + + cdfObject->addVariable(latitude); + latitude->allocateData(lon1DDim->length * lat1DDim->length); + + CDBDebug("Making latitude and longitude grids"); + for (size_t latIndex = 0; latIndex < lat1DDim->length; latIndex += 1) { + for (size_t lonIndex = 0; lonIndex < lon1DDim->length; lonIndex += 1) { + size_t p = lonIndex + latIndex * lon1DDim->length; + ((double *)longitude->data)[p] = ((double *)lon1DVar->data)[lonIndex]; + ((double *)latitude->data)[p] = ((double *)lat1DVar->data)[latIndex]; + } + } + } + } + + CDF::Variable *latVar = getLat2D(cdfObject); + CDF::Variable *lonVar = getLon2D(cdfObject); + bool hasLatLonVariables = (latVar != NULL && lonVar != NULL); + + if (hasXYDimensions && !hasXYVariables && hasLatLonVariables) { + if (latVar->dimensionlinks.size() == 2 && lonVar->dimensionlinks.size() == 2) { + if (latVar->dimensionlinks[0]->name.equals("y") && lonVar->dimensionlinks[0]->name.equals("y") && latVar->dimensionlinks[1]->name.equals("x") && lonVar->dimensionlinks[1]->name.equals("x")) { + cdfObject->setAttributeText("ConvertLatLonGridActive", "TRUE"); + return true; + } + } + } + return false; +} diff --git a/adagucserverEC/CConvertLatLonGrid.h b/adagucserverEC/CConvertLatLonGrid.h new file mode 100644 index 000000000..a863219c2 --- /dev/null +++ b/adagucserverEC/CConvertLatLonGrid.h @@ -0,0 +1,44 @@ +/****************************************************************************** + * + * Project: ADAGUC Server + * Purpose: ADAGUC OGC Server + * Author: Maarten Plieger, plieger "at" knmi.nl + * Date: 2024-01-26 + * + ****************************************************************************** + * + * Copyright 2013, Royal Netherlands Meteorological Institute (KNMI) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +#ifndef CCONVERTLATLONGRID_H +#define CConvertLatLonGrid_H +#include "CDataSource.h" +class CConvertLatLonGrid { +private: + DEF_ERRORFUNCTION(); + + static bool isLatLonGrid(CDFObject *cdfObject); + static bool checkIfIrregularLatLon(CDFObject *cdfObject); + static CDF::Variable *getLat1D(CDFObject *cdfObject); + static CDF::Variable *getLat2D(CDFObject *cdfObject); + static CDF::Variable *getLon1D(CDFObject *cdfObject); + static CDF::Variable *getLon2D(CDFObject *cdfObject); + +public: + static int convertLatLonGridHeader(CDFObject *cdfObject, CServerParams *srvParams); + static int convertLatLonGridData(CDataSource *dataSource, int mode); +}; +#endif diff --git a/adagucserverEC/CConvertLatLonGridData.cpp b/adagucserverEC/CConvertLatLonGridData.cpp new file mode 100644 index 000000000..b99cb11d9 --- /dev/null +++ b/adagucserverEC/CConvertLatLonGridData.cpp @@ -0,0 +1,293 @@ +/****************************************************************************** + * + * Project: ADAGUC Server + * Purpose: ADAGUC OGC Server + * Author: Maarten Plieger, plieger "at" knmi.nl + * Date: 2024-01-26 + * + ****************************************************************************** + * + * Copyright 2024, Royal Netherlands Meteorological Institute (KNMI) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +#include "CConvertLatLonGrid.h" +#include "CFillTriangle.h" +#include "CImageWarper.h" +/** + * This function draws the virtual 2D variable into a new 2D field + */ +int CConvertLatLonGrid::convertLatLonGridData(CDataSource *dataSource, int mode) { + + CDFObject *cdfObject = dataSource->getDataObject(0)->cdfObject; + if (!isLatLonGrid(cdfObject)) return 1; + size_t nrDataObjects = dataSource->getNumDataObjects(); + if (nrDataObjects <= 0) return 1; + + CDataSource::DataObject *dataObjects[nrDataObjects]; + for (size_t d = 0; d < nrDataObjects; d++) { + dataObjects[d] = dataSource->getDataObject(d); + } +#ifdef CConvertLatLonGrid_DEBUG + + CDBDebug("convertLatLonGridData %s", dataObjects[0]->cdfVariable->name.c_str()); +#endif + CDF::Variable *destRegularGrid[nrDataObjects]; + CDF::Variable *irregularGridVar[nrDataObjects]; + + // Make references destRegularGrid and irregularGridVar + for (size_t d = 0; d < nrDataObjects; d++) { + destRegularGrid[d] = dataObjects[d]->cdfVariable; + CT::string orgName = destRegularGrid[d]->name.c_str(); + orgName.concat("_backup"); + irregularGridVar[d] = cdfObject->getVariableNE(orgName.c_str()); + if (irregularGridVar[d] == NULL) { + CDBError("Unable to find orignal variable with name %s", orgName.c_str()); + return 1; + } + } + + CDF::Variable *longitudeGrid = getLon2D(cdfObject); + CDF::Variable *latitudeGrid = getLat2D(cdfObject); + + // Read original data first + for (size_t d = 0; d < nrDataObjects; d++) { + dataSource->readVariableDataForCDFDims(irregularGridVar[d], CDF_FLOAT); + + CDF::Attribute *fillValue = irregularGridVar[d]->getAttributeNE("_FillValue"); + if (fillValue != NULL) { + dataObjects[d]->hasNodataValue = true; + fillValue->getData(&dataObjects[d]->dfNodataValue, 1); +#ifdef CConvertLatLonGrid_DEBUG + CDBDebug("_FillValue = %f", dataObjects[d]->dfNodataValue); +#endif + float f = dataObjects[d]->dfNodataValue; + destRegularGrid[d]->getAttribute("_FillValue")->setData(CDF_FLOAT, &f, 1); + } else + dataObjects[d]->hasNodataValue = false; + } + + // If the data was not populated in the code above, try to read it from the file + if (longitudeGrid->data == nullptr) { + longitudeGrid->readData(CDF_DOUBLE, true); + } + if (latitudeGrid->data == nullptr) { + latitudeGrid->readData(CDF_DOUBLE, true); + } + + float fill = (float)dataObjects[0]->dfNodataValue; + + // Detect minimum and maximum values + MinMax minMax = getMinMax(((float *)irregularGridVar[0]->data), dataObjects[0]->hasNodataValue, fill, irregularGridVar[0]->getSize()); + +#ifdef CConvertLatLonGrid_DEBUG + CDBDebug("Calculated min/max : %f %f", min, max); +#endif + + // Set statistics + if (dataSource->stretchMinMax) { +#ifdef CConvertLatLonGrid_DEBUG + CDBDebug("dataSource->stretchMinMax"); +#endif + if (dataSource->statistics == NULL) { +#ifdef CConvertLatLonGrid_DEBUG + CDBDebug("Setting statistics: min/max : %f %f", minMax.min, minMax.max); +#endif + dataSource->statistics = new CDataSource::Statistics(); + dataSource->statistics->setMaximum(minMax.max); + dataSource->statistics->setMinimum(minMax.min); + } + } + + // Make the width and height of the new regular grid field the same as the viewing window + dataSource->dWidth = dataSource->srvParams->Geo->dWidth; + dataSource->dHeight = dataSource->srvParams->Geo->dHeight; + + if (dataSource->dWidth == 1 && dataSource->dHeight == 1) { + dataSource->srvParams->Geo->dfBBOX[0] = dataSource->srvParams->Geo->dfBBOX[0]; + dataSource->srvParams->Geo->dfBBOX[1] = dataSource->srvParams->Geo->dfBBOX[1]; + dataSource->srvParams->Geo->dfBBOX[2] = dataSource->srvParams->Geo->dfBBOX[2]; + dataSource->srvParams->Geo->dfBBOX[3] = dataSource->srvParams->Geo->dfBBOX[3]; + } + + // Width needs to be at least 2 in this case. + if (dataSource->dWidth == 1) dataSource->dWidth = 2; + if (dataSource->dHeight == 1) dataSource->dHeight = 2; + double cellSizeX = (dataSource->srvParams->Geo->dfBBOX[2] - dataSource->srvParams->Geo->dfBBOX[0]) / double(dataSource->dWidth); + double cellSizeY = (dataSource->srvParams->Geo->dfBBOX[3] - dataSource->srvParams->Geo->dfBBOX[1]) / double(dataSource->dHeight); + double offsetX = dataSource->srvParams->Geo->dfBBOX[0]; + double offsetY = dataSource->srvParams->Geo->dfBBOX[1]; + +#ifdef CConvertLatLonGrid_DEBUG + CDBDebug("Datasource bbox:%f %f %f %f", dataSource->srvParams->Geo->dfBBOX[0], dataSource->srvParams->Geo->dfBBOX[1], dataSource->srvParams->Geo->dfBBOX[2], dataSource->srvParams->Geo->dfBBOX[3]); + CDBDebug("Datasource width height %d %d", dataSource->dWidth, dataSource->dHeight); + CDBDebug("L2 %d %d", dataSource->dWidth, dataSource->dHeight); +#endif + + if (mode == CNETCDFREADER_MODE_OPEN_ALL) { +#ifdef CConvertLatLonGrid_DEBUG + CDBDebug("Drawing %s", destRegularGrid[0]->name.c_str()); +#endif + + CDF::Dimension *dimX; + CDF::Dimension *dimY; + CDF::Variable *varX; + CDF::Variable *varY; + + // Create new dimensions and variables (X,Y,T) + dimX = cdfObject->getDimension("adx"); + dimX->setSize(dataSource->dWidth); + + dimY = cdfObject->getDimension("ady"); + dimY->setSize(dataSource->dHeight); + + varX = cdfObject->getVariable("adx"); + varY = cdfObject->getVariable("ady"); + + varX->allocateData(dimX->length); + varY->allocateData(dimY->length); + + // Fill in the X and Y dimensions with the array of coordinates + for (size_t j = 0; j < dimX->length; j++) { + double x = offsetX + double(j) * cellSizeX + cellSizeX / 2; + ((double *)varX->data)[j] = x; + } + for (size_t j = 0; j < dimY->length; j++) { + double y = offsetY + double(j) * cellSizeY + cellSizeY / 2; + ((double *)varY->data)[j] = y; + } + + size_t fieldSize = dataSource->dWidth * dataSource->dHeight; + + // Allocate and clear data + for (size_t d = 0; d < nrDataObjects; d++) { + destRegularGrid[d]->setSize(fieldSize); + CDF::allocateData(destRegularGrid[d]->getType(), &(destRegularGrid[d]->data), fieldSize); + for (size_t j = 0; j < fieldSize; j++) { + ((float *)dataObjects[d]->cdfVariable->data)[j] = NAN; + } + } + + double *lonData = (double *)longitudeGrid->data; + double *latData = (double *)latitudeGrid->data; + + int numY = longitudeGrid->dimensionlinks[0]->getSize(); + int numX = longitudeGrid->dimensionlinks[1]->getSize(); +#ifdef CConvertLatLonGrid_DEBUG + CDBDebug("numRows %d numCells %d", numY, numX); +#endif + + CImageWarper imageWarper; + bool projectionRequired = false; + if (dataSource->srvParams->Geo->CRS.length() > 0) { + projectionRequired = true; + for (size_t d = 0; d < nrDataObjects; d++) { + destRegularGrid[d]->setAttributeText("grid_mapping", "customgridprojection"); + } + if (cdfObject->getVariableNE("customgridprojection") == NULL) { + CDF::Variable *projectionVar = new CDF::Variable(); + projectionVar->name.copy("customgridprojection"); + cdfObject->addVariable(projectionVar); + dataSource->nativeEPSG = dataSource->srvParams->Geo->CRS.c_str(); + imageWarper.decodeCRS(&dataSource->nativeProj4, &dataSource->nativeEPSG, &dataSource->srvParams->cfg->Projection); + if (dataSource->nativeProj4.length() == 0) { + dataSource->nativeProj4 = LATLONPROJECTION; + dataSource->nativeEPSG = "EPSG:4326"; + projectionRequired = false; + } + projectionVar->setAttributeText("proj4_params", dataSource->nativeProj4.c_str()); + } + } + if (projectionRequired) { + int status = imageWarper.initreproj(dataSource, dataSource->srvParams->Geo, &dataSource->srvParams->cfg->Projection); + if (status != 0) { + CDBError("Unable to init projection"); + return 1; + } + } + + bool drawBilinear = false; + CStyleConfiguration *styleConfiguration = dataSource->getStyle(); + if (styleConfiguration->styleCompositionName.indexOf("bilinear") >= 0) { + drawBilinear = true; + } + +#ifdef CConvertLatLonGrid_DEBUG + CDBDebug("Start projecting numRows %d numCells %d", numY, numX); +#endif + size_t num = numY * numX; + + proj_trans_generic(imageWarper.projLatlonToDest, PJ_FWD, lonData, sizeof(double), num, latData, sizeof(double), num, nullptr, 0, 0, nullptr, 0, 0); +#ifdef CConvertLatLonGrid_DEBUG + CDBDebug("Done projecting numRows %d numCells %d, now start drawing", numY, numX); +#endif + + for (int indexY = 0; indexY < numY - 1; indexY++) { + for (int indexX = 0; indexX < numX - 1; indexX++) { + int gridPointer = indexX + indexY * numX; + int bottom = 1 * numX; //(indexY != 0 ? -numX : numX); + int right = 1; //(indexX != 0 ? -1 : 1); + double lons[4], lats[4]; + lons[0] = lonData[gridPointer]; // topleft + lons[1] = lonData[gridPointer + right]; // topright + lons[2] = lonData[gridPointer + bottom]; // bottomleft + lons[3] = lonData[gridPointer + bottom + right]; // bottomright + lats[0] = latData[gridPointer]; + lats[1] = latData[gridPointer + right]; + lats[2] = latData[gridPointer + bottom]; + lats[3] = latData[gridPointer + bottom + right]; + + int dlons[4], dlats[4]; + for (size_t dataObjectIndex = 0; dataObjectIndex < nrDataObjects; dataObjectIndex++) { + float *destinationGrid = ((float *)dataObjects[dataObjectIndex]->cdfVariable->data); + float *sourceIrregularGrid = (float *)irregularGridVar[dataObjectIndex]->data; + float irregularGridValues[4]; + irregularGridValues[0] = sourceIrregularGrid[gridPointer]; + + if (drawBilinear) { + // Bilinear mode will use the four corner values to draw a quad with those values interpolated + irregularGridValues[1] = sourceIrregularGrid[gridPointer + right]; + irregularGridValues[2] = sourceIrregularGrid[gridPointer + bottom]; + irregularGridValues[3] = sourceIrregularGrid[gridPointer + bottom + right]; + } else { + // Nearest mode will use the topleft value for all values in the quad + irregularGridValues[1] = irregularGridValues[0]; + irregularGridValues[2] = irregularGridValues[0]; + irregularGridValues[3] = irregularGridValues[0]; + } + bool irregularGridCellHasNoData = false; + // Check if this is no data (irregularGridCellHasNoData) + if (irregularGridValues[0] == fill || irregularGridValues[1] == fill || irregularGridValues[2] == fill || irregularGridValues[3] == fill) irregularGridCellHasNoData = true; + + if (irregularGridCellHasNoData == false) { + if (dataObjectIndex == 0) { + for (int j = 0; j < 4; j++) { + dlons[j] = int((lons[j] - offsetX) / cellSizeX); + dlats[j] = int((lats[j] - offsetY) / cellSizeY); + } + } + // Draw the data into the new regular grid variable + fillQuadGouraud(destinationGrid, irregularGridValues, dataSource->dWidth, dataSource->dHeight, dlons, dlats); + } + } + } + } + imageWarper.closereproj(); + } +#ifdef CConvertLatLonGrid_DEBUG + CDBDebug("/convertLatLonGridData"); +#endif + return 0; +} diff --git a/adagucserverEC/CConvertLatLonGridHeader.cpp b/adagucserverEC/CConvertLatLonGridHeader.cpp new file mode 100644 index 000000000..871dd0d47 --- /dev/null +++ b/adagucserverEC/CConvertLatLonGridHeader.cpp @@ -0,0 +1,209 @@ +/****************************************************************************** + * + * Project: ADAGUC Server + * Purpose: ADAGUC OGC Server + * Author: Maarten Plieger, plieger "at" knmi.nl + * Date: 2024-01-26 + * + ****************************************************************************** + * + * Copyright 2024, Royal Netherlands Meteorological Institute (KNMI) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +#include "CConvertLatLonGrid.h" +#include "CFillTriangle.h" +#include "CImageWarper.h" + +/** + * This function adjusts the cdfObject by creating virtual 2D variables + */ +int CConvertLatLonGrid::convertLatLonGridHeader(CDFObject *cdfObject, CServerParams *) { +#ifdef CConvertLatLonGrid_DEBUG + CDBDebug("CHECKING convertLatLonGridHeader"); +#endif + if (!isLatLonGrid(cdfObject)) return 1; + + // Determine bbox based on 2D lat/lon + double dfBBOX[] = {-180, -90, 180, 90}; + CDF::Variable *longitudeGrid = getLon2D(cdfObject); + CDF::Variable *latitudeGrid = getLat2D(cdfObject); + + if (longitudeGrid == nullptr) { + CDBDebug("longitudeGrid = longitudeGrid"); + longitudeGrid = getLon1D(cdfObject); + CDBDebug("longitudeGrid = longitudeGrid"); + } + if (latitudeGrid == nullptr) { + latitudeGrid = getLat1D(cdfObject); + } + + if (longitudeGrid != nullptr && latitudeGrid != nullptr) { + // If the data was not populated in the code above, try to read it from the file + if (longitudeGrid->data == nullptr) { + longitudeGrid->readData(CDF_DOUBLE, true); + } + if (latitudeGrid->data == nullptr) { + latitudeGrid->readData(CDF_DOUBLE, true); + } + if (longitudeGrid->data != nullptr && latitudeGrid->data != nullptr) { + try { + MinMax lonMinMax = getMinMax(longitudeGrid); + MinMax latMinMax = getMinMax(latitudeGrid); + dfBBOX[0] = lonMinMax.min; + dfBBOX[1] = latMinMax.min; + dfBBOX[2] = lonMinMax.max; + dfBBOX[3] = latMinMax.max; + } catch (int e) { + CDBDebug("CALC BOX ERROR %d", e); + } + } + } else { + CDBWarning("Unable to determine BBOX from lat/lon variables"); + } + // Default size of new virtual 2dField is 2x2 + int width = 2; + int height = 2; + + double cellSizeX = (dfBBOX[2] - dfBBOX[0]) / double(width); + double cellSizeY = (dfBBOX[3] - dfBBOX[1]) / double(height); + double offsetX = dfBBOX[0]; + double offsetY = dfBBOX[1]; + + // Add geo variables, only if they are not there already + CDF::Dimension *dimX = cdfObject->getDimensionNE("adx"); + CDF::Dimension *dimY = cdfObject->getDimensionNE("ady"); + CDF::Variable *varX = cdfObject->getVariableNE("adx"); + CDF::Variable *varY = cdfObject->getVariableNE("ady"); + if (dimX == NULL || dimY == NULL || varX == NULL || varY == NULL) { + // If not available, create new dimensions and variables (X,Y,T) + // For x + dimX = new CDF::Dimension(); + dimX->name = "adx"; + dimX->setSize(width); + cdfObject->addDimension(dimX); + varX = new CDF::Variable(); + varX->setType(CDF_DOUBLE); + varX->name.copy("adx"); + varX->isDimension = true; + varX->dimensionlinks.push_back(dimX); + cdfObject->addVariable(varX); + varX->allocateData(dimX->length); + + // For y + dimY = new CDF::Dimension(); + dimY->name = "ady"; + dimY->setSize(height); + cdfObject->addDimension(dimY); + varY = new CDF::Variable(); + varY->setType(CDF_DOUBLE); + varY->name.copy("ady"); + varY->isDimension = true; + varY->dimensionlinks.push_back(dimY); + cdfObject->addVariable(varY); + varY->allocateData(dimY->length); + + // Fill in the X and Y dimensions with the array of coordinates + for (size_t j = 0; j < dimX->length; j++) { + double x = offsetX + double(j) * cellSizeX + cellSizeX / 2; + ((double *)varX->data)[j] = x; + } + for (size_t j = 0; j < dimY->length; j++) { + double y = offsetY + double(j) * cellSizeY + cellSizeY / 2; + ((double *)varY->data)[j] = y; + } + } + + // Make a list of variables which will be available as 2D fields + CT::StackList varsToConvert; + for (size_t v = 0; v < cdfObject->variables.size(); v++) { + CDF::Variable *var = cdfObject->variables[v]; + if (var->isDimension == false) { + if (var->dimensionlinks.size() >= 2 && !var->name.equals("acquisition_time") && !var->name.equals("time") && !var->name.equals("lon") && !var->name.equals("lat") && + !var->name.equals("longitude") && !var->name.equals("latitude")) { + varsToConvert.add(CT::string(var->name.c_str())); + } + } + } + + // Add time dimension + bool addCustomTimeDimension = false; + try { + CDF::Dimension *timeDim = cdfObject->getDimensionNE("time"); + if (timeDim == nullptr) { + + CDF::Variable *timeVar = cdfObject->getVariableNE("acquisition_time"); + if (timeVar != nullptr) { + timeVar->name = "time"; + timeDim = new CDF::Dimension("time", 1); + cdfObject->addDimension(timeDim); + timeVar->dimensionlinks.push_back(timeDim); + addCustomTimeDimension = true; + } + } + + } catch (int e) { + } + + // Create the new regular grid field variables based on the irregular grid variables + for (size_t v = 0; v < varsToConvert.size(); v++) { + CDF::Variable *irregularGridVar = cdfObject->getVariable(varsToConvert[v].c_str()); + if (irregularGridVar->dimensionlinks.size() >= 2) { +#ifdef CConvertLatLonGrid_DEBUG + CDBDebug("Converting %s", irregularGridVar->name.c_str()); +#endif + + CDF::Variable *destRegularGrid = new CDF::Variable(); + cdfObject->addVariable(destRegularGrid); + + // Assign all other dims except Y and X + for (size_t dimlinkNr = 0; dimlinkNr < irregularGridVar->dimensionlinks.size() - 2; dimlinkNr += 1) { + auto dimlink = irregularGridVar->dimensionlinks[dimlinkNr]; + destRegularGrid->dimensionlinks.push_back(dimlink); + } + + if (addCustomTimeDimension) { + CDF::Dimension *timeDim = cdfObject->getDimensionNE("time"); + if (timeDim) { + destRegularGrid->dimensionlinks.push_back(timeDim); + } + } + + // Assign X,Y + destRegularGrid->dimensionlinks.push_back(dimY); + destRegularGrid->dimensionlinks.push_back(dimX); + + destRegularGrid->setType(irregularGridVar->getType()); + destRegularGrid->name = irregularGridVar->name.c_str(); + irregularGridVar->name.concat("_backup"); + + // Copy variable attributes + for (size_t j = 0; j < irregularGridVar->attributes.size(); j++) { + CDF::Attribute *a = irregularGridVar->attributes[j]; + destRegularGrid->setAttribute(a->name.c_str(), a->getType(), a->data, a->length); + destRegularGrid->setAttributeText("ADAGUC_VECTOR", "true"); + } + + // The irregularGridVar variable is not directly plotable, so skip it + irregularGridVar->setAttributeText("ADAGUC_SKIP", "true"); + + // Scale and offset are already applied + destRegularGrid->removeAttribute("scale_factor"); + destRegularGrid->removeAttribute("add_offset"); + destRegularGrid->setType(CDF_FLOAT); + } + } + return 0; +} diff --git a/adagucserverEC/CDFObjectStore.cpp b/adagucserverEC/CDFObjectStore.cpp index 5ea65c53c..51e65543d 100644 --- a/adagucserverEC/CDFObjectStore.cpp +++ b/adagucserverEC/CDFObjectStore.cpp @@ -36,6 +36,7 @@ const char *CDFObjectStore::className = "CDFObjectStore"; #include "CConvertGeoJSON.h" #include "CConvertEProfile.h" #include "CConvertTROPOMI.h" +#include "CConvertLatLonGrid.h" #include "CDataReader.h" #include "CCDFCSVReader.h" // #define CDFOBJECTSTORE_DEBUG @@ -421,6 +422,11 @@ CDFObject *CDFObjectStore::getCDFObject(CDataSource *dataSource, CServerParams * if (CConvertKNMIH5VolScan::convertKNMIH5VolScanHeader(cdfObject, srvParams) == 0) { formatConverterActive = true; }; + + if (!formatConverterActive) + if (CConvertLatLonGrid::convertLatLonGridHeader(cdfObject, srvParams) == 0) { + formatConverterActive = true; + }; } return cdfObject; diff --git a/adagucserverEC/CDataReader.cpp b/adagucserverEC/CDataReader.cpp index 14f91df90..1b55e7d4a 100755 --- a/adagucserverEC/CDataReader.cpp +++ b/adagucserverEC/CDataReader.cpp @@ -37,6 +37,7 @@ #include "CConvertEProfile.h" #include "CConvertTROPOMI.h" #include "CConvertKNMIH5VolScan.h" +#include "CConvertLatLonGrid.h" #include "CDBFactory.h" #include "CReporter.h" #include "CCDFHDF5IO.h" @@ -382,6 +383,8 @@ int CDataReader::parseDimensions(CDataSource *dataSource, int mode, int x, int y if (CConvertKNMIH5VolScan::convertKNMIH5VolScanData(dataSource, mode) == 0) dataSource->formatConverterActive = true; if (!dataSource->formatConverterActive) if (CConvertKNMIH5EchoToppen::convertKNMIH5EchoToppenData(dataSource, mode) == 0) dataSource->formatConverterActive = true; + if (!dataSource->formatConverterActive) + if (CConvertLatLonGrid::convertLatLonGridData(dataSource, mode) == 0) dataSource->formatConverterActive = true; CDF::Variable *dataSourceVar = dataSource->getDataObject(0)->cdfVariable; CDFObject *cdfObject = dataSource->getDataObject(0)->cdfObject; diff --git a/adagucserverEC/CDataSource.cpp b/adagucserverEC/CDataSource.cpp index d34431b2b..ab580e2dd 100644 --- a/adagucserverEC/CDataSource.cpp +++ b/adagucserverEC/CDataSource.cpp @@ -113,7 +113,7 @@ MinMax getMinMax(double *data, bool hasFillValue, double fillValue, size_t numEl } } if (minMax.isSet == false) { - throw __LINE__ + 100; + throw __LINE__; } return minMax; } @@ -212,8 +212,7 @@ MinMax getMinMax(CDF::Variable *var) { } } else { - // CDBError("getMinMax: Variable has not been set"); - throw __LINE__ + 100; + throw __LINE__; } return minMax; } @@ -1425,4 +1424,32 @@ void CDataSource::detachCDFObject() { getDataObject(j)->cdfVariable = NULL; getDataObject(j)->cdfObject = NULL; } +} + +int CDataSource::readVariableDataForCDFDims(CDF::Variable *variableToRead, CDFType dataTypeToReturnData) { + if (variableToRead == nullptr) { + CDBError("Variable is not defined"); + return 1; + } + size_t numDimensionsForVariableToRead = variableToRead->dimensionlinks.size(); + size_t start[numDimensionsForVariableToRead]; + size_t count[numDimensionsForVariableToRead]; + ptrdiff_t stride[numDimensionsForVariableToRead]; + auto *cdfDims = this->getCDFDims(); + for (size_t dimNr = 0; dimNr < numDimensionsForVariableToRead; dimNr += 1) { + auto *dimensionLink = variableToRead->dimensionlinks[dimNr]; + size_t startCountIndex = dimNr; + start[startCountIndex] = 0; + stride[startCountIndex] = 1; + count[startCountIndex] = dimensionLink->getSize(); + int cdfDimIndex = cdfDims->getArrayIndexForName(dimensionLink->name.c_str()); + if (cdfDimIndex >= 0) { +#ifdef CDATASOURCE_DEBUG + CDBDebug("Start %d/%d:%s %d:%s ==> %d", startCountIndex, dimNr, dimensionLink->name.c_str(), cdfDimIndex, cdfDims->getDimensionName(cdfDimIndex), cdfDims->getDimensionIndex(cdfDimIndex)); +#endif + start[startCountIndex] = cdfDims->getDimensionIndex(cdfDimIndex); + count[startCountIndex] = 1; + } + } + return variableToRead->readData(dataTypeToReturnData, start, count, stride, true); } \ No newline at end of file diff --git a/adagucserverEC/CDataSource.h b/adagucserverEC/CDataSource.h index 15d0cde8d..c25a4a56b 100644 --- a/adagucserverEC/CDataSource.h +++ b/adagucserverEC/CDataSource.h @@ -365,6 +365,14 @@ class CDataSource { * Returns the amount of need image map scaling for elements lice contours. This can be the case if the scalecontours property in the RenderSettings is set */ double getContourScaling(); + + /** + * Reads a variable with requested type according dimensions indices set in the DataSource CDFDims object + * @param CDF::Variable *variableToRead: The variable to read. Can be the one from the datasource itself + * @param CDFType dataTypeToReturnData Type to read + * @return 0 on succes, 1 on failure + */ + int readVariableDataForCDFDims(CDF::Variable *variableToRead, CDFType dataTypeToReturnData); }; #endif diff --git a/adagucserverEC/CMakeLists.txt b/adagucserverEC/CMakeLists.txt index 9f144f7a1..df5018f15 100644 --- a/adagucserverEC/CMakeLists.txt +++ b/adagucserverEC/CMakeLists.txt @@ -43,6 +43,10 @@ add_library( CConvertKNMIH5EchoToppen.h CConvertKNMIH5VolScan.cpp CConvertKNMIH5VolScan.h + CConvertLatLonGrid.h + CConvertLatLonGrid.cpp + CConvertLatLonGridHeader.cpp + CConvertLatLonGridData.cpp CConvertTROPOMI.cpp CConvertTROPOMI.h CConvertUGRIDMesh.cpp diff --git a/adagucserverEC/COGCDims.h b/adagucserverEC/COGCDims.h index e207d6d38..fec45a38d 100644 --- a/adagucserverEC/COGCDims.h +++ b/adagucserverEC/COGCDims.h @@ -30,7 +30,10 @@ #include "CDebugger.h" class COGCDims { public: - COGCDims() { isATimeDimension = false; hasFixedValue = false; } + COGCDims() { + isATimeDimension = false; + hasFixedValue = false; + } /** * OGC name */ @@ -88,6 +91,12 @@ class CCDFDims { CT::string getDimensionValue(int j); const char *getDimensionName(int j); void copy(CCDFDims *dim); + + /** + * Find the dimension index by name in the CCDFDim object + * @param name: Name of the dimension in the CDF model to find + * @return The index of the dimension, or -1 when not found + */ int getArrayIndexForName(const char *name); }; #endif diff --git a/adagucserverEC/Definitions.h b/adagucserverEC/Definitions.h index 252d585bd..acd1fa6ae 100755 --- a/adagucserverEC/Definitions.h +++ b/adagucserverEC/Definitions.h @@ -28,7 +28,7 @@ #ifndef Definitions_H #define Definitions_H -#define ADAGUCSERVER_VERSION "2.18.0" // Please also update in the Dockerfile to the same version +#define ADAGUCSERVER_VERSION "2.19.0" // Please also update in the Dockerfile to the same version // CConfigReaderLayerType #define CConfigReaderLayerTypeUnknown 0 diff --git a/data/datasets/example_file_irregular_1Dlat1Dlon_grid.nc b/data/datasets/example_file_irregular_1Dlat1Dlon_grid.nc new file mode 100644 index 000000000..5c267dec7 Binary files /dev/null and b/data/datasets/example_file_irregular_1Dlat1Dlon_grid.nc differ diff --git a/data/datasets/example_file_irregular_2Dlat2Dlon_grid.nc b/data/datasets/example_file_irregular_2Dlat2Dlon_grid.nc new file mode 100644 index 000000000..fed4c7900 Binary files /dev/null and b/data/datasets/example_file_irregular_2Dlat2Dlon_grid.nc differ diff --git a/doc/format_standard/README.md b/doc/format_standard/README.md index 9f029ae89..86bf5c60e 100644 --- a/doc/format_standard/README.md +++ b/doc/format_standard/README.md @@ -4,6 +4,8 @@ * [Grid format](data_format_standard_grid.md) * [Vector format](data_format_standard_vector.md) * [Point format](data_format_standard_point.md) +* [Irregular grid using 1 dimensional lat and lon variables](data_format_standard_irregular_1Dlat1Dlon_grid.md) +* [Irregular grid using 2 dimensional lat and lon variables](data_format_standard_irregular_2Dlat2Dlon_grid.md) [Data examples](../info/DataExamples.md) diff --git a/doc/format_standard/data_format_standard_irregular_1Dlat1Dlon_grid.md b/doc/format_standard/data_format_standard_irregular_1Dlat1Dlon_grid.md new file mode 100644 index 000000000..efca9ef41 --- /dev/null +++ b/doc/format_standard/data_format_standard_irregular_1Dlat1Dlon_grid.md @@ -0,0 +1,58 @@ +# Example format + +[Back](./README.md) + +This script will create a file where lon and lat variables have irregular spacing. These variables are still 1 dimensional. + +[Script to generate NetCDF file](../../python/format_standard/generate_example_file_irregular_1Dlat1Dlon_grid.py) + +## NetCDF file +``` +netcdf example_file_irregular_1Dlat1Dlon_grid { +dimensions: + time = 2 ; + lat = 16 ; + lon = 20 ; + extra = 10 ; +variables: + double time(time) ; + time:standard_name = "time" ; + time:long_name = "time" ; + time:units = "seconds since 1970-01-01 00:00:00" ; + double lon(lon) ; + lon:standard_name = "longitude" ; + lon:long_name = "longitude" ; + lon:units = "degrees_east" ; + double lat(lat) ; + lat:standard_name = "latitude" ; + lat:long_name = "latitude" ; + lat:units = "degrees_north" ; + double extra(extra) ; + extra:standard_name = "extra" ; + extra:long_name = "extra" ; + extra:units = "extra" ; + float data_var_1(extra, time, lat, lon) ; + data_var_1:_FillValue = -9999.f ; + data_var_1:least_significant_digit = 3LL ; + data_var_1:standard_name = "data_var_1" ; + data_var_1:long_name = "data_var_1" ; + data_var_1:units = "none" ; +data: + + time = 1483229400, 1483229460 ; + + lon = -12, -8, -4, -2, -1.5, -0.5, -0.4, -0.3, -0.2, -0.1, 0, 0.1, 0.2, 0.3, + 0.5, 1.5, 2, 4, 8, 12 ; + + lat = 48, 50, 50.5, 51.5, 51.6, 51.7, 51.8, 51.9, 52, 52.1, 52.2, 52.3, + 52.5, 53.5, 54, 56 ; + + extra = 0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9 ; +} + + +``` + +### Resulting image + +irregular_1dLat1Dlon_example \ No newline at end of file diff --git a/doc/format_standard/data_format_standard_irregular_2Dlat2Dlon_grid.md b/doc/format_standard/data_format_standard_irregular_2Dlat2Dlon_grid.md new file mode 100644 index 000000000..b7c533d2a --- /dev/null +++ b/doc/format_standard/data_format_standard_irregular_2Dlat2Dlon_grid.md @@ -0,0 +1,98 @@ +# Example format + +[Back](./README.md) + +This script will create a file where lon and lat variables have irregular spacing. These lat/lon dimension variables are 2 dimensional and irregular + +[Script to generate NetCDF file](../../python/format_standard/generate_example_file_irregular_2Dlat2Dlon_grid.py) + +## NetCDF file +``` +etcdf example_file_irregular_2Dlat2Dlon_grid { +dimensions: + time = 2 ; + x = 8 ; + y = 10 ; + extra = 10 ; +variables: + double time(time) ; + time:standard_name = "time" ; + time:long_name = "time" ; + time:units = "seconds since 1970-01-01 00:00:00" ; + double lon(y, x) ; + lon:_FillValue = -9999. ; + lon:standard_name = "longitude" ; + lon:long_name = "longitude" ; + lon:units = "degrees_east" ; + double lat(y, x) ; + lat:_FillValue = -9999. ; + lat:standard_name = "latitude" ; + lat:long_name = "latitude" ; + lat:units = "degrees_north" ; + double extra(extra) ; + extra:standard_name = "extra" ; + extra:long_name = "extra" ; + extra:units = "extra" ; + float data_var_1(extra, time, y, x) ; + data_var_1:_FillValue = -9999.f ; + data_var_1:least_significant_digit = 3LL ; + data_var_1:standard_name = "data_var_1" ; + data_var_1:long_name = "data_var_1" ; + data_var_1:units = "none" ; +data: + + time = 1483229400, 1483229460 ; + + lon = + 0.17794546007564, 2.76480838102692, 4.18878554126185, 6.13140594851911, + 8.25776443616664, 10.594877870279, 12.6994494223276, 14.9190516896196, + 0.250179766560622, 2.03846475387248, 4.96279145610287, 6.10010711919148, + 8.53888120539574, 10.0442026384773, 12.7947215058503, 14.5174747914901, + 0.520132373754771, 2.07786471685772, 4.78821607525056, 6.73300274514291, + 8.81005201784217, 10.7796777101128, 12.212128379423, 14.8731199702957, + 0.0950808312378734, 2.83455620410034, 4.25121510057895, 6.20757154982021, + 8.69302343038157, 10.8463579390503, 12.9164545990549, 14.4880197896557, + 0.110912784256131, 2.39952142338174, 4.6405981739942, 6.67454840309456, + 8.86832035853982, 10.1012125785341, 12.2478697044722, 14.3806055174274, + 0.848407784614987, 2.12410021411385, 4.30126835165127, 6.08272948203336, + 8.31954829047515, 10.7080640256276, 12.9351212345098, 14.9232850865539, + 0.279728330934168, 2.86001619849343, 4.31039283813434, 6.2097886086892, + 8.42641603281344, 10.9348982911965, 12.5587664498595, 14.8200704365968, + 0.741518115934441, 2.87785746836924, 4.21260624077126, 6.09849026654236, + 8.16643573020496, 10.9591619876402, 12.0163700125252, 14.5746437160627, + 0.355139096406225, 2.20215020007337, 4.1006551793198, 6.34067883431823, + 8.60366187834832, 10.4913397187034, 12.5052984144651, 14.2266966585043, + 0.22662417247866, 2.18970602130296, 4.50407504733109, 6.40181010730075, + 8.16291736289111, 10.8512048695319, 12.96194553871, 14.9727413428817 ; + + lat = + 50.7835602005565, 50.2566524707529, 50.6505335310805, 50.8525251838766, + 50.1486121685993, 50.788071291356, 50.4125423477156, 50.63144348784, + 52.6309115340556, 52.6489428533515, 52.3996505162228, 52.3454048004656, + 52.242045569055, 52.6458637017658, 52.0970846820258, 52.8587770928727, + 54.8006872616247, 54.0337608186355, 54.5137934031104, 54.3259442117892, + 54.6548699322524, 54.3282364118544, 54.9226820200184, 54.9789360825204, + 56.4761611893831, 56.0460781257951, 56.7037110129775, 56.0683750958518, + 56.5734562203457, 56.3804867150557, 56.4702253118413, 56.339942952224, + 58.1350815550656, 58.0530889323638, 58.9834045309851, 58.0794081595164, + 58.7299343647019, 58.2261688104002, 58.3510507109013, 58.58408167691, + 60.7548469287539, 60.7251020231181, 60.2021318780373, 60.2528506391094, + 60.6555717934058, 60.908430109801, 60.2503025738482, 60.1990098182258, + 62.9260703102219, 62.5416859872935, 62.8266417667752, 62.1628282254356, + 62.0234318054562, 62.9034849754582, 62.4573636163949, 62.9822058820299, + 64.681229929912, 64.2054385258265, 64.9486242347524, 64.2243687845649, + 64.300630953565, 64.8457157280671, 64.4728181200989, 64.9486940907017, + 66.8177072145075, 66.9299871881733, 66.8248778002961, 66.792602873387, + 66.1862385808535, 66.8119552679746, 66.1380619843272, 66.7299187799637, + 68.2534738273635, 68.8071668043109, 68.1165498253522, 68.532499135546, + 68.9735815723013, 68.5102396983322, 68.8026199167508, 68.8670237316549 ; + + extra = 0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9 ; +} + + +``` + +### Resulting image + +irregular_1dLat1Dlon_example \ No newline at end of file diff --git a/doc/format_standard/data_format_standard_vector.md b/doc/format_standard/data_format_standard_vector.md index a2c3bc43a..23d27fa0a 100644 --- a/doc/format_standard/data_format_standard_vector.md +++ b/doc/format_standard/data_format_standard_vector.md @@ -2,7 +2,7 @@ [Back](./README.md) -[Script to generate NetCDF file](../../python/format_standard/format_standard/generate_example_file_vector_format.py) +[Script to generate NetCDF file](../../python/format_standard/generate_example_file_vector_format.py) ## NetCDF file ``` diff --git a/doc/format_standard/images/irregular_1dLat1Dlon_example.png b/doc/format_standard/images/irregular_1dLat1Dlon_example.png new file mode 100644 index 000000000..7a2cf41f6 Binary files /dev/null and b/doc/format_standard/images/irregular_1dLat1Dlon_example.png differ diff --git a/doc/format_standard/images/irregular_2dLat2Dlon_example.png b/doc/format_standard/images/irregular_2dLat2Dlon_example.png new file mode 100644 index 000000000..60b8b3a1b Binary files /dev/null and b/doc/format_standard/images/irregular_2dLat2Dlon_example.png differ diff --git a/python/format_standard/generate_example_file_irregular_1Dlat1Dlon_grid.py b/python/format_standard/generate_example_file_irregular_1Dlat1Dlon_grid.py new file mode 100755 index 000000000..0bd25cd79 --- /dev/null +++ b/python/format_standard/generate_example_file_irregular_1Dlat1Dlon_grid.py @@ -0,0 +1,189 @@ +#!/usr/bin/python + +# *********************************************************************** +# * All rights reserved ** +# * Copyright (c) 2024 KNMI ** +# * Royal Netherlands Meteorological Institute ** +# *********************************************************************** +# * Purpose : This script will create a file where lon and lat variables have irregular spacing. These variables are still 1 dimensional. +# * +# * Project : ADAGUC +# * +# * initial programmer : Maarten Plieger +# * initial date : 20240202 +# ********************************************************************** + +import netCDF4 +import numpy +import datetime +import os.path +import random + +TIME_DIM_NAME = "time" +EXTRA_DIM_NAME = "extra" +LATITUDE_1D_DIM_NAME = "lat" +LONGITUDE_1D_DIM_NAME = "lon" +filename = os.path.join("example_file_irregular_1Dlat1Dlon_grid.nc") + +""" + +This script will create a file where lon and lat variables have irregular spacing. These variables are still 1 dimensional. + +See the NetCDF structure below: + +netcdf irreglatlongrid { +dimensions: + time = 2 ; + lat = 16 ; + lon = 20 ; + extra = 10 ; +variables: + double time(time) ; + time:standard_name = "time" ; + time:long_name = "time" ; + time:units = "seconds since 1970-01-01 00:00:00" ; + double lon(lon) ; + lon:standard_name = "longitude" ; + lon:long_name = "longitude" ; + lon:units = "degrees_east" ; + double lat(lat) ; + lat:standard_name = "latitude" ; + lat:long_name = "latitude" ; + lat:units = "degrees_north" ; + double extra(extra) ; + extra:standard_name = "extra" ; + extra:long_name = "extra" ; + extra:units = "extra" ; + double data_var_1(extra, time, lat, lon) ; + data_var_1:_FillValue = -9999. ; + data_var_1:standard_name = "data_var_1" ; + data_var_1:long_name = "data_var_1" ; + data_var_1:units = "none" ; + + +""" + + +def main(): + netcdf_file = netCDF4.Dataset(filename, mode="w", format="NETCDF4") + + # Create the dimensions. + netcdf_file.createDimension(TIME_DIM_NAME, 2) + netcdf_file.createDimension(LATITUDE_1D_DIM_NAME, 16) + netcdf_file.createDimension(LONGITUDE_1D_DIM_NAME, 20) + netcdf_file.createDimension(EXTRA_DIM_NAME, 10) + + # Create the coordinate variables. + time_var = netcdf_file.createVariable("time", "f8", (TIME_DIM_NAME)) + time_var.standard_name = "time" + time_var.long_name = "time" + time_var.units = "seconds since 1970-01-01 00:00:00" + + lon_var = netcdf_file.createVariable( + "lon", + "f8", + (LONGITUDE_1D_DIM_NAME), + ) + lon_var.standard_name = "longitude" + lon_var.long_name = "longitude" + lon_var.units = "degrees_east" + + lat_var = netcdf_file.createVariable( + "lat", + "f8", + (LATITUDE_1D_DIM_NAME), + ) + lat_var.standard_name = "latitude" + lat_var.long_name = "latitude" + lat_var.units = "degrees_north" + + # Create extra dim variable + extra_var = netcdf_file.createVariable("extra", "f8", (EXTRA_DIM_NAME)) + extra_var.standard_name = "extra" + extra_var.long_name = "extra" + extra_var.units = "extra" + + extra_var[:] = numpy.arange(0, 10, 1) / 10.0 + + # Create the measured value variables. + data_var_1 = netcdf_file.createVariable( + "data_var_1", + "f4", + (EXTRA_DIM_NAME, TIME_DIM_NAME, LATITUDE_1D_DIM_NAME, LONGITUDE_1D_DIM_NAME), + fill_value=-9999, + compression="zlib", + least_significant_digit=3, + ) + data_var_1.standard_name = "data_var_1" + data_var_1.long_name = "data_var_1" + data_var_1.units = "none" + + print("NetCDF file initialized.") + + # Fill the data variables. + # data_var_1[:] = [1, 2] + + lon_var[:] = [ + -12, + -8, + -4, + -2, + -1.5, + -0.5, + -0.4, + -0.3, + -0.2, + -0.1, + 0, + 0.1, + 0.2, + 0.3, + 0.5, + 1.5, + 2, + 4, + 8, + 12, + ] + latoffset = 52 + lat_var[:] = [ + -4 + latoffset, + -2 + latoffset, + -1.5 + latoffset, + -0.5 + latoffset, + -0.4 + latoffset, + -0.3 + latoffset, + -0.2 + latoffset, + -0.1 + latoffset, + 0 + latoffset, + 0.1 + latoffset, + 0.2 + latoffset, + 0.3 + latoffset, + 0.5 + latoffset, + 1.5 + latoffset, + 2 + latoffset, + 4 + latoffset, + ] + time_var[:] = [ + netCDF4.date2num( + datetime.datetime(2017, 1, 1, 0, 10, 0), "seconds since 1970-01-01 00:00:00" + ), + netCDF4.date2num( + datetime.datetime(2017, 1, 1, 0, 11, 0), "seconds since 1970-01-01 00:00:00" + ), + ] + + for iextra, extra in enumerate(extra_var[:]): + for it, time in enumerate(time_var[:]): + for iy, lat in enumerate(lat_var[:]): + for ix, lon in enumerate(lon_var[:]): + na = numpy.cos((iy / 15) * 3.141592654 + extra) + 0.5 * it + nb = numpy.cos((ix / 19) * 3.141592654 + extra) + 0.5 * it + data_var_1[iextra, it, iy, ix] = na * na + nb * nb + # print(data_var_1[:]) + netcdf_file.close() + print("Done.") + + +if __name__ == "__main__": + main() diff --git a/python/format_standard/generate_example_file_irregular_2Dlat2Dlon_grid.py b/python/format_standard/generate_example_file_irregular_2Dlat2Dlon_grid.py new file mode 100755 index 000000000..f854d83ea --- /dev/null +++ b/python/format_standard/generate_example_file_irregular_2Dlat2Dlon_grid.py @@ -0,0 +1,212 @@ +#!/usr/bin/python + +# *********************************************************************** +# * All rights reserved ** +# * Copyright (c) 2024 KNMI ** +# * Royal Netherlands Meteorological Institute ** +# *********************************************************************** +# * Purpose : This script will create a file where lon and lat variables have irregular spacing. These lat/lon dimension variables are 2 dimensional and irregular. +# * +# * Project : ADAGUC +# * +# * initial programmer : Maarten Plieger +# * initial date : 20240202 +# ********************************************************************** + +import netCDF4 +import numpy +import datetime +import os.path +import random + +TIME_DIM_NAME = "time" +EXTRA_DIM_NAME = "extra" + +X_DIM_NAME = "x" +Y_DIM_NAME = "y" +LATITUDE_2D_VAR_NAME = "lat" +LONGITUDE_2D_VAR_NAME = "lon" +filename = os.path.join("example_file_irregular_2Dlat2Dlon_grid.nc") + +""" + +This script will create a file where lon and lat variables have irregular spacing. These lat/lon dimension variables are 2 dimensional and irregular. + +See the NetCDF structure below: + +netcdf example_file_irregular_2Dlat2Dlon_grid { +dimensions: + time = 2 ; + x = 8 ; + y = 10 ; + extra = 10 ; +variables: + double time(time) ; + time:standard_name = "time" ; + time:long_name = "time" ; + time:units = "seconds since 1970-01-01 00:00:00" ; + double lon(y, x) ; + lon:_FillValue = -9999. ; + lon:standard_name = "longitude" ; + lon:long_name = "longitude" ; + lon:units = "degrees_east" ; + double lat(y, x) ; + lat:_FillValue = -9999. ; + lat:standard_name = "latitude" ; + lat:long_name = "latitude" ; + lat:units = "degrees_north" ; + double extra(extra) ; + extra:standard_name = "extra" ; + extra:long_name = "extra" ; + extra:units = "extra" ; + float data_var_1(extra, time, y, x) ; + data_var_1:_FillValue = -9999.f ; + data_var_1:least_significant_digit = 3LL ; + data_var_1:standard_name = "data_var_1" ; + data_var_1:long_name = "data_var_1" ; + data_var_1:units = "none" ; +data: + + time = 1483229400, 1483229460 ; + + lon = + 0.17794546007564, 2.76480838102692, 4.18878554126185, 6.13140594851911, + 8.25776443616664, 10.594877870279, 12.6994494223276, 14.9190516896196, + 0.250179766560622, 2.03846475387248, 4.96279145610287, 6.10010711919148, + 8.53888120539574, 10.0442026384773, 12.7947215058503, 14.5174747914901, + 0.520132373754771, 2.07786471685772, 4.78821607525056, 6.73300274514291, + 8.81005201784217, 10.7796777101128, 12.212128379423, 14.8731199702957, + 0.0950808312378734, 2.83455620410034, 4.25121510057895, 6.20757154982021, + 8.69302343038157, 10.8463579390503, 12.9164545990549, 14.4880197896557, + 0.110912784256131, 2.39952142338174, 4.6405981739942, 6.67454840309456, + 8.86832035853982, 10.1012125785341, 12.2478697044722, 14.3806055174274, + 0.848407784614987, 2.12410021411385, 4.30126835165127, 6.08272948203336, + 8.31954829047515, 10.7080640256276, 12.9351212345098, 14.9232850865539, + 0.279728330934168, 2.86001619849343, 4.31039283813434, 6.2097886086892, + 8.42641603281344, 10.9348982911965, 12.5587664498595, 14.8200704365968, + 0.741518115934441, 2.87785746836924, 4.21260624077126, 6.09849026654236, + 8.16643573020496, 10.9591619876402, 12.0163700125252, 14.5746437160627, + 0.355139096406225, 2.20215020007337, 4.1006551793198, 6.34067883431823, + 8.60366187834832, 10.4913397187034, 12.5052984144651, 14.2266966585043, + 0.22662417247866, 2.18970602130296, 4.50407504733109, 6.40181010730075, + 8.16291736289111, 10.8512048695319, 12.96194553871, 14.9727413428817 ; + + lat = + 50.7835602005565, 50.2566524707529, 50.6505335310805, 50.8525251838766, + 50.1486121685993, 50.788071291356, 50.4125423477156, 50.63144348784, + 52.6309115340556, 52.6489428533515, 52.3996505162228, 52.3454048004656, + 52.242045569055, 52.6458637017658, 52.0970846820258, 52.8587770928727, + 54.8006872616247, 54.0337608186355, 54.5137934031104, 54.3259442117892, + 54.6548699322524, 54.3282364118544, 54.9226820200184, 54.9789360825204, + 56.4761611893831, 56.0460781257951, 56.7037110129775, 56.0683750958518, + 56.5734562203457, 56.3804867150557, 56.4702253118413, 56.339942952224, + 58.1350815550656, 58.0530889323638, 58.9834045309851, 58.0794081595164, + 58.7299343647019, 58.2261688104002, 58.3510507109013, 58.58408167691, + 60.7548469287539, 60.7251020231181, 60.2021318780373, 60.2528506391094, + 60.6555717934058, 60.908430109801, 60.2503025738482, 60.1990098182258, + 62.9260703102219, 62.5416859872935, 62.8266417667752, 62.1628282254356, + 62.0234318054562, 62.9034849754582, 62.4573636163949, 62.9822058820299, + 64.681229929912, 64.2054385258265, 64.9486242347524, 64.2243687845649, + 64.300630953565, 64.8457157280671, 64.4728181200989, 64.9486940907017, + 66.8177072145075, 66.9299871881733, 66.8248778002961, 66.792602873387, + 66.1862385808535, 66.8119552679746, 66.1380619843272, 66.7299187799637, + 68.2534738273635, 68.8071668043109, 68.1165498253522, 68.532499135546, + 68.9735815723013, 68.5102396983322, 68.8026199167508, 68.8670237316549 ; + + extra = 0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9 ; +} + + + +""" + + +def main(): + netcdf_file = netCDF4.Dataset(filename, mode="w", format="NETCDF4") + + # Create the dimensions. + netcdf_file.createDimension(TIME_DIM_NAME, 2) + netcdf_file.createDimension(X_DIM_NAME, 8) + netcdf_file.createDimension(Y_DIM_NAME, 10) + netcdf_file.createDimension(EXTRA_DIM_NAME, 10) + + # Create the coordinate variables. + time_var = netcdf_file.createVariable("time", "f8", (TIME_DIM_NAME)) + time_var.standard_name = "time" + time_var.long_name = "time" + time_var.units = "seconds since 1970-01-01 00:00:00" + + lon_var = netcdf_file.createVariable( + "lon", + "f8", + (Y_DIM_NAME, X_DIM_NAME), + fill_value=-9999, + ) + lon_var.standard_name = "longitude" + lon_var.long_name = "longitude" + lon_var.units = "degrees_east" + + lat_var = netcdf_file.createVariable( + "lat", + "f8", + (Y_DIM_NAME, X_DIM_NAME), + fill_value=-9999, + ) + lat_var.standard_name = "latitude" + lat_var.long_name = "latitude" + lat_var.units = "degrees_north" + + # Create extra dim variable + extra_var = netcdf_file.createVariable("extra", "f8", (EXTRA_DIM_NAME)) + extra_var.standard_name = "extra" + extra_var.long_name = "extra" + extra_var.units = "extra" + + extra_var[:] = numpy.arange(0, 10, 1) / 10.0 + + # Create the measured value variables. + data_var_1 = netcdf_file.createVariable( + "data_var_1", + "f4", + (EXTRA_DIM_NAME, TIME_DIM_NAME, Y_DIM_NAME, X_DIM_NAME), + fill_value=-9999, + compression="zlib", + least_significant_digit=3, + ) + data_var_1.standard_name = "data_var_1" + data_var_1.long_name = "data_var_1" + data_var_1.units = "none" + + print("NetCDF file initialized.") + + # Fill the data variables. + # data_var_1[:] = [1, 2] + + for x in range(8): + for y in range(10): + lon_var[y, x] = 0 + x * 2 + random.random() + lat_var[y, x] = 50 + y * 2 + random.random() + + time_var[:] = [ + netCDF4.date2num( + datetime.datetime(2017, 1, 1, 0, 10, 0), "seconds since 1970-01-01 00:00:00" + ), + netCDF4.date2num( + datetime.datetime(2017, 1, 1, 0, 11, 0), "seconds since 1970-01-01 00:00:00" + ), + ] + + for iextra, extra in enumerate(extra_var[:]): + for it, time in enumerate(time_var[:]): + for ix in range(8): + for iy in range(10): + na = numpy.cos((iy / 15) * 3.141592654 + extra) + 0.5 * it + nb = numpy.cos((ix / 19) * 3.141592654 + extra) + 0.5 * it + data_var_1[iextra, it, iy, ix] = na * na + nb * nb + # print(data_var_1[:]) + netcdf_file.close() + print("Done.") + + +if __name__ == "__main__": + main() diff --git a/tests/AdagucTests/TestWMS.py b/tests/AdagucTests/TestWMS.py index c695a869c..18ecf8773 100644 --- a/tests/AdagucTests/TestWMS.py +++ b/tests/AdagucTests/TestWMS.py @@ -2193,8 +2193,6 @@ def test_WMSGetMapWithHarmWindBarbs(self): def test_WMSGetMap_EPSG3067(self): AdagucTestTools().cleanTempDir() - config = ADAGUC_PATH + "/data/config/adaguc.tests.dataset.xml" - env = {"ADAGUC_CONFIG": config} filename = "test_WMSGetMap_EPSG3067.png" # pylint: disable=unused-variable @@ -2403,3 +2401,103 @@ def test_WMSGetMap_WithCaching(self): self.assertEqual( headers, ["Content-Type:image/png", "Cache-Control:max-age=60"] ) + + def test_WMSGetMap_IrregularGrid_1Dimensional_latlon(self): + """ + This will test a file where lon and lat variables have irregular spacing. These variables are still 1 dimensional. + """ + AdagucTestTools().cleanTempDir() + config = ADAGUC_PATH + "/data/config/adaguc.autoresource.xml" + + filename = "test_WMSGetMap_IrregularGrid_1Dimensional_latlon.png" + # pylint: disable=unused-variable + status, data, headers = AdagucTestTools().runADAGUCServer( + "source=example_file_irregular_1Dlat1Dlon_grid.nc&SERVICE=WMS&VERSION=1.3.0&REQUEST=GetMap&LAYERS=data_var_1&WIDTH=512&HEIGHT=320&CRS=EPSG:3857&BBOX=-1612507.3080954754,5830283.642777597,1616299.7724732205,7796173.721992844&STYLES=auto/nearest&FORMAT=image/png&TRANSPARENT=FALSE&BGCOLOR=0x0000FF&DIM_extra=0&time=2017-01-01T00:10:00Z&colorscalerange=0,2&", + {"ADAGUC_CONFIG": config}, + ) + AdagucTestTools().writetofile(self.testresultspath + filename, data.getvalue()) + self.assertEqual(status, 0) + self.assertEqual( + data.getvalue(), + AdagucTestTools().readfromfile(self.expectedoutputsspath + filename), + ) + + def test_WMSGetMap_IrregularGrid_1Dimensional_latlon_nextdimensionstep(self): + """ + This will test a file where lon and lat variables have irregular spacing. These variables are still 1 dimensional. Dimensions have been changed to allow for generating another new image. + """ + AdagucTestTools().cleanTempDir() + config = ADAGUC_PATH + "/data/config/adaguc.autoresource.xml" + + filename_getcapabilities = "test_WMSGetMap_IrregularGrid_1Dimensional_latlon_nextdimensionstep_getcapabilities.xml" + + filename = ( + "test_WMSGetMap_IrregularGrid_1Dimensional_latlon_nextdimensionstep.png" + ) + + status, data, headers = AdagucTestTools().runADAGUCServer( + "source=example_file_irregular_1Dlat1Dlon_grid.nc&SERVICE=WMS&request=GetCapabilities", + {"ADAGUC_CONFIG": config}, + ) + AdagucTestTools().writetofile( + self.testresultspath + filename_getcapabilities, data.getvalue() + ) + self.assertEqual(status, 0) + self.assertTrue( + AdagucTestTools().compareGetCapabilitiesXML( + self.testresultspath + filename_getcapabilities, + self.expectedoutputsspath + filename_getcapabilities, + ) + ) + + # pylint: disable=unused-variable + status, data, headers = AdagucTestTools().runADAGUCServer( + "source=example_file_irregular_1Dlat1Dlon_grid.nc&SERVICE=WMS&VERSION=1.3.0&REQUEST=GetMap&LAYERS=data_var_1&WIDTH=512&HEIGHT=320&CRS=EPSG%3A3857&BBOX=-1612507.3080954754,5830283.642777597,1616299.7724732205,7796173.721992844&STYLES=auto/nearest&FORMAT=image/png&TRANSPARENT=FALSE&BGCOLOR=0xFF0000&DIM_extra=0.8&time=2017-01-01T00:11:00Z&colorscalerange=0,2&", + {"ADAGUC_CONFIG": config}, + ) + AdagucTestTools().writetofile(self.testresultspath + filename, data.getvalue()) + self.assertEqual(status, 0) + self.assertEqual( + data.getvalue(), + AdagucTestTools().readfromfile(self.expectedoutputsspath + filename), + ) + + def test_WMSGetMap_IrregularGrid_2Dimensional_latlon_nextdimensionstep(self): + """ + This will test a file where lon and lat variables have irregular spacing. These lat/lon variables are 2D. Dimensions have been changed to allow for generating another new image. + """ + AdagucTestTools().cleanTempDir() + config = ADAGUC_PATH + "/data/config/adaguc.autoresource.xml" + + filename_getcapabilities = "test_WMSGetMap_IrregularGrid_2Dimensional_latlon_nextdimensionstep_getcapabilities.xml" + + filename = ( + "test_WMSGetMap_IrregularGrid_2Dimensional_latlon_nextdimensionstep.png" + ) + + status, data, headers = AdagucTestTools().runADAGUCServer( + "source=example_file_irregular_2Dlat2Dlon_grid.nc&SERVICE=WMS&request=GetCapabilities", + {"ADAGUC_CONFIG": config}, + ) + AdagucTestTools().writetofile( + self.testresultspath + filename_getcapabilities, data.getvalue() + ) + self.assertEqual(status, 0) + self.assertTrue( + AdagucTestTools().compareGetCapabilitiesXML( + self.testresultspath + filename_getcapabilities, + self.expectedoutputsspath + filename_getcapabilities, + ) + ) + + # pylint: disable=unused-variable + status, data, headers = AdagucTestTools().runADAGUCServer( + "source=example_file_irregular_2Dlat2Dlon_grid.nc&SERVICE=WMS&VERSION=1.3.0&REQUEST=GetMap&LAYERS=data_var_1&WIDTH=512&HEIGHT=320&CRS=EPSG%3A3857&BBOX=-162669.9922884648,6411680.00083944,1827705.159150465,10780913.16460056&STYLES=auto/nearest&FORMAT=image/png&TRANSPARENT=FALSE&BGCOLOR=0xFF0000&DIM_extra=0.8&time=2017-01-01T00:11:00Z&colorscalerange=0,2&", + {"ADAGUC_CONFIG": config}, + ) + AdagucTestTools().writetofile(self.testresultspath + filename, data.getvalue()) + self.assertEqual(status, 0) + self.assertEqual( + data.getvalue(), + AdagucTestTools().readfromfile(self.expectedoutputsspath + filename), + ) diff --git a/tests/expectedoutputs/TestWMS/test_WMSGetMap_IrregularGrid_1Dimensional_latlon.png b/tests/expectedoutputs/TestWMS/test_WMSGetMap_IrregularGrid_1Dimensional_latlon.png new file mode 100644 index 000000000..29421592d Binary files /dev/null and b/tests/expectedoutputs/TestWMS/test_WMSGetMap_IrregularGrid_1Dimensional_latlon.png differ diff --git a/tests/expectedoutputs/TestWMS/test_WMSGetMap_IrregularGrid_1Dimensional_latlon_nextdimensionstep.png b/tests/expectedoutputs/TestWMS/test_WMSGetMap_IrregularGrid_1Dimensional_latlon_nextdimensionstep.png new file mode 100644 index 000000000..a1c6d9c52 Binary files /dev/null and b/tests/expectedoutputs/TestWMS/test_WMSGetMap_IrregularGrid_1Dimensional_latlon_nextdimensionstep.png differ diff --git a/tests/expectedoutputs/TestWMS/test_WMSGetMap_IrregularGrid_1Dimensional_latlon_nextdimensionstep_getcapabilities.xml b/tests/expectedoutputs/TestWMS/test_WMSGetMap_IrregularGrid_1Dimensional_latlon_nextdimensionstep_getcapabilities.xml new file mode 100644 index 000000000..cf08197b1 --- /dev/null +++ b/tests/expectedoutputs/TestWMS/test_WMSGetMap_IrregularGrid_1Dimensional_latlon_nextdimensionstep_getcapabilities.xml @@ -0,0 +1,127 @@ + + + + WMS + AutoResource example_file_irregular_1Dlat1Dlon_grid.nc + This service demonstrates how the ADAGUC server can be used to create OGC services. + + view + infoMapAccessService + ADAGUCServer version 2.15.0, of Feb 2 2024 11:40:13 + + + + + + no conditions apply + None + 8192 + 8192 + + + + + text/xml + + + + image/png + image/png;mode=8bit + image/png;mode=8bit_noalpha + image/png;mode=24bit + image/png;mode=32bit + image/gif + image/jpeg + + + + + image/png + text/plain + text/html + text/xml + application/json + + + + + XML + INIMAGE + BLANK + + + +WMS of AutoResource example_file_irregular_1Dlat1Dlon_grid.nc +EPSG:3411 +EPSG:3412 +EPSG:3575 +EPSG:3857 +EPSG:4258 +EPSG:4326 +CRS:84 +EPSG:25831 +EPSG:25832 +EPSG:28992 +EPSG:7399 +EPSG:50001 +EPSG:54030 +EPSG:32661 +EPSG:40000 +EPSG:900913 +EPSG:3067 + + + + + + + + + + + + + + + + + + +data_var_1 +data_var_1 (data_var_1) + + -12.000000 + 12.000000 + 48.000000 + 56.000000 + + + + + + + + + + + + + + + + + +0.0,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9 +2017-01-01T00:10:00Z,2017-01-01T00:11:00Z + + + + diff --git a/tests/expectedoutputs/TestWMS/test_WMSGetMap_IrregularGrid_2Dimensional_latlon_nextdimensionstep.png b/tests/expectedoutputs/TestWMS/test_WMSGetMap_IrregularGrid_2Dimensional_latlon_nextdimensionstep.png new file mode 100644 index 000000000..66dfa62c9 Binary files /dev/null and b/tests/expectedoutputs/TestWMS/test_WMSGetMap_IrregularGrid_2Dimensional_latlon_nextdimensionstep.png differ diff --git a/tests/expectedoutputs/TestWMS/test_WMSGetMap_IrregularGrid_2Dimensional_latlon_nextdimensionstep_getcapabilities.xml b/tests/expectedoutputs/TestWMS/test_WMSGetMap_IrregularGrid_2Dimensional_latlon_nextdimensionstep_getcapabilities.xml new file mode 100644 index 000000000..d2abdc9eb --- /dev/null +++ b/tests/expectedoutputs/TestWMS/test_WMSGetMap_IrregularGrid_2Dimensional_latlon_nextdimensionstep_getcapabilities.xml @@ -0,0 +1,127 @@ + + + + WMS + AutoResource example_file_irregular_2Dlat2Dlon_grid.nc + This service demonstrates how the ADAGUC server can be used to create OGC services. + + view + infoMapAccessService + ADAGUCServer version 2.15.1, of Feb 2 2024 15:50:33 + + + + + + no conditions apply + None + 8192 + 8192 + + + + + text/xml + + + + image/png + image/png;mode=8bit + image/png;mode=8bit_noalpha + image/png;mode=24bit + image/png;mode=32bit + image/gif + image/jpeg + + + + + image/png + text/plain + text/html + text/xml + application/json + + + + + XML + INIMAGE + BLANK + + + +WMS of AutoResource example_file_irregular_2Dlat2Dlon_grid.nc +EPSG:3411 +EPSG:3412 +EPSG:3575 +EPSG:3857 +EPSG:4258 +EPSG:4326 +CRS:84 +EPSG:25831 +EPSG:25832 +EPSG:28992 +EPSG:7399 +EPSG:50001 +EPSG:54030 +EPSG:32661 +EPSG:40000 +EPSG:900913 +EPSG:3067 + + + + + + + + + + + + + + + + + + +data_var_1 +data_var_1 (data_var_1) + + 0.079045 + 14.878220 + 50.047555 + 68.959048 + + + + + + + + + + + + + + + + + +0.0,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9 +2017-01-01T00:10:00Z,2017-01-01T00:11:00Z + + + +