diff --git a/.github/workflows/build-and-release.yaml b/.github/workflows/build-and-release.yaml index fd1c91b8..7245d9de 100644 --- a/.github/workflows/build-and-release.yaml +++ b/.github/workflows/build-and-release.yaml @@ -62,7 +62,7 @@ jobs: fail-fast: true matrix: include: - - os: ubuntu-22.04 + - os: [ubuntu-22.04, ubuntu-24.04] runs-on: ${{ matrix.os }} diff --git a/CHANGELOG.md b/CHANGELOG.md index 81cf5368..d420d935 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,18 @@ Le format est basé sur [Keep a Changelog](https://keepachangelog.com/) et ce pr ## [Unreleased] +## [3.1.0] - 2026-06-25 + +### Added + +- Compilation des artefacts sous ubuntu 24.04 (compatible debian 13 / trixie) +- `Colorize` : Ajout d'un style colorize permettant de remplacer une couleur en une autre avec une possibilité de transparence. + +### Fixed + +- `Boundingbox` : Mise en place de valeur maximale et minimale pour les bbox en wms-r. +- `Style` : Ajout des préfix ogc pour le title et l'abstract sur le wmts + ## [3.0.0] - 2026-03-12 ### Added @@ -213,6 +225,7 @@ Les librairies sont gérées de manière indépendantes, conditionnées pour êt [1.0.3]: https://github.com/rok4/core-cpp/releases/tag/1.0.3 -[Unreleased]: https://github.com/rok4/core-cpp/compare/3.0.0...HEAD +[Unreleased]: https://github.com/rok4/core-cpp/compare/3.1.0...HEAD +[3.1.0]: https://github.com/rok4/core-cpp/compare/3.0.0...3.1.0 [3.0.0]: https://github.com/rok4/core-cpp/compare/2.0.6...3.0.0 diff --git a/CMakeLists.txt b/CMakeLists.txt index ec967c4a..dbf659d5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -178,13 +178,13 @@ if(CEPH_ENABLED) else(CEPH_ENABLED) set(CPACK_PACKAGE_FILE_NAME "librok4-base-${VERSION}-${CPACK_SYSTEM_NAME}-${BUILD_ARCHITECTURE}") endif(CEPH_ENABLED) -set(CPACK_PACKAGING_INSTALL_PREFIX "/usr") +set(CPACK_PACKAGING_INSTALL_PREFIX "/") set(CPACK_PACKAGE_VERSION ${VERSION}) set(CPACK_PACKAGE_VENDOR "IGN") set(CPACK_PACKAGE_CONTACT "IGN - Géoportail ()") -if(CPACK_SYSTEM_NAME AND CPACK_SYSTEM_NAME STREQUAL "ubuntu-20.04" OR CPACK_SYSTEM_NAME STREQUAL "ubuntu-22.04") +if(CPACK_SYSTEM_NAME AND CPACK_SYSTEM_NAME STREQUAL "ubuntu-20.04" OR CPACK_SYSTEM_NAME STREQUAL "ubuntu-22.04" OR CPACK_SYSTEM_NAME STREQUAL "ubuntu-24.04") set(CPACK_GENERATOR "DEB;TGZ") # Debian diff --git a/README.md b/README.md index 089caf0a..cc260c99 100644 --- a/README.md +++ b/README.md @@ -27,9 +27,9 @@ Installations système requises (listées dans le paquet debian, installées ave Depuis [GitHub](https://github.com/rok4/core-cpp/releases/) : ``` -curl -o librok4-dev.deb https://github.com/rok4/core-cpp/releases/download/x.y.z/librok4-base-x.y.z-ubuntu-20.04-amd64.deb +curl -o librok4-dev.deb https://github.com/rok4/core-cpp/releases/download/x.y.z/librok4-base-x.y.z-ubuntu-24.04-amd64.deb # or, with ceph driver -curl -o librok4-dev.deb https://github.com/rok4/core-cpp/releases/download/x.y.z/librok4-ceph-x.y.z-ubuntu-20.04-amd64.deb +curl -o librok4-dev.deb https://github.com/rok4/core-cpp/releases/download/x.y.z/librok4-ceph-x.y.z-ubuntu-24.04-amd64.deb apt install ./librok4-dev.deb ``` diff --git a/include/rok4/style/Colorize.h b/include/rok4/style/Colorize.h new file mode 100644 index 00000000..fac5e1a7 --- /dev/null +++ b/include/rok4/style/Colorize.h @@ -0,0 +1,109 @@ +/* + * Copyright © (2011) Institut national de l'information + * géographique et forestière + * + * Géoportail SAV + * + * This software is a computer program whose purpose is to publish geographic + * data using OGC WMS and WMTS protocol. + * + * This software is governed by the CeCILL-C license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/ or redistribute the software under the terms of the CeCILL-C + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * + * knowledge of the CeCILL-C license and that you accept its terms. + */ + + /** + * \file Colorize.h + ** \~french + * \brief D�finition de la classe Colorize + ** \~english + * \brief Define class Colorize + */ + + +#pragma once + +#include "rok4/utils/Configuration.h" + +#include +#include +#include +#include + +class Colorize : public Configuration +{ +public: + /** \~french + * \brief tolerance : seuil de tolérance pour gérer les teintes de blanc plus larges + ** \~english + * \brief tolerance : tolerance threshold for managing wider shades of white + */ + int tolerance; + + /** \~french + * \brief source : valeur visée en entrée + ** \~english + * \brief source : input target value + */ + std::vector source; + + /** \~french + * \brief destination : valeur visée en sortie + ** \~english + * \brief destination : output target value + */ + std::vector destination; + + /** \~french + * \brief noData : valeur de nodata pour l'image source + ** \~english + * \brief noData : value of nodata for the source image + */ + std::vector input_nodata_value; + + /** \~french + * \brief noData : valeur de nodata pour le colorize + ** \~english + * \brief noData : value of nodata for the colorize + */ + float colorize_nodata_value; + + /** + * \~french + * \brief Constructeurs avec des arguments + * \~english + * \brief Constructor with arguments + */ + Colorize(json11::Json doc); + + /** + * \~french + * \brief Destructeur + * \~english + * \brief Destructor + */ + virtual ~Colorize(); +}; diff --git a/include/rok4/style/Style.h b/include/rok4/style/Style.h index f1667c6d..00c8d548 100644 --- a/include/rok4/style/Style.h +++ b/include/rok4/style/Style.h @@ -56,6 +56,7 @@ class Style; #include "rok4/style/Estompage.h" #include "rok4/style/Aspect.h" #include "rok4/style/Terrainrgb.h" +#include "rok4/style/Colorize.h" #include "rok4/enums/Interpolation.h" #include "rok4/utils/Configuration.h" #include "rok4/utils/StoragePool.h" @@ -169,6 +170,11 @@ private : * \~english \brief Define wether the server must compute a RGB terrain */ Terrainrgb* terrainrgb; + /** + * \~french \brief Définit si un calcul de white to alpha doit être appliqué + * \~english \brief Define wether the server must compute a white to alpha + */ + Colorize* colorize; /** * \~french \brief Valeur de nodata attendue dans les données en entrée @@ -226,9 +232,16 @@ private : * \~english \brief Style is allowed ? */ bool handle (int spp) { - if (estompage_defined() || pente_defined() || aspect_defined() || terrainrgb_defined()) { + if (estompage_defined() || pente_defined() || aspect_defined() || terrainrgb_defined() ) { + return (spp == 1); + } + else if (colorize_defined()) { + return (spp == colorize->source.size()); + } + else if (palette && ! palette->is_empty()) { return (spp == 1); } else { + // identité return true; } } @@ -258,6 +271,14 @@ private : return orig_channels; } } + else if (colorize_defined()){ + if (orig_channels ==3 || orig_channels ==4){ + return colorize->destination.size(); + } + else { + return orig_channels; + } + } else { if (estompage_defined() || pente_defined() || aspect_defined()) { return 1; @@ -274,7 +295,7 @@ private : * \~english \brief Which sample format after style */ SampleFormat::eSampleFormat get_sample_format (SampleFormat::eSampleFormat sf) { - if ((palette && ! palette->is_empty()) || terrainrgb_defined()) { + if ((palette && ! palette->is_empty()) || terrainrgb_defined() || colorize_defined()) { return SampleFormat::UINT8; } else { return sf; @@ -314,7 +335,7 @@ private : return false; } - if (estompage_defined() || pente_defined() || aspect_defined() || terrainrgb_defined()) { + if (estompage_defined() || pente_defined() || aspect_defined() || terrainrgb_defined() || colorize_defined()) { return false; } else { return true; @@ -475,6 +496,27 @@ private : inline Terrainrgb* get_terrainrgb() { return terrainrgb; } + + /** + * \~french + * \brief Return vrai si le style est un white to alpha + * \return bool + * \~english + * \brief Return true if the style is an white to alpha + * \return bool + */ + inline bool colorize_defined() { + return (colorize != 0); + } + /** + * \~french + * \brief Retourne le white to alpha + * \~english + * \brief Return white to alpha + */ + inline Colorize* get_colorize() { + return colorize; + } /** diff --git a/include/rok4/utils/BoundingBox.h b/include/rok4/utils/BoundingBox.h index 435900ec..df03d873 100644 --- a/include/rok4/utils/BoundingBox.h +++ b/include/rok4/utils/BoundingBox.h @@ -409,10 +409,10 @@ class BoundingBox { if (geographical) { ptree& node = parent.add("EX_GeographicBoundingBox", ""); - node.add("westBoundLongitude", xmin); - node.add("eastBoundLongitude", xmax); - node.add("southBoundLatitude", ymin); - node.add("northBoundLatitude", ymax); + node.add("westBoundLongitude", std::max(xmin,-180.0)); + node.add("eastBoundLongitude", std::min(xmax,180.0)); + node.add("southBoundLatitude", std::max(ymin,-90.0)); + node.add("northBoundLatitude", std::min(ymax,90.0)); } else { ptree& node = parent.add("BoundingBox", ""); node.add(".CRS", crs); diff --git a/src/image/StyledImage.cpp b/src/image/StyledImage.cpp index 5c355a50..543c184e 100644 --- a/src/image/StyledImage.cpp +++ b/src/image/StyledImage.cpp @@ -169,6 +169,14 @@ StyledImage::StyledImage(Image *input_image, Style *input_style, int offset) : I } } + else if (style->colorize_defined()) { + if (source_image->get_channels() == 3 || source_image->get_channels() == 4) { + channels = style->get_colorize()->destination.size(); + } else { + channels = input_image->get_channels(); + } + } + if (style->palette_defined()){ // Il n'y aura application de la palette et modification des canaux que si // - la palette n'est pas nulle et pas vide @@ -489,6 +497,138 @@ int StyledImage::_getline(T *buffer, int line) { space = width * sizeof ( T ) * channels; } + else if (style->colorize_defined()) { + Colorize* wta = style->get_colorize(); + switch ( channels ) { + case 3: + if (source_image->get_channels()==3){ + for (int i = 0; i < source_image->get_width() ; i++ ) { + //image de départ à 3 canaux pour une arrivée en 3 canaux + int red = *(source+i*3); + int green = *(source+i*3+1); + int blue = *(source+i*3+2); + red = red < 0 ? 0 : (red > 255 ? 255 : red); + green = green < 0 ? 0 : (green > 255 ? 255 : green); + blue = blue < 0 ? 0 : (blue > 255 ? 255 : blue); + int s_red = wta->source[0]; + int s_green = wta->source[1]; + int s_blue = wta->source[2]; + + if (red>=s_red-wta->tolerance && red<=s_red+wta->tolerance && + green>=s_green-wta->tolerance && green<=s_green+wta->tolerance && + blue>=s_blue-wta->tolerance && blue<=s_blue+wta->tolerance){ + * ( buffer+i*3 ) = (T) wta->destination[0]; + * ( buffer+i*3+1 ) = (T) wta->destination[1]; + * ( buffer+i*3+2 ) = (T) wta->destination[2]; + } + else{ + * ( buffer+i*3 ) = (T) red; + * ( buffer+i*3+1 ) = (T) green; + * ( buffer+i*3+2 ) = (T) blue; + } + } + } + if (source_image->get_channels()==4){ + for (int i = 0; i < source_image->get_width() ; i++ ) { + //image de départ à 4 canaux pour une arrivée en 3 canaux + int red = *(source+i*4); + int green = *(source+i*4+1); + int blue = *(source+i*4+2); + int alpha = *(source+i*4+3); + red = red < 0 ? 0 : (red > 255 ? 255 : red); + green = green < 0 ? 0 : (green > 255 ? 255 : green); + blue = blue < 0 ? 0 : (blue > 255 ? 255 : blue); + alpha = alpha < 0 ? 0 : (alpha > 255 ? 255 : alpha); + int s_red = wta->source[0]; + int s_green = wta->source[1]; + int s_blue = wta->source[2]; + int s_alpha = wta->source[3]; + + if (red>=s_red-wta->tolerance && red<=s_red+wta->tolerance && + green>=s_green-wta->tolerance && green<=s_green+wta->tolerance && + blue>=s_blue-wta->tolerance && blue<=s_blue+wta->tolerance && + alpha>=s_alpha-wta->tolerance && alpha<=s_alpha+wta->tolerance){ + * ( buffer+i*3 ) = (T) wta->destination[0]; + * ( buffer+i*3+1 ) = (T) wta->destination[1]; + * ( buffer+i*3+2 ) = (T) wta->destination[2]; + } + else{ + * ( buffer+i*3 ) = (T) red; + * ( buffer+i*3+1 ) = (T) green; + * ( buffer+i*3+2 ) = (T) blue; + } + } + } + break; + case 4: + if (source_image->get_channels()==3){ + for (int i = 0; i < source_image->get_width() ; i++ ) { + //image de départ à 3 canaux pour une arrivée en 4 canaux + int red = *(source+i*3); + int green = *(source+i*3+1); + int blue = *(source+i*3+2); + red = red < 0 ? 0 : (red > 255 ? 255 : red); + green = green < 0 ? 0 : (green > 255 ? 255 : green); + blue = blue < 0 ? 0 : (blue > 255 ? 255 : blue); + int s_red = wta->source[0]; + int s_green = wta->source[1]; + int s_blue = wta->source[2]; + + if (red>=s_red-wta->tolerance && red<=s_red+wta->tolerance && + green>=s_green-wta->tolerance && green<=s_green+wta->tolerance && + blue>=s_blue-wta->tolerance && blue<=s_blue+wta->tolerance){ + * ( buffer+i*4 ) = (T) wta->destination[0]; + * ( buffer+i*4+1 ) = (T) wta->destination[1]; + * ( buffer+i*4+2 ) = (T) wta->destination[2]; + * ( buffer+i*4+3 ) = (T) wta->destination[3]; + } + else{ + * ( buffer+i*4 ) = (T) red; + * ( buffer+i*4+1 ) = (T) green; + * ( buffer+i*4+2 ) = (T) blue; + * ( buffer+i*4+3 ) = (T) 255; + } + } + } + if (source_image->get_channels()==4){ + for (int i = 0; i < source_image->get_width() ; i++ ) { + //image de départ à 4 canaux pour une arrivée en 4 canaux + int red = *(source+i*4); + int green = *(source+i*4+1); + int blue = *(source+i*4+2); + int alpha = *(source+i*4+3); + red = red < 0 ? 0 : (red > 255 ? 255 : red); + green = green < 0 ? 0 : (green > 255 ? 255 : green); + blue = blue < 0 ? 0 : (blue > 255 ? 255 : blue); + alpha = alpha < 0 ? 0 : (alpha > 255 ? 255 : alpha); + int s_red = wta->source[0]; + int s_green = wta->source[1]; + int s_blue = wta->source[2]; + int s_alpha = wta->source[3]; + + if (red>=s_red-wta->tolerance && red<=s_red+wta->tolerance && + green>=s_green-wta->tolerance && green<=s_green+wta->tolerance && + blue>=s_blue-wta->tolerance && blue<=s_blue+wta->tolerance && + alpha>=s_alpha-wta->tolerance && alpha<=s_alpha+wta->tolerance){ + * ( buffer+i*4 ) = (T) wta->destination[0]; + * ( buffer+i*4+1 ) = (T) wta->destination[1]; + * ( buffer+i*4+2 ) = (T) wta->destination[2]; + * ( buffer+i*4+3 ) = (T) wta->destination[3]; + } + else{ + * ( buffer+i*4 ) = (T) red; + * ( buffer+i*4+1 ) = (T) green; + * ( buffer+i*4+2 ) = (T) blue; + * ( buffer+i*4+3 ) = (T) alpha; + } + } + } + break; + } + + space = width * sizeof ( T ) * channels; + } + if (style->palette_defined()){ switch ( channels ) { case 4: @@ -541,6 +681,16 @@ StyledImage *StyledImage::create(Image *input_image, Style *input_style) { return NULL; } } + if (input_style->colorize_defined()){ + if (input_image->get_channels()!=3 && input_image->get_channels()!=4){ + BOOST_LOG_TRIVIAL(error)<<"Ce style ne s'applique que sur une image source à trois ou quatre canaux"; + return NULL; + } + if (input_style->palette_defined()){ + BOOST_LOG_TRIVIAL(error)<<"Le style colorize n'est pas compatible avec une palette"; + return NULL; + } + } return new StyledImage(input_image,input_style,offset); } @@ -570,6 +720,9 @@ void StyledImage::print() { if (style->terrainrgb_defined()){ BOOST_LOG_TRIVIAL(info) << "--------- Terrainrgb -----------" ; } + if (style->colorize_defined()){ + BOOST_LOG_TRIVIAL(info) << "--------- Colorize -----------" ; + } if (style->palette_defined()){ BOOST_LOG_TRIVIAL(info) << "--------- Palette -----------" ; } diff --git a/src/style/Colorize.cpp b/src/style/Colorize.cpp new file mode 100644 index 00000000..f2877b8e --- /dev/null +++ b/src/style/Colorize.cpp @@ -0,0 +1,79 @@ +/* + * Copyright © (2011) Institut national de l'information + * géographique et forestière + * + * Géoportail SAV + * + * This software is a computer program whose purpose is to publish geographic + * data using OGC WMS and WMTS protocol. + * + * This software is governed by the CeCILL-C license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/ or redistribute the software under the terms of the CeCILL-C + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * + * knowledge of the CeCILL-C license and that you accept its terms. + */ + + /** + * \file Colorize.cpp + * \~french + * \brief Implémentation de la classe Colorize pour rendre transparent les pixels blancs. + * \~english + * \brief Implement the Colorize Class handling transparency for white pixels definition. + */ + +#include "style/Colorize.h" +#include +#include "zlib.h" +#include +#include "byteswap.h" +#include + +Colorize::Colorize(json11::Json doc) : Configuration() +{ + + if (doc["tolerance"].is_number()) { + tolerance = doc["tolerance"].number_value(); + } else { + tolerance = 0; + } + if (doc["source"].is_array()) { + for (json11::Json v : doc["source"].array_items()) { + source.push_back(v.number_value()); + } + } else { + source = {255,255,255}; + } + if (doc["destination"].is_array()) { + for (json11::Json v : doc["destination"].array_items()) { + destination.push_back(v.number_value()); + } + } else { + destination = {255,255,255,0}; + } + input_nodata_value=source; +} + +Colorize::~Colorize() { +} diff --git a/src/style/Style.cpp b/src/style/Style.cpp index 2d0ef49b..3c0aee42 100644 --- a/src/style/Style.cpp +++ b/src/style/Style.cpp @@ -61,6 +61,7 @@ bool Style::parse(json11::Json& doc) { aspect = 0; estompage = 0; palette = 0; + colorize = 0; terrainrgb = 0; input_nodata_value = NULL; @@ -148,7 +149,7 @@ bool Style::parse(json11::Json& doc) { if (doc["terrainrgb"].is_object()) { if (estompage != 0 || pente != 0 || aspect !=0 || palette !=0) { - error_message = "Style " + id + " define exposition, estompage, pente or palette rules"; + error_message = "Style " + id + " define terrainrgb, which is not compatible with exposition, estompage, pente or palette rules"; return false; } terrainrgb = new Terrainrgb(doc["terrainrgb"].object_items()); @@ -157,6 +158,18 @@ bool Style::parse(json11::Json& doc) { return false; } } + + if (doc["colorize"].is_object()) { + if (estompage != 0 || pente != 0 || aspect !=0 || palette !=0 || terrainrgb !=0) { + error_message = "Style " + id + " define colorize, which is not compatible with exposition, estompage, pente, terrainrgb or palette rules"; + return false; + } + colorize = new Colorize(doc["colorize"].object_items()); + if (! colorize->is_ok()) { + error_message = "Colorize issue for style " + id + ": " + colorize->get_error_message(); + return false; + } + } return true; } @@ -167,6 +180,7 @@ Style::Style ( std::string path ) : Configuration(path) { palette = 0; aspect = 0; terrainrgb = 0; + colorize = 0; input_nodata_value = NULL; output_nodata_value = NULL; @@ -237,6 +251,15 @@ Style::Style ( std::string path ) : Configuration(path) { else if (terrainrgb_defined()) { input_nodata_value = new int[1]; input_nodata_value[0] = (int) terrainrgb->input_nodata_value; + } + else if (colorize_defined()) { + int i = colorize->input_nodata_value.size(); + input_nodata_value = new int[i]; + + for (int j = 0; j < i; ++j){ + input_nodata_value[j] = colorize->input_nodata_value[j]; + } + } else if (palette && ! palette->is_empty()) { input_nodata_value = new int[1]; @@ -277,6 +300,14 @@ Style::Style ( std::string path ) : Configuration(path) { output_nodata_value[1] = 0; output_nodata_value[2] = 0; } + else if (colorize_defined()) { + int i = colorize->input_nodata_value.size(); + output_nodata_value = new int[i]; + + for (int j = 0; j < i; ++j){ + output_nodata_value[j] = colorize->destination[j]; + } + } } Style::~Style() { @@ -295,6 +326,9 @@ Style::~Style() { if (terrainrgb != 0) { delete terrainrgb; } + if (colorize != 0) { + delete colorize; + } if (input_nodata_value != NULL) { delete[] input_nodata_value; } @@ -312,10 +346,10 @@ void Style::add_node_wmts(ptree& parent, bool default_style) { } for (std::string t : titles) { - node.add("Title", t); + node.add("ows:Title", t); } for (std::string a : abstracts) { - node.add("Abstract", a); + node.add("ows:Abstract", a); } if ( keywords.size() != 0 ) {