// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only

/*!
    \example geojson_viewer
    \title GeoJson Viewer (C++/QML)
    \ingroup qtlocation-examples
    \examplecategory {Positioning & Location}

    \brief The GeoJson viewer example demonstrates how to manipulate MapItems, handle user input
    and I/O to and from a GeoJson file.

    \image geojson_viewer.png

    The example displays a map with various MapItems. The MapItems are either imported from a
    GeoJson file, using the \l {QGeoJson} API of \l {QtLocation} or drawn by the user using
    \l {TapHandler}{TapHandlers}.

    Examples for GeoJson files can be found in the directory data within the example directory.
    They are read and written using the \l {QGeoJson::importGeoJson}{importGeoJson} and
    \l {QGeoJson::exportGeoJson}{exportGeoJson} functions.

    To draw a MapItem, right click on an empty part of the map and select an item type of your
    choice in the appearing menu. The next clicks will define the chosen item. The example allows
    to draw \l {MapCircle}{MapCircles}, \l {MapRectangle}{MapRectangles}, \l {MapPolygon}{MapPolygons}
    and \l {MapPolyline}{MapPolylines}. Items that are fully defined by two points, i.e. circles
    and rectangles, are drawn with two clicks of the left mouse button. Items that are defined by
    multiple points, i.e. polygons and polylines, are created by an arbitrary amount of left button
    clicks and completed with the right mouse button. Items drawn this way are saved as points,
    polygons and polylines to fit the GeoJson specification, see \l {https://geojson.org/}.


    \include examples-run.qdocinc


    \section1 Creating a MapView

    First we create a base map on which all items can be placed on. We take advantage of a
    \l {MapView} element that combines a basic \l Map with input handling (mouse wheel, drag,
    etc.). The underlying \l Map can be accessed with \l {MapView::map}{map} property. If you miss
    a property in \l {MapView} it can be most likely accessed with \l {MapView::map}{MapView.map}.

    \snippet geojson_viewer/main.qml MapView Creation


    \section1 Setting up the GeoJson Model / Display MapItems

    In order to display file contents on the map we will use a design pattern known as
    \l {model-view-programming.html}{Model/View Programming}. First we need to set up a suitable view,
    in this example a \l {MapItemView} element. Its parent must be set to the underlying map of the
    \l {MapView} to correctly display all items placed in it.

    \snippet geojson_viewer/main.qml MapItemView

    Next we need a suitable model, representing a GeoJson file. The GeoJson file is converted into
    a tree of \l {QVariantMap}{QVariantMaps} and \l{QVariantList}{QVariantLists} within a C++ class
    in this example.

    \snippet geojson_viewer/main.cpp GeoJsoner

    The class contains a member \c{model} which will be set after reading a GeoJson file.

    \snippet geojson_viewer/main.cpp Conversion QVariantList

    The class is made available to the QML Engine to further process the model in QML.

    \snippet geojson_viewer/main.cpp QMLEngine

    The \l {MapItemView::model}{model} property of the \l{MapItemView} element can then be set to
    the \l{QVariant} representation of the model:

    \snippet geojson_viewer/main.qml MapItemView Model

    Finally we need a delegate, translating the model data into a representation of items, filling
    the \l {MapItemView}. It is set to the \l {MapItemView::delegate}{delegate} property of the
    \l{MapItemView}:

    \snippet geojson_viewer/main.qml MapItemView Delegate

    We use a \l {DelegateChooser} element defined the file \c{GeoJsonDelegate.qml} to take into
    account the varying representation of different geometry types of GeoJson objects.

    \snippet geojson_viewer/GeoJsonDelegate.qml DelegateChooser

    Various \l {DelegateChoice}{DelegateChoices} are included, each representing a different
    geometry type to be found in a GeoJson file. The property \l {DelegateChooser::role}{role}
    will be matched with \l {DelegateChoice::roleValue}{DelegateChoice.roleValue} to determine the
    correct delegate.

    As an example, a point, described with \c {"type":"Point"} in GeoJson, is represented by a
    \l {MapCircle} on the \l {MapItemView}:

    \snippet geojson_viewer/GeoJsonDelegate.qml DelegateChoice Point

    Properties of the \l {MapCircle}, such as \l {MapCircle::color}{color} or
    \l{MapCircle::radius}{radius} are attempted to be read from the GeoJson file that is
    available in form of the modelData property. However, this is not a strict standard of GeoJson
    and fallback values are set for all properties.


    \section1 Writing MapItems to GeoJson

    To write MapItems to a GeoJson file we first will convert the Mapitems into a representation
    of \l {QVariantMap}{QVariantMaps} and \l{QVariantList}{QVariantLists}. This is conducted in C++
    in this example, as part of the \c {GeoJsoner} class used before:

    \snippet geojson_viewer/main.cpp Conversion QVariantList From Items

    \c {extractor} is a helper class that converts Mapitems into \l {QVariant} representations, for
    example the \l{MapCircle}:

    \snippet geojson_viewer/main.cpp Extractor Example Circle

    In a second step the \l {QVariant} representation can be dumped into a file using
    \l {QJsonDocument}.

    \snippet geojson_viewer/main.cpp Write QVariantList to Json

    The two C++ functions that are required for writing files can be called from QML, thanks to the
    definition as \l {Q_INVOKABLE}:

    \snippet geojson_viewer/main.qml Write File

    Note that we could use \c {geoJsoner.model} instead of rebuilding the \l {QVariant}
    representation. However, the latter is done for demonstration purposes here.

    \section1 User Interaction with MapItems

    To handle user interactions we will use \l {PointHandler}{PointHandlers}. They are especially
    well suited for the task as they conform to the exact shape of the underlying item, in contrast
    to \l{MouseArea}{MouseArea}, which always covers a square shape. MapItems that are imported
    from a GeoJson file get their own \l {HoverHandler} and \l {TapHandler} directly in the delegate:

    \snippet geojson_viewer/GeoJsonDelegate.qml Handler Point

    The \l {TapHandler} is used to write some information about the item on the console when the
    item is tapped. The \l {HoverHandler} is used to highlight items that lie beneath the mouse
    pointer. This is implemented by describing the property
    \l {MapCircle::border.color}{border.color} depending on the property / state
    \l {HoverHandler::hovered}{hovered} of the \l{HoverHandler}.


    \section1 Adding new Items

    A combination of \l {HoverHandler} and \l {TapHandler} for the \l {MapView} allows us to
    react to mouse movements and clicks by the user.

    If the \l{TapHandler} emits a \l{TapHandler::singleTapped}{singleTapped} signal, we will
    create or modify a new MapItem on \l{Qt::LeftButton}{LeftButton} and finish the MapItem on
    \l{Qt::RightButton}{RightButton}. If there is no item to finish then the
    \l{Qt::RightButton}{RightButton} will open a menu.

    \snippet geojson_viewer/main.qml Taphandler Map

    The \l {SinglePointHandler::point}{pointChanged} signal is used to temporarily update a
    MapItem, giving the user a preview.

    \snippet geojson_viewer/main.qml Hoverhandler Map

    Mapitems are generated from prototypes that are defined in separate qml files. They are created
    using the \l{Qt::createComponent}{createComponent} function and added to the map with
    \l {Map::addMapItem}{addMapItem}. A reference to the new item is stored for further manipulation
    by the user.

    \snippet geojson_viewer/main.qml add item


    Adding the item to the \l {Map} is sufficient to display the item. However, in order to
    further use the item (e.g. saving it to a file), it has has to be registered with the model.
    This is done after editing is finished:

    \snippet geojson_viewer/main.qml finish item

    The class GeoJsoner converts the new item into a \l {QVariant} representation and inserts the
    respective result into the existing \l {QVariant} representation of all items:

    \snippet geojson_viewer/main.cpp add item


    \section1 Removing Items

    To remove all items from the map, we simply reset the model to an empty \l {QVariantList}.
    This is possible because we registered all new items with the model and items not added to the
    mode will not be affected. This is implemented in C++,

    \snippet geojson_viewer/main.cpp clear

    and executed from QML

    \snippet geojson_viewer/main.qml clearAllItems
*/
