Oops - old version.  Here's the correct one:

#include <osmium.hpp>
#include <osmium/storage/byid.hpp>
#include <osmium/handler/coordinates_for_ways.hpp>
#include <osmium/handler/multipolygon.hpp>
#include <osmium/geometry/multipolygon.hpp>

#include <geos/geom/Geometry.h>
#include <geos/geom/CoordinateFilter.h>
#include <geos/geom/Envelope.h>
#include <proj_api.h>

#include <unicode/coll.h>
#include <unicode/stsearch.h>

#include <cmath>
#include <vector>
#include <unordered_map>

#include <autosprintf.h>

static inline double deg2rad(double r) { return r * M_PI / 180; }

class ProjectFromWGS84 : public geos::geom::CoordinateFilter
{
    projPJ source;
    projPJ target;

public:
    ProjectFromWGS84(const char *target_projection)
        : geos::geom::CoordinateFilter(),
          source(pj_init_plus("+proj=latlon +datum=WGS84")),
          target(pj_init_plus(target_projection))
    {
        assert(source);
        assert(target);
    }

    ~ProjectFromWGS84()
    {
        pj_free(source);
        pj_free(target);
    }

    virtual void filter_ro(const geos::geom::Coordinate *)
    {
        // not used
    }

    virtual void filter_rw(geos::geom::Coordinate *c) const
    {
        c->x *= DEG_TO_RAD;
        c->y *= DEG_TO_RAD;
        c->z *= DEG_TO_RAD;

        pj_transform(source, target, 1, 1, &c->x, &c->y, &c->z);
    }
};



typedef Osmium::Storage::SparseTable<Osmium::OSM::Position> storage_sparsetable_t;
typedef Osmium::Storage::Mmap<Osmium::OSM::Position> storage_mmap_t;
typedef Osmium::Handler::CoordinatesForWays<storage_sparsetable_t, storage_mmap_t> cfw_handler_t;


template<typename Base>
class ComputeAreaHandler : public Base {
    storage_sparsetable_t store_pos;
    storage_mmap_t store_neg;
    cfw_handler_t handler_cfw;

    ProjectFromWGS84 project_north;
    ProjectFromWGS84 project_world;
    ProjectFromWGS84 project_south;


public:

    template<typename... Args>
    ComputeAreaHandler(Args&&... args)
        : Base(std::forward<Args>(args)...),
          handler_cfw(store_pos, store_neg),
          project_north("+init=epsg:3408"),
          project_world("+init=epsg:3410"),
          project_south("+init=epsg:3409")
    {
    }

    ~ComputeAreaHandler() {
    }

    void init(Osmium::OSM::Meta& meta) {
        handler_cfw.init(meta);
        Base::init(meta);
    }

    void node(const shared_ptr<Osmium::OSM::Node>& n) {
        handler_cfw.node(n);
        Base::node(n);
    }

    void after_nodes() {
        handler_cfw.after_nodes();
        std::cerr << "nodes done" << std::endl;
        Base::after_nodes();

    }

    void way(const shared_ptr<Osmium::OSM::Way>& w) {
#ifdef TWOPASS
        handler_multipolygon->way(way);
#else
        if (w->is_closed() && w->node_count() > 3) {
            // remember, a triangle has 4 nodes (as one appears twice)
            Osmium::Geometry::Polygon p(*w);
            geos::geom::Geometry *g = p.create_geos_geometry();
            addAreaTag(&*w, g);
            delete g;
        }
#endif
        Base::way(w);
    }

    void tag_way(const shared_ptr<Osmium::OSM::Way>& w) {
        if (w->is_closed() && w->node_count() > 3) {
            // remember, a triangle has 4 nodes (as one appears twice)
            Osmium::Geometry::Polygon p(*w);
            geos::geom::Geometry *g = p.create_geos_geometry();
            addAreaTag(&*w, g);
            delete g;
        }
    }

    void area(Osmium::OSM::Area *a)
    {
        geos::geom::Geometry *g = a->get_geometry();
        addAreaTag(a, g);
        Base::area(a);
    }

private:
    // Area of the supplied WGS-84 lon/lat geometry, in square metres
    double computeArea(geos::geom::Geometry *g) {
        if (!g)
            return 0;

        // We need to project to an equal-area projection for g->getArea() to give useful results
        // We use azimuthal near the poles, and cylindrical near the equator, for precision
        static const double max_lat = 60;
        static const double hysteresis = 15;
        static const geos::geom::Envelope north_rect(-180, 180,  max_lat,  90);
        static const geos::geom::Envelope world_rect(-180, 180, -max_lat,  max_lat);
        static const geos::geom::Envelope south_rect(-180, 180, -90, -60);

        const geos::geom::Envelope *e = g->getEnvelopeInternal();
        if (!e) return 0;

        if (e->getMaxY() < (max_lat + hysteresis) && e->getMinY() > -(max_lat + hysteresis)) {
            // most of the world => EPSG:3410
            g->apply_rw(&project_world);
        } else if (e->getMinY() > (max_lat - hysteresis)) {
            // north polar region => EPSG:3408
            g->apply_rw(&project_north);
        } else if (e->getMaxY() < -(max_lat - hysteresis)) {
            // south polar region => EPSG:3409
            g->apply_rw(&project_south);
        } else {
            // big area - split into sub-areas
            return computeArea(g->intersection(g->getFactory()->toGeometry(&north_rect)))
                +  computeArea(g->intersection(g->getFactory()->toGeometry(&world_rect)))
                +  computeArea(g->intersection(g->getFactory()->toGeometry(&south_rect)));
        }

        return g->getArea();
    }


    void addAreaTag(Osmium::OSM::Object *o, geos::geom::Geometry *g) {
        std::ostringstream os;
        os.precision(2);
        os << std::fixed << computeArea(g);
        o->tags().add("computed:area", os.str().c_str());
    }

};

#if PJ_VERSION < 480
extern "C" {
    void pj_clear_initcache(void);
}
#endif


int main(int argc, char *argv[])
{

    std::string input;
    std::string output;

    if (argc > 3) {
        std::cerr << "Usage: " << argv[0] << " [INFILE [OUTFILE]]" << std::endl;
        exit(1);
    }
    if (argc > 1) {
        input =  argv[1];
    }
    if (argc > 2) {
        output = argv[2];
    }

    Osmium::OSMFile outfile(output);
    Osmium::Output::PBF out(outfile);
    ComputeAreaHandler<Osmium::Handler::Forward<decltype(out)> > handler(&out);

    Osmium::OSMFile infile(input);
    infile.read(handler);

    // clean up to help Valgrind
    pj_clear_initcache();
    xmlCleanupParser();
    xmlCleanupCharEncodingHandlers();
    return 0;
}

Sorry!
_______________________________________________
mkgmap-dev mailing list
mkgmap-dev@lists.mkgmap.org.uk
http://lists.mkgmap.org.uk/mailman/listinfo/mkgmap-dev

Reply via email to