/* osgEarth
 * Copyright 2025 Pelican Mapping
 * MIT License
 */

#ifndef OSGEARTH_LOCATORS_H
#define OSGEARTH_LOCATORS_H 1

#include <osgEarth/Common>
#include <osgEarth/GeoData>
#include <osg/Matrixd>

namespace osgEarth { namespace Util
{
    /**
     * Object that facilitates the conversion between map or world coordinates
     * and a local unit coordinate space defined by a geospatial extent.
     */
    class /* header only */ GeoLocator
    {
    public:
        //! Construct with an extent that defines the unit [0..1] space
        GeoLocator(const GeoExtent& extent)
        {
            _extent = extent;

            _srs = extent.getSRS();

            _transform.set(
                extent.width(), 0.0, 0.0, 0.0,
                0.0, extent.height(), 0.0, 0.0,
                0.0, 0.0, 1.0, 0.0,
                extent.xMin(), extent.yMin(), 0.0, 1.0);

            _inverse.invert(_transform);
        }

        //! Convert world coordinates to unit space
        inline void worldToUnit(const osg::Vec3d& world, osg::Vec3d& unit) const
        {
            if (_srs->isGeographic())
                unit = _srs->getEllipsoid().geocentricToGeodetic(world) * _inverse;
            else
                unit = world * _inverse;
        }

        //! Convert unit coordinates to world space
        inline void unitToWorld(const osg::Vec3d& unit, osg::Vec3d& world) const
        {
            world = unit * _transform;
            if (_srs->isGeographic())
                world = _srs->getEllipsoid().geodeticToGeocentric(world);
        }

        GeoExtent _extent;
        osg::Matrixd _transform;
        osg::Matrixd _inverse;
        double _x0, _y0, _x1, _y1;
        osg::ref_ptr<const SpatialReference> _srs;
    };
} }

#endif // OSGEARTH_LOCATORS_H
