<commit_before>/*****************************************************************************
 *
 * This file is part of Mapnik (c++ mapping toolkit)
 *
 * Copyright (C) 2015 Artem Pavlenko
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *****************************************************************************/

#ifndef MAPNIK_LABEL_COLLISION_DETECTOR_HPP
#define MAPNIK_LABEL_COLLISION_DETECTOR_HPP

// mapnik
#include <mapnik/quad_tree.hpp>
#include <mapnik/util/noncopyable.hpp>
#include <mapnik/value_types.hpp>

// icu
#include <unicode/unistr.h>

// stl
#include <vector>

namespace mapnik
{
//this needs to be tree structure
//as a proof of a concept _only_ we use sequential scan

struct label_collision_detector
{
    using label_placements = std::vector<box2d<double> >;

    bool has_placement(box2d<double> const& box)
    {
        for (auto const& label : labels_)
        {
            if (label.intersects(box)) return false;
        }
        labels_.push_back(box);
        return true;
    }
    void clear()
    {
        labels_.clear();
    }

private:

    label_placements labels_;
};

// quad_tree based label collision detector
class label_collision_detector2 : util::noncopyable
{
    using tree_t = quad_tree<box2d<double> >;
    tree_t tree_;
public:

    explicit label_collision_detector2(box2d<double> const& extent)
        : tree_(extent) {}

    bool has_placement(box2d<double> const& box)
    {
        tree_t::query_iterator itr = tree_.query_in_box(box);
        tree_t::query_iterator end = tree_.query_end();
        for ( ;itr != end; ++itr)
        {
            if (itr->get().intersects(box)) return false;
        }
        tree_.insert(box,box);
        return true;
    }

    void clear()
    {
        tree_.clear();
    }

};

// quad_tree based label collision detector with seperate check/insert
class label_collision_detector3 : util::noncopyable
{
    using tree_t = quad_tree< box2d<double> >;
    tree_t tree_;
public:

    explicit label_collision_detector3(box2d<double> const& extent)
        : tree_(extent) {}

    bool has_placement(box2d<double> const& box)
    {
        tree_t::query_iterator itr = tree_.query_in_box(box);
        tree_t::query_iterator end = tree_.query_end();

        for ( ;itr != end; ++itr)
        {
            if (itr->get().intersects(box)) return false;
        }
        return true;
    }

    void insert(box2d<double> const& box)
    {
        tree_.insert(box, box);
    }

    void clear()
    {
        tree_.clear();
    }
};

//quad tree based label collision detector so labels dont appear within a given distance
class label_collision_detector4 : util::noncopyable
{
public:
    struct label
    {
        label(box2d<double> const& b) : box(b), text() {}
        label(box2d<double> const& b, mapnik::value_unicode_string const& t) : box(b), text(t) {}

        box2d<double> box;
        mapnik::value_unicode_string text;
    };

private:
    using tree_t = quad_tree< label >;
    tree_t tree_;

public:
    using query_iterator = tree_t::query_iterator;

    explicit label_collision_detector4(box2d<double> const& extent)
        : tree_(extent) {}

    bool has_placement(box2d<double> const& box)
    {
        tree_t::query_iterator itr = tree_.query_in_box(box);
        tree_t::query_iterator end = tree_.query_end();

        for ( ;itr != end; ++itr)
        {
            if (itr->get().box.intersects(box)) return false;
        }

        return true;
    }

    bool has_placement(box2d<double> const& box, double margin)
    {
        box2d<double> const& margin_box = (margin > 0
                                               ? box2d<double>(box.minx() - margin, box.miny() - margin,
                                                               box.maxx() + margin, box.maxy() + margin)
                                               : box);

        tree_t::query_iterator itr = tree_.query_in_box(margin_box);
        tree_t::query_iterator end = tree_.query_end();

        for (;itr != end; ++itr)
        {
            if (itr->get().box.intersects(margin_box))
            {
                return false;
            }
        }
        return true;
    }

    bool has_placement(box2d<double> const& box, double margin, mapnik::value_unicode_string const& text, double repeat_distance)
    {
        // Don't bother with any of the repeat checking unless the repeat distance is greater than the margin
        if (repeat_distance <= margin) {
            return has_placement(box, margin);
        }

        box2d<double> repeat_box(box.minx() - repeat_distance, box.miny() - repeat_distance,
                                 box.maxx() + repeat_distance, box.maxy() + repeat_distance);

        box2d<double> const& margin_box = (margin > 0
                                               ? box2d<double>(box.minx() - margin, box.miny() - margin,
                                                               box.maxx() + margin, box.maxy() + margin)
                                               : box);

        tree_t::query_iterator itr = tree_.query_in_box(repeat_box);
        tree_t::query_iterator end = tree_.query_end();

        for ( ;itr != end; ++itr)
        {
            if (itr->get().box.intersects(margin_box) || (text == itr->get().text && itr->get().box.intersects(repeat_box)))
            {
                return false;
            }
        }

        return true;
    }

    void insert(box2d<double> const& box)
    {
        tree_.insert(label(box), box);
    }

    void insert(box2d<double> const& box, mapnik::value_unicode_string const& text)
    {
        tree_.insert(label(box, text), box);
    }

    void clear()
    {
        tree_.clear();
    }

    box2d<double> const& extent() const
    {
        return tree_.extent();
    }

    query_iterator begin() { return tree_.query_in_box(extent()); }
    query_iterator end() { return tree_.query_end(); }
};
}

