/***************************** LICENSE START ***********************************

 Copyright 2021 ECMWF and INPE. This software is distributed under the terms
 of the Apache License version 2.0. In applying this license, ECMWF does not
 waive the privileges and immunities granted to it by virtue of its status as
 an Intergovernmental Organization or submit itself to any jurisdiction.

 ***************************** LICENSE END *************************************/

#include "MvQFeatureShapeItem.h"

#include <cmath>

#include <QApplication>
#include <QtGlobal>
#include <QDebug>
#include <QGraphicsSceneMouseEvent>
#include <QImage>
#include <QJsonArray>
#include <QMouseEvent>
#include <QStyle>

#include "GenAppService.hpp"
#include "MvQJson.h"
#include "MvQPlotView.h"
#include "MvQFeatureFactory.h"
#include "WsType.h"
#include "MvQMethods.h"
#include "ObjectList.h"
#include "MvSci.h"

#include "MgQLayoutItem.h"

#include <map>
#include <string>

//#define MVQFEATURESHAPE_DEBUG_
//#define MVQFEATUREGEOSHAPE_DEBUG_

//========================================
//
// MvQFeatureShapeItem
//
//========================================

MvQFeatureShapeItem::MvQFeatureShapeItem(WsType* feature) :
    MvQFeatureItem(feature)
{
}

void MvQFeatureShapeItem::init(const QJsonObject& obj)
{
    QJsonObject g = obj["geometry"].toObject();
    auto gcLst = MvQJson::toPointFList(g["coordinates"].toArray(), g["type"].toString());
    if (!gcLst.isEmpty()) {
        geoCoord_ = gcLst[0];
        hasValidGeoCoord_ = isValidPoint(geoCoord_);
    }
    else {
        hasValidGeoCoord_ = false;
    }
    MvQFeatureItem::init(obj);
}

void MvQFeatureShapeItem::paint(QPainter* painter, const QStyleOptionGraphicsItem*, QWidget*)
{
    painter->save();

    painter->setRenderHint(QPainter::Antialiasing, true);
    painter->setPen(pen_);
    painter->setBrush(brush_);

    if (shapePath_.isEmpty()) {
        painter->drawPolygon(shape_);
    }
    else {
        painter->drawPath(shapePath_);
    }

#ifdef MVQ_FEATURE_DEV_MODE_
    if (outOfView_) {
        auto pen = pen_;
        pen.setColor(devModeColor_);
        painter->setPen(pen);
        painter->setBrush(Qt::NoBrush);
        if (shapePath_.isEmpty()) {
            painter->drawPolygon(shape_);
        }
        else {
            painter->drawPath(shapePath_);
        }
    }
#endif

    painter->restore();
}

void MvQFeatureShapeItem::resize(QRectF targetRectInParent)
{
    prepareGeometryChange();

    // the input rect is in parent coordinates. It defines the desired
    // boundingRect size
    QRectF targetRect = mapFromParent(targetRectInParent).boundingRect();

    // reduce the targetRect with the halo. We want to fit the itemRect into it!!!
    targetRect.adjust(halo(), halo(), -halo(), -halo());

    auto w = targetRect.width();
    auto h = targetRect.height();
    if (anchorPos_ == CentreAnchor) {
        itemRect_ = QRectF(-w / 2., -h / 2., w, h);
        setPos(targetRectInParent.center());
    }
    else if (anchorPos_ == BottomCentreAnchor) {
        itemRect_ = QRectF(-w / 2., -h, w, h);
        setPos(QPointF(targetRectInParent.center().x(),
                       targetRectInParent.y()));
    }
    else {
        Q_ASSERT(0);
    }
#ifdef MVQFEATURESHAPE_DEBUG_
    qDebug() << "ItemRect:" << itemRect_ << "pos:" << pos();
#endif
    bRect_ = itemRect_;
    width_ = itemRect_.width();
    height_ = itemRect_.height();
    adjustBRect();
    adjustGeoCoord();
    buildShape();
    adjustSizeInReq();
}

void MvQFeatureShapeItem::resizeTo(const MvQFeatureGeometry& geom)
{
    prepareGeometryChange();
    setPos(geom.pos_);
    itemRect_ = geom.itemRect_;
    bRect_ = itemRect_;
    width_ = itemRect_.width();
    height_ = itemRect_.height();
    adjustBRect();
    adjustGeoCoord();
    buildShape();
    adjustSizeInReq();
}

