[Git][debian-gis-team/mapnik][experimental] 9 commits: Revert "Update branch in gbp.conf & Vcs-Git URL."

Bas Couwenberg gitlab at salsa.debian.org
Thu Apr 12 15:01:23 UTC 2018


Bas Couwenberg pushed to branch experimental at Debian GIS Project / mapnik


Commits:
c828cba7 by Bas Couwenberg at 2018-03-10T09:06:47+01:00
Revert "Update branch in gbp.conf & Vcs-Git URL."

This reverts commit e261b8d6f2b9921e6c25f229cb96b9af4dde1965.

- - - - -
202c80d8 by Bas Couwenberg at 2018-03-10T09:07:35+01:00
Set distribution to unstable.

- - - - -
4d11bdac by Bas Couwenberg at 2018-03-31T12:24:35+02:00
Update Vcs-* URLs for Salsa.

- - - - -
a2641dcf by Bas Couwenberg at 2018-04-12T15:41:19+02:00
Update branch in gbp.conf & Vcs-Git URL.

- - - - -
7a260eae by Bas Couwenberg at 2018-04-12T15:41:39+02:00
New upstream version 3.0.20+ds
- - - - -
fd852d53 by Bas Couwenberg at 2018-04-12T15:41:53+02:00
Merge tag 'upstream/3.0.20+ds' into experimental

Upstream version 3.0.20+ds

- - - - -
247f9c25 by Bas Couwenberg at 2018-04-12T15:42:17+02:00
New upstream release.

- - - - -
25c31094 by Bas Couwenberg at 2018-04-12T16:23:16+02:00
Bump Standards-Version to 4.1.4, no changes.

- - - - -
7a5c7880 by Bas Couwenberg at 2018-04-12T16:23:16+02:00
Set distribution to experimental.

- - - - -


14 changed files:

- CHANGELOG.md
- debian/changelog
- debian/control
- include/mapnik/image.hpp
- include/mapnik/image_impl.hpp
- include/mapnik/text/symbolizer_helpers.hpp
- include/mapnik/version.hpp
- plugins/input/gdal/gdal_datasource.cpp
- plugins/input/gdal/gdal_datasource.hpp
- plugins/input/gdal/gdal_featureset.cpp
- plugins/input/gdal/gdal_featureset.hpp
- src/image.cpp
- src/raster_colorizer.cpp
- src/text/symbolizer_helpers.cpp


Changes:

=====================================
CHANGELOG.md
=====================================
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,17 @@ Developers: Please commit along with changes.
 
 For a complete change history, see the git log.
 
