[mkgmap-dev] Computing area (was: length () in relations file)

2013-02-26 Thread Toby Speight
0 In article 51194252.7080...@web.de,
0 WanMil URL:mailto:wmgc...@web.de (Wanmil) wrote:

Wanmil It might be possible. The problem to be solved is that the
Wanmil garmin map geometry is not rectangular. So at the moment I
Wanmil don't know how the area size of a polygon can be calculated in
Wanmil a metric unit.

I did this by preprocessing in C++, which adds a computed:area tag to
each area (including multipolygons if TWOPASS is defined):


#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::SparseTableOsmium::OSM::Position storage_sparsetable_t;
typedef Osmium::Storage::MmapOsmium::OSM::Position storage_mmap_t;
typedef Osmium::Handler::CoordinatesForWaysstorage_sparsetable_t, storage_mmap_t cfw_handler_t;

#if 1
class TagMap : public std::unordered_mapstd::string,UnicodeString
{

public:
TagMap(const Osmium::OSM::TagList tags) {
reserve(tags.size());
for (const Osmium::OSM::Tag tag: tags)
operator[](tag.key()) = UnicodeString::fromUTF8(tag.value());
}

operator Osmium::OSM::TagList() {
Osmium::OSM::TagList l;
for (const value_type p: *this) {
std::string s;
p.second.toUTF8String(s);
l.add(p.first.c_str(), s.c_str());
}
return l;
}

bool contains(const key_type k) const {
return find(k) != end();
}

const mapped_type get(const key_type k) const {
return contains(k) ? at(k) : empty_value;
}

private:
static const mapped_type empty_value;
};
const TagMap::mapped_type TagMap::empty_value = ;

templatetypename Base
class ModifyTagsHandler : public Base {
UErrorCode collator_error;
Collator *const collator;

public:
templatetypename... Args
ModifyTagsHandler(Args... args)
: Base(std::forwardArgs(args)...),
  collator_error(U_ZERO_ERROR),
  collator(Collator::createInstance(collator_error))
{
collator-setStrength(Collator::PRIMARY);
}

void node(const shared_ptrOsmium::OSM::Node n) {
do_tag_update(n-tags(), n-get_type());
Base::node(n);
}

void way(const shared_ptrOsmium::OSM::Way w) {
do_tag_update(w-tags(), w-get_type());
Base::way(w);
}

void relation(const shared_ptrOsmium::OSM::Relation r) {
do_tag_update(r-tags(), r-get_type());
Base::relation(r);
}

private:
void do_tag_update(Osmium::OSM::TagList tags, osm_object_type_t) {
using std::string;
TagMap m(tags);
UnicodeString oper = m.get(operator);

if (!oper.isEmpty()) {
static const Locale gb_locale(en, GB);
UErrorCode status = U_ZERO_ERROR;
StringSearch iter(oper, m.get(name), gb_locale, NULL, status);
//iter.getCollator()-setStrength(Collator::PRIMARY);
if (iter.first(status) != USEARCH_DONE) {
// name contains operator
m.erase(operator);
} else if (m.get(amenity) == post_box  oper.caseCompare(Royal Mail, U_FOLD_CASE_DEFAULT))
m.erase(operator);
else if (m.get(amenity) == telephone  oper.caseCompare(BT, U_FOLD_CASE_DEFAULT))
m.erase(operator);
}
tags = m;
}
};
#endif

#if 0
templatetypename Callback
class Multipolygon : public Base {
using std::vector;

/// a list of areas that need to be completed
vectorOsmium::OSM::AreaFromRelation* m_areas;

// a map from way_id to a vector of indexes into the areas array
// this is used to find in which multipolygon relations a way is
typedef google::sparse_hash_maposm_object_id_t, vectorosm_object_id_t  way2areaidx_t;
way2areaidx_t m_way2areaidx;

bool m_attempt_repair;
Callback 

Re: [mkgmap-dev] Computing area

2013-02-26 Thread Toby Speight
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::SparseTableOsmium::OSM::Position storage_sparsetable_t;
typedef Osmium::Storage::MmapOsmium::OSM::Position storage_mmap_t;
typedef Osmium::Handler::CoordinatesForWaysstorage_sparsetable_t, storage_mmap_t cfw_handler_t;


templatetypename 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:

templatetypename... Args
ComputeAreaHandler(Args... args)
: Base(std::forwardArgs(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_ptrOsmium::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_ptrOsmium::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_ptrOsmium::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)))
+