void MvQFeatureShapeItem::adjustSizeInReq()
{
    if (keepAspectRatio_) {
        req_("WIDTH") = width_;
    }
    else {
        req_("WIDTH") = width_;
        req_("HEIGHT") = height_;
    }
    broadcastRequestChanged();
}

void MvQFeatureShapeItem::moveBy(QPointF delta)
{
    MvQFeatureItem::moveBy(delta);
    if (dataLayout_ && parentItem()) {
        updateRect();
        adjustBRect();
    }
}

void MvQFeatureShapeItem::moveTo(const MvQFeatureGeometry& g)
{
    MvQFeatureItem::moveTo(g);
    if (dataLayout_ && parentItem()) {
        updateRect();
        adjustBRect();
    }
}

void MvQFeatureShapeItem::initAtGeoCoord(QPointF co)
{
#ifdef MVQFEATUREGEOSHAPE_DEBUG_
    qDebug() << "MvQFeatureShapeItem::moveToGeoCoord";
    qDebug() << " co:" << co;
#endif
    if (shape_.isEmpty() && shapePath_.isEmpty()) {
        buildShape();
    }
    geoCoord_ = co;
    if (dataLayout_ && parentItem()) {
        QPointF scPos;
        dataLayout_->mapFromGeoToSceneCoords(geoCoord_, scPos);
        setPos(parentItem()->mapFromScene(scPos));
#ifdef MVQFEATUREGEOSHAPE_DEBUG_
        qDebug() << " shape:" << shape_;
        qDebug() << " geoCoords:" << geoCoords_;
#endif
        updateRect();
        adjustBRect();
    }
}

void MvQFeatureShapeItem::initAtScenePos(const QPointF& scPos)
{
    if (shape_.isEmpty() && shapePath_.isEmpty()) {
        buildShape();
    }

    setPos(parentItem()->mapFromScene(scPos));
    adjustGeoCoord();
    updateRect();
    adjustBRect();
}

void MvQFeatureShapeItem::updateRect()
{
    prepareGeometryChange();
    if (anchorPos_ == CentreAnchor) {
        itemRect_ = QRectF(-width_ / 2., -height_ / 2., width_, height_);
    }
    else if (anchorPos_ == BottomCentreAnchor) {
        itemRect_ = QRectF(-width_ / 2., -height_, width_, height_);
    }
    else {
        Q_ASSERT(0);
    }
    bRect_ = itemRect_;
}

void MvQFeatureShapeItem::adjustBRect()
{
    if (fabs(halo() > 1E-5)) {
        prepareGeometryChange();
        bRect_ = itemRect_.adjusted(-halo(), -halo(), halo(), halo());
    }
}

double MvQFeatureShapeItem::halo() const
{
    auto w = pen_.width();
    return (w <= 1) ? 0 : 1 + w / 2;
}

bool MvQFeatureShapeItem::getRequestParameters()
{
    if (!req_)
        return false;

    expandStyleRequest();

    std::string val;

    // Set line parameters
    req_.getValue("LINE", val);
    if (val == "OFF") {
        pen_ = QPen(Qt::NoPen);
    }
    else {
        pen_ = MvQ::makePen((const char*)req_("LINE_STYLE"),
                            (int)req_("LINE_THICKNESS"),
                            (const char*)req_("LINE_COLOUR"),
                            Qt::SquareCap, Qt::MiterJoin);
    }

    // Set fill parameters
    req_.getValue("FILL", val);
    if (val == "OFF") {
        brush_ = QBrush(Qt::NoBrush);
    }
    else {
        brush_ = MvQ::makeBrush("SOLID",
                                (const char*)req_("FILL_COLOUR"));
    }

    // size
    bool sizeChanged = false;
    if (keepAspectRatio_) {
        req_.getValue("WIDTH", val);
        int iv = 0;
        try {
            iv = std::stoi(val);
        }
        catch (...) {
            iv = 0;
        }
        if (iv > 0 && iv != width_) {
            sizeChanged = true;
            width_ = iv;
            height_ = iv;
        }
    }
    else {
        int iw = (int)req_("WIDTH");
        int ih = (int)req_("HEIGHT");
        if (iw > 0 && ih > 0 && (iw != width_ || ih != height_)) {
            sizeChanged = true;
            width_ = iw;
            height_ = ih;
        }
    }

    // TODO: make absolutely sure that this works!
    if (sizeChanged) {
        buildShape();
        updateRect();
    }

    return true;
}