+## 3.0.20
+
+Released: April 12, 2018
+
+(Packaged from f02c7bcdb)
+
+    - Make max_image_area a datasource parameter for GDAL.
+    - GDAL Driver Overview Fix and Memory Reduction (#3872)
+    - Raster colorizer: check image bounds (#3879)
+    - Removed usage of `typename` in template template declarations (available in c++17) (#3882)
+
 ## 3.0.19
 
 Released: March 06, 2018


=====================================
debian/changelog
=====================================
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,9 +1,18 @@
-mapnik (3.0.19+ds-1~exp2) UNRELEASED; urgency=medium
+mapnik (3.0.20+ds-1~exp1) experimental; urgency=medium
+
+  * New upstream release.
+  * Update Vcs-* URLs for Salsa.
+  * Bump Standards-Version to 4.1.4, no changes.
+
+ -- Bas Couwenberg <sebastic at debian.org>  Thu, 12 Apr 2018 15:43:20 +0200
+
+mapnik (3.0.19+ds-1) unstable; urgency=medium
 
   * Add patch to use pkg-config when freetype-config is not available.
     (closes: #892062)
+  * Move from experimental to unstable.
 
- -- Bas Couwenberg <sebastic at debian.org>  Fri, 09 Mar 2018 10:24:28 +0100
+ -- Bas Couwenberg <sebastic at debian.org>  Sat, 10 Mar 2018 09:07:21 +0100
 
 mapnik (3.0.19+ds-1~exp1) experimental; urgency=medium
 


=====================================
debian/control
=====================================
--- a/debian/control
+++ b/debian/control
@@ -31,9 +31,9 @@ Build-Depends: debhelper (>= 9~),
                pkg-config,
                scons,
                zlib1g-dev
-Standards-Version: 4.1.3
-Vcs-Browser: https://anonscm.debian.org/cgit/pkg-grass/mapnik.git
-Vcs-Git: https://anonscm.debian.org/git/pkg-grass/mapnik.git -b experimental
+Standards-Version: 4.1.4
+Vcs-Browser: https://salsa.debian.org/debian-gis-team/mapnik
+Vcs-Git: https://salsa.debian.org/debian-gis-team/mapnik.git -b experimental
 Homepage: http://www.mapnik.org/
 
 Package: libmapnik3.0


=====================================
include/mapnik/image.hpp
=====================================
--- a/include/mapnik/image.hpp
+++ b/include/mapnik/image.hpp
@@ -74,7 +74,7 @@ public:
     static constexpr image_dtype dtype = T::id;
     static constexpr std::size_t pixel_size = sizeof(pixel_type);
 private:
-    detail::image_dimensions<65535> dimensions_;
+    detail::image_dimensions<4294836225> dimensions_;
     detail::buffer buffer_;
     double offset_;
     double scaling_;


=====================================
include/mapnik/image_impl.hpp
=====================================
--- a/include/mapnik/image_impl.hpp
+++ b/include/mapnik/image_impl.hpp
@@ -39,8 +39,10 @@ image_dimensions<max_size>::image_dimensions(int width, int height)
     : width_(width),
       height_(height)
 {
-    if (width < 0 || static_cast<std::size_t>(width) > max_size) throw std::runtime_error("Invalid width for image dimensions requested");
-    if (height < 0 || static_cast<std::size_t>(height) > max_size) throw std::runtime_error("Invalid height for image dimensions requested");
+    int64_t area = (int64_t)width * (int64_t)height;
+    if (width < 0) throw std::runtime_error("Invalid width for image dimensions requested");
+    if (height < 0) throw std::runtime_error("Invalid height for image dimensions requested");
+    if (area > max_size) throw std::runtime_error("Image area too large based on image dimensions");
 }
 
 template <std::size_t max_size>


=====================================
include/mapnik/text/symbolizer_helpers.hpp
=====================================
--- a/include/mapnik/text/symbolizer_helpers.hpp
+++ b/include/mapnik/text/symbolizer_helpers.hpp
@@ -155,7 +155,7 @@ public:
 protected:
     void init_converters();
     void initialize_points() const;
-    template <template <typename, typename> typename GridAdapter>
+    template <template <typename, typename> class GridAdapter>
     void initialize_grid_points() const;
     bool next_point_placement() const;
     bool next_line_placement() const;


=====================================
include/mapnik/version.hpp
=====================================
--- a/include/mapnik/version.hpp
+++ b/include/mapnik/version.hpp
@@ -27,7 +27,7 @@
 
 #define MAPNIK_MAJOR_VERSION 3
 #define MAPNIK_MINOR_VERSION 0
-#define MAPNIK_PATCH_VERSION 19
+#define MAPNIK_PATCH_VERSION 20
 
 #define MAPNIK_VERSION (MAPNIK_MAJOR_VERSION*100000) + (MAPNIK_MINOR_VERSION*100) + (MAPNIK_PATCH_VERSION)
 


=====================================
plugins/input/gdal/gdal_datasource.cpp
=====================================
--- a/plugins/input/gdal/gdal_datasource.cpp
+++ b/plugins/input/gdal/gdal_datasource.cpp
@@ -82,6 +82,13 @@ gdal_datasource::gdal_datasource(parameters const& params)
 
     shared_dataset_ = *params.get<mapnik::boolean_type>("shared", false);
     band_ = *params.get<mapnik::value_integer>("band", -1);
+    
+    // Maximum memory limitation for image will be simply based on the maximum
+    // area we allow for an image. The true memory footprint therefore will vary based
+    // on the type of imagery that exists. This is not the maximum size of an image
+    // on disk but rather the maximum size we will load into mapnik from GDAL.
+    // max_im_area based on 50 mb limit for RGBA
+    max_image_area_ = *params.get<mapnik::value_integer>("max_image_area", (50*1024*1024) / 4);
 
 #if GDAL_VERSION_NUM >= 1600
     if (shared_dataset_)
@@ -235,7 +242,8 @@ featureset_ptr gdal_datasource::features(query const& q) const
                                               dx_,
                                               dy_,
                                               nodata_value_,
-                                              nodata_tolerance_);
+                                              nodata_tolerance_,
+                                              max_image_area_);
 }
 
 featureset_ptr gdal_datasource::features_at_point(coord2d const& pt, double tol) const
@@ -254,5 +262,6 @@ featureset_ptr gdal_datasource::features_at_point(coord2d const& pt, double tol)
                                               dx_,
                                               dy_,
                                               nodata_value_,
-                                              nodata_tolerance_);
+                                              nodata_tolerance_,
+                                              max_image_area_);
 }