#endif // MAPNIK_LABEL_COLLISION_DETECTOR_HPP
<commit_msg>do not store out of extent collision boxes (fixes #2963)<commit_after>/*****************************************************************************
 *
 * This file is part of Mapnik (c++ mapping toolkit)
 *
 * Copyright (C) 2015 Artem Pavlenko
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *****************************************************************************/

#ifndef MAPNIK_LABEL_COLLISION_DETECTOR_HPP
#define MAPNIK_LABEL_COLLISION_DETECTOR_HPP

// mapnik
#include <mapnik/quad_tree.hpp>
#include <mapnik/util/noncopyable.hpp>
#include <mapnik/value_types.hpp>

// icu
#include <unicode/unistr.h>

// stl
#include <vector>

namespace mapnik
{
//this needs to be tree structure
//as a proof of a concept _only_ we use sequential scan

struct label_collision_detector
{
    using label_placements = std::vector<box2d<double> >;

    bool has_placement(box2d<double> const& box)
    {
        for (auto const& label : labels_)
        {
            if (label.intersects(box)) return false;
        }
        labels_.push_back(box);
        return true;
    }
    void clear()
    {
        labels_.clear();
    }

private:

    label_placements labels_;
};

// quad_tree based label collision detector
class label_collision_detector2 : util::noncopyable
{
    using tree_t = quad_tree<box2d<double> >;
    tree_t tree_;
public:

    explicit label_collision_detector2(box2d<double> const& extent)
        : tree_(extent) {}

    bool has_placement(box2d<double> const& box)
    {
        tree_t::query_iterator itr = tree_.query_in_box(box);
        tree_t::query_iterator end = tree_.query_end();
        for ( ;itr != end; ++itr)
        {
            if (itr->get().intersects(box)) return false;
        }
        tree_.insert(box,box);
        return true;
    }

    void clear()
    {
        tree_.clear();
    }

};

// quad_tree based label collision detector with seperate check/insert
class label_collision_detector3 : util::noncopyable
{
    using tree_t = quad_tree< box2d<double> >;
    tree_t tree_;
public:

    explicit label_collision_detector3(box2d<double> const& extent)
        : tree_(extent) {}

    bool has_placement(box2d<double> const& box)
    {
        tree_t::query_iterator itr = tree_.query_in_box(box);
        tree_t::query_iterator end = tree_.query_end();

        for ( ;itr != end; ++itr)
        {
            if (itr->get().intersects(box)) return false;
        }
        return true;
    }

    void insert(box2d<double> const& box)
    {
        tree_.insert(box, box);
    }

    void clear()
    {
        tree_.clear();
    }
};

//quad tree based label collision detector so labels dont appear within a given distance
class label_collision_detector4 : util::noncopyable
{
public:
    struct label
    {
        label(box2d<double> const& b) : box(b), text() {}
        label(box2d<double> const& b, mapnik::value_unicode_string const& t) : box(b), text(t) {}

        box2d<double> box;
        mapnik::value_unicode_string text;
    };

private:
    using tree_t = quad_tree< label >;
    tree_t tree_;

public:
    using query_iterator = tree_t::query_iterator;

    explicit label_collision_detector4(box2d<double> const& extent)
        : tree_(extent) {}

    bool has_placement(box2d<double> const& box)
    {
        tree_t::query_iterator itr = tree_.query_in_box(box);
        tree_t::query_iterator end = tree_.query_end();

        for ( ;itr != end; ++itr)
        {
            if (itr->get().box.intersects(box)) return false;
        }

        return true;
    }

    bool has_placement(box2d<double> const& box, double margin)
    {
        box2d<double> const& margin_box = (margin > 0
                                               ? box2d<double>(box.minx() - margin, box.miny() - margin,
                                                               box.maxx() + margin, box.maxy() + margin)
                                               : box);

        tree_t::query_iterator itr = tree_.query_in_box(margin_box);
        tree_t::query_iterator end = tree_.query_end();

        for (;itr != end; ++itr)
        {
            if (itr->get().box.intersects(margin_box))
            {
                return false;
            }
        }
        return true;
    }

    bool has_placement(box2d<double> const& box, double margin, mapnik::value_unicode_string const& text, double repeat_distance)
    {
        // Don't bother with any of the repeat checking unless the repeat distance is greater than the margin
        if (repeat_distance <= margin) {
            return has_placement(box, margin);
        }

        box2d<double> repeat_box(box.minx() - repeat_distance, box.miny() - repeat_distance,
                                 box.maxx() + repeat_distance, box.maxy() + repeat_distance);

        box2d<double> const& margin_box = (margin > 0
                                               ? box2d<double>(box.minx() - margin, box.miny() - margin,
                                                               box.maxx() + margin, box.maxy() + margin)
                                               : box);

        tree_t::query_iterator itr = tree_.query_in_box(repeat_box);
        tree_t::query_iterator end = tree_.query_end();

        for ( ;itr != end; ++itr)
        {
            if (itr->get().box.intersects(margin_box) || (text == itr->get().text && itr->get().box.intersects(repeat_box)))
            {
                return false;
            }
        }

        return true;
    }

    void insert(box2d<double> const& box)
    {
        if (tree_.extent().intersects(box))
        {
            tree_.insert(label(box), box);
        }
    }

    void insert(box2d<double> const& box, mapnik::value_unicode_string const& text)
    {
        if (tree_.extent().intersects(box))
        {
            tree_.insert(label(box, text), box);
        }
    }

    void clear()
    {
        tree_.clear();
    }

    box2d<double> const& extent() const
    {
        return tree_.extent();
    }

    query_iterator begin() { return tree_.query_in_box(extent()); }
    query_iterator end() { return tree_.query_end(); }
};
}

#endif // MAPNIK_LABEL_COLLISION_DETECTOR_HPP
<|endoftext|>


<commit_before>// This file is distributed under the MIT license.
// See the LICENSE file for details.

#include <algorithm>
#include <cstring>

#include <visionaray/gl/compositing.h>
#include <visionaray/aligned_vector.h>

#include "color_conversion.h"

namespace visionaray
{

//-------------------------------------------------------------------------------------------------
// Private implementation
//

template <pixel_format ColorFormat, pixel_format DepthFormat>
struct cpu_buffer_rt<ColorFormat, DepthFormat>::impl
{
    impl() : compositor(nullptr) {}

    std::unique_ptr<gl::depth_compositor>   compositor;

    aligned_vector<color_type>              color_buffer;
    aligned_vector<depth_type>              depth_buffer;
};

//-------------------------------------------------------------------------------------------------
// cpu_buffer_rt
//

template <pixel_format ColorFormat, pixel_format DepthFormat>
cpu_buffer_rt<ColorFormat, DepthFormat>::cpu_buffer_rt()
    : impl_(new impl)
{
}

template <pixel_format ColorFormat, pixel_format DepthFormat>
cpu_buffer_rt<ColorFormat, DepthFormat>::~cpu_buffer_rt() = default;

template <pixel_format ColorFormat, pixel_format DepthFormat>
typename cpu_buffer_rt<ColorFormat, DepthFormat>::color_type* cpu_buffer_rt<ColorFormat, DepthFormat>::color()
{
    return impl_->color_buffer.data();
}

template <pixel_format ColorFormat, pixel_format DepthFormat>
typename cpu_buffer_rt<ColorFormat, DepthFormat>::depth_type* cpu_buffer_rt<ColorFormat, DepthFormat>::depth()
{
    return impl_->depth_buffer.data();
}

template <pixel_format ColorFormat, pixel_format DepthFormat>
typename cpu_buffer_rt<ColorFormat, DepthFormat>::color_type const* cpu_buffer_rt<ColorFormat, DepthFormat>::color() const
{
    return impl_->color_buffer.data();
}

template <pixel_format ColorFormat, pixel_format DepthFormat>
typename cpu_buffer_rt<ColorFormat, DepthFormat>::depth_type const* cpu_buffer_rt<ColorFormat, DepthFormat>::depth() const
{
    return impl_->depth_buffer.data();
}

template <pixel_format ColorFormat, pixel_format DepthFormat>
typename cpu_buffer_rt<ColorFormat, DepthFormat>::ref_type cpu_buffer_rt<ColorFormat, DepthFormat>::ref()
{
#if defined(__HCC__)
    // TODO: check why aggregate initialization doesn't work with hcc here
    typename cpu_buffer_rt<ColorFormat, DepthFormat>::ref_type result;
    result.color_  = color();
    result.depth_  = depth();
    result.width_  = width();
    result.height_ = height();
    return result;
#else
    return { color(), depth(), width(), height() };
#endif
}

template <pixel_format ColorFormat, pixel_format DepthFormat>
void cpu_buffer_rt<ColorFormat, DepthFormat>::clear_color_buffer(vec4 const& c)
{
    // Convert from RGBA32F to internal color format
    color_type cc;
    convert(
        pixel_format_constant<ColorFormat>{},
        pixel_format_constant<PF_RGBA32F>{},
        cc,
        c
        );

    std::fill(impl_->color_buffer.begin(), impl_->color_buffer.end(), cc);
}

template <pixel_format ColorFormat, pixel_format DepthFormat>
void cpu_buffer_rt<ColorFormat, DepthFormat>::clear_depth_buffer(float d)
{
    // Convert from DEPTH32F to internal depth format
    depth_type dd;
    convert(
        pixel_format_constant<DepthFormat>{},
        pixel_format_constant<PF_DEPTH32F>{},
        dd,
        d
        );

    std::fill(impl_->depth_buffer.begin(), impl_->depth_buffer.end(), dd);
}

template <pixel_format ColorFormat, pixel_format DepthFormat>
void cpu_buffer_rt<ColorFormat, DepthFormat>::begin_frame()
{
}

template <pixel_format ColorFormat, pixel_format DepthFormat>
void cpu_buffer_rt<ColorFormat, DepthFormat>::end_frame()
{
}

template <pixel_format ColorFormat, pixel_format DepthFormat>
void cpu_buffer_rt<ColorFormat, DepthFormat>::resize(int w, int h)
{
    render_target::resize(w, h);

    // Allocate storage

    impl_->color_buffer.resize(w * h);

    if (DepthFormat != PF_UNSPECIFIED)
    {
        impl_->depth_buffer.resize(w * h);
    }

    if (!impl_->compositor)
    {
        impl_->compositor.reset(new gl::depth_compositor);
    }

    // Allocate texture storage

    pixel_format_info cinfo = map_pixel_format(ColorFormat);

    impl_->compositor->setup_color_texture(cinfo, w, h);

    if (DepthFormat != PF_UNSPECIFIED)
    {
        pixel_format_info dinfo = map_pixel_format(DepthFormat);

        impl_->compositor->setup_depth_texture(dinfo, w, h);
    }
}

template <pixel_format ColorFormat, pixel_format DepthFormat>
void cpu_buffer_rt<ColorFormat, DepthFormat>::display_color_buffer() const
{
    if (DepthFormat != PF_UNSPECIFIED)
    {
        // Update color texture

        pixel_format_info cinfo = map_pixel_format(ColorFormat);

        impl_->compositor->update_color_texture(
                cinfo,
                width(),
                height(),
                impl_->color_buffer.data()
                );

        // Update depth texture

        pixel_format_info dinfo = map_pixel_format(DepthFormat);

        impl_->compositor->update_depth_texture(
                dinfo,
                width(),
                height(),
                impl_->depth_buffer.data()
                );

        // Combine textures using a shader

        impl_->compositor->composite_textures();
    }
    else
    {
        pixel_format_info cinfo = map_pixel_format(ColorFormat);

        impl_->compositor->update_color_texture(
                cinfo,
                width(),
                height(),
                impl_->color_buffer.data()
                );

        impl_->compositor->display_color_texture();
    }
}

} // visionaray
<commit_msg>Unused include<commit_after>// This file is distributed under the MIT license.
// See the LICENSE file for details.

#include <algorithm>

#include <visionaray/gl/compositing.h>
#include <visionaray/aligned_vector.h>

#include "color_conversion.h"

namespace visionaray
{

//-------------------------------------------------------------------------------------------------
// Private implementation
//

template <pixel_format ColorFormat, pixel_format DepthFormat>
struct cpu_buffer_rt<ColorFormat, DepthFormat>::impl
{
    impl() : compositor(nullptr) {}

    std::unique_ptr<gl::depth_compositor>   compositor;

    aligned_vector<color_type>              color_buffer;
    aligned_vector<depth_type>              depth_buffer;
};

//-------------------------------------------------------------------------------------------------
// cpu_buffer_rt
//

template <pixel_format ColorFormat, pixel_format DepthFormat>
cpu_buffer_rt<ColorFormat, DepthFormat>::cpu_buffer_rt()
    : impl_(new impl)
{
}

template <pixel_format ColorFormat, pixel_format DepthFormat>
cpu_buffer_rt<ColorFormat, DepthFormat>::~cpu_buffer_rt() = default;

template <pixel_format ColorFormat, pixel_format DepthFormat>
typename cpu_buffer_rt<ColorFormat, DepthFormat>::color_type* cpu_buffer_rt<ColorFormat, DepthFormat>::color()
{
    return impl_->color_buffer.data();
}

template <pixel_format ColorFormat, pixel_format DepthFormat>
typename cpu_buffer_rt<ColorFormat, DepthFormat>::depth_type* cpu_buffer_rt<ColorFormat, DepthFormat>::depth()
{
    return impl_->depth_buffer.data();
}

template <pixel_format ColorFormat, pixel_format DepthFormat>
typename cpu_buffer_rt<ColorFormat, DepthFormat>::color_type const* cpu_buffer_rt<ColorFormat, DepthFormat>::color() const
{
    return impl_->color_buffer.data();
}

template <pixel_format ColorFormat, pixel_format DepthFormat>
typename cpu_buffer_rt<ColorFormat, DepthFormat>::depth_type const* cpu_buffer_rt<ColorFormat, DepthFormat>::depth() const
{
    return impl_->depth_buffer.data();
}

template <pixel_format ColorFormat, pixel_format DepthFormat>
typename cpu_buffer_rt<ColorFormat, DepthFormat>::ref_type cpu_buffer_rt<ColorFormat, DepthFormat>::ref()
{
#if defined(__HCC__)
    // TODO: check why aggregate initialization doesn't work with hcc here
    typename cpu_buffer_rt<ColorFormat, DepthFormat>::ref_type result;
    result.color_  = color();
    result.depth_  = depth();
    result.width_  = width();
    result.height_ = height();
    return result;
#else
    return { color(), depth(), width(), height() };
#endif
}

template <pixel_format ColorFormat, pixel_format DepthFormat>
void cpu_buffer_rt<ColorFormat, DepthFormat>::clear_color_buffer(vec4 const& c)
{
    // Convert from RGBA32F to internal color format
    color_type cc;
    convert(
        pixel_format_constant<ColorFormat>{},
        pixel_format_constant<PF_RGBA32F>{},
        cc,
        c
        );

    std::fill(impl_->color_buffer.begin(), impl_->color_buffer.end(), cc);
}

template <pixel_format ColorFormat, pixel_format DepthFormat>
void cpu_buffer_rt<ColorFormat, DepthFormat>::clear_depth_buffer(float d)
{
    // Convert from DEPTH32F to internal depth format
    depth_type dd;
    convert(
        pixel_format_constant<DepthFormat>{},
        pixel_format_constant<PF_DEPTH32F>{},
        dd,
        d
        );

    std::fill(impl_->depth_buffer.begin(), impl_->depth_buffer.end(), dd);
}

template <pixel_format ColorFormat, pixel_format DepthFormat>
void cpu_buffer_rt<ColorFormat, DepthFormat>::begin_frame()
{
}

template <pixel_format ColorFormat, pixel_format DepthFormat>
void cpu_buffer_rt<ColorFormat, DepthFormat>::end_frame()
{
}

template <pixel_format ColorFormat, pixel_format DepthFormat>
void cpu_buffer_rt<ColorFormat, DepthFormat>::resize(int w, int h)
{
    render_target::resize(w, h);

    // Allocate storage

    impl_->color_buffer.resize(w * h);

    if (DepthFormat != PF_UNSPECIFIED)
    {
        impl_->depth_buffer.resize(w * h);
    }

    if (!impl_->compositor)
    {
        impl_->compositor.reset(new gl::depth_compositor);
    }

    // Allocate texture storage

    pixel_format_info cinfo = map_pixel_format(ColorFormat);

    impl_->compositor->setup_color_texture(cinfo, w, h);

    if (DepthFormat != PF_UNSPECIFIED)
    {
        pixel_format_info dinfo = map_pixel_format(DepthFormat);

        impl_->compositor->setup_depth_texture(dinfo, w, h);
    }
}

template <pixel_format ColorFormat, pixel_format DepthFormat>
void cpu_buffer_rt<ColorFormat, DepthFormat>::display_color_buffer() const
{
    if (DepthFormat != PF_UNSPECIFIED)
    {
        // Update color texture

        pixel_format_info cinfo = map_pixel_format(ColorFormat);

        impl_->compositor->update_color_texture(
                cinfo,
                width(),
                height(),
                impl_->color_buffer.data()
                );

        // Update depth texture

        pixel_format_info dinfo = map_pixel_format(DepthFormat);

        impl_->compositor->update_depth_texture(
                dinfo,
                width(),
                height(),
                impl_->depth_buffer.data()
                );

        // Combine textures using a shader

        impl_->compositor->composite_textures();
    }
    else
    {
        pixel_format_info cinfo = map_pixel_format(ColorFormat);

        impl_->compositor->update_color_texture(
                cinfo,
                width(),
                height(),
                impl_->color_buffer.data()
                );

        impl_->compositor->display_color_texture();
    }
}

} // visionaray
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2013-2014 Kajetan Swierk <k0zmo@outlook.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 */

#include "Logic/NodeType.h"
#include "Logic/NodeFactory.h"

#include <opencv2/imgproc/imgproc.hpp>

class EqualizeHistogramNodeType : public NodeType
{
public:
    ExecutionStatus execute(NodeSocketReader& reader, NodeSocketWriter& writer) override
    {
        const cv::Mat& src = reader.readSocket(0).getImageMono();
        cv::Mat& dst = writer.acquireSocket(0).getImageMono();

        if(src.rows == 0 || src.cols == 0)
            return ExecutionStatus(EStatus::Ok);

        cv::equalizeHist(src, dst);

        return ExecutionStatus(EStatus::Ok);
    }

    void configuration(NodeConfig& nodeConfig) const override
    {
        static const InputSocketConfig in_config[] = {
            { ENodeFlowDataType::ImageMono, "source", "Source", "" },
            { ENodeFlowDataType::Invalid, "", "", "" }
        };
        static const OutputSocketConfig out_config[] = {
            { ENodeFlowDataType::ImageMono, "output", "Output", "" },
            { ENodeFlowDataType::Invalid, "", "", "" }
        };

        nodeConfig.description = "";
        nodeConfig.pInputSockets = in_config;
        nodeConfig.pOutputSockets = out_config;
    }
};

class ClaheNodeType : public NodeType
{
public:
    ClaheNodeType()
        : _clipLimit(2.0)
        , _tilesGridSize(8)
    {
    }

    bool setProperty(PropertyID propId, const NodeProperty& newValue) override
    {
        switch(static_cast<pid>(propId))
        {
        case pid::ClipLimit:
            _clipLimit = newValue.toDouble();
            return true;
        case pid::TilesGridSize:
            _tilesGridSize = newValue.toInt();
            return true;
        }

        return false;
    }

    NodeProperty property(PropertyID propId) const override
    {
        switch(static_cast<pid>(propId))
        {
        case pid::ClipLimit: return _clipLimit;
        case pid::TilesGridSize: return _tilesGridSize;
        }

        return NodeProperty();
    }

    ExecutionStatus execute(NodeSocketReader& reader, NodeSocketWriter& writer) override
    {
        const cv::Mat& src = reader.readSocket(0).getImageMono();
        cv::Mat& dst = writer.acquireSocket(0).getImageMono();

        cv::Ptr<cv::CLAHE> clahe = cv::createCLAHE();
        clahe->setClipLimit(_clipLimit);
        clahe->setTilesGridSize(cv::Size(_tilesGridSize, _tilesGridSize));

        clahe->apply(src, dst);

        return ExecutionStatus(EStatus::Ok);
    }

    void configuration(NodeConfig& nodeConfig) const override
    {
        static const InputSocketConfig in_config[] = {
            { ENodeFlowDataType::ImageMono, "source", "Source", "" },
            { ENodeFlowDataType::Invalid, "", "", "" }
        };
        static const OutputSocketConfig out_config[] = {
            { ENodeFlowDataType::ImageMono, "output", "Output", "" },
            { ENodeFlowDataType::Invalid, "", "", "" }
        };
        static const PropertyConfig prop_config[] = {
            { EPropertyType::Double, "Clip limit", "min:0.0" },
            { EPropertyType::Integer, "Tiles size", "min:1" },
            { EPropertyType::Unknown, "", "" }
        };

        nodeConfig.description = "Performs CLAHE (Contrast Limited Adaptive Histogram Equalization)";
        nodeConfig.pInputSockets = in_config;
        nodeConfig.pOutputSockets = out_config;
        nodeConfig.pProperties = prop_config;
    }

private:
    enum class pid
    {
        ClipLimit,
        TilesGridSize
    };

    double _clipLimit;
    int _tilesGridSize;
};

class DrawHistogramNodeType : public NodeType
{
public:
    ExecutionStatus execute(NodeSocketReader& reader, NodeSocketWriter& writer) override
    {
        // Read input sockets
        const cv::Mat& src = reader.readSocket(0).getImage();
        // Acquire output sockets
        cv::Mat& canvas = writer.acquireSocket(0).getImageRgb();

        // Validate inputs
        if(src.empty())
            return ExecutionStatus(EStatus::Ok);

        if(src.channels() == 1)
        {
            // Compute histogram
            const int bins = 256;
            const float range[] = { 0, 256 } ;
            const float* histRange = { range };
            const bool uniform = true;
            const bool accumulate = false;
            cv::Mat hist;
            cv::calcHist(&src, 1, 0, cv::noArray(), hist, 1, &bins, &histRange, uniform, accumulate);

            // Draw histogram
            const int hist_h = 125;
            canvas = cv::Mat::zeros(hist_h, bins, CV_8UC3);
            cv::normalize(hist, hist, 0, canvas.rows, cv::NORM_MINMAX, -1, cv::noArray());

            for(int j = 0; j < bins-1; ++j)
            {
                cv::line(canvas,
                    cv::Point(j, hist_h),
                    cv::Point(j, hist_h - cvRound(hist.at<float>(j))),
                    cv::Scalar(200,200,200),
                    1, 8, 0);
            }
        }
        else if(src.channels() == 3)
        {
            // Quantization
            const int bins = 256;
            // Range of given channel (0-255 for BGR)
            const float range[] = { 0, 256 } ;
            const float* histRange = { range };
            const bool uniform = true;
            const bool accumulate = false;

            cv::Mat bhist, ghist, rhist;
            int bchannels[] = {0}, gchannels[] = {1}, rchannels[] = {2};

            cv::calcHist(&src, 1, bchannels, cv::noArray(), bhist, 1, &bins, &histRange, uniform, accumulate);
            cv::calcHist(&src, 1, gchannels, cv::noArray(), ghist, 1, &bins, &histRange, uniform, accumulate);
            cv::calcHist(&src, 1, rchannels, cv::noArray(), rhist, 1, &bins, &histRange, uniform, accumulate);

            // Draw histogram
            const int hist_h = 125;
            canvas = cv::Mat::zeros(hist_h, bins, CV_8UC3);
            cv::normalize(bhist, bhist, 0, canvas.rows, cv::NORM_MINMAX, -1, cv::noArray());
            cv::normalize(ghist, ghist, 0, canvas.rows, cv::NORM_MINMAX, -1, cv::noArray());
            cv::normalize(rhist, rhist, 0, canvas.rows, cv::NORM_MINMAX, -1, cv::noArray());

            for(int j = 1; j < bins; ++j)
            {
                cv::line(canvas,
                    cv::Point(j-1, hist_h - cvRound(bhist.at<float>(j-1))),
                    cv::Point(j,   hist_h - cvRound(bhist.at<float>(j))),
                    cv::Scalar(255, 0, 0),
                    1, 8, 0);
                cv::line(canvas,
                    cv::Point(j-1, hist_h - cvRound(ghist.at<float>(j-1))),
                    cv::Point(j,   hist_h - cvRound(ghist.at<float>(j))),
                    cv::Scalar(0, 255, 0),
                    1, 8, 0);
                cv::line(canvas,
                    cv::Point(j-1, hist_h - cvRound(rhist.at<float>(j-1))),
                    cv::Point(j,   hist_h - cvRound(rhist.at<float>(j))),
                    cv::Scalar(0, 0, 255),
                    1, 8, 0);
            }
        }

        return ExecutionStatus(EStatus::Ok);
    }

    void configuration(NodeConfig& nodeConfig) const override
    {
        static const InputSocketConfig in_config[] = {
            { ENodeFlowDataType::Image, "source", "Source", "" },
            { ENodeFlowDataType::Invalid, "", "", "" }
        };
        static const OutputSocketConfig out_config[] = {
            { ENodeFlowDataType::ImageRgb, "output", "Output", "" },
            { ENodeFlowDataType::Invalid, "", "", "" }
        };

        nodeConfig.description = "";
        nodeConfig.pInputSockets = in_config;
        nodeConfig.pOutputSockets = out_config;
    }
};

REGISTER_NODE("Histogram/CLAHE", ClaheNodeType)
REGISTER_NODE("Histogram/Equalize histogram", EqualizeHistogramNodeType)
REGISTER_NODE("Histogram/Draw histogram", DrawHistogramNodeType)
<commit_msg>updated histogram-related nodes for new node conf. interface [WIP]<commit_after>/*
 * Copyright (c) 2013-2014 Kajetan Swierk <k0zmo@outlook.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 */

#include "Logic/NodeType.h"
#include "Logic/NodeFactory.h"

#include <opencv2/imgproc/imgproc.hpp>

class EqualizeHistogramNodeType : public NodeType
{
public:
    EqualizeHistogramNodeType()
    {
        addInput("Source", ENodeFlowDataType::ImageMono);
        addOutput("Output", ENodeFlowDataType::ImageMono);
    }

    ExecutionStatus execute(NodeSocketReader& reader, NodeSocketWriter& writer) override
    {
        const cv::Mat& src = reader.readSocket(0).getImageMono();
        cv::Mat& dst = writer.acquireSocket(0).getImageMono();

        if(src.rows == 0 || src.cols == 0)
            return ExecutionStatus(EStatus::Ok);

        cv::equalizeHist(src, dst);

        return ExecutionStatus(EStatus::Ok);
    }
};

class ClaheNodeType : public NodeType
{
public:
    ClaheNodeType()
        : _clipLimit(2.0)
        , _tilesGridSize(8)
    {
        addInput("Source", ENodeFlowDataType::ImageMono);
        addOutput("Output", ENodeFlowDataType::ImageMono);
        addProperty("Clip limit", _clipLimit)
            .setValidator(make_validator<MinPropertyValidator<double>>(0.0))
            .setUiHints("min:0.0");
        addProperty("Tiles size", _tilesGridSize)
            .setValidator(make_validator<MinPropertyValidator<int>>(1))
            .setUiHints("min:1");
        setDescription("Performs CLAHE (Contrast Limited Adaptive Histogram Equalization)");
    }

    ExecutionStatus execute(NodeSocketReader& reader, NodeSocketWriter& writer) override
    {
        const cv::Mat& src = reader.readSocket(0).getImageMono();
        cv::Mat& dst = writer.acquireSocket(0).getImageMono();

        cv::Ptr<cv::CLAHE> clahe = cv::createCLAHE();
        clahe->setClipLimit(_clipLimit);
        clahe->setTilesGridSize(cv::Size(_tilesGridSize, _tilesGridSize));

        clahe->apply(src, dst);

        return ExecutionStatus(EStatus::Ok);
    }

private:
    TypedNodeProperty<double> _clipLimit;
    TypedNodeProperty<int> _tilesGridSize;
};

class DrawHistogramNodeType : public NodeType
{
public:
    DrawHistogramNodeType()
    {
        addInput("Source", ENodeFlowDataType::Image);
        addOutput("Output", ENodeFlowDataType::ImageRgb);
    }

    ExecutionStatus execute(NodeSocketReader& reader, NodeSocketWriter& writer) override
    {
        // Read input sockets
        const cv::Mat& src = reader.readSocket(0).getImage();
        // Acquire output sockets
        cv::Mat& canvas = writer.acquireSocket(0).getImageRgb();

        // Validate inputs
        if(src.empty())
            return ExecutionStatus(EStatus::Ok);

        if(src.channels() == 1)
        {
            // Compute histogram
            const int bins = 256;
            const float range[] = { 0, 256 } ;
            const float* histRange = { range };
            const bool uniform = true;
            const bool accumulate = false;
            cv::Mat hist;
            cv::calcHist(&src, 1, 0, cv::noArray(), hist, 1, &bins, &histRange, uniform, accumulate);

            // Draw histogram
            const int hist_h = 125;
            canvas = cv::Mat::zeros(hist_h, bins, CV_8UC3);
            cv::normalize(hist, hist, 0, canvas.rows, cv::NORM_MINMAX, -1, cv::noArray());

            for(int j = 0; j < bins-1; ++j)
            {
                cv::line(canvas,
                    cv::Point(j, hist_h),
                    cv::Point(j, hist_h - cvRound(hist.at<float>(j))),
                    cv::Scalar(200,200,200),
                    1, 8, 0);
            }
        }
        else if(src.channels() == 3)
        {
            // Quantization
            const int bins = 256;
            // Range of given channel (0-255 for BGR)
            const float range[] = { 0, 256 } ;
            const float* histRange = { range };
            const bool uniform = true;
            const bool accumulate = false;

            cv::Mat bhist, ghist, rhist;
            int bchannels[] = {0}, gchannels[] = {1}, rchannels[] = {2};

            cv::calcHist(&src, 1, bchannels, cv::noArray(), bhist, 1, &bins, &histRange, uniform, accumulate);
            cv::calcHist(&src, 1, gchannels, cv::noArray(), ghist, 1, &bins, &histRange, uniform, accumulate);
            cv::calcHist(&src, 1, rchannels, cv::noArray(), rhist, 1, &bins, &histRange, uniform, accumulate);

            // Draw histogram
            const int hist_h = 125;
            canvas = cv::Mat::zeros(hist_h, bins, CV_8UC3);
            cv::normalize(bhist, bhist, 0, canvas.rows, cv::NORM_MINMAX, -1, cv::noArray());
            cv::normalize(ghist, ghist, 0, canvas.rows, cv::NORM_MINMAX, -1, cv::noArray());
            cv::normalize(rhist, rhist, 0, canvas.rows, cv::NORM_MINMAX, -1, cv::noArray());

            for(int j = 1; j < bins; ++j)
            {
                cv::line(canvas,
                    cv::Point(j-1, hist_h - cvRound(bhist.at<float>(j-1))),
                    cv::Point(j,   hist_h - cvRound(bhist.at<float>(j))),
                    cv::Scalar(255, 0, 0),
                    1, 8, 0);
                cv::line(canvas,
                    cv::Point(j-1, hist_h - cvRound(ghist.at<float>(j-1))),
                    cv::Point(j,   hist_h - cvRound(ghist.at<float>(j))),
                    cv::Scalar(0, 255, 0),
                    1, 8, 0);
                cv::line(canvas,
                    cv::Point(j-1, hist_h - cvRound(rhist.at<float>(j-1))),
                    cv::Point(j,   hist_h - cvRound(rhist.at<float>(j))),
                    cv::Scalar(0, 0, 255),
                    1, 8, 0);
            }
        }

        return ExecutionStatus(EStatus::Ok);
    }
};

REGISTER_NODE("Histogram/CLAHE", ClaheNodeType)
REGISTER_NODE("Histogram/Equalize histogram", EqualizeHistogramNodeType)
REGISTER_NODE("Histogram/Draw histogram", DrawHistogramNodeType)
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  $RCSfile: xexch.hxx,v $
 *
 *  $Revision: 1.3 $
 *
 *  last change: $Author: ka $ $Date: 2001-07-02 09:58:04 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (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.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/

#ifndef _SVX_XEXCH_HXX
#define _SVX_XEXCH_HXX

#ifndef _STREAM_HXX
#include <tools/stream.hxx>
#endif

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//                Clipboard-Format fuer XOutDev-Fuellattribute
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

class XFillAttrSetItem;
class SfxItemPool;

class XFillExchangeData
{
private:
    XFillAttrSetItem*   pXFillAttrSetItem;
    SfxItemPool*        pPool;

public:
                        TYPEINFO();

                        XFillExchangeData();
                        XFillExchangeData(const XFillAttrSetItem rXFillAttrSetItem);
                        ~XFillExchangeData();

                        // Zuweisungsoperator
                        XFillExchangeData& operator=( const XFillExchangeData& rXFillExchangeData );

    friend SvStream&    operator<<( SvStream& rOStm, const XFillExchangeData& rXFillExchangeData );
    friend SvStream&    operator>>( SvStream& rIStm, XFillExchangeData& rXFillExchangeData );

    static ULONG        RegisterClipboardFormatName();
    XFillAttrSetItem*   GetXFillAttrSetItem() { return pXFillAttrSetItem; }
};

#endif                  // _SVX_XEXCH_HXX
<commit_msg>INTEGRATION: CWS visibility01 (1.3.1032); FILE MERGED 2004/12/06 08:10:58 mnicel 1.3.1032.1: Part of symbol visibility markup - #i35758#<commit_after>/*************************************************************************
 *
 *  $RCSfile: xexch.hxx,v $
 *
 *  $Revision: 1.4 $
 *
 *  last change: $Author: kz $ $Date: 2005-01-21 15:58:42 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (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.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/

#ifndef _SVX_XEXCH_HXX
#define _SVX_XEXCH_HXX

#ifndef _STREAM_HXX
#include <tools/stream.hxx>
#endif

#ifndef INCLUDED_SVXDLLAPI_H
#include "svx/svxdllapi.h"
#endif

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//                Clipboard-Format fuer XOutDev-Fuellattribute
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

class XFillAttrSetItem;
class SfxItemPool;

class SVX_DLLPUBLIC XFillExchangeData
{
private:
    XFillAttrSetItem*   pXFillAttrSetItem;
    SfxItemPool*        pPool;

public:
                        TYPEINFO();

                        XFillExchangeData();
                        XFillExchangeData(const XFillAttrSetItem rXFillAttrSetItem);
                        ~XFillExchangeData();

                        // Zuweisungsoperator
                        XFillExchangeData& operator=( const XFillExchangeData& rXFillExchangeData );

    friend SvStream&    operator<<( SvStream& rOStm, const XFillExchangeData& rXFillExchangeData );
    SVX_DLLPUBLIC friend SvStream&    operator>>( SvStream& rIStm, XFillExchangeData& rXFillExchangeData );

    static ULONG        RegisterClipboardFormatName();
    XFillAttrSetItem*   GetXFillAttrSetItem() { return pXFillAttrSetItem; }
};

#endif                  // _SVX_XEXCH_HXX
<|endoftext|>


<commit_before>/** \file    add_synonyms.cc
 *  \brief   Generic version for augmenting title data with synonyms found
             in the authority data
 *  \author  Johannes Riedl
 */

/*
    Copyright (C) 2016, Library of the University of Tübingen

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as
    published by the Free Software Foundation, either version 3 of the
    License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

/*  We offer a list of tags and subfields where the primary data resides along
    with a list of tags and subfields where the synonym data is found and
    a list of unused fields in the title data where the synonyms can be stored
*/

#include <iostream>
#include <map>
#include <vector>
#include <cstdlib>
#include "Compiler.h"
#include "FileUtil.h"
#include "MarcReader.h"
#include "MarcRecord.h"
#include "MarcWriter.h"
#include "MediaTypeUtil.h"
#include "RegexMatcher.h"
#include "StringUtil.h"
#include "Subfields.h"
#include "util.h"

static unsigned modified_count(0);
static unsigned record_count(0);

void Usage() {
    std::cerr << "Usage: " << ::progname << " master_marc_input norm_data_marc_input marc_output\n";
    std::exit(EXIT_FAILURE);
}

std::string GetTag(const std::string &tag_and_subfields_spec) {
    return tag_and_subfields_spec.substr(0, 3);
}

std::string GetSubfieldCodes(const std::string &tag_and_subfields_spec) {
    return tag_and_subfields_spec.substr(3);
}

bool FilterPasses(const MarcRecord &record, const std::map<std::string, std::pair<std::string, std::string>> &filter_specs, const std::string &field_spec) {
      auto filter_spec(filter_specs.find(field_spec));
      if (filter_spec == filter_specs.cend())
          return true;

      auto rule(filter_spec->second);
      // We have field_spec in key and rule to match in value
      std::string subfield_codes(GetSubfieldCodes(rule.first));
      if (subfield_codes.length() != 1)
         Error("Invalid subfield specification "  + subfield_codes + " for filter");

      std::string subfield_value;
      if ((subfield_value = record.extractFirstSubfield(GetTag(rule.first), subfield_codes.c_str()[0])).empty())
          return false;

      return subfield_value == rule.second;
}

void ExtractSynonyms(MarcReader * const authority_reader,
                     const std::set<std::string> &primary_tags_and_subfield_codes,
                     const std::set<std::string> &synonym_tags_and_subfield_codes,
                     std::vector<std::map<std::string, std::string>> * const synonym_maps,
                     const std::map<std::string, std::pair<std::string, std::string>> &filter_spec)
{
    while (const MarcRecord record = authority_reader->read()) {
        std::set<std::string>::const_iterator primary;
        std::set<std::string>::const_iterator synonym;
        unsigned int i(0);
        for (primary = primary_tags_and_subfield_codes.begin(), synonym = synonym_tags_and_subfield_codes.begin();
            primary != primary_tags_and_subfield_codes.end();
            ++primary, ++synonym, ++i)
        {
            // Fill maps with synonyms
            std::vector<std::string> primary_values;
            std::vector<std::string> synonym_values;

            if (FilterPasses(record, filter_spec, *primary) and
                record.extractSubfields(GetTag(*primary), GetSubfieldCodes(*primary), &primary_values) and
                record.extractSubfields(GetTag(*synonym), GetSubfieldCodes(*synonym), &synonym_values))
                    (*synonym_maps)[i].emplace(StringUtil::Join(primary_values, ','),
                                               StringUtil::Join(synonym_values, ','));
        }
    }
}

inline std::string GetMapValueOrEmptyString(const std::map<std::string, std::string> &map,
                                            const std::string &searchterm)
{
    auto value(map.find(searchterm));
    return (value != map.cend()) ? value->second : "";
}

void ProcessRecord(MarcRecord * const record, const std::vector<std::map<std::string, std::string>> &synonym_maps,
                   const std::set<std::string> &primary_tags_and_subfield_codes,
                   const std::set<std::string> &output_tags_and_subfield_codes)
{
    std::set<std::string>::const_iterator primary;
    std::set<std::string>::const_iterator output;
    unsigned int i(0);
    bool modified_record(false);

    if (primary_tags_and_subfield_codes.size() == output_tags_and_subfield_codes.size()) {
        for (primary = primary_tags_and_subfield_codes.begin(), output = output_tags_and_subfield_codes.begin();
            primary != primary_tags_and_subfield_codes.end();
            ++primary, ++output, ++i)
        {
            std::vector<std::string> primary_values;
            std::set<std::string> synonym_values;
            std::vector<size_t> field_indices;
            if (record->getFieldIndices(GetTag(*primary), &field_indices) != MarcRecord::FIELD_NOT_FOUND) {
                for (auto field_index : field_indices) {
                     primary_values.clear();
                     if (record->getSubfields(field_index).extractSubfields(GetSubfieldCodes(*primary), &primary_values)) {
                        std::string searchterm = StringUtil::Join(primary_values, ',');
                        // First case: Look up synonyms only in one category
                        if (i < synonym_maps.size()) {
                            const auto &synonym_map(synonym_maps[i]);
                            const auto synonym(GetMapValueOrEmptyString(synonym_map, searchterm));
                            if (not synonym.empty())
                                synonym_values.insert(synonym);
                        }

                        // Second case: Look up synonyms in all categories
                        else {
                            for (auto &synonym_map : synonym_maps) {
                                const auto &synonym(GetMapValueOrEmptyString(synonym_map, searchterm));
                                if (not synonym.empty())
                                   synonym_values.insert(synonym);
                            }
                        }
                    }
                 }

                if (synonym_values.empty())
                     continue;

                // Insert synonyms
                // Abort if field is already populated
                std::string tag(GetTag(*output));
                if (record->getFieldIndex(tag) != MarcRecord::FIELD_NOT_FOUND)
                    Error("Field with tag " + tag + " is not empty for PPN " + record->getControlNumber() + '\n');
                std::string subfield_spec(GetSubfieldCodes(*output));
                if (unlikely(subfield_spec.size() != 1))
                    Error("We currently only support a single subfield and thus specifying " + subfield_spec
                          + " as output subfield is not valid\n");

                std::string synonyms;
                unsigned current_length(0);
                unsigned indicator2(0);

                for (auto synonym_it(synonym_values.cbegin()); synonym_it != synonym_values.cend(); /*Intentionally empty*/) {
                    if (indicator2 > 9)
                        Error ("Currently cannot handle synonyms with total length greater than " + std::to_string(9 * MarcRecord::MAX_FIELD_LENGTH) + '\n');

                    if (current_length + synonym_it->length() < MarcRecord::MAX_FIELD_LENGTH) {
                         bool synonyms_empty(synonyms.empty());
                         synonyms += (synonyms_empty ? *synonym_it : " , " + *synonym_it);
                         current_length += synonym_it->length() + (synonyms_empty ? 0 : 3);
                         ++synonym_it;
                    } else {
                        if (not(record->insertSubfield(tag, subfield_spec[0], synonyms, '0', indicator2 + '0')))
                            Error("Could not insert field " + tag + " for PPN " + record->getControlNumber() + '\n');
                        synonyms.clear();
                        current_length = 0;
                        ++indicator2;
                        modified_record = true;
                    }
                }
                // Write rest of data
                if (not synonyms.empty()) {
                    if (not(record->insertSubfield(tag, subfield_spec[0], synonyms, '0', indicator2 + '0')))
                            Error("Could not insert field " + tag + " for PPN " + record->getControlNumber() + '\n');
                    modified_record = true;
                }
            }
        }
        if (modified_record)
            ++modified_count;
    }
}

void InsertSynonyms(MarcReader * const marc_reader, MarcWriter * const marc_writer,
                    const std::set<std::string> &primary_tags_and_subfield_codes,
                    const std::set<std::string> &output_tags_and_subfield_codes,
                    std::vector<std::map<std::string, std::string>> &synonym_maps)
{
    while (MarcRecord record = marc_reader->read()) {
        ProcessRecord(&record, synonym_maps, primary_tags_and_subfield_codes, output_tags_and_subfield_codes);
        marc_writer->write(record);
        ++record_count;
    }

    std::cerr << "Modified " << modified_count << " of " << record_count << " record(s).\n";
}

bool ParseSpec(std::string spec_str, std::set<std::string> * const field_specs, std::map<std::string, std::pair<std::string, std::string>> * filter_specs = nullptr) {
    std::set<std::string> raw_field_specs;

    if (unlikely(StringUtil::Split(spec_str, ':', &raw_field_specs) == 0)) {
        Error("Need at least one field");
        return false;
    }

    if (filter_specs == nullptr) {
        *field_specs = raw_field_specs;
	return true;
    }

    // Iterate over all Field-specs and extract possible filters
    static RegexMatcher * const matcher(RegexMatcher::RegexMatcherFactory("(\\d{1,3}[a-z]+)\\[(\\d{1,3}[a-z])=(.*)\\]"));

    for (auto field_spec : raw_field_specs) {
        if (matcher->matched(field_spec)) {
            filter_specs->emplace((*matcher)[1], std::make_pair((*matcher)[2], (*matcher)[3]));
            auto bracket = field_spec.find("[");
            field_spec = (bracket != std::string::npos) ? field_spec.erase(bracket, field_spec.length()) : field_spec;
        }
        field_specs->insert(field_spec);
    }
    return true;
}

int main(int argc, char **argv) {
    ::progname = argv[0];

    if (argc != 4)
        Usage();

    const std::string marc_input_filename(argv[1]);
    const std::string authority_data_marc_input_filename(argv[2]);
    const std::string marc_output_filename(argv[3]);
    if (unlikely(marc_input_filename == marc_output_filename))
        Error("Title data input file name equals output file name!");
    if (unlikely(authority_data_marc_input_filename == marc_output_filename))
        Error("Authority data input file name equals output file name!");

    std::unique_ptr<MarcReader> marc_reader(MarcReader::Factory(marc_input_filename, MarcReader::BINARY));
    std::unique_ptr<MarcReader> authority_reader(MarcReader::Factory(authority_data_marc_input_filename,
                                                                     MarcReader::BINARY));
    std::unique_ptr<MarcWriter> marc_writer(MarcWriter::Factory(marc_output_filename, MarcWriter::BINARY));

    try {
        // Determine possible mappings
        // Values in square brackets specify a positive criterion for values to be taken into account
        const std::string AUTHORITY_DATA_PRIMARY_SPEC("100abcd[079v=piz]:110abcd:111abcd:130abcd:150abcd:151abcd");
        const std::string AUTHORITY_DATA_SYNONYM_SPEC("400abcd:410abcd:411abcd:430abcd:450abcd:451abcd");
        const std::string TITLE_DATA_PRIMARY_SPEC("600abcd:610abcd:611abcd:630abcd:650abcd:651abcd:689abcd");
        const std::string TITLE_DATA_UNUSED_FIELDS_FOR_SYNONYMS("180a:181a:182a:183a:184a:185a:186a");

        // Determine fields to handle
        std::set<std::string> primary_tags_and_subfield_codes;
        std::set<std::string> synonym_tags_and_subfield_codes;
        std::set<std::string> input_tags_and_subfield_codes;
        std::set<std::string> output_tags_and_subfield_codes;

        std::map<std::string, std::pair<std::string, std::string>> filter_specs;

        if (not unlikely(ParseSpec(AUTHORITY_DATA_PRIMARY_SPEC, &primary_tags_and_subfield_codes, &filter_specs)))
            Error("Could not properly parse " + AUTHORITY_DATA_PRIMARY_SPEC);

        if (unlikely(StringUtil::Split(AUTHORITY_DATA_SYNONYM_SPEC, ":", &synonym_tags_and_subfield_codes) == 0))
            Error("Need at least one synonym field");

        if (unlikely(StringUtil::Split(TITLE_DATA_PRIMARY_SPEC, ":", &input_tags_and_subfield_codes) == 0))
            Error("Need at least one input field");

        if (unlikely(StringUtil::Split(TITLE_DATA_UNUSED_FIELDS_FOR_SYNONYMS, ":", &output_tags_and_subfield_codes)
                     == 0))
            Error("Need at least one output field");

        unsigned num_of_authority_entries(primary_tags_and_subfield_codes.size());

        if (synonym_tags_and_subfield_codes.size() != num_of_authority_entries)
            Error("Number of authority primary specs must match number of synonym specs");
        if (input_tags_and_subfield_codes.size() != output_tags_and_subfield_codes.size())
            Error("Number of fields title entry specs must match number of output specs");

        std::vector<std::map<std::string, std::string>> synonym_maps(num_of_authority_entries,
                                                                     std::map<std::string, std::string>());

        // Extract the synonyms from authority data
        ExtractSynonyms(authority_reader.get(), primary_tags_and_subfield_codes, synonym_tags_and_subfield_codes,
                        &synonym_maps, filter_specs);

        // Iterate over the title data
        InsertSynonyms(marc_reader.get(), marc_writer.get(), input_tags_and_subfield_codes,
                       output_tags_and_subfield_codes, synonym_maps);

    } catch (const std::exception &x) {
        Error("caught exception: " + std::string(x.what()));
    }
}
<commit_msg>Fixing constant length<commit_after>/** \file    add_synonyms.cc
 *  \brief   Generic version for augmenting title data with synonyms found
             in the authority data
 *  \author  Johannes Riedl
 */

/*
    Copyright (C) 2016, Library of the University of Tübingen

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as
    published by the Free Software Foundation, either version 3 of the
    License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

/*  We offer a list of tags and subfields where the primary data resides along
    with a list of tags and subfields where the synonym data is found and
    a list of unused fields in the title data where the synonyms can be stored
*/

#include <iostream>
#include <map>
#include <vector>
#include <cstdlib>
#include "Compiler.h"
#include "FileUtil.h"
#include "MarcReader.h"
#include "MarcRecord.h"
#include "MarcWriter.h"
#include "MediaTypeUtil.h"
#include "RegexMatcher.h"
#include "StringUtil.h"
#include "Subfields.h"
#include "util.h"

static unsigned modified_count(0);
static unsigned record_count(0);

void Usage() {
    std::cerr << "Usage: " << ::progname << " master_marc_input norm_data_marc_input marc_output\n";
    std::exit(EXIT_FAILURE);
}

std::string GetTag(const std::string &tag_and_subfields_spec) {
    return tag_and_subfields_spec.substr(0, 3);
}

std::string GetSubfieldCodes(const std::string &tag_and_subfields_spec) {
    return tag_and_subfields_spec.substr(3);
}

bool FilterPasses(const MarcRecord &record, const std::map<std::string, std::pair<std::string, std::string>> &filter_specs, const std::string &field_spec) {
      auto filter_spec(filter_specs.find(field_spec));
      if (filter_spec == filter_specs.cend())
          return true;

      auto rule(filter_spec->second);
      // We have field_spec in key and rule to match in value
      std::string subfield_codes(GetSubfieldCodes(rule.first));
      if (subfield_codes.length() != 1)
         Error("Invalid subfield specification "  + subfield_codes + " for filter");

      std::string subfield_value;
      if ((subfield_value = record.extractFirstSubfield(GetTag(rule.first), subfield_codes.c_str()[0])).empty())
          return false;

      return subfield_value == rule.second;
}

void ExtractSynonyms(MarcReader * const authority_reader,
                     const std::set<std::string> &primary_tags_and_subfield_codes,
                     const std::set<std::string> &synonym_tags_and_subfield_codes,
                     std::vector<std::map<std::string, std::string>> * const synonym_maps,
                     const std::map<std::string, std::pair<std::string, std::string>> &filter_spec)
{
    while (const MarcRecord record = authority_reader->read()) {
        std::set<std::string>::const_iterator primary;
        std::set<std::string>::const_iterator synonym;
        unsigned int i(0);
        for (primary = primary_tags_and_subfield_codes.begin(), synonym = synonym_tags_and_subfield_codes.begin();
            primary != primary_tags_and_subfield_codes.end();
            ++primary, ++synonym, ++i)
        {
            // Fill maps with synonyms
            std::vector<std::string> primary_values;
            std::vector<std::string> synonym_values;

            if (FilterPasses(record, filter_spec, *primary) and
                record.extractSubfields(GetTag(*primary), GetSubfieldCodes(*primary), &primary_values) and
                record.extractSubfields(GetTag(*synonym), GetSubfieldCodes(*synonym), &synonym_values))
                    (*synonym_maps)[i].emplace(StringUtil::Join(primary_values, ','),
                                               StringUtil::Join(synonym_values, ','));
        }
    }
}

inline std::string GetMapValueOrEmptyString(const std::map<std::string, std::string> &map,
                                            const std::string &searchterm)
{
    auto value(map.find(searchterm));
    return (value != map.cend()) ? value->second : "";
}

void ProcessRecord(MarcRecord * const record, const std::vector<std::map<std::string, std::string>> &synonym_maps,
                   const std::set<std::string> &primary_tags_and_subfield_codes,
                   const std::set<std::string> &output_tags_and_subfield_codes)
{
    std::set<std::string>::const_iterator primary;
    std::set<std::string>::const_iterator output;
    unsigned int i(0);
    bool modified_record(false);

    if (primary_tags_and_subfield_codes.size() == output_tags_and_subfield_codes.size()) {
        for (primary = primary_tags_and_subfield_codes.begin(), output = output_tags_and_subfield_codes.begin();
            primary != primary_tags_and_subfield_codes.end();
            ++primary, ++output, ++i)
        {
            std::vector<std::string> primary_values;
            std::set<std::string> synonym_values;
            std::vector<size_t> field_indices;
            if (record->getFieldIndices(GetTag(*primary), &field_indices) != MarcRecord::FIELD_NOT_FOUND) {
                for (auto field_index : field_indices) {
                     primary_values.clear();
                     if (record->getSubfields(field_index).extractSubfields(GetSubfieldCodes(*primary), &primary_values)) {
                        std::string searchterm = StringUtil::Join(primary_values, ',');
                        // First case: Look up synonyms only in one category
                        if (i < synonym_maps.size()) {
                            const auto &synonym_map(synonym_maps[i]);
                            const auto synonym(GetMapValueOrEmptyString(synonym_map, searchterm));
                            if (not synonym.empty())
                                synonym_values.insert(synonym);
                        }

                        // Second case: Look up synonyms in all categories
                        else {
                            for (auto &synonym_map : synonym_maps) {
                                const auto &synonym(GetMapValueOrEmptyString(synonym_map, searchterm));
                                if (not synonym.empty())
                                   synonym_values.insert(synonym);
                            }
                        }
                    }
                 }

                if (synonym_values.empty())
                     continue;

                // Insert synonyms
                // Abort if field is already populated
                std::string tag(GetTag(*output));
                if (record->getFieldIndex(tag) != MarcRecord::FIELD_NOT_FOUND)
                    Error("Field with tag " + tag + " is not empty for PPN " + record->getControlNumber() + '\n');
                std::string subfield_spec(GetSubfieldCodes(*output));
                if (unlikely(subfield_spec.size() != 1))
                    Error("We currently only support a single subfield and thus specifying " + subfield_spec
                          + " as output subfield is not valid\n");

                std::string synonyms;
                unsigned current_length(0);
                unsigned indicator2(0);

                for (auto synonym_it(synonym_values.cbegin()); synonym_it != synonym_values.cend(); /*Intentionally empty*/) {
                    if (indicator2 > 9)
                        Error ("Currently cannot handle synonyms with total length greater than " + std::to_string(9 * (MarcRecord::MAX_FIELD_LENGTH - 4)) + '\n');

                    if (current_length + synonym_it->length() < MarcRecord::MAX_FIELD_LENGTH - 4) {
                         bool synonyms_empty(synonyms.empty());
                         synonyms += (synonyms_empty ? *synonym_it : " , " + *synonym_it);
                         current_length += synonym_it->length() + (synonyms_empty ? 0 : 3);
                         ++synonym_it;
                    } else {
                        if (not(record->insertSubfield(tag, subfield_spec[0], synonyms, '0', indicator2 + '0')))
                            Error("Could not insert field " + tag + " for PPN " + record->getControlNumber() + '\n');
                        synonyms.clear();
                        current_length = 0;
                        ++indicator2;
                        modified_record = true;
                    }
                }
                // Write rest of data
                if (not synonyms.empty()) {
                    if (not(record->insertSubfield(tag, subfield_spec[0], synonyms, '0', indicator2 + '0')))
                            Error("Could not insert field " + tag + " for PPN " + record->getControlNumber() + '\n');
                    modified_record = true;
                }
            }
        }
        if (modified_record)
            ++modified_count;
    }
}

void InsertSynonyms(MarcReader * const marc_reader, MarcWriter * const marc_writer,
                    const std::set<std::string> &primary_tags_and_subfield_codes,
                    const std::set<std::string> &output_tags_and_subfield_codes,
                    std::vector<std::map<std::string, std::string>> &synonym_maps)
{
    while (MarcRecord record = marc_reader->read()) {
        ProcessRecord(&record, synonym_maps, primary_tags_and_subfield_codes, output_tags_and_subfield_codes);
        marc_writer->write(record);
        ++record_count;
    }

    std::cerr << "Modified " << modified_count << " of " << record_count << " record(s).\n";
}

bool ParseSpec(std::string spec_str, std::set<std::string> * const field_specs, std::map<std::string, std::pair<std::string, std::string>> * filter_specs = nullptr) {
    std::set<std::string> raw_field_specs;

    if (unlikely(StringUtil::Split(spec_str, ':', &raw_field_specs) == 0)) {
        Error("Need at least one field");
        return false;
    }

    if (filter_specs == nullptr) {
        *field_specs = raw_field_specs;
	return true;
    }

    // Iterate over all Field-specs and extract possible filters
    static RegexMatcher * const matcher(RegexMatcher::RegexMatcherFactory("(\\d{1,3}[a-z]+)\\[(\\d{1,3}[a-z])=(.*)\\]"));

    for (auto field_spec : raw_field_specs) {
        if (matcher->matched(field_spec)) {
            filter_specs->emplace((*matcher)[1], std::make_pair((*matcher)[2], (*matcher)[3]));
            auto bracket = field_spec.find("[");
            field_spec = (bracket != std::string::npos) ? field_spec.erase(bracket, field_spec.length()) : field_spec;
        }
        field_specs->insert(field_spec);
    }
    return true;
}

int main(int argc, char **argv) {
    ::progname = argv[0];

    if (argc != 4)
        Usage();

    const std::string marc_input_filename(argv[1]);
    const std::string authority_data_marc_input_filename(argv[2]);
    const std::string marc_output_filename(argv[3]);
    if (unlikely(marc_input_filename == marc_output_filename))
        Error("Title data input file name equals output file name!");
    if (unlikely(authority_data_marc_input_filename == marc_output_filename))
        Error("Authority data input file name equals output file name!");

    std::unique_ptr<MarcReader> marc_reader(MarcReader::Factory(marc_input_filename, MarcReader::BINARY));
    std::unique_ptr<MarcReader> authority_reader(MarcReader::Factory(authority_data_marc_input_filename,
                                                                     MarcReader::BINARY));
    std::unique_ptr<MarcWriter> marc_writer(MarcWriter::Factory(marc_output_filename, MarcWriter::BINARY));

    try {
        // Determine possible mappings
        // Values in square brackets specify a positive criterion for values to be taken into account
        const std::string AUTHORITY_DATA_PRIMARY_SPEC("100abcd[079v=piz]:110abcd:111abcd:130abcd:150abcd:151abcd");
        const std::string AUTHORITY_DATA_SYNONYM_SPEC("400abcd:410abcd:411abcd:430abcd:450abcd:451abcd");
        const std::string TITLE_DATA_PRIMARY_SPEC("600abcd:610abcd:611abcd:630abcd:650abcd:651abcd:689abcd");
        const std::string TITLE_DATA_UNUSED_FIELDS_FOR_SYNONYMS("180a:181a:182a:183a:184a:185a:186a");

        // Determine fields to handle
        std::set<std::string> primary_tags_and_subfield_codes;
        std::set<std::string> synonym_tags_and_subfield_codes;
        std::set<std::string> input_tags_and_subfield_codes;
        std::set<std::string> output_tags_and_subfield_codes;

        std::map<std::string, std::pair<std::string, std::string>> filter_specs;

        if (not unlikely(ParseSpec(AUTHORITY_DATA_PRIMARY_SPEC, &primary_tags_and_subfield_codes, &filter_specs)))
            Error("Could not properly parse " + AUTHORITY_DATA_PRIMARY_SPEC);

        if (unlikely(StringUtil::Split(AUTHORITY_DATA_SYNONYM_SPEC, ":", &synonym_tags_and_subfield_codes) == 0))
            Error("Need at least one synonym field");

        if (unlikely(StringUtil::Split(TITLE_DATA_PRIMARY_SPEC, ":", &input_tags_and_subfield_codes) == 0))
            Error("Need at least one input field");

        if (unlikely(StringUtil::Split(TITLE_DATA_UNUSED_FIELDS_FOR_SYNONYMS, ":", &output_tags_and_subfield_codes)
                     == 0))
            Error("Need at least one output field");

        unsigned num_of_authority_entries(primary_tags_and_subfield_codes.size());

        if (synonym_tags_and_subfield_codes.size() != num_of_authority_entries)
            Error("Number of authority primary specs must match number of synonym specs");
        if (input_tags_and_subfield_codes.size() != output_tags_and_subfield_codes.size())
            Error("Number of fields title entry specs must match number of output specs");

        std::vector<std::map<std::string, std::string>> synonym_maps(num_of_authority_entries,
                                                                     std::map<std::string, std::string>());

        // Extract the synonyms from authority data
        ExtractSynonyms(authority_reader.get(), primary_tags_and_subfield_codes, synonym_tags_and_subfield_codes,
                        &synonym_maps, filter_specs);

        // Iterate over the title data
        InsertSynonyms(marc_reader.get(), marc_writer.get(), input_tags_and_subfield_codes,
                       output_tags_and_subfield_codes, synonym_maps);

    } catch (const std::exception &x) {
        Error("caught exception: " + std::string(x.what()));
    }
}
<|endoftext|>