QString MvQFeatureShapeItem::describe() const
{
    QString txt = MvQFeatureItem::describe();
    MvQFeatureDescriber ds;
    ds.add("width=", width_);
    ds.add("height=", height_);
    return txt + ds.text();
}

// this makes sure the geometry is properly initialised
void MvQFeatureShapeItem::initGeometry()
{
    if (bRect_.isNull() || itemRect_.isNull()) {
        updateRect();
        adjustBRect();
    }

    if (shape_.isEmpty() && shapePath_.isEmpty()) {
        buildShape();
    }
}

//========================================
//
// MvQFeatureRectItem
//
//========================================

// Note: we have to use a QPainterPath for the painting. If we simply use a polygon in
// Qt 6.2 a rectangle shape is not filled correctly.
void MvQFeatureRectItem::buildShape()
{
    auto w = width_ / 2.;
    auto h = height_ / 2;
    QPolygonF p;
    p << QPointF(-w, -h) << QPointF(w, -h) << QPointF(w, h) << QPointF(-w, h);

    shapePath_ = QPainterPath();
    shapePath_.addPolygon(p);
    shapePath_.closeSubpath();
}

//========================================
//
// MvQFeatureTriangleItem
//
//========================================

void MvQFeatureTriangleItem::buildShape()
{
    shape_.clear();
    auto w = width_ / 2.;
    auto h = height_ / 2;
    shape_ << QPointF(-w, h) << QPointF(w, h) << QPointF(0., -h);
}

//========================================
//
// MvQFeatureDiamondItem
//
//========================================

void MvQFeatureDiamondItem::buildShape()
{
    shape_.clear();
    auto w = width_ / 2.;
    auto h = height_ / 2;
    shape_ << QPointF(-w, 0.) << QPointF(0., -h) << QPointF(w, 0.) << QPointF(0., h);
}

//========================================
//
// MvQFeatureStarItem
//
//========================================

MvQFeatureStarItem::MvQFeatureStarItem(WsType* feature) :
    MvQFeatureShapeItem(feature)
{
    keepAspectRatio_ = true;
}

void MvQFeatureStarItem::buildShape()
{
    shape_.clear();

    auto outerRadius = height_ / (1. + cos(MvSci::degToRad(36)));
    QPointF cp(0, outerRadius - height_ / 2);
    auto innerRadius = outerRadius / 2.;
    for (int i = 0; i < 5; i++) {
        float outer = 2. * i * M_PI / 5. + M_PI / 2.;
        float inner = outer + M_PI / 5.;
        shape_ << cp + QPointF(outerRadius * cos(outer), -outerRadius * sin(outer));
        shape_ << cp + QPointF(innerRadius * cos(inner), -innerRadius * sin(inner));
    }
}

//========================================
//
// MvQFeatureEllipseItem
//
//========================================

void MvQFeatureEllipseItem::buildShape()
{
#if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0)
    shapePath_.clear();
#else
    shapePath_ = QPainterPath();
#endif
    shapePath_.addEllipse(QRectF(-width_ / 2., -height_ / 2., width_, height_));
}

//========================================
//
// MvQFeaturePlacemarkerItem
//
//========================================

MvQFeaturePlacemarkerItem::MvQFeaturePlacemarkerItem(WsType* feature) :
    MvQFeatureShapeItem(feature)
{
    anchorPos_ = BottomCentreAnchor;
    keepAspectRatio_ = true;
    width_ = 25;
    height_ = 25;
}

// MvQFeaturePlacemarkerItem::MvQFeaturePlacemarkerItem(
//                                          const MvQFeaturePlacemarkerItem& o) :
//      MvQFeatureShapeItem(o)
//{
//     fillHole_ = o.fillHole_;
//     holeBrush_ = o.holeBrush_;
//     holePath_ = o.holePath_;
// }

void MvQFeaturePlacemarkerItem::paint(QPainter* painter, const QStyleOptionGraphicsItem*, QWidget*)
{
    painter->save();

#if 0
    // clip to map boundaries
    setClipping(painter);
#endif

    painter->setRenderHint(QPainter::Antialiasing, true);
    painter->setPen(pen_);
    painter->setBrush(brush_);
    painter->drawPath(shapePath_);

    if (fillHole_) {
        painter->setPen(Qt::NoPen);
        painter->setBrush(holeBrush_);
        painter->drawPath(holePath_);
    }

#ifdef MVQ_FEATURE_DEV_MODE_
    if (outOfView_) {
        auto pen = pen_;
        pen.setColor(devModeColor_);
        painter->setPen(pen);
        painter->setBrush(Qt::NoBrush);
        painter->drawPath(shapePath_);
    }
#endif

    painter->restore();
}