=====================================
plugins/input/gdal/gdal_datasource.hpp
=====================================
--- a/plugins/input/gdal/gdal_datasource.hpp
+++ b/plugins/input/gdal/gdal_datasource.hpp
@@ -68,6 +68,7 @@ private:
     bool shared_dataset_;
     boost::optional<double> nodata_value_;
     double nodata_tolerance_;
+    int64_t max_image_area_;
 };
 
 #endif // GDAL_DATASOURCE_HPP


=====================================
plugins/input/gdal/gdal_featureset.cpp
=====================================
--- a/plugins/input/gdal/gdal_featureset.cpp
+++ b/plugins/input/gdal/gdal_featureset.cpp
@@ -89,7 +89,8 @@ gdal_featureset::gdal_featureset(GDALDataset& dataset,
                                  double dx,
                                  double dy,
                                  boost::optional<double> const& nodata,
-                                 double nodata_tolerance)
+                                 double nodata_tolerance,
+                                 int64_t max_image_area)
     : dataset_(dataset),
       ctx_(std::make_shared<mapnik::context_type>()),
       band_(band),
@@ -102,6 +103,7 @@ gdal_featureset::gdal_featureset(GDALDataset& dataset,
       nbands_(nbands),
       nodata_value_(nodata),
       nodata_tolerance_(nodata_tolerance),
+      max_image_area_(max_image_area),
       first_(true)
 {
     ctx_->push("nodata");
@@ -155,6 +157,8 @@ feature_ptr gdal_featureset::get_feature(mapnik::query const& q)
     //size of resized output pixel in source image domain
     double margin_x = 1.0 / (std::fabs(dx_) * std::get<0>(q.resolution()));
     double margin_y = 1.0 / (std::fabs(dy_) * std::get<1>(q.resolution()));
+    MAPNIK_LOG_DEBUG(gdal) << "gdal_featureset: margin_x=" << margin_x;
+    MAPNIK_LOG_DEBUG(gdal) << "gdal_featureset: margin_y=" << margin_y;
     if (margin_x < 1)
     {
         margin_x = 1.0;
@@ -169,6 +173,10 @@ feature_ptr gdal_featureset::get_feature(mapnik::query const& q)
     int y_off = rint(box.miny() - margin_y);
     int end_x = rint(box.maxx() + margin_x);
     int end_y = rint(box.maxy() + margin_y);
+    MAPNIK_LOG_DEBUG(gdal) << "gdal_featureset: x_off=" << x_off;
+    MAPNIK_LOG_DEBUG(gdal) << "gdal_featureset: y_off=" << y_off;
+    MAPNIK_LOG_DEBUG(gdal) << "gdal_featureset: end_x=" << end_x;
+    MAPNIK_LOG_DEBUG(gdal) << "gdal_featureset: end_y=" << end_y;
 
     //clip to available data
     if (x_off < 0)
@@ -187,21 +195,134 @@ feature_ptr gdal_featureset::get_feature(mapnik::query const& q)
     {
         end_y = raster_height_;
     }
+
+    // width and height of the portion of the source image we are requesting
     int width = end_x - x_off;
     int height = end_y - y_off;
 
+    // In many cases we want GDAL to simply return the exact image so we
+    // can handle resampling internally in mapnik. In other cases such as 
+    // when overviews exist or when the image allocated might be too large
+    // we want to utilize some resampling in GDAL instead.
+    int im_height = height;
+    int im_width = width;
+    double im_offset_x = x_off;
+    double im_offset_y = y_off;
+    int current_width = (int)raster_width_;
+    int current_height = (int)raster_height_;
+
+    // loop through overviews -- snap up in resolution to closest overview if necessary
+    // we find an image size that most resembles the resolution of our output image.
+    double width_res = std::get<0>(q.resolution());
+    double height_res = std::get<1>(q.resolution());
+    int res_adjusted_raster_width = static_cast<int>(std::floor(((double)raster_width_ * width_res) + .5));
+    int res_adjusted_raster_height = static_cast<int>(std::floor(((double)raster_height_ * height_res) + .5));
+    if (band_ > 0 && band_ < nbands_)
+    {
+        GDALRasterBand * band = dataset_.GetRasterBand(band_);
+        int band_overviews = band->GetOverviewCount();
+        if (band_overviews > 0)
+        {
+            for (int b = 0; b < band_overviews; b++)
+            {
+                GDALRasterBand * overview = band->GetOverview(b);
+                int overview_width = overview->GetXSize();
+                int overview_height = overview->GetYSize();
+                if ((overview_width < current_width || overview_height < current_height) &&
+                    res_adjusted_raster_width <= overview_width &&
+                    res_adjusted_raster_height <= overview_height)
+                {
+                    current_width = overview_width;
+                    current_height = overview_height;
+                }
+            }
+        }
+    }
+    else
+    {
+        for (int i = 0; i < nbands_; ++i)
+        {
+            GDALRasterBand * band = dataset_.GetRasterBand(i + 1);
+            int band_overviews = band->GetOverviewCount();
+            if (band_overviews > 0)
+            {
+                for (int b = 0; b < band_overviews; b++)
+                {
+                    GDALRasterBand * overview = band->GetOverview(b);
+                    int overview_width = overview->GetXSize();
+                    int overview_height = overview->GetYSize();
+                    if ((overview_width < current_width || overview_height < current_height) &&
+                        res_adjusted_raster_width <= overview_width &&
+                        res_adjusted_raster_height <= overview_height)
+                    {
+                        current_width = overview_width;
+                        current_height = overview_height;
+                    }
+                }
+            }
+        }
+    }
+    if (current_width != (int)raster_width_ || current_height != (int)raster_height_)
+    {
+        if (current_width != (int)raster_width_)
+        {
+            double ratio = (double)current_width / (double)raster_width_;
+            int adjusted_width = static_cast<int>(std::floor((ratio * im_width) + 0.5));
+            double adjusted_ratio = (double)adjusted_width / (double)im_width;
+            im_offset_x = adjusted_ratio * im_offset_x;
+            im_width = adjusted_width;
+        }
+        if (current_height != (int)raster_height_)
+        {
+            double ratio = (double)current_height / (double)raster_height_;
+            int adjusted_height = static_cast<int>(std::floor((ratio * im_height) + 0.5));
+            double adjusted_ratio = (double)adjusted_height / (double)im_height;
+            im_offset_y = adjusted_ratio * im_offset_y;
+            im_height = adjusted_height;
+        }
+    }
+    
+    int64_t im_area = (int64_t)im_width * (int64_t)im_height;
+    if (im_area > max_image_area_)
+    {
+        int adjusted_width = static_cast<int>(std::round(std::sqrt(max_image_area_ * ((double)im_width / (double)im_height))));
+        int adjusted_height = static_cast<int>(std::round(std::sqrt(max_image_area_ * ((double)im_height / (double)im_width))));
+        if (adjusted_width < 1)
+        {
+            adjusted_width = 1;
+        }
+        if (adjusted_height < 1)
+        {
+            adjusted_height = 1;
+        }
+        double ratio_x = (double)adjusted_width / (double)im_width;
+        double ratio_y = (double)adjusted_height / (double)im_height;
+        im_offset_x = ratio_x * im_offset_x;
+        im_offset_y = ratio_y * im_offset_y;
+        im_width = adjusted_width;
+        im_height = adjusted_height;
+        current_width = static_cast<int>(std::floor((ratio_x * current_width) + 0.5));
+        current_height = static_cast<int>(std::floor((ratio_y * current_height) + 0.5));
+    }
+    
     //calculate actual box2d of returned raster
-    box2d<double> feature_raster_extent(x_off, y_off, x_off + width, y_off + height);
-    feature_raster_extent = t.backward(feature_raster_extent);
+    view_transform t2(current_width, current_height, raster_extent_, 0, 0);
+    box2d<double> feature_raster_extent(im_offset_x, im_offset_y, im_offset_x + im_width, im_offset_y + im_height);
+    MAPNIK_LOG_DEBUG(gdal) << "gdal_featureset: Feature Raster extent=" << feature_raster_extent;
+    feature_raster_extent = t2.backward(feature_raster_extent);
 
     MAPNIK_LOG_DEBUG(gdal) << "gdal_featureset: Raster extent=" << raster_extent_;
+    MAPNIK_LOG_DEBUG(gdal) << "gdal_featureset: Feature Raster extent=" << feature_raster_extent;
     MAPNIK_LOG_DEBUG(gdal) << "gdal_featureset: View extent=" << intersect;
     MAPNIK_LOG_DEBUG(gdal) << "gdal_featureset: Query resolution=" << std::get<0>(q.resolution()) << "," << std::get<1>(q.resolution());
     MAPNIK_LOG_DEBUG(gdal) << "gdal_featureset: StartX=" << x_off << " StartY=" << y_off << " Width=" << width << " Height=" << height;
+    MAPNIK_LOG_DEBUG(gdal) << "gdal_featureset: IM StartX=" << im_offset_x << " StartY=" << im_offset_y << " Width=" << im_width << " Height=" << im_height;
 
     if (width > 0 && height > 0)
     {
-        MAPNIK_LOG_DEBUG(gdal) << "gdal_featureset: Image Size=(" << width << "," << height << ")";
+
+        MAPNIK_LOG_DEBUG(gdal) << "gdal_featureset: Requested Image Size=(" << width << "," << height << ")";
+        MAPNIK_LOG_DEBUG(gdal) << "gdal_featureset: Image Size=(" << im_width << "," << im_height << ")";
         MAPNIK_LOG_DEBUG(gdal) << "gdal_featureset: Reading band=" << band_;
         if (band_ > 0) // we are querying a single band
         {
@@ -217,7 +338,7 @@ feature_ptr gdal_featureset::get_feature(mapnik::query const& q)
             {
             case GDT_Byte:
             {
-                mapnik::image_gray8 image(width, height);
+                mapnik::image_gray8 image(im_width, im_height);
                 image.set(std::numeric_limits<std::uint8_t>::max());
                 raster_nodata = band->GetNoDataValue(&raster_has_nodata);
                 raster_io_error = band->RasterIO(GF_Read, x_off, y_off, width, height,
@@ -237,7 +358,7 @@ feature_ptr gdal_featureset::get_feature(mapnik::query const& q)
             case GDT_Float64:
             case GDT_Float32:
             {
-                mapnik::image_gray32f image(width, height);
+                mapnik::image_gray32f image(im_width, im_height);
                 image.set(std::numeric_limits<float>::max());
                 raster_nodata = band->GetNoDataValue(&raster_has_nodata);
                 raster_io_error = band->RasterIO(GF_Read, x_off, y_off, width, height,
@@ -256,7 +377,7 @@ feature_ptr gdal_featureset::get_feature(mapnik::query const& q)
             }
             case GDT_UInt16:
             {
-                mapnik::image_gray16 image(width, height);
+                mapnik::image_gray16 image(im_width, im_height);
                 image.set(std::numeric_limits<std::uint16_t>::max());
                 raster_nodata = band->GetNoDataValue(&raster_has_nodata);
                 raster_io_error = band->RasterIO(GF_Read, x_off, y_off, width, height,
@@ -276,7 +397,7 @@ feature_ptr gdal_featureset::get_feature(mapnik::query const& q)
             default:
             case GDT_Int16:
             {
-                mapnik::image_gray16s image(width, height);
+                mapnik::image_gray16s image(im_width, im_height);
                 image.set(std::numeric_limits<std::int16_t>::max());
                 raster_nodata = band->GetNoDataValue(&raster_has_nodata);
                 raster_io_error = band->RasterIO(GF_Read, x_off, y_off, width, height,
@@ -297,7 +418,7 @@ feature_ptr gdal_featureset::get_feature(mapnik::query const& q)
         }
         else // working with all bands
         {
-            mapnik::image_rgba8 image(width, height);
+            mapnik::image_rgba8 image(im_width, im_height);
             image.set(std::numeric_limits<std::uint32_t>::max());
             for (int i = 0; i < nbands_; ++i)
             {


=====================================
plugins/input/gdal/gdal_featureset.hpp
=====================================
--- a/plugins/input/gdal/gdal_featureset.hpp
+++ b/plugins/input/gdal/gdal_featureset.hpp
@@ -66,7 +66,8 @@ public:
                     double dx,
                     double dy,
                     boost::optional<double> const& nodata,
-                    double nodata_tolerance);
+                    double nodata_tolerance,
+                    int64_t max_image_area);
     virtual ~gdal_featureset();
     mapnik::feature_ptr next();
 
@@ -85,6 +86,7 @@ private:
     int nbands_;
     boost::optional<double> nodata_value_;
     double nodata_tolerance_;
+    int64_t max_image_area_;
     bool first_;
 };
 


=====================================
src/image.cpp
=====================================
--- a/src/image.cpp
+++ b/src/image.cpp
@@ -84,7 +84,7 @@ void buffer::swap(buffer & rhs)
     std::swap(owns_, rhs.owns_);
 }
 
-template struct MAPNIK_DECL image_dimensions<65535>;
+template struct MAPNIK_DECL image_dimensions<4294836225>;
 
 } // end ns detail
 


=====================================
src/raster_colorizer.cpp
=====================================
--- a/src/raster_colorizer.cpp
+++ b/src/raster_colorizer.cpp
@@ -129,20 +129,25 @@ void raster_colorizer::colorize(image_rgba8 & out, T const& in,
 {
     using image_type = T;
     using pixel_type = typename image_type::pixel_type;
-    // TODO: assuming in/out have the same width/height for now
-    std::uint32_t * out_data = out.data();
-    pixel_type const* in_data = in.data();
-    int len = out.width() * out.height();
-    for (int i=0; i<len; ++i)
+
+    const std::size_t width = std::min(in.width(), out.width());
+    const std::size_t height = std::min(in.height(), out.height());
+
+    for (std::size_t y = 0; y < height; ++y)
     {
-        pixel_type value = in_data[i];
-        if (nodata && (std::fabs(value - *nodata) < epsilon_))
-        {
-            out_data[i] = 0; // rgba(0,0,0,0)
-        }
-        else
+        pixel_type const * in_row = in.get_row(y);
+        image_rgba8::pixel_type * out_row = out.get_row(y);
+        for (std::size_t x = 0; x < width; ++x)
         {
-            out_data[i] = get_color(value);
+            pixel_type val = in_row[x];
+            if (nodata && (std::fabs(val - *nodata) < epsilon_))
+            {
+                out_row[x] = 0; // rgba(0,0,0,0)
+            }
+            else
+            {
+                out_row[x] = get_color(val);
+            }
         }
     }
 }


=====================================
src/text/symbolizer_helpers.cpp
=====================================
--- a/src/text/symbolizer_helpers.cpp
+++ b/src/text/symbolizer_helpers.cpp
@@ -108,7 +108,7 @@ struct apply_vertex_placement
     proj_transform const& prj_trans_;
 };
 
-template <template <typename, typename> typename GridAdapter, typename T, typename Points>
+template <template <typename, typename> class GridAdapter, typename T, typename Points>
 struct grid_placement_finder_adapter
 {
     grid_placement_finder_adapter(T dx, T dy, Points & points, double scale_factor)
@@ -560,7 +560,7 @@ void text_symbolizer_helper::init_marker() const
     finder_.set_marker(std::make_shared<marker_info>(marker, trans), bbox, unlock_image, marker_displacement);
 }
 
-template <template <typename, typename> typename GridAdapter>
+template <template <typename, typename> class GridAdapter>
 void text_symbolizer_helper::initialize_grid_points() const
 {
     for (auto const& geom : geometries_to_process_)



View it on GitLab: https://salsa.debian.org/debian-gis-team/mapnik/compare/8588c516be133d1f40176b843bd2da98df75c363...7a5c788003e1b5f7a5f1a015fecbbe382a8b479a

---
View it on GitLab: https://salsa.debian.org/debian-gis-team/mapnik/compare/8588c516be133d1f40176b843bd2da98df75c363...7a5c788003e1b5f7a5f1a015fecbbe382a8b479a
You're receiving this email because of your account on salsa.debian.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.alioth.debian.org/pipermail/pkg-grass-devel/attachments/20180412/50e8f3e2/attachment-0001.html>


More information about the Pkg-grass-devel mailing list