// Copyright (c) 2016 INRIA Sophia-Antipolis (France). // All rights reserved. // // This file is part of CGAL (www.cgal.org). // You can redistribute it and/or modify it under the terms of the GNU // General Public License as published by the Free Software Foundation, // either version 3 of the License, or (at your option) any later version. // // Licensees holding a valid commercial license may use this file in // accordance with the commercial license agreement provided with the software. // // This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE // WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. // // $URL$ // $Id$ // // Author(s) : Simon Giraudot #ifndef CGAL_POINT_SET_CLASSIFIER_H #define CGAL_POINT_SET_CLASSIFIER_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace CGAL { /*! \ingroup PkgClassification \brief Classifies a point set based on a set of attributes and a set of classification types. This class specializes `Classifier` to point sets. It takes care of generating necessary data structures and automatically generate a set of generic attributes. Attributes can be generated at multiple scales to increase the reliability of the classification. \tparam Geom_traits model of \cgal Kernel. \tparam PointRange model of `ConstRange`. Its iterator type is `RandomAccessIterator`. \tparam PointMap model of `ReadablePropertyMap` whose key type is the value type of the iterator of `PointRange` and value type is `Geom_traits::Point_3`. \tparam DiagonalizeTraits model of `DiagonalizeTraits` used for matrix diagonalization. */ template > class Point_set_classifier : public Classifier { public: typedef typename Geom_traits::Iso_cuboid_3 Iso_cuboid_3; /// \cond SKIP_IN_MANUAL typedef Classifier Base; typedef typename PointRange::const_iterator Iterator; using Base::m_input; using Base::m_item_map; /// \endcond typedef Classification::Planimetric_grid Planimetric_grid; typedef Classification::Point_set_neighborhood Neighborhood; typedef Classification::Local_eigen_analysis Local_eigen_analysis; /// \cond SKIP_IN_MANUAL typedef Classification::Attribute_handle Attribute_handle; typedef Classification::Type Type; typedef Classification::Type_handle Type_handle; typedef typename Geom_traits::Point_3 Point; typedef Classification::Attribute::Anisotropy Anisotropy; typedef Classification::Attribute::Distance_to_plane Distance_to_plane; typedef Classification::Attribute::Eigentropy Eigentropy; typedef Classification::Attribute::Elevation Elevation; typedef Classification::Attribute::Linearity Linearity; typedef Classification::Attribute::Omnivariance Omnivariance; typedef Classification::Attribute::Planarity Planarity; typedef Classification::Attribute::Sphericity Sphericity; typedef Classification::Attribute::Sum_eigenvalues Sum_eigen; typedef Classification::Attribute::Surface_variation Surface_variation; typedef Classification::Attribute::Vertical_dispersion Dispersion; typedef Classification::Attribute::Verticality Verticality; typedef typename Classification::RGB_Color RGB_Color; /// \endcond private: struct Scale { Neighborhood* neighborhood; Planimetric_grid* grid; Local_eigen_analysis* eigen; double voxel_size; std::vector attributes; Scale (const PointRange& input, PointMap point_map, const Iso_cuboid_3& bbox, double voxel_size) : voxel_size (voxel_size) { CGAL::Timer t; t.start(); if (voxel_size < 0.) neighborhood = new Neighborhood (input, point_map); else neighborhood = new Neighborhood (input, point_map, voxel_size); t.stop(); if (voxel_size < 0.) std::cerr << "Neighborhood computed in " << t.time() << " second(s)" << std::endl; else std::cerr << "Neighborhood with voxel size " << voxel_size << " computed in " << t.time() << " second(s)" << std::endl; t.reset(); t.start(); eigen = new Local_eigen_analysis (input, point_map, neighborhood->k_neighbor_query(6)); double range = eigen->mean_range(); if (this->voxel_size < 0) this->voxel_size = range / 3; t.stop(); std::cerr << "Eigen values computed in " << t.time() << " second(s)" << std::endl; std::cerr << "Range = " << range << std::endl; t.reset(); t.start(); grid = new Planimetric_grid (input, point_map, bbox, this->voxel_size); t.stop(); std::cerr << "Planimetric grid computed in " << t.time() << " second(s)" << std::endl; t.reset(); } ~Scale() { delete neighborhood; delete grid; delete eigen; } double grid_resolution() const { return voxel_size; } double radius_neighbors() const { return voxel_size * 5; } double radius_dtm() const { return voxel_size * 100; } }; Iso_cuboid_3 m_bbox; std::vector m_scales; public: /// \name Constructor /// @{ /*! \brief Initializes a classification object. \param input input range. \param point_map property map to access the input points. */ Point_set_classifier(const PointRange& input, PointMap point_map) : Base (input, point_map) { m_bbox = CGAL::bounding_box (boost::make_transform_iterator (m_input.begin(), CGAL::Property_map_to_unary_function(m_item_map)), boost::make_transform_iterator (m_input.end(), CGAL::Property_map_to_unary_function(m_item_map))); } /// @} /// \cond SKIP_IN_MANUAL virtual ~Point_set_classifier() { clear(); } /// \endcond /// \name Attributes /// @{ /*! \brief Generate all possible attributes from an input range. The size of the smallest scale is automatically estimated and the data structures needed (`Neighborhood`, `Planimetric_grid` and `Local_eigen_analysis`) are computed at `nb_scales` recursively larger scales. At each scale, the following attributes are generated: - `CGAL::Classification::Attribute::Anisotropy` - `CGAL::Classification::Attribute::Distance_to_plane` - `CGAL::Classification::Attribute::Eigentropy` - `CGAL::Classification::Attribute::Elevation` - `CGAL::Classification::Attribute::Linearity` - `CGAL::Classification::Attribute::Omnivariance` - `CGAL::Classification::Attribute::Planarity` - `CGAL::Classification::Attribute::Sphericity` - `CGAL::Classification::Attribute::Sum_eigenvalues` - `CGAL::Classification::Attribute::Surface_variation` - `CGAL::Classification::Attribute::Vertical_dispersion` based on eigenvalues If normal vectors are provided (if `VectorMap` is different from `CGAL::Default`), the following attribute is generated at each scale: - `CGAL::Classification::Attribute::Vertical_dispersion` based on normal vectors If colors are provided (if `ColorMap` is different from `CGAL::Default`), the following attributes are generated at each scale: - 9 attributes `CGAL::Classification::Attribute::Hsv` on channel 0 (hue) with mean ranging from 0° to 360° and standard deviation of 22.5. - 5 attributes `CGAL::Classification::Attribute::Hsv` on channel 1 (saturation) with mean ranging from 0 to 100 and standard deviation of 12.5. - 5 attributes `CGAL::Classification::Attribute::Hsv` on channel 2 (value) with mean ranging from 0 to 100 and standard deviation of 12.5. If echo numbers are provided (if `EchoMap` is different from `CGAL::Default`), the following attribute is computed at each scale: - `CGAL::Classification::Attribute::Echo_scatter` \tparam VectorMap model of `ReadablePropertyMap` whose key type is the value type of the iterator of `PointRange` and value type is `Geom_traits::Vector_3`. \tparam ColorMap model of `ReadablePropertyMap` whose key type is the value type of the iterator of `PointRange` and value type is `CGAL::Classification::RGB_Color`. \tparam EchoMap model of `ReadablePropertyMap` whose key type is the value type of the iterator of `PointRange` and value type is `std::size_t`. \param nb_scales number of scales to compute. \param normal_map property map to access the normal vectors of the input points (if any). \param color_map property map to access the colors of the input points (if any). \param echo_map property map to access the echo values of the input points (if any). */ template void generate_attributes (std::size_t nb_scales, VectorMap normal_map = VectorMap(), ColorMap color_map = ColorMap(), EchoMap echo_map = EchoMap()) { typedef typename Default::Get::type Vmap; typedef typename Default::Get::type Cmap; typedef typename Default::Get::type Emap; generate_attributes_impl (nb_scales, get_parameter(normal_map), get_parameter(color_map), get_parameter(echo_map)); } /// @} /// \name Data Structures and Parameters /// @{ /*! \brief Returns the bounding box of the input point set. */ const Iso_cuboid_3& bbox() const { return m_bbox; } /*! \brief Returns the neighborhood structure at scale `scale`. \note `generate_attributes()` must have been called before calling this method. */ const Neighborhood& neighborhood(std::size_t scale = 0) const { return (*m_scales[scale]->neighborhood); } /*! \brief Returns the planimetric grid structure at scale `scale`. \note `generate_attributes()` must have been called before calling this method. */ const Planimetric_grid& grid(std::size_t scale = 0) const { return *(m_scales[scale]->grid); } /*! \brief Returns the local eigen analysis structure at scale `scale`. \note `generate_attributes()` must have been called before calling this method. */ const Local_eigen_analysis& eigen(std::size_t scale = 0) const { return *(m_scales[scale]->eigen); } /*! \brief Returns the number of scales that were computed. */ std::size_t number_of_scales() const { return m_scales.size(); } /*! \brief Returns the grid resolution at scale `scale`. This resolution is the length and width of a cell of the `Planimetric_grid` defined at this scale. \note `generate_attributes()` must have been called before calling this method. */ double grid_resolution(std::size_t scale = 0) const { return m_scales[scale]->grid_resolution(); } /*! \brief Returns the radius used for neighborhood queries at scale `scale`. This radius is the smallest radius that is relevant from a geometric point of view at this scale (that is to say that encloses a few cells of `Planimetric_grid`). \note `generate_attributes()` must have been called before calling this method. */ double radius_neighbors(std::size_t scale = 0) const { return m_scales[scale]->radius_neighbors(); } /*! \brief Returns the radius used for digital terrain modeling at scale `scale`. This radius represents the minimum size of a building at this scale. \note `generate_attributes()` must have been called before calling this method. */ double radius_dtm(std::size_t scale = 0) const { return m_scales[scale]->radius_dtm(); } /*! \brief Clears all computed data structures. */ void clear() { for (std::size_t i = 0; i < m_scales.size(); ++ i) delete m_scales[i]; m_scales.clear(); this->clear_classification_types(); this->clear_attributes(); } /// @} /// @} /// \cond SKIP_IN_MANUAL void info() const { std::cerr << m_scales.size() << " scale(s) used:" << std::endl; for (std::size_t i = 0; i < m_scales.size(); ++ i) { std::size_t nb_useful = 0; for (std::size_t j = 0; j < m_scales[i]->attributes.size(); ++ j) if (m_scales[i]->attributes[j]->weight() != 0.) nb_useful ++; std::cerr << " * scale " << i << " with size " << m_scales[i]->voxel_size << ", " << nb_useful << " useful attribute(s)"; if (nb_useful != 0) std::cerr << ":" << std::endl; else std::cerr << std::endl; for (std::size_t j = 0; j < m_scales[i]->attributes.size(); ++ j) if (m_scales[i]->attributes[j]->weight() != 0.) std::cerr << " - " << m_scales[i]->attributes[j]->name() << " (weight = " << m_scales[i]->attributes[j]->weight() << ")" << std::endl; } } void generate_point_based_attributes () { CGAL::Timer teigen, tpoint; teigen.start(); generate_multiscale_attribute_variant_0 (); generate_multiscale_attribute_variant_1 (); generate_multiscale_attribute_variant_0 (); generate_multiscale_attribute_variant_0 (); generate_multiscale_attribute_variant_0 (); generate_multiscale_attribute_variant_0 (); generate_multiscale_attribute_variant_0 (); generate_multiscale_attribute_variant_0 (); generate_multiscale_attribute_variant_0 (); teigen.stop(); tpoint.start(); generate_multiscale_attribute_variant_2 (); generate_multiscale_attribute_variant_3 (); tpoint.stop(); std::cerr << "Point based attributes computed in " << tpoint.time() << " second(s)" << std::endl; std::cerr << "Eigen based attributes computed in " << teigen.time() << " second(s)" << std::endl; } template void generate_normal_based_attributes(VectorMap normal_map) { CGAL::Timer t; t.start(); this->template add_attribute (normal_map); m_scales[0]->attributes.push_back (this->get_attribute (this->number_of_attributes() - 1)); t.stop(); std::cerr << "Normal based attributes computed in " << t.time() << " second(s)" << std::endl; } void generate_normal_based_attributes(const CGAL::Default_property_map&) { CGAL::Timer t; t.start(); generate_multiscale_attribute_variant_0 (); std::cerr << "Normal based attributes computed in " << t.time() << " second(s)" << std::endl; } template void generate_color_based_attributes(ColorMap color_map) { typedef Classification::Attribute::Hsv Hsv; CGAL::Timer t; t.start(); for (std::size_t i = 0; i <= 8; ++ i) { this->template add_attribute (color_map, 0, 45 * i, 22.5); m_scales[0]->attributes.push_back (this->get_attribute (this->number_of_attributes() - 1)); } for (std::size_t i = 0; i <= 4; ++ i) { this->template add_attribute (color_map, 1, 25 * i, 12.5); m_scales[0]->attributes.push_back (this->get_attribute (this->number_of_attributes() - 1)); } for (std::size_t i = 0; i <= 4; ++ i) { this->template add_attribute (color_map, 2, 25 * i, 12.5); m_scales[0]->attributes.push_back (this->get_attribute (this->number_of_attributes() - 1)); } t.stop(); std::cerr << "Color based attributes computed in " << t.time() << " second(s)" << std::endl; } void generate_color_based_attributes(const CGAL::Default_property_map&) { } template void generate_echo_based_attributes(EchoMap echo_map) { typedef Classification::Attribute::Echo_scatter Echo_scatter; CGAL::Timer t; t.start(); for (std::size_t i = 0; i < m_scales.size(); ++ i) { this->template add_attribute(echo_map, *(m_scales[i]->grid), m_scales[i]->grid_resolution(), m_scales[i]->radius_neighbors()); m_scales[i]->attributes.push_back (this->get_attribute (this->number_of_attributes() - 1)); } t.stop(); std::cerr << "Echo based attributes computed in " << t.time() << " second(s)" << std::endl; } void generate_echo_based_attributes(const CGAL::Default_property_map&) { } void get_map_scale (std::map& map_scale) { for (std::size_t i = 0; i < m_scales.size(); ++ i) for (std::size_t j = 0; j < m_scales[i]->attributes.size(); ++ j) map_scale[m_scales[i]->attributes[j]] = i; } std::size_t scale_of_attribute (Attribute_handle att) { for (std::size_t i = 0; i < m_scales.size(); ++ i) for (std::size_t j = 0; j < m_scales[i]->attributes.size(); ++ j) if (m_scales[i]->attributes[j] == att) return i; return (std::size_t)(-1); } std::string name_att (Attribute_handle att, std::map& map_scale) { std::ostringstream oss; oss << att->name() << "_" << map_scale[att]; return oss.str(); } /// \endcond /// \name Input/Output /// @{ /*! \brief Saves the current configuration in the stream `output`. This allows to easily save and recover a specific classification configuration, that is to say: - The size of the smallest scale - The attributes and their respective weights - The classification types and the effects of the attributes on them The output file is written in an XML format that is readable by the `load_configuration()` method. */ void save_configuration (std::ostream& output) { boost::property_tree::ptree tree; // tree.put("classification.parameters.grid_resolution", m_grid_resolution); // tree.put("classification.parameters.radius_neighbors", m_radius_neighbors); tree.put("classification.parameters.voxel_size", m_scales[0]->voxel_size); std::map map_scale; get_map_scale (map_scale); for (std::size_t i = 0; i < this->number_of_attributes(); ++ i) { Attribute_handle att = this->get_attribute(i); if (att->weight() == 0) continue; boost::property_tree::ptree ptr; ptr.put("id", name_att (att, map_scale)); ptr.put("weight", att->weight()); tree.add_child("classification.attributes.attribute", ptr); } for (std::size_t i = 0; i < this->number_of_classification_types(); ++ i) { Type_handle type = this->get_classification_type(i); boost::property_tree::ptree ptr; ptr.put("id", type->name()); for (std::size_t j = 0; j < this->number_of_attributes(); ++ j) { Attribute_handle att = this->get_attribute(j); if (att->weight() == 0) continue; boost::property_tree::ptree ptr2; ptr2.put("id", name_att (att, map_scale)); Classification::Attribute::Effect effect = type->attribute_effect(att); if (effect == Classification::Attribute::PENALIZING) ptr2.put("effect", "penalized"); else if (effect == Classification::Attribute::NEUTRAL) ptr2.put("effect", "neutral"); else if (effect == Classification::Attribute::FAVORING) ptr2.put("effect", "favored"); ptr.add_child("attribute", ptr2); } tree.add_child("classification.types.type", ptr); } // Write property tree to XML file boost::property_tree::xml_writer_settings settings(' ', 3); boost::property_tree::write_xml(output, tree, settings); } /*! \brief Loads a configuration from the stream `input`. All data structures, attributes and types specified in the input stream `input` are instantiated if possible (in particular, property maps needed should be provided), similarly to what is done in `generate_attributes()`. The input file should be in the XML format written by the `save_configuration()` method. \tparam VectorMap model of `ReadablePropertyMap` whose key type is the value type of the iterator of `PointRange` and value type is `Geom_traits::Vector_3`. \tparam ColorMap model of `ReadablePropertyMap` whose key type is the value type of the iterator of `PointRange` and value type is `CGAL::Classification::RGB_Color`. \tparam EchoMap model of `ReadablePropertyMap` whose key type is the value type of the iterator of `PointRange` and value type is `std::size_t`. \param input input stream. \param normal_map property map to access the normal vectors of the input points (if any). \param color_map property map to access the colors of the input points (if any). \param echo_map property map to access the echo values of the input points (if any). */ template bool load_configuration (std::istream& input, VectorMap normal_map = VectorMap(), ColorMap color_map = ColorMap(), EchoMap echo_map = EchoMap()) { typedef typename Default::Get::type Vmap; typedef typename Default::Get::type Cmap; typedef typename Default::Get::type Emap; return load_configuration_impl (input, get_parameter(normal_map), get_parameter(color_map), get_parameter(echo_map)); } /*! \brief Writes the classified point set in a colored and labeled PLY format in the stream `output`. The input points are written in a PLY format with the addition of the following PLY properties: - a property `label` that indicates which classification type is assigned to the point. The types are indexed from 0 to N (the correspondancy is given as comments in the PLY header). - 3 properties `red`, `green` and `blue` to associate each label to a color (this is useful to visualize the classification in a viewer that supports PLY colors). Colors are picked randomly. */ void write_classification_to_ply (std::ostream& output) { output << "ply" << std::endl << "format ascii 1.0" << std::endl << "comment Generated by the CGAL library www.cgal.org" << std::endl << "element vertex " << m_input.size() << std::endl << "property double x" << std::endl << "property double y" << std::endl << "property double z" << std::endl << "property uchar red" << std::endl << "property uchar green" << std::endl << "property uchar blue" << std::endl << "property int label" << std::endl; std::vector colors; std::map map_types; output << "comment label -1 is (unclassified)" << std::endl; for (std::size_t i = 0; i < this->number_of_classification_types(); ++ i) { map_types.insert (std::make_pair (this->get_classification_type(i), i)); output << "comment label " << i << " is " << this->get_classification_type(i)->name() << std::endl; RGB_Color c = {{ (unsigned char)(64 + rand() % 128), (unsigned char)(64 + rand() % 128), (unsigned char)(64 + rand() % 128) }}; colors.push_back (c); } map_types.insert (std::make_pair (Type_handle(), this->number_of_classification_types())); output << "end_header" << std::endl; std::size_t i = 0; for (Iterator it = m_input.begin(); it != m_input.end(); ++ it) { Type_handle t = this->classification_type_of(i); std::size_t idx = map_types[t]; if (idx == this->number_of_classification_types()) output << get(m_item_map, *it) << " 0 0 0 -1" << std::endl; else output << get(m_item_map, *it) << " " << (int)(colors[idx][0]) << " " << (int)(colors[idx][1]) << " " << (int)(colors[idx][2]) << " " << idx << std::endl; ++ i; } } /// @} private: template const T& get_parameter (const T& t) { return t; } template Default_property_map get_parameter (const Default&) { return Default_property_map(); } template void generate_attributes_impl (std::size_t nb_scales, VectorMap normal_map, ColorMap color_map, EchoMap echo_map) { CGAL::Timer t; t.start(); m_scales.reserve (nb_scales); double voxel_size = - 1.; m_scales.push_back (new Scale (m_input, m_item_map, m_bbox, voxel_size)); voxel_size = m_scales[0]->grid_resolution(); for (std::size_t i = 1; i < nb_scales; ++ i) { voxel_size *= 2; m_scales.push_back (new Scale (m_input, m_item_map, m_bbox, voxel_size)); } generate_point_based_attributes (); generate_normal_based_attributes (normal_map); generate_color_based_attributes (color_map); generate_echo_based_attributes (echo_map); } template void generate_multiscale_attribute_variant_0 () { for (std::size_t i = 0; i < m_scales.size(); ++ i) { this->template add_attribute(*(m_scales[i]->eigen)); m_scales[i]->attributes.push_back (this->get_attribute (this->number_of_attributes() - 1)); } } template void generate_multiscale_attribute_variant_1 () { for (std::size_t i = 0; i < m_scales.size(); ++ i) { this->template add_attribute(m_item_map, *(m_scales[i]->eigen)); m_scales[i]->attributes.push_back (this->get_attribute (this->number_of_attributes() - 1)); } } template void generate_multiscale_attribute_variant_2 () { for (std::size_t i = 0; i < m_scales.size(); ++ i) { this->template add_attribute(m_item_map, *(m_scales[i]->grid), m_scales[i]->grid_resolution(), m_scales[i]->radius_neighbors()); m_scales[i]->attributes.push_back (this->get_attribute (this->number_of_attributes() - 1)); } } template void generate_multiscale_attribute_variant_3 () { for (std::size_t i = 0; i < m_scales.size(); ++ i) { this->template add_attribute(m_item_map, *(m_scales[i]->grid), m_scales[i]->grid_resolution(), m_scales[i]->radius_dtm()); m_scales[i]->attributes.push_back (this->get_attribute (this->number_of_attributes() - 1)); } } template bool load_configuration_impl (std::istream& input, VectorMap normal_map, ColorMap color_map, EchoMap echo_map) { typedef Classification::Attribute::Echo_scatter Echo_scatter; typedef Classification::Attribute::Hsv Hsv; clear(); boost::property_tree::ptree tree; boost::property_tree::read_xml(input, tree); double voxel_size = tree.get("classification.parameters.voxel_size"); m_scales.push_back (new Scale (m_input, m_item_map, m_bbox, voxel_size)); CGAL::Timer t; std::map att_map; BOOST_FOREACH(boost::property_tree::ptree::value_type &v, tree.get_child("classification.attributes")) { std::string full_id = v.second.get("id"); std::vector splitted_id; boost::split(splitted_id, full_id, boost::is_any_of("_")); std::string id = splitted_id[0]; for (std::size_t i = 1; i < splitted_id.size() - 1; ++ i) id = id + "_" + splitted_id[i]; std::size_t scale = std::atoi (splitted_id.back().c_str()); while (m_scales.size() <= scale) { voxel_size *= 2; m_scales.push_back (new Scale (m_input, m_item_map, m_bbox, voxel_size)); } double weight = v.second.get("weight"); // Generate the right attribute if possible if (id == "anisotropy") this->template add_attribute(*(m_scales[scale]->eigen)); else if (id == "distance_to_plane") this->template add_attribute(m_item_map, *(m_scales[scale]->eigen)); else if (id == "eigentropy") this->template add_attribute(*(m_scales[scale]->eigen)); else if (id == "elevation") { t.start(); this->template add_attribute(m_item_map, *(m_scales[scale]->grid), m_scales[scale]->grid_resolution(), m_scales[scale]->radius_dtm()); t.stop(); } else if (id == "linearity") this->template add_attribute(*(m_scales[scale]->eigen)); else if (id == "omnivariance") this->template add_attribute(*(m_scales[scale]->eigen)); else if (id == "planarity") this->template add_attribute(*(m_scales[scale]->eigen)); else if (id == "sphericity") this->template add_attribute(*(m_scales[scale]->eigen)); else if (id == "sum_eigen") this->template add_attribute(*(m_scales[scale]->eigen)); else if (id == "surface_variation") this->template add_attribute(*(m_scales[scale]->eigen)); else if (id == "vertical_dispersion") this->template add_attribute(m_item_map, *(m_scales[scale]->grid), m_scales[scale]->grid_resolution(), m_scales[scale]->radius_neighbors()); else if (id == "verticality") { if (boost::is_convertible >::value) this->template add_attribute(*(m_scales[scale]->eigen)); else this->template add_attribute(normal_map); } else if (id == "echo_scatter") { if (boost::is_convertible >::value) { std::cerr << "Warning: echo_scatter required but no echo map given." << std::endl; continue; } this->template add_attribute(echo_map, *(m_scales[scale]->grid), m_scales[scale]->grid_resolution(), m_scales[scale]->radius_neighbors()); } else if (boost::starts_with(id.c_str(), "hue") || boost::starts_with(id.c_str(), "saturation") || boost::starts_with(id.c_str(), "value")) { if (boost::is_convertible >::value) { std::cerr << "Warning: color attribute required but no color map given." << std::endl; continue; } if (boost::starts_with(id.c_str(), "hue")) { double value = boost::lexical_cast(id.c_str() + 4); this->template add_attribute(color_map, 0, value, 22.5); } else if (boost::starts_with(id.c_str(), "saturation")) { double value = boost::lexical_cast(id.c_str() + 11); this->template add_attribute(color_map, 1, value, 12.5); } else if (boost::starts_with(id.c_str(), "value")) { double value = boost::lexical_cast(id.c_str() + 6); this->template add_attribute(color_map, 2, value, 12.5); } } else { std::cerr << "Warning: unknown attribute \"" << id << "\"" << std::endl; continue; } Attribute_handle att = this->get_attribute (this->number_of_attributes() - 1); m_scales[scale]->attributes.push_back (att); att->set_weight(weight); att_map[full_id] = att; } std::cerr << "Elevation took " << t.time() << " second(s)" << std::endl; BOOST_FOREACH(boost::property_tree::ptree::value_type &v, tree.get_child("classification.types")) { std::string type_id = v.second.get("id"); Type_handle new_type = this->add_classification_type (type_id.c_str()); BOOST_FOREACH(boost::property_tree::ptree::value_type &v2, v.second) { if (v2.first == "id") continue; std::string att_id = v2.second.get("id"); std::map::iterator it = att_map.find(att_id); if (it == att_map.end()) continue; Attribute_handle att = it->second; std::string effect = v2.second.get("effect"); if (effect == "penalized") new_type->set_attribute_effect (att, Classification::Attribute::PENALIZING); else if (effect == "neutral") new_type->set_attribute_effect (att, Classification::Attribute::NEUTRAL); else new_type->set_attribute_effect (att, Classification::Attribute::FAVORING); } } return true; } }; } // namespace CGAL #endif // CGAL_POINT_SET_CLASSIFIER_H