void MvQFeaturePlacemarkerItem::buildShape()
{
    shapePath_ = QPainterPath();
    holePath_ = QPainterPath();

    // the origo is at the bottom-centre of the itemRect_!

    // Build an arc (bigger than a semi-circle)
    auto radius = 0.6 * height_;
    QRectF rect(-radius / 2., -height_ / 2. - radius / 2., radius, radius);
    rect.moveTopLeft(QPointF(-radius / 2., -height_));
    shapePath_.arcTo(rect, 330., 240.);

    // Initial point is (0,0), replace it with the arc's initial point
    QPointF p1 = shapePath_.elementAt(1);
    shapePath_.setElementPositionAt(0, p1.x(), p1.y());

    // Build bottom part of the placemarker
    shapePath_.lineTo(0., 0);
    shapePath_.closeSubpath();

    // Build the inner circle
    auto innerRadius = radius / 4.;
    auto c = rect.center();
    auto innerRect = QRectF(c.x() - innerRadius, c.y() - innerRadius, 2 * innerRadius, 2 * innerRadius);
    if (fillHole_) {
        holePath_.addEllipse(innerRect);
    }
    else {
        shapePath_.addEllipse(innerRect);
    }
}

bool MvQFeaturePlacemarkerItem::getRequestParameters()
{
    if (!req_)
        return false;

    expandStyleRequest();

    std::string val;

    // Set line parameters
    req_.getValue("LINE", val);
    if (val == "OFF") {
        pen_ = QPen(Qt::NoPen);
    }
    else {
        pen_ = MvQ::makePen((const char*)req_("LINE_STYLE"),
                            (int)req_("LINE_THICKNESS"),
                            (const char*)req_("LINE_COLOUR"),
                            Qt::SquareCap, Qt::MiterJoin);
    }

    // Set fill parameters
    req_.getValue("FILL", val);
    if (val == "OFF") {
        brush_ = QBrush(Qt::NoBrush);
    }
    else {
        brush_ = MvQ::makeBrush("SOLID",
                                (const char*)req_("FILL_COLOUR"));
    }

    req_.getValue("INNER_FILL", val);
    if (val == "" || val == "OFF") {
        holeBrush_ = QBrush(Qt::NoBrush);
        fillHole_ = false;
    }
    else {
        holeBrush_ = MvQ::makeBrush("SOLID",
                                    (const char*)req_("INNER_FILL_COLOUR"));
        fillHole_ = true;
    }

    // size
    bool sizeChanged = false;
    req_.getValue("WIDTH", val);
    int iv = 0;
    try {
        iv = std::stoi(val);
    }
    catch (...) {
        iv = 0;
    }
    if (iv > 0 && iv != width_) {
        sizeChanged = true;
        width_ = iv;
        height_ = iv;
    }

    buildShape();

    // TODO: make absolutely sure that this works!
    if (sizeChanged) {
        updateRect();
    }

    return true;
}

//========================================
//
// MvQFeatureGeoShapeItem
//
//========================================

MvQFeatureGeoShapeItem::MvQFeatureGeoShapeItem(WsType* feature) :
    MvQFeatureItem(feature)
{
    // NOTE: do not implement initGeometry! Since the shape is
    // based on the projection we do it by calling reproject.
    // What important here is that the object should have a meaningful
    // initial width and height.
}

void MvQFeatureGeoShapeItem::init(const QJsonObject& obj)
{
    // coord and size must be extracted before the base-class
    // implementation is called
    QJsonObject g = obj["geometry"].toObject();
    auto gcLst = MvQJson::toPointFList(g["coordinates"].toArray(), g["type"].toString());
    if (!gcLst.isEmpty()) {
        geoCoord_ = gcLst[0];
        hasValidGeoCoord_ = isValidPoint(geoCoord_);
    }

    QJsonObject p = obj["properties"].toObject();
    auto w = p["width"].toInt();
    auto h = p["height"].toInt();
    if (w > 0 && w < 5000 && h > 0 && h < 5000) {
        width_ = w;
        height_ = h;
    }
    MvQFeatureItem::init(obj);
}

void MvQFeatureGeoShapeItem::toJson(QJsonObject& obj)
{
    MvQFeatureItem::toJson(obj);

    //    QJsonObject g;
    //    g["type"] = "Polygon";
    //    g["coordinates"] = MvQJson::fromPointF(geoCoord());
    //    obj["geometry"] = g;

    QJsonObject p = obj["properties"].toObject();
    p["width"] = width_;
    p["height"] = height_;
    obj["properties"] = p;
}

// Note: we have to use a QPainterPath for the painting. If we simply use a polygon in
// Qt 6.2 a rectangle shape is not filled correctly.
void MvQFeatureGeoShapeItem::paint(QPainter* painter, const QStyleOptionGraphicsItem*, QWidget*)
{
    painter->save();

    // clip to map boundaries
    setClipping(painter);

    painter->setPen(pen_);
    painter->setBrush(brush_);
    painter->drawPath(shapePath_);

#ifdef MVQ_FEATURE_DEV_MODE_
    if (outOfView_) {
        auto pen = pen_;
        pen.setColor(devModeColor_);
        painter->setPen(pen);
        painter->setBrush(Qt::NoBrush);
        painter->drawPath(shapePath_);
    }
#endif
    painter->restore();
}

void MvQFeatureGeoShapeItem::resize(QRectF targetRectInParent)
{
    prepareGeometryChange();

    // the input rect is in parent coordinates. It defines the desired
    // boundingRect size
    QRectF targetRect = mapFromParent(targetRectInParent).boundingRect();

    // reduce the targetRect with the halo. We want to fit the itemRect into it!!!
    targetRect.adjust(halo(), halo(), -halo(), -halo());

    auto w = targetRect.width();
    auto h = targetRect.height();
    itemRect_ = QRectF(-w / 2., -h / 2., w, h);
    bRect_ = itemRect_;
    width_ = itemRect_.width();
    height_ = itemRect_.height();
    setPos(targetRectInParent.center());
    adjustBRect();
    adjustGeoCoord();
    buildShape();
}

void MvQFeatureGeoShapeItem::resizeTo(const MvQFeatureGeometry& geom)
{
    prepareGeometryChange();
    setPos(geom.pos_);
    itemRect_ = geom.itemRect_;
    bRect_ = itemRect_;
    width_ = itemRect_.width();
    height_ = itemRect_.height();
    adjustBRect();
    adjustGeoCoord();
    buildShape();
}

void MvQFeatureGeoShapeItem::moveBy(QPointF delta)
{
    MvQFeatureItem::moveBy(delta);
    if (dataLayout_ && parentItem()) {
        updateRect();
        adjustBRect();
        buildShape();
    }
}

void MvQFeatureGeoShapeItem::moveTo(const MvQFeatureGeometry& g)
{
    MvQFeatureItem::moveTo(g);
    if (dataLayout_ && parentItem()) {
        updateRect();
        adjustBRect();
        buildShape();
    }
}

void MvQFeatureGeoShapeItem::initAtGeoCoord(QPointF co)
{
#ifdef MVQFEATUREGEOSHAPE_DEBUG_
    qDebug() << "MvQFeatureShapeItem::moveToGeoCoord";
    qDebug() << " co:" << co;
#endif
    geoCoord_ = co;
    if (dataLayout_ && parentItem()) {
        QPointF scPos;
        dataLayout_->mapFromGeoToSceneCoords(geoCoord_, scPos);
        setPos(parentItem()->mapFromScene(scPos));
        buildShape();
#ifdef MVQFEATUREGEOSHAPE_DEBUG_
        qDebug() << " shape:" << shape_;
        qDebug() << " geoCoords:" << geoCoords_;
#endif
        updateRect();
        adjustBRect();
    }
}

// Recompute the position of the item using the current geoCoord
void MvQFeatureGeoShapeItem::reproject(bool init)
{
    if (dataLayout_) {
        QPointF scPos;
        dataLayout_->mapFromGeoToSceneCoords(geoCoord_, scPos);
        setPos(parentItem()->mapFromScene(scPos));
        // qDebug() << "REPROJECT -> pos:" << pos() << "gc:" << geoCoord_;
        if (init) {
            buildShape();
        }
        else {
            reprojectShape();
            adjustShape();
        }
        // qDebug() << " geoCoords" << geoCoords_;
        updateRect();
        adjustBRect();
    }
}

void MvQFeatureGeoShapeItem::updateRect()
{
    prepareGeometryChange();
    itemRect_ = shapeBoundingRect();
    width_ = itemRect_.width();
    height_ = itemRect_.height();
    bRect_ = itemRect_;
}

void MvQFeatureGeoShapeItem::adjustBRect()
{
    if (fabs(halo() > 1E-5)) {
        prepareGeometryChange();
        bRect_ = itemRect_.adjusted(-halo(), -halo(), halo(), halo());
    }
}

QRectF MvQFeatureGeoShapeItem::shapeBoundingRect()
{
    return shape_.boundingRect();
}

double MvQFeatureGeoShapeItem::halo() const
{
    auto w = pen_.width();
    return (w <= 1) ? 0 : 1 + w / 2;
}

bool MvQFeatureGeoShapeItem::getRequestParameters()
{
    if (!req_)
        return false;

    expandStyleRequest();

    std::string val;

    // Set line parameters
    req_.getValue("LINE", val);
    if (val == "OFF") {
        pen_ = QPen(Qt::NoPen);
    }
    else {
        pen_ = MvQ::makePen((const char*)req_("LINE_STYLE"),
                            (int)req_("LINE_THICKNESS"),
                            (const char*)req_("LINE_COLOUR"),
                            Qt::SquareCap, Qt::MiterJoin);
    }

    // Set fill parameters
    req_.getValue("FILL", val);
    if (val == "OFF") {
        brush_ = QBrush(Qt::NoBrush);
    }
    else {
        brush_ = MvQ::makeBrush("SOLID",
                                (const char*)req_("FILL_COLOUR"));
    }
    return true;
}

QString MvQFeatureGeoShapeItem::describe() const
{
    QString txt = MvQFeatureItem::describe();
    MvQFeatureDescriber ds;
    ds.add("width=", width_);
    ds.add("height=", height_);
    return txt + ds.text();
}

//========================================
//
// MvQFeatureGeoRectItem
//
//========================================

MvQFeatureGeoRectItem::MvQFeatureGeoRectItem(WsType* feature) :
    MvQFeatureGeoShapeItem(feature)
{
}

void MvQFeatureGeoRectItem::reprojectShape()
{
    shape_.clear();
    shapePath_ = QPainterPath();
    for (auto p : geoCoords_) {
        QPointF scPos;
        dataLayout_->mapFromGeoToSceneCoords(p, scPos);
        shape_ << mapFromScene(scPos);
        shapePath_.addPolygon(shape_);
        shapePath_.closeSubpath();
    }

    if (shape_.size() == 4) {
        width_ = fabs(shape_[1].x() - shape_[0].x());
        height_ = fabs(shape_[3].y() - shape_[0].y());
    }
}

void MvQFeatureGeoRectItem::buildShape()
{
    shape_.clear();
    shapePath_ = QPainterPath();
    geoCoords_.clear();
    if (dataLayout_) {
        auto w = width_ / 2.;
        auto h = height_ / 2;
        shape_ << QPointF(-w, -h) << QPointF(w, -h) << QPointF(w, h) << QPointF(-w, h);

        shapePath_.addPolygon(shape_);
        shapePath_.closeSubpath();
        updateGeoCoordsFromShape();
    }
}

void MvQFeatureGeoRectItem::adjustShape()
{
    if (shape_.size() == 4) {
        double eps = 1E-6;
        if ((fabs(shape_[0].y() - shape_[1].y()) > eps) ||
            (fabs(shape_[2].y() - shape_[3].y()) > eps) ||
            (fabs(shape_[0].x() - shape_[3].x()) > eps) ||
            (fabs(shape_[1].x() - shape_[2].x()) > eps)) {
            buildShape();
        }
    }
}

void MvQFeatureGeoRectItem::updateGeoCoordsFromShape()
{
    for (auto sp : shape_) {
        QPointF pp = mapToScene(sp);
        QPointF co;
        dataLayout_->mapFromSceneToGeoCoords(pp, co);
        geoCoords_ << co;
    }
}


static MvQFeatureMaker<MvQFeatureGeoRectItem> geoRectMaker("georect");
static MvQFeatureMaker<MvQFeatureRectItem> rectMaker("rect");
static MvQFeatureMaker<MvQFeatureTriangleItem> triangleMaker("triangle");
static MvQFeatureMaker<MvQFeatureDiamondItem> diamondMaker("diamond");
static MvQFeatureMaker<MvQFeatureStarItem> starMaker("star");
static MvQFeatureMaker<MvQFeatureEllipseItem> ellipseMaker("ellipse");
static MvQFeatureMaker<MvQFeaturePlacemarkerItem> placemarMaker("placemark");
