From effddd5ed3a1080a0b3f1605d954bd0c914c9f91 Mon Sep 17 00:00:00 2001 From: vng Date: Thu, 6 Jan 2011 21:56:36 +0200 Subject: [PATCH] - Geometry and Triangles simplification in features. - Store Geometry and Triangles in different files of files-container for each scale range. - Make small epsilon for simplification. Remark: map_tests doesn't work --- data/minsk-pass.mwm | Bin 70537 -> 77508 bytes indexer/covering.cpp | 2 +- indexer/covering.hpp | 4 +- indexer/feature.cpp | 642 ++++++++++-------- indexer/feature.hpp | 279 ++++---- indexer/feature_impl.hpp | 126 +++- indexer/feature_processor.hpp | 11 +- .../indexer_tests/feature_bucketer_test.cpp | 16 +- indexer/indexer_tests/feature_routine.cpp | 39 +- indexer/indexer_tests/feature_routine.hpp | 7 +- indexer/indexer_tests/feature_test.cpp | 53 +- indexer/indexer_tool/feature_bucketer.hpp | 2 +- indexer/indexer_tool/feature_generator.cpp | 6 +- indexer/indexer_tool/feature_generator.hpp | 7 +- indexer/indexer_tool/feature_sorter.cpp | 70 +- indexer/indexer_tool/osm_element.hpp | 18 +- indexer/indexer_tool/tesselator.cpp | 33 +- indexer/scale_index_builder.hpp | 3 +- indexer/scales.cpp | 15 +- indexer/scales.hpp | 1 + map/feature_vec_model.hpp | 15 - map/map_tests/map_foreach_test.cpp | 8 +- qt/draw_widget.hpp | 1 - qt/searchwindow.hpp | 1 - 24 files changed, 732 insertions(+), 627 deletions(-) diff --git a/data/minsk-pass.mwm b/data/minsk-pass.mwm index a8119b134e832805039d14013efad7fdee1ed005..9c9b10ce171d93e93a766a12c84d43c109200e78 100644 GIT binary patch delta 33495 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 DQvDYM delta 28692 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 covering::CoverFeature(FeatureGeom const & feature, int level) +vector covering::CoverFeature(FeatureType const & feature, int level) { vector geometry; feature.ForEachPoint(MakeBackInsertFunctor(geometry), level); diff --git a/indexer/covering.hpp b/indexer/covering.hpp index 9cc0b5a781..54452194c8 100644 --- a/indexer/covering.hpp +++ b/indexer/covering.hpp @@ -7,12 +7,12 @@ #include "../std/utility.hpp" #include "../std/vector.hpp" -class FeatureGeom; +class FeatureType; namespace covering { // Cover feature with RectIds and return their integer representations. - vector CoverFeature(FeatureGeom const & feature, int level); + vector CoverFeature(FeatureType const & feature, int level); // Cover viewport with RectIds and append their RectIds as well. vector > CoverViewportAndAppendLowerLevels(m2::RectD const & rect); // Given a vector of intervals [a, b), sort them and merge overlapping intervals. diff --git a/indexer/feature.cpp b/indexer/feature.cpp index 4c4c9803fc..9efed8e853 100644 --- a/indexer/feature.cpp +++ b/indexer/feature.cpp @@ -14,68 +14,65 @@ #include "../base/start_mem_debug.hpp" -namespace pts -{ - inline m2::PointD ToPoint(int64_t i) - { - CoordPointT const pt = Int64ToPoint(i); - return m2::PointD(pt.first, pt.second); - } - - struct Fpt2id - { - int64_t operator() (m2::PointD const & p) const - { - return PointToInt64(p.x, p.y); - } - }; -} - /////////////////////////////////////////////////////////////////////////////////////////////////// -// FeatureBuilderGeom implementation +// FeatureBuilder1 implementation /////////////////////////////////////////////////////////////////////////////////////////////////// -FeatureBuilderGeom::FeatureBuilderGeom() : m_Layer(0) +FeatureBuilder1::FeatureBuilder1() +: m_Layer(0), m_bArea(false), m_bHasCenter(false) { } -bool FeatureBuilderGeom::IsGeometryClosed() const +bool FeatureBuilder1::IsGeometryClosed() const { return !m_Geometry.empty() && m_Geometry.front() == m_Geometry.back(); } -void FeatureBuilderGeom::AddPoint(m2::PointD const & p) +void FeatureBuilder1::SetCenter(m2::PointD const & p) +{ + m_Center = p; + m_bHasCenter = true; + m_LimitRect.Add(p); +} + +void FeatureBuilder1::AddPoint(m2::PointD const & p) { m_Geometry.push_back(p); m_LimitRect.Add(p); } -void FeatureBuilderGeom::AddTriangle(m2::PointD const & a, m2::PointD const & b, m2::PointD const & c) +void FeatureBuilder1::SetAreaAddHoles(list > & holes) { - pts::Fpt2id fn; - m_Triangles.push_back(fn(a)); - m_Triangles.push_back(fn(b)); - m_Triangles.push_back(fn(c)); + m_bArea = true; + + m_Holes.swap(holes); + + for (list::iterator i = m_Holes.begin(); i != m_Holes.end();) + { + if (i->size() < 3) + i = m_Holes.erase(i); + else + ++i; + } } -void FeatureBuilderGeom::AddName(string const & name) +void FeatureBuilder1::AddName(string const & name) { - CHECK_EQUAL(m_Name, "", (name)); m_Name = name; } -void FeatureBuilderGeom::AddLayer(int32_t layer) +void FeatureBuilder1::AddLayer(int32_t layer) { - CHECK_EQUAL(m_Layer, 0, (layer)); - int const bound = 10; if (layer < -bound) layer = -bound; else if (layer > bound) layer = bound; m_Layer = layer; } -FeatureBase FeatureBuilderGeom::GetFeatureBase() const +FeatureBase FeatureBuilder1::GetFeatureBase() const { + CHECK ( CheckValid(), () ); + FeatureBase f; f.SetHeader(GetHeader()); @@ -97,6 +94,12 @@ namespace //return my::AlmostEqual(d1, d2, 100000000); return (fabs(d1 - d2) < MercatorBounds::GetCellID2PointAbsEpsilon()); } + + bool is_equal(m2::PointD const & p1, m2::PointD const & p2) + { + return p1.EqualDxDy(p2, MercatorBounds::GetCellID2PointAbsEpsilon()); + } + bool is_equal(m2::RectD const & r1, m2::RectD const & r2) { return (is_equal(r1.minX(), r2.minX()) && @@ -104,121 +107,200 @@ namespace is_equal(r1.maxX(), r2.maxX()) && is_equal(r1.maxY(), r2.maxY())); } -} -bool FeatureBuilderGeom::operator == (FeatureBuilderGeom const & fb) const -{ - if (m_Geometry.size() != fb.m_Geometry.size()) - return false; - - double const eps = MercatorBounds::GetCellID2PointAbsEpsilon(); - for (size_t i = 0; i < m_Geometry.size(); ++i) - if (!m_Geometry[i].EqualDxDy(fb.m_Geometry[i], eps)) + bool is_equal(vector const & v1, vector const & v2) + { + if (v1.size() != v2.size()) return false; - return - m_Types == fb.m_Types && - m_Layer == fb.m_Layer && - m_Name == fb.m_Name && - m_Triangles == fb.m_Triangles && - is_equal(m_LimitRect, fb.m_LimitRect); + for (size_t i = 0; i < v1.size(); ++i) + if (!is_equal(v1[i], v2[i])) + return false; + + return true; + } } -uint8_t FeatureBuilderGeom::GetHeader() const +bool FeatureBuilder1::operator == (FeatureBuilder1 const & fb) const +{ + if (m_Types != fb.m_Types || + m_Layer != fb.m_Layer || + m_Name != fb.m_Name || + m_bHasCenter != fb.m_bHasCenter || + m_bArea != fb.m_bArea) + { + return false; + } + + if (m_bHasCenter && !is_equal(m_Center, fb.m_Center)) + return false; + + if (!is_equal(m_LimitRect, fb.m_LimitRect)) + return false; + + if (!is_equal(m_Geometry, fb.m_Geometry)) + return false; + + if (m_Holes.size() != fb.m_Holes.size()) + return false; + + list::const_iterator i = m_Holes.begin(); + list::const_iterator j = fb.m_Holes.begin(); + for (; i != m_Holes.end(); ++i, ++j) + if (!is_equal(*i, *j)) + return false; + + return true; +} + +bool FeatureBuilder1::CheckValid() const +{ + CHECK(!m_Types.empty() && m_Types.size() <= m_maxTypesCount, ()); + + CHECK(m_Layer >= -10 && m_Layer <= 10, ()); + + CHECK(m_bHasCenter || m_Geometry.size() >= 2, ()); + + CHECK(!m_bArea || m_Geometry.size() >= 3, ()); + + CHECK(m_Holes.empty() || m_bArea, ()); + + for (list::const_iterator i = m_Holes.begin(); i != m_Holes.end(); ++i) + CHECK(i->size() >= 3, ()); + + return true; +} + +uint8_t FeatureBuilder1::GetHeader() const { uint8_t header = static_cast(m_Types.size()); - if (m_Layer != 0) - header |= FeatureBase::HEADER_HAS_LAYER; - if (m_Geometry.size() > 1) - { - if (m_Triangles.empty()) - header |= FeatureBase::HEADER_IS_LINE; - else - header |= FeatureBase::HEADER_IS_AREA; - } + if (!m_Name.empty()) header |= FeatureBase::HEADER_HAS_NAME; + + if (m_Layer != 0) + header |= FeatureBase::HEADER_HAS_LAYER; + + if (m_bHasCenter) + header |= FeatureBase::HEADER_HAS_POINT; + + size_t const count = m_Geometry.size(); + + if (count > 0) + { + ASSERT ( count > 1, (count) ); + header |= FeatureBase::HEADER_IS_LINE; + + if (m_bArea) + { + ASSERT ( count > 2, (count) ); + header |= FeatureBase::HEADER_IS_AREA; + } + } + return header; } -void FeatureBuilderGeom::SerializeBase(buffer_t & data) const +void FeatureBuilder1::SerializeBase(buffer_t & data) const { - CHECK(!m_Geometry.empty(), ()); - CHECK(m_Geometry.size() > 1 || m_Triangles.empty(), ()); - CHECK_LESS(m_Types.size(), 16, ()); + CHECK ( CheckValid(), () ); PushBackByteSink sink(data); - // Serialize header. WriteToSink(sink, GetHeader()); - // Serialize types. - { - for (size_t i = 0; i < m_Types.size(); ++i) - WriteVarUint(sink, m_Types[i]); - } + for (size_t i = 0; i < m_Types.size(); ++i) + WriteVarUint(sink, m_Types[i]); - // Serialize layer. if (m_Layer != 0) WriteVarInt(sink, m_Layer); - // Serialize name. if (!m_Name.empty()) { WriteVarUint(sink, m_Name.size() - 1); sink.Write(&m_Name[0], m_Name.size()); } + + if (m_bHasCenter) + WriteVarInt(sink, feature::pts::FromPoint(m_Center)); } -void FeatureBuilderGeom::Serialize(buffers_holder_t & data) const +void FeatureBuilder1::Serialize(buffer_t & data) const { data.clear(); SerializeBase(data); - SerializePoints(data); - SerializeTriangles(data); - ASSERT ( CheckCorrect(data), () ); -} - -void FeatureBuilderGeom::SerializeTriangles(buffer_t & data) const -{ PushBackByteSink sink(data); - feature::SerializeTriangles(m_Triangles, sink); + + if (!m_Geometry.empty()) + feature::SavePoints(m_Geometry, sink); + + if (m_bArea) + { + WriteVarUint(sink, uint32_t(m_Holes.size())); + + for (list::const_iterator i = m_Holes.begin(); i != m_Holes.end(); ++i) + feature::SavePoints(*i, sink); + } + + // check for correct serialization +#ifdef DEBUG + buffer_t tmp(data); + FeatureBuilder1 fb; + fb.Deserialize(tmp); + ASSERT ( fb == *this, () ); +#endif } -void FeatureBuilderGeom::SerializePoints(buffer_t & data) const +namespace { - PushBackByteSink sink(data); - feature::SerializePoints(m_Geometry, sink); + void CalcRect(vector const & points, m2::RectD & rect) + { + for (size_t i = 0; i < points.size(); ++i) + rect.Add(points[i]); + } } -bool FeatureBuilderGeom::CheckCorrect(vector const & data) const +void FeatureBuilder1::Deserialize(buffer_t & data) { - FeatureGeom::read_source_t src; - src.m_data = data; - FeatureGeom f(src); + FeatureBase f; + f.Deserialize(data, 0); + f.InitFeatureBuilder(*this); - FeatureBuilderGeom fb; - f.InitFeatureBuilder(fb); + ArrayByteSource src(f.DataPtr() + f.m_GeometryOffset); - string const s = f.DebugString(); + FeatureBase::FeatureType const ft = f.GetFeatureType(); - ASSERT_EQUAL(m_Layer, f.m_Layer, (s)); - ASSERT_EQUAL(m_Name, f.m_Name, (s)); - ASSERT(is_equal(m_LimitRect, f.m_LimitRect), (s)); - ASSERT(*this == fb, (s)); + if (ft != FeatureBase::FEATURE_TYPE_POINT) + { + feature::LoadPoints(m_Geometry, src); - return true; + CalcRect(m_Geometry, m_LimitRect); + } + + if (ft == FeatureBase::FEATURE_TYPE_AREA) + { + m_bArea = true; + + uint32_t const count = ReadVarUint(src); + for (uint32_t i = 0; i < count; ++i) + { + m_Holes.push_back(points_t()); + feature::LoadPoints(m_Holes.back(), src); + } + } + + CHECK ( CheckValid(), () ); } /////////////////////////////////////////////////////////////////////////////////////////////////// // FeatureBuilderGeomRef implementation /////////////////////////////////////////////////////////////////////////////////////////////////// -bool FeatureBuilderGeomRef::IsDrawableLikeLine(int lowS, int highS) const +bool FeatureBuilder2::IsDrawableLikeLine(int lowS, int highS) const { - if (m_Geometry.size() > 1) + if (!m_Geometry.empty()) { FeatureBase const fb = GetFeatureBase(); @@ -230,45 +312,48 @@ bool FeatureBuilderGeomRef::IsDrawableLikeLine(int lowS, int highS) const return false; } -void FeatureBuilderGeomRef::Serialize(buffers_holder_t & data) const +void FeatureBuilder2::SerializeOffsets(uint32_t mask, offsets_t const & offsets, buffer_t & buffer) +{ + if (mask > 0) + { + PushBackByteSink sink(buffer); + + WriteVarUint(sink, mask); + for (size_t i = 0; i < offsets.size(); ++i) + WriteVarUint(sink, offsets[i]); + } +} + +void FeatureBuilder2::Serialize(buffers_holder_t & data) { data.m_buffer.clear(); + // make flags actual before header serialization + if (data.m_lineMask == 0) + m_Geometry.clear(); + + if (data.m_trgMask == 0) + m_bArea = false; + + // header data serialization SerializeBase(data.m_buffer); - PushBackByteSink sink(data.m_buffer); - - // for point feature write geometry-point immediately - if (m_Geometry.size() == 1) - { - SerializePoints(data.m_buffer); - } - else - { - CHECK(data.m_mask > 0, (data.m_mask)); // feature should be visible - - // serialize geometry offsets - WriteVarUint(sink, data.m_mask); - - for (size_t i = 0; i < data.m_lineOffset.size(); ++i) - WriteVarUint(sink, data.m_lineOffset[i]); - - for (size_t i = 0; i < data.m_trgOffset.size(); ++i) - WriteVarUint(sink, data.m_trgOffset[i]); - } + // geometry offsets serialization + SerializeOffsets(data.m_lineMask, data.m_lineOffset, data.m_buffer); + SerializeOffsets(data.m_trgMask, data.m_trgOffset, data.m_buffer); } /////////////////////////////////////////////////////////////////////////////////////////////////// // FeatureBase implementation /////////////////////////////////////////////////////////////////////////////////////////////////// -void FeatureBase::Deserialize(vector & data, uint32_t offset) +void FeatureBase::Deserialize(buffer_t & data, uint32_t offset) { m_Offset = offset; m_Data.swap(data); - m_LayerOffset = m_GeometryOffset = m_TrianglesOffset = m_NameOffset = 0; - m_bTypesParsed = m_bLayerParsed = m_bGeometryParsed = m_bTrianglesParsed = m_bNameParsed = false; + m_LayerOffset = m_NameOffset = m_CenterOffset = m_GeometryOffset = m_TrianglesOffset = 0; + m_bTypesParsed = m_bLayerParsed = m_bNameParsed = m_bCenterParsed = m_bGeometryParsed = m_bTrianglesParsed = false; m_Layer = 0; m_Name.clear(); @@ -327,256 +412,211 @@ void FeatureBase::ParseName() const } m_bNameParsed = true; + m_CenterOffset = CalcOffset(source); +} + +void FeatureBase::ParseCenter() const +{ + ASSERT(!m_bCenterParsed, ()); + if (!m_bNameParsed) + ParseName(); + + ArrayByteSource source(DataPtr() + m_CenterOffset); + if (Header() & HEADER_HAS_POINT) + { + m_Center = feature::pts::ToPoint(ReadVarInt(source)); + m_LimitRect.Add(m_Center); + } + + m_bCenterParsed = true; m_GeometryOffset = CalcOffset(source); } +void FeatureBase::ParseAll() const +{ + if (!m_bCenterParsed) + ParseCenter(); +} + string FeatureBase::DebugString() const { ASSERT(m_bNameParsed, ()); - string res("Feature("); + string res("FEATURE: "); res += "'" + m_Name + "' "; for (size_t i = 0; i < GetTypesCount(); ++i) res += "Type:" + debug_print(m_Types[i]) + " "; res += "Layer:" + debug_print(m_Layer) + " "; + + if (Header() & HEADER_HAS_POINT) + res += "Center:" + debug_print(m_Center) + " "; + return res; } -void FeatureBase::InitFeatureBuilder(FeatureBuilderGeom & fb) const +void FeatureBase::InitFeatureBuilder(FeatureBuilder1 & fb) const { - ASSERT(m_bNameParsed, ()); + ParseAll(); fb.AddTypes(m_Types, m_Types + GetTypesCount()); fb.AddLayer(m_Layer); fb.AddName(m_Name); + + uint8_t const h = Header(); + + if (h & HEADER_HAS_POINT) + fb.SetCenter(m_Center); + + if (h & HEADER_IS_AREA) + fb.SetAreaAddHoles(list >()); } /////////////////////////////////////////////////////////////////////////////////////////////////// -// FeatureGeom implementation +// FeatureType implementation /////////////////////////////////////////////////////////////////////////////////////////////////// -FeatureGeom::FeatureGeom(read_source_t & src) +FeatureType::FeatureType(read_source_t & src) { Deserialize(src); } -void FeatureGeom::Deserialize(read_source_t & src) +void FeatureType::Deserialize(read_source_t & src) { + m_cont = &src.m_cont; + + m_bOffsetsParsed = false; + m_lineOffsets.clear(); + m_trgOffsets.clear(); + base_type::Deserialize(src.m_data, src.m_offset); - - m_Geometry.clear(); - m_Triangles.clear(); } -template -void FeatureGeom::ParseGeometryImpl(TSource & src) const +uint32_t FeatureType::GetOffset(int scale, offsets_t const & offsets) const { - ASSERT(!m_bGeometryParsed, ()); - uint32_t const geometrySize = - (GetFeatureType() == FEATURE_TYPE_POINT ? 1 : ReadVarUint(src) + 1); + for (size_t i = 0; i < ARRAY_SIZE(feature::g_arrScales); ++i) + if (scale <= feature::g_arrScales[i]) + return offsets[i]; - m_Geometry.resize(geometrySize); - int64_t id = 0; - for (size_t i = 0; i < geometrySize; ++i) - m_LimitRect.Add(m_Geometry[i] = pts::ToPoint(id += ReadVarInt(src))); - - m_bGeometryParsed = true; + return m_invalidOffset; } -void FeatureGeom::ParseGeometry(int) const +namespace { - if (!m_bNameParsed) - ParseName(); - - ArrayByteSource source(DataPtr() + m_GeometryOffset); - ParseGeometryImpl(source); - - m_TrianglesOffset = CalcOffset(source); -} - -template -void FeatureGeom::ParseTrianglesImpl(TSource & src) const -{ - ASSERT(!m_bTrianglesParsed, ()); - if (GetFeatureType() == FEATURE_TYPE_AREA) + void Points2String(string & s, vector const & points) { - uint32_t const trgPoints = (ReadVarUint(src) + 1) * 3; - m_Triangles.resize(trgPoints); - int64_t id = 0; - for (size_t i = 0; i < trgPoints; ++i) - m_Triangles[i] = pts::ToPoint(id += ReadVarInt(src)); + for (size_t i = 0; i < points.size(); ++i) + s += debug_print(points[i]) + " "; } - m_bTrianglesParsed = true; } -void FeatureGeom::ParseTriangles(int scale) const +string FeatureType::DebugString(int scale) const { - if (!m_bGeometryParsed) - ParseGeometry(scale); + // force to load all geometry + (void)GetLimitRect(scale); - ArrayByteSource source(DataPtr() + m_TrianglesOffset); - ParseTrianglesImpl(source); + string s = base_type::DebugString(); - ASSERT_EQUAL ( CalcOffset(source), m_Data.size() - m_Offset, () ); + s += "Points:"; + Points2String(s, m_Geometry); + + s += "Triangles:"; + Points2String(s, m_Triangles); + + return s; } -void FeatureGeom::ParseAll(int scale) const +m2::RectD FeatureType::GetLimitRect(int scale) const { if (!m_bGeometryParsed) ParseGeometry(scale); if (!m_bTrianglesParsed) ParseTriangles(scale); -} -string FeatureGeom::DebugString(int scale) const -{ - ParseAll(scale); - string res = base_type::DebugString(); - res += debug_print(m_Geometry) + " "; - res += debug_print(m_Triangles) + ")"; - return res; -} - -string FeatureGeom::DebugString() const -{ - string ret = DebugString(m_defScale); - ASSERT ( !ret.empty(), () ); - return ret; -} - -void FeatureGeom::InitFeatureBuilder(FeatureBuilderGeom & fb) const -{ - ParseAll(m_defScale); - base_type::InitFeatureBuilder(fb); - - for (size_t i = 0; i < m_Geometry.size(); ++i) - fb.AddPoint(m_Geometry[i]); - - ASSERT_EQUAL(m_Triangles.size() % 3, 0, ()); - uint32_t const triangleCount = m_Triangles.size() / 3; - for (size_t i = 0; i < triangleCount; ++i) - fb.AddTriangle(m_Triangles[3*i + 0], m_Triangles[3*i + 1], m_Triangles[3*i + 2]); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -// FeatureGeomRef implementation -/////////////////////////////////////////////////////////////////////////////////////////////////// - -FeatureGeomRef::FeatureGeomRef(read_source_t & src) - : base_type(src), m_cont(&src.m_cont) -{ -} - -void FeatureGeomRef::Deserialize(read_source_t & src) -{ - m_cont = &src.m_cont; - - m_bOffsetsParsed = false; - m_mask = 0; - m_gOffsets.clear(); - - base_type::Deserialize(src); -} - -uint32_t FeatureGeomRef::GetOffset(int scale) const -{ - for (size_t i = 0; i < ARRAY_SIZE(feature::g_arrScales); ++i) - if (scale <= feature::g_arrScales[i]) - return m_gOffsets[i]; - - return m_invalidOffset; -} - -string FeatureGeomRef::DebugString(int scale) const -{ - if (!m_bOffsetsParsed) - ParseOffsets(); - - if (!m_bGeometryParsed && GetOffset(scale) == m_invalidOffset) - return string(); - else - return base_type::DebugString(scale); -} - -namespace -{ - inline string get_tag(char const * prefix, int scale) + if (m_Triangles.empty() && m_Geometry.empty() && (Header() & HEADER_HAS_POINT) == 0) { - string str; - str.reserve(strlen(prefix) + 1); - str = prefix; - - static char arrChar[] = { '0', '1', '2', '3', '4', '5' }; - for (size_t i = 0; i < ARRAY_SIZE(feature::g_arrScales); ++i) - if (scale <= feature::g_arrScales[i]) - { - str += arrChar[i]; - break; - } - - return str; + // This function is called during indexing, when we need + // to check visibility according to feature sizes. + // So, if no geometry for this scale, assume tha rect has zero dimensions. + m_LimitRect = m2::RectD(0, 0, 0, 0); } + + return m_LimitRect; } -void FeatureGeomRef::ParseGeometry(int scale) const +void FeatureType::ParseGeometry(int scale) const { if (!m_bOffsetsParsed) ParseOffsets(); - if (m_bGeometryParsed) - return; - - ReaderSource source(m_cont->GetReader(get_tag(GEOMETRY_FILE_TAG, scale))); - uint32_t const offset = GetOffset(scale); - CHECK ( offset != m_invalidOffset, (offset) ); - source.Skip(offset); - - ParseGeometryImpl(source); -} - -void FeatureGeomRef::ParseTriangles(int scale) const -{ - if (!m_bOffsetsParsed) - ParseOffsets(); - - ReaderSource source(m_cont->GetReader(get_tag(TRIANGLE_FILE_TAG, 0))); - source.Skip(m_trgOffset); - - ParseTrianglesImpl(source); -} - -void FeatureGeomRef::ParseOffsets() const -{ - if (!m_bNameParsed) - ParseName(); - - ArrayByteSource source(DataPtr() + m_GeometryOffset); - FeatureType const type = GetFeatureType(); - - if (type == FEATURE_TYPE_POINT) + if (Header() & HEADER_IS_LINE) { - ParseGeometryImpl(source); - } - else - { - uint32_t mask = m_mask = ReadVarUint(source); - ASSERT ( mask > 0, () ); - while (mask > 0) + uint32_t const offset = GetOffset(scale, m_lineOffsets); + if (offset != m_invalidOffset) { - if (mask & 0x01) - m_gOffsets.push_back(ReadVarUint(source)); - else - m_gOffsets.push_back((uint32_t)m_invalidOffset); + ReaderSource src(m_cont->GetReader(feature::GetTagForScale(GEOMETRY_FILE_TAG, scale))); + src.Skip(offset); + feature::LoadPoints(m_Geometry, src); - mask = mask >> 1; + CalcRect(m_Geometry, m_LimitRect); } - - if (type == FEATURE_TYPE_AREA) - m_trgOffset = ReadVarUint(source); } + m_bGeometryParsed = true; +} + +void FeatureType::ParseTriangles(int scale) const +{ + if (!m_bOffsetsParsed) + ParseOffsets(); + + if (Header() & HEADER_IS_AREA) + { + uint32_t const offset = GetOffset(scale, m_trgOffsets); + if (offset != m_invalidOffset) + { + ReaderSource src(m_cont->GetReader(feature::GetTagForScale(TRIANGLE_FILE_TAG, scale))); + src.Skip(offset); + feature::LoadTriangles(m_Triangles, src); + + CalcRect(m_Triangles, m_LimitRect); + } + } + + m_bTrianglesParsed = true; +} + +void FeatureType::ReadOffsetsImpl(ArrayByteSource & src, offsets_t & offsets) +{ + uint32_t mask = ReadVarUint(src); + ASSERT ( mask > 0, () ); + while (mask > 0) + { + if (mask & 0x01) + offsets.push_back(ReadVarUint(src)); + else + offsets.push_back((uint32_t)m_invalidOffset); + + mask = mask >> 1; + } +} + +void FeatureType::ParseOffsets() const +{ + if (!m_bCenterParsed) + ParseCenter(); + + ArrayByteSource src(DataPtr() + m_GeometryOffset); + + uint8_t const h = Header(); + + if (h & HEADER_IS_LINE) + ReadOffsetsImpl(src, m_lineOffsets); + + if (h & HEADER_IS_AREA) + ReadOffsetsImpl(src, m_trgOffsets); + m_bOffsetsParsed = true; } diff --git a/indexer/feature.hpp b/indexer/feature.hpp index 75c3d2facf..89b042d0fd 100644 --- a/indexer/feature.hpp +++ b/indexer/feature.hpp @@ -18,20 +18,32 @@ class ArrayByteSource; class FeatureBase; -class FeatureBuilderGeom +/// Used for serialization\deserialization of features during --generate_features. +class FeatureBuilder1 { public: - FeatureBuilderGeom(); + FeatureBuilder1(); + + /// @name Geometry manipulating functions. + //@{ + /// Set center (origin) point of feature. + void SetCenter(m2::PointD const & p); + + /// Add point to geometry. + void AddPoint(m2::PointD const & p); + + /// Set that featue is area and get ownership of holes. + void SetAreaAddHoles(list > & holes); + //@} void AddName(string const & name); - void AddPoint(m2::PointD const & p); - void AddTriangle(m2::PointD const & a, m2::PointD const & b, m2::PointD const & c); + + static const int m_maxTypesCount = 7; template inline void AddTypes(TIter beg, TIter end) { - // 15 - is the maximum count of types (@see Feature::GetTypesCount()) - size_t const count = min(15, static_cast(distance(beg, end))); + int const count = min(m_maxTypesCount, static_cast(distance(beg, end))); m_Types.assign(beg, beg + count); } inline void SetType(uint32_t type) @@ -43,71 +55,113 @@ public: void AddLayer(int32_t layer); typedef vector buffer_t; - typedef buffer_t buffers_holder_t; - void Serialize(buffers_holder_t & data) const; + /// @name Serialization. + //@{ + void Serialize(buffer_t & data) const; + void SerializeBase(buffer_t & data) const; + + void Deserialize(buffer_t & data); + //@} + + ///@name Selectors. + //@{ inline m2::RectD GetLimitRect() const { return m_LimitRect; } - // Get common parameters of feature. + /// Get common parameters of feature. FeatureBase GetFeatureBase() const; bool IsGeometryClosed() const; - size_t GetPointsCount() const { return m_Geometry.size(); } - bool operator == (FeatureBuilderGeom const &) const; + inline size_t GetPointsCount() const { return m_Geometry.size(); } + + template + void ForEachPointRef(ToDo & toDo) const + { + for_each(m_Geometry.begin(), m_Geometry.end(), bind(ref(toDo), _1)); + } + //@} protected: - typedef vector points_t; - void SerializeBase(buffer_t & data) const; - void SerializePoints(buffer_t & data) const; - void SerializeTriangles(buffer_t & data) const; + /// @name For diagnostic use only. + //@{ + bool operator == (FeatureBuilder1 const &) const; + + bool CheckValid() const; + //@} + + typedef vector points_t; uint8_t GetHeader() const; - bool CheckCorrect(vector const & data) const; - - int32_t m_Layer; + /// Name. Can be empty. Check HEADER_HAS_NAME. string m_Name; + + /// Feature classificator-types. Can not be empty. vector m_Types; + /// Drawable layer of feature. Can be empty, Check HEADER_HAS_LAYER. + int32_t m_Layer; + m2::RectD m_LimitRect; - points_t m_Geometry; // store points as is for further processing + /// Can be one of the following: + /// - point in point-feature + /// - origin point of text [future] in line-feature + /// - origin point of text or symbol in area-feature + m2::PointD m_Center; // Check HEADER_HAS_POINT - vector m_Triangles; + /// Can be one of the following: + /// - geometry in line-feature + /// - boundary in area-feature + points_t m_Geometry; // Check HEADER_IS_LINE + + /// List of holes in area-feature. + list m_Holes; // Check HEADER_IS_AREA + + bool m_bArea; ///< this is area-feature + bool m_bHasCenter; ///< m_center exists }; -class FeatureBuilderGeomRef : public FeatureBuilderGeom +/// Used for serialization of features during final pass. +class FeatureBuilder2 : public FeatureBuilder1 { - typedef FeatureBuilderGeom base_type; + typedef FeatureBuilder1 base_type; + + typedef vector offsets_t; + + static void SerializeOffsets(uint32_t mask, offsets_t const & offsets, buffer_t & buffer); public: struct buffers_holder_t { - vector m_lineOffset; // in - vector m_trgOffset; // in - uint32_t m_mask; + offsets_t m_lineOffset; // in + offsets_t m_trgOffset; // in + uint32_t m_lineMask, m_trgMask; base_type::buffer_t m_buffer; // out - buffers_holder_t() : m_mask(0) {} + buffers_holder_t() : m_lineMask(0), m_trgMask(0) {} }; bool IsDrawableLikeLine(int lowS, int highS) const; + bool IsDrawableLikeArea() const { return m_bArea; } points_t const & GetGeometry() const { return m_Geometry; } - vector const & GetTriangles() const { return m_Triangles; } + list const & GetHoles() const { return m_Holes; } /// @name Overwrite from base_type. //@{ - void Serialize(buffers_holder_t & data) const; + void Serialize(buffers_holder_t & data); //@} }; +/// Base feature class for storing common data (without geometry). class FeatureBase { + static const int m_maxTypesCount = 7; public: enum FeatureType { @@ -119,22 +173,25 @@ public: FeatureBase() : m_Offset(0) {} - /// @name Use like polymorfic functions. Need to overwrite in derived classes. - //@{ - void Deserialize(vector & data, uint32_t offset = 0); - string DebugString() const; - void InitFeatureBuilder(FeatureBuilderGeom & fb) const; - //@} + typedef vector buffer_t; inline FeatureType GetFeatureType() const { - ASSERT_NOT_EQUAL((Header() >> 4) & 3, 3, (DebugString())); - return static_cast((Header() >> 4) & 3); + uint8_t const h = Header(); + if (h & HEADER_IS_AREA) + return FEATURE_TYPE_AREA; + else if (h & HEADER_IS_LINE) + return FEATURE_TYPE_LINE; + else + { + ASSERT ( h & HEADER_HAS_POINT, () ); + return FEATURE_TYPE_POINT; + } } inline uint32_t GetTypesCount() const { - return Header() & 0xF; + return Header() & m_maxTypesCount; } inline int32_t GetLayer() const @@ -157,7 +214,7 @@ public: inline m2::RectD GetLimitRect() const { - ASSERT(m_bGeometryParsed, ()); + ASSERT ( m_bGeometryParsed || m_bTrianglesParsed, () ); return m_LimitRect; } @@ -166,7 +223,7 @@ public: public: vector m_types; - GetTypesFn() { m_types.reserve(16); } + GetTypesFn() { m_types.reserve(m_maxTypesCount); } void operator() (uint32_t t) { m_types.push_back(t); @@ -178,6 +235,7 @@ public: { if (!m_bTypesParsed) ParseTypes(); + uint32_t const typeCount = GetTypesCount(); for (size_t i = 0; i < typeCount; ++i) f(m_Types[i]); @@ -188,14 +246,22 @@ public: HEADER_HAS_LAYER = 1U << 7, HEADER_HAS_NAME = 1U << 6, HEADER_IS_AREA = 1U << 5, - HEADER_IS_LINE = 1U << 4 + HEADER_IS_LINE = 1U << 4, + HEADER_HAS_POINT = 1U << 3 }; + void InitFeatureBuilder(FeatureBuilder1 & fb) const; + protected: - vector m_Data; + void Deserialize(buffer_t & data, uint32_t offset = 0); + string DebugString() const; + +protected: + + buffer_t m_Data; uint32_t m_Offset; - friend class FeatureBuilderGeom; + friend class FeatureBuilder1; void SetHeader(uint8_t h); @@ -203,39 +269,48 @@ protected: inline uint8_t Header() const { return static_cast(*DataPtr()); } uint32_t CalcOffset(ArrayByteSource const & source) const; - mutable uint32_t m_Types[16]; + mutable uint32_t m_Types[m_maxTypesCount]; mutable int32_t m_Layer; mutable string m_Name; + mutable m2::PointD m_Center; mutable m2::RectD m_LimitRect; mutable uint32_t m_LayerOffset; mutable uint32_t m_NameOffset; + mutable uint32_t m_CenterOffset; mutable uint32_t m_GeometryOffset; mutable uint32_t m_TrianglesOffset; mutable bool m_bTypesParsed; mutable bool m_bLayerParsed; mutable bool m_bNameParsed; + mutable bool m_bCenterParsed; mutable bool m_bGeometryParsed; mutable bool m_bTrianglesParsed; void ParseTypes() const; void ParseLayer() const; void ParseName() const; + void ParseCenter() const; + + void ParseAll() const; }; -class FeatureGeom : public FeatureBase +/// Working feature class with geometry. +class FeatureType : public FeatureBase { typedef FeatureBase base_type; public: struct read_source_t { - vector m_data; + buffer_t m_data; uint32_t m_offset; - read_source_t() : m_offset(0) {} + FilesContainerR m_cont; + + read_source_t(FilesContainerR const & cont) : m_offset(0), m_cont(cont) {} void assign(char const * data, uint32_t size) { @@ -243,53 +318,30 @@ public: } }; - FeatureGeom() {} - FeatureGeom(read_source_t & src); + FeatureType() {} + FeatureType(read_source_t & src); - /// @name Overwrite from base_type. - //@{ void Deserialize(read_source_t & src); - string DebugString() const; - void InitFeatureBuilder(FeatureBuilderGeom & fb) const; - //@} - inline m2::RectD GetLimitRect() const - { - if (!m_bGeometryParsed) - { - // this function use only in index generation, - // so get geometry for upper scale - ParseGeometry(m_defScale); - } - return base_type::GetLimitRect(); - } - - /// @name Used only in unit-tests. So remove it. + /// @name Geometry. //@{ - //inline uint32_t GetGeometrySize() const - //{ - // if (!m_bGeometryParsed) - // ParseGeometry(); - // return m_Geometry.size(); - //} - - //inline uint32_t GetTriangleCount() const - //{ - // if (!m_bTrianglesParsed) - // ParseTriangles(); - // return (m_Triangles.size() / 3); - //} - //@} - - static int const m_defScale = 17; + m2::RectD GetLimitRect(int scale) const; template void ForEachPointRef(FunctorT & f, int scale) const { if (!m_bGeometryParsed) ParseGeometry(scale); - for (size_t i = 0; i < m_Geometry.size(); ++i) - f(CoordPointT(m_Geometry[i].x, m_Geometry[i].y)); + + if (m_Geometry.empty()) + { + f(CoordPointT(m_Center.x, m_Center.y)); + } + else + { + for (size_t i = 0; i < m_Geometry.size(); ++i) + f(CoordPointT(m_Geometry[i].x, m_Geometry[i].y)); + } } template @@ -303,6 +355,7 @@ public: { if (!m_bTrianglesParsed) ParseTriangles(scale); + for (size_t i = 0; i < m_Triangles.size();) { f(m_Triangles[i], m_Triangles[i+1], m_Triangles[i+2]); @@ -317,64 +370,30 @@ public: ForEachTriangleRef(f, scale); f.EndPrimitive(); } + //@} -protected: - template void ParseGeometryImpl(TSource & src) const; - template void ParseTrianglesImpl(TSource & src) const; + /// For test cases only. + string DebugString(int scale) const; - virtual string DebugString(int scale) const; - virtual void ParseGeometry(int scale) const; - virtual void ParseTriangles(int scale) const; - - void ParseAll(int scale) const; +private: + void ParseOffsets() const; + void ParseGeometry(int scale) const; + void ParseTriangles(int scale) const; mutable vector m_Geometry; mutable vector m_Triangles; -}; -class FeatureGeomRef : public FeatureGeom -{ - typedef FeatureGeom base_type; - -public: - struct read_source_t : public base_type::read_source_t - { - FilesContainerR m_cont; - read_source_t(FilesContainerR const & cont) : m_cont(cont) {} - }; - - FeatureGeomRef() {} - FeatureGeomRef(read_source_t & src); - - void Deserialize(read_source_t & src); - - /// @name Overwrite from base_type. - //@{ - virtual string DebugString(int scale) const; -protected: - virtual void ParseGeometry(int scale) const; - virtual void ParseTriangles(int scale) const; - //@} - -private: FilesContainerR * m_cont; - void ParseOffsets() const; mutable bool m_bOffsetsParsed; + typedef vector offsets_t; + + static void ReadOffsetsImpl(ArrayByteSource & src, offsets_t & offsets); + static uint32_t const m_invalidOffset = uint32_t(-1); - uint32_t GetOffset(int scale) const; + uint32_t GetOffset(int scale, offsets_t const & offset) const; - mutable uint32_t m_mask; - mutable vector m_gOffsets; - mutable uint32_t m_trgOffset; + mutable offsets_t m_lineOffsets, m_trgOffsets; }; - -inline string debug_print(FeatureGeom const & f) -{ - return f.DebugString(); -} - -typedef FeatureGeomRef FeatureType; -typedef FeatureBuilderGeomRef FeatureBuilderType; diff --git a/indexer/feature_impl.hpp b/indexer/feature_impl.hpp index 8f7a914ec1..4177719b66 100644 --- a/indexer/feature_impl.hpp +++ b/indexer/feature_impl.hpp @@ -9,50 +9,110 @@ namespace feature { + namespace pts + { + inline int64_t FromPoint(m2::PointD const & p) + { + return PointToInt64(p.x, p.y); + } + + inline m2::PointD ToPoint(int64_t i) + { + CoordPointT const pt = Int64ToPoint(i); + return m2::PointD(pt.first, pt.second); + } + } + namespace detail { - struct pt_2_id + inline void TransformPoints(vector const & points, vector & cells) { - int64_t operator() (m2::PointD const & p) const - { - return PointToInt64(p.x, p.y); - } - }; - } - - template - void SerializePoints(vector const & points, TSink & sink) - { - uint32_t const ptsCount = points.size(); - ASSERT_GREATER_OR_EQUAL(ptsCount, 1, ()); - - vector geom; - geom.reserve(ptsCount); - transform(points.begin(), points.end(), back_inserter(geom), detail::pt_2_id()); - - if (ptsCount == 1) - { - WriteVarInt(sink, geom[0]); + cells.reserve(points.size()); + transform(points.begin(), points.end(), back_inserter(cells), &pts::FromPoint); } - else + + template + void WriteCells(vector & cells, TSink & sink) { - WriteVarUint(sink, ptsCount - 1); - for (size_t i = 0; i < ptsCount; ++i) - WriteVarInt(sink, i == 0 ? geom[0] : geom[i] - geom[i-1]); + for (size_t i = 0; i < cells.size(); ++i) + WriteVarInt(sink, i == 0 ? cells[0] : cells[i] - cells[i-1]); + } + + template + void ReadPoints(vector & points, TSource & src) + { + int64_t id = 0; + for (size_t i = 0; i < points.size(); ++i) + points[i] = pts::ToPoint(id += ReadVarInt(src)); } } template - void SerializeTriangles(vector triangles, TSink & sink) + void SavePoints(vector const & points, TSink & sink) { - if (!triangles.empty()) - { - ASSERT_EQUAL(triangles.size() % 3, 0, (triangles.size())); - WriteVarUint(sink, triangles.size() / 3 - 1); - for (size_t i = 0; i < triangles.size(); ++i) - WriteVarInt(sink, i == 0 ? triangles[i] : (triangles[i] - triangles[i-1])); - } + uint32_t const count = points.size(); + ASSERT_GREATER(count, 1, ()); + + vector cells; + detail::TransformPoints(points, cells); + + WriteVarUint(sink, count - 2); + + detail::WriteCells(cells, sink); } + template + void LoadPoints(vector & points, TSource & src) + { + uint32_t const count = ReadVarUint(src) + 2; + points.resize(count); + + detail::ReadPoints(points, src); + } + + template + void SaveTriangles(vector const & triangles, TSink & sink) + { + uint32_t const count = triangles.size(); + ASSERT_GREATER(count, 0, ()); + ASSERT_EQUAL(count % 3, 0, (count)); + + vector cells; + detail::TransformPoints(triangles, cells); + + WriteVarUint(sink, count / 3 - 1); + + detail::WriteCells(cells, sink); + } + + template + void LoadTriangles(vector & points, TSource & src) + { + uint32_t const count = 3 * (ReadVarUint(src) + 1); + points.resize(count); + + detail::ReadPoints(points, src); + } + + static int g_arrScales[] = { 5, 10, 14, 17 }; // 17 = scales::GetUpperScale() + + inline string GetTagForScale(char const * prefix, int scale) + { + string str; + str.reserve(strlen(prefix) + 1); + str = prefix; + + static char arrChar[] = { '0', '1', '2', '3' }; + STATIC_ASSERT ( ARRAY_SIZE(arrChar) == ARRAY_SIZE(g_arrScales) ); + + for (size_t i = 0; i < ARRAY_SIZE(feature::g_arrScales); ++i) + if (scale <= feature::g_arrScales[i]) + { + str += arrChar[i]; + break; + } + + return str; + } } diff --git a/indexer/feature_processor.hpp b/indexer/feature_processor.hpp index e9e09fe00d..f2cbcf443a 100644 --- a/indexer/feature_processor.hpp +++ b/indexer/feature_processor.hpp @@ -19,11 +19,11 @@ namespace feature /// Read feature from feature source. template - void ReadFromSource(TSource & src, FeatureGeom & f, typename FeatureGeom::read_source_t & buffer) + void ReadFromSourceRowFormat(TSource & src, FeatureBuilder1 & f) { uint32_t const sz = ReadVarUint(src); - buffer.m_data.resize(sz); - src.Read(&buffer.m_data[0], sz); + typename FeatureBuilder1::buffer_t buffer(sz); + src.Read(&buffer[0], sz); f.Deserialize(buffer); } @@ -41,11 +41,10 @@ namespace feature uint64_t const fSize = reader.Size(); // read features one by one - typename FeatureGeom::read_source_t buffer; while (currPos < fSize) { - FeatureGeom f; - ReadFromSource(src, f, buffer); + FeatureBuilder1 f; + ReadFromSourceRowFormat(src, f); toDo(f, currPos); currPos = src.Pos(); } diff --git a/indexer/indexer_tests/feature_bucketer_test.cpp b/indexer/indexer_tests/feature_bucketer_test.cpp index eec9b87415..7c449dca3f 100644 --- a/indexer/indexer_tests/feature_bucketer_test.cpp +++ b/indexer/indexer_tests/feature_bucketer_test.cpp @@ -23,11 +23,12 @@ namespace { } - void operator() (FeatureBuilderGeom const & fb) + void operator() (FeatureBuilder1 const & fb) { - FeatureGeom f; - FeatureBuilder2Feature(fb, f); - m_pContainer->push_back(f.DebugString()); + FeatureType f; + FeatureBuilder2Feature( + static_cast(const_cast(fb)), f); + m_pContainer->push_back(f.DebugString(0)); } private: @@ -47,14 +48,15 @@ UNIT_TEST(FeatureBucketerSmokeTest) map > out, expectedOut; FeatureBucketer bucketer(1, &out); - FeatureBuilderGeom fb; + FeatureBuilder2 fb; fb.AddPoint(m2::PointD(10, 10)); fb.AddPoint(m2::PointD(20, 20)); + fb.SetType(0); bucketer(fb); - FeatureGeom f; + FeatureType f; FeatureBuilder2Feature(fb, f); - expectedOut["3"].push_back(f.DebugString()); + expectedOut["3"].push_back(f.DebugString(0)); TEST_EQUAL(out, expectedOut, ()); vector bucketNames; diff --git a/indexer/indexer_tests/feature_routine.cpp b/indexer/indexer_tests/feature_routine.cpp index 41d258adaa..8e5ac75756 100644 --- a/indexer/indexer_tests/feature_routine.cpp +++ b/indexer/indexer_tests/feature_routine.cpp @@ -12,7 +12,7 @@ namespace class feature_source_initializer { string m_name; - FeatureGeomRef::read_source_t * m_source; + FeatureType::read_source_t * m_source; public: feature_source_initializer(string const & fName) @@ -20,10 +20,10 @@ namespace { } - FeatureGeomRef::read_source_t & get_source(vector & buffer) + FeatureType::read_source_t & get_source(vector & buffer) { delete m_source; - m_source = new FeatureGeomRef::read_source_t(FilesContainerR(m_name)); + m_source = new FeatureType::read_source_t(FilesContainerR(m_name)); m_source->m_data.swap(buffer); return *m_source; } @@ -36,14 +36,14 @@ namespace }; } -void FeatureBuilder2Feature(FeatureBuilderGeomRef const & fb, FeatureGeomRef & f) +void FeatureBuilder2Feature(FeatureBuilder2 & fb, FeatureType & f) { string const datFile = "indexer_tests_tmp.dat"; - FeatureBuilderGeomRef::buffers_holder_t buffers; + FeatureBuilder2::buffers_holder_t buffers; buffers.m_lineOffset.push_back(0); buffers.m_trgOffset.push_back(0); - buffers.m_mask = 1; + buffers.m_lineMask = 1; fb.Serialize(buffers); { @@ -51,13 +51,13 @@ void FeatureBuilder2Feature(FeatureBuilderGeomRef const & fb, FeatureGeomRef & f { FileWriter geom = writer.GetWriter(string(GEOMETRY_FILE_TAG) + '0'); - feature::SerializePoints(fb.GetGeometry(), geom); + feature::SavePoints(fb.GetGeometry(), geom); } - { - FileWriter trg = writer.GetWriter(string(TRIANGLE_FILE_TAG) + '0'); - feature::SerializeTriangles(fb.GetTriangles(), trg); - } + //{ + // FileWriter trg = writer.GetWriter(string(TRIANGLE_FILE_TAG) + '0'); + // feature::SaveTriangles(fb.GetTriangles(), trg); + //} writer.Finish(); } @@ -66,22 +66,7 @@ void FeatureBuilder2Feature(FeatureBuilderGeomRef const & fb, FeatureGeomRef & f f.Deserialize(staticInstance.get_source(buffers.m_buffer)); } -void Feature2FeatureBuilder(FeatureGeomRef const & f, FeatureBuilderGeomRef & fb) -{ - f.InitFeatureBuilder(fb); -} - -void FeatureBuilder2Feature(FeatureBuilderGeom const & fb, FeatureGeom & f) -{ - FeatureBuilderGeom::buffers_holder_t buffers; - fb.Serialize(buffers); - - FeatureGeom::read_source_t source; - source.m_data.swap(buffers); - f.Deserialize(source); -} - -void Feature2FeatureBuilder(FeatureGeom const & f, FeatureBuilderGeom & fb) +void Feature2FeatureBuilder(FeatureType const & f, FeatureBuilder2 & fb) { f.InitFeatureBuilder(fb); } diff --git a/indexer/indexer_tests/feature_routine.hpp b/indexer/indexer_tests/feature_routine.hpp index 330df0e49b..2f90c80a10 100644 --- a/indexer/indexer_tests/feature_routine.hpp +++ b/indexer/indexer_tests/feature_routine.hpp @@ -2,8 +2,5 @@ #include "../feature.hpp" -void FeatureBuilder2Feature(FeatureBuilderGeomRef const & fb, FeatureGeomRef & f); -void Feature2FeatureBuilder(FeatureGeomRef const & f, FeatureBuilderGeomRef & fb); - -void FeatureBuilder2Feature(FeatureBuilderGeom const & fb, FeatureGeom & f); -void Feature2FeatureBuilder(FeatureGeom const & f, FeatureBuilderGeom & fb); +void FeatureBuilder2Feature(FeatureBuilder2 & fb, FeatureType & f); +void Feature2FeatureBuilder(FeatureType const & f, FeatureBuilder2 & fb); diff --git a/indexer/indexer_tests/feature_test.cpp b/indexer/indexer_tests/feature_test.cpp index 0718008dd3..fb1d43a678 100644 --- a/indexer/indexer_tests/feature_test.cpp +++ b/indexer/indexer_tests/feature_test.cpp @@ -45,10 +45,7 @@ UNIT_TEST(Feature_Deserialize) platform.ReadPathForFile("classificator.txt"), platform.ReadPathForFile("visibility.txt")); - vector a; - a.push_back(1); - a.push_back(2); - FeatureBuilderType fb; + FeatureBuilder2 fb; fb.AddName("name"); @@ -62,14 +59,14 @@ UNIT_TEST(Feature_Deserialize) fb.AddPoint(points[i]); } - vector triangles; - { - triangles.push_back(m2::PointD(0.5, 0.5)); - triangles.push_back(m2::PointD(0.25, 0.5)); - triangles.push_back(m2::PointD(1.0, 1.0)); - for (size_t i = 0; i < triangles.size(); i += 3) - fb.AddTriangle(triangles[i], triangles[i+1], triangles[i+2]); - } + //vector triangles; + //{ + // triangles.push_back(m2::PointD(0.5, 0.5)); + // triangles.push_back(m2::PointD(0.25, 0.5)); + // triangles.push_back(m2::PointD(1.0, 1.0)); + // for (size_t i = 0; i < triangles.size(); i += 3) + // fb.AddTriangle(triangles[i], triangles[i+1], triangles[i+2]); + //} fb.AddLayer(3); @@ -91,7 +88,7 @@ UNIT_TEST(Feature_Deserialize) FeatureType f; FeatureBuilder2Feature(fb, f); - TEST_EQUAL(f.GetFeatureType(), FeatureBase::FEATURE_TYPE_AREA, ()); + TEST_EQUAL(f.GetFeatureType(), FeatureBase::FEATURE_TYPE_LINE, ()); FeatureBase::GetTypesFn doGetTypes; f.ForEachTypeRef(doGetTypes); @@ -108,22 +105,24 @@ UNIT_TEST(Feature_Deserialize) f.ForEachPointRef(featurePoints, level); TEST_EQUAL(points, featurePoints.m_V, ()); - PointAccumulator featureTriangles; - f.ForEachTriangleRef(featureTriangles, level); - TEST_EQUAL(triangles, featureTriangles.m_V, ()); + //PointAccumulator featureTriangles; + //f.ForEachTriangleRef(featureTriangles, level); + //TEST_EQUAL(triangles, featureTriangles.m_V, ()); double const eps = MercatorBounds::GetCellID2PointAbsEpsilon(); - TEST_LESS(fabs(f.GetLimitRect().minX() - 0.25), eps, ()); - TEST_LESS(fabs(f.GetLimitRect().minY() - 0.20), eps, ()); - TEST_LESS(fabs(f.GetLimitRect().maxX() - 1.00), eps, ()); - TEST_LESS(fabs(f.GetLimitRect().maxY() - 1.00), eps, ()); + m2::RectD const & rect = f.GetLimitRect(level); - { - FeatureBuilderType fbTest; - Feature2FeatureBuilder(f, fbTest); + TEST_LESS(fabs(rect.minX() - 0.25), eps, ()); + TEST_LESS(fabs(rect.minY() - 0.20), eps, ()); + TEST_LESS(fabs(rect.maxX() - 1.00), eps, ()); + TEST_LESS(fabs(rect.maxY() - 1.00), eps, ()); - FeatureType fTest; - FeatureBuilder2Feature(fbTest, fTest); - TEST_EQUAL(f.DebugString(level), fTest.DebugString(level), ()); - } + //{ + // FeatureBuilder2 fbTest; + // Feature2FeatureBuilder(f, fbTest); + + // FeatureType fTest; + // FeatureBuilder2Feature(fbTest, fTest); + // TEST_EQUAL(f.DebugString(level), fTest.DebugString(level), ()); + //} } diff --git a/indexer/indexer_tool/feature_bucketer.hpp b/indexer/indexer_tool/feature_bucketer.hpp index 8f291b8716..7d7a9df703 100644 --- a/indexer/indexer_tool/feature_bucketer.hpp +++ b/indexer/indexer_tool/feature_bucketer.hpp @@ -104,7 +104,7 @@ private: class SimpleFeatureClipper { public: - typedef FeatureBuilderGeom feature_builder_t; + typedef FeatureBuilder1 feature_builder_t; private: feature_builder_t const & m_Feature; diff --git a/indexer/indexer_tool/feature_generator.cpp b/indexer/indexer_tool/feature_generator.cpp index 2db4eb9782..e2dd878663 100644 --- a/indexer/indexer_tool/feature_generator.cpp +++ b/indexer/indexer_tool/feature_generator.cpp @@ -164,7 +164,7 @@ uint32_t FeaturesCollector::GetFileSize(FileWriter const & f) return ret; } -void FeaturesCollector::WriteFeatureBase(vector const & bytes, FeatureBuilderGeom const & fb) +void FeaturesCollector::WriteFeatureBase(vector const & bytes, FeatureBuilder1 const & fb) { size_t const sz = bytes.size(); CHECK ( sz != 0, ("Empty feature not allowed here!") ); @@ -178,11 +178,11 @@ void FeaturesCollector::WriteFeatureBase(vector const & bytes, FeatureBuil } } -void FeaturesCollector::operator() (FeatureBuilderGeom const & fb) +void FeaturesCollector::operator() (FeatureBuilder1 const & fb) { (void)GetFileSize(m_datFile); - FeatureBuilderGeom::buffers_holder_t bytes; + FeatureBuilder1::buffer_t bytes; fb.Serialize(bytes); WriteFeatureBase(bytes, fb); } diff --git a/indexer/indexer_tool/feature_generator.hpp b/indexer/indexer_tool/feature_generator.hpp index 80263fe7a3..b7914a9abb 100644 --- a/indexer/indexer_tool/feature_generator.hpp +++ b/indexer/indexer_tool/feature_generator.hpp @@ -9,8 +9,7 @@ #include "../../std/vector.hpp" #include "../../std/string.hpp" -class FeatureBuilderGeom; -class FeatureBuilderGeomRef; +class FeatureBuilder1; namespace feature { @@ -44,7 +43,7 @@ namespace feature void WriteHeader(); - void WriteFeatureBase(vector const & bytes, FeatureBuilderGeom const & fb); + void WriteFeatureBase(vector const & bytes, FeatureBuilder1 const & fb); public: // Stores prefix and suffix of a dat file name. @@ -54,6 +53,6 @@ namespace feature FeaturesCollector(string const & bucket, InitDataType const & prefix); ~FeaturesCollector(); - void operator() (FeatureBuilderGeom const & f); + void operator() (FeatureBuilder1 const & f); }; } diff --git a/indexer/indexer_tool/feature_sorter.cpp b/indexer/indexer_tool/feature_sorter.cpp index 701f79c824..57e8643a0d 100644 --- a/indexer/indexer_tool/feature_sorter.cpp +++ b/indexer/indexer_tool/feature_sorter.cpp @@ -34,28 +34,28 @@ namespace public: std::vector m_vec; - void operator() (FeatureGeom const & ft, uint64_t pos) + void operator() (FeatureBuilder1 const & ft, uint64_t pos) { // reset state m_midX = 0.0; m_midY = 0.0; m_counter = 0; - ft.ForEachPointRef(*this, FeatureGeom::m_defScale); + ft.ForEachPointRef(*this); m_midX /= m_counter; m_midY /= m_counter; uint64_t const pointAsInt64 = PointToInt64(m_midX, m_midY); - uint64_t const minScale = feature::MinDrawableScaleForFeature(ft); + uint64_t const minScale = feature::MinDrawableScaleForFeature(ft.GetFeatureBase()); CHECK(minScale <= scales::GetUpperScale(), ("Dat file contain invisible feature")); uint64_t const order = (minScale << 59) | (pointAsInt64 >> 5); m_vec.push_back(make_pair(order, pos)); } - void operator() (CoordPointT const & point) + void operator() (m2::PointD const & p) { - m_midX += point.first; - m_midY += point.second; + m_midX += p.x; + m_midY += p.y; ++m_counter; } }; @@ -75,7 +75,7 @@ namespace feature if (in.size() >= 2) { SimplifyNearOptimal >(20, in.begin(), in.end()-1, - my::sq(scales::GetEpsilonForLevel(level + 1)), MakeBackInsertFunctor(out)); + my::sq(scales::GetEpsilonForSimplify(level)), MakeBackInsertFunctor(out)); switch (out.size()) { @@ -91,6 +91,8 @@ namespace feature } } + void TesselateInterior(points_t const & bound, list const & holes, points_t & triangles); + class FeaturesCollectorRef : public FeaturesCollector { @@ -146,35 +148,56 @@ namespace feature m_writer.Finish(); } - void operator() (FeatureBuilderGeomRef const & fb) + void operator() (FeatureBuilder2 & fb) { (void)GetFileSize(m_datFile); - FeatureBuilderGeomRef::buffers_holder_t buffer; + FeatureBuilder2::buffers_holder_t buffer; int lowS = 0; for (int i = 0; i < m_scales; ++i) { if (fb.IsDrawableLikeLine(lowS, g_arrScales[i])) { - buffer.m_mask |= (1 << i); + buffer.m_lineMask |= (1 << i); buffer.m_lineOffset.push_back(GetFileSize(*m_geoFile[i])); - // serialize points + // simplify and serialize geometry points_t points; SimplifyPoints(fb.GetGeometry(), points, g_arrScales[i]); - feature::SerializePoints(points, *m_geoFile[i]); + feature::SavePoints(points, *m_geoFile[i]); + + if (fb.IsDrawableLikeArea() && points.size() > 2) + { + // simplify and serialize triangles + + list const & holes = fb.GetHoles(); + list simpleHoles; + for (list::const_iterator iH = holes.begin(); iH != holes.end(); ++iH) + { + simpleHoles.push_back(points_t()); + + SimplifyPoints(*iH, simpleHoles.back(), g_arrScales[i]); + + if (simpleHoles.back().size() < 3) + simpleHoles.pop_back(); + } + + points_t triangles; + feature::TesselateInterior(points, simpleHoles, triangles); + + if (!triangles.empty()) + { + buffer.m_trgMask |= (1 << i); + buffer.m_trgOffset.push_back(GetFileSize(*m_trgFile[i])); + feature::SaveTriangles(triangles, *m_trgFile[i]); + } + } } lowS = g_arrScales[i]+1; } - if (!fb.GetTriangles().empty()) - { - buffer.m_trgOffset.push_back(GetFileSize(*m_trgFile[0])); - feature::SerializeTriangles(fb.GetTriangles(), *m_trgFile[0]); - } - fb.Serialize(buffer); WriteFeatureBase(buffer.m_buffer, fb); @@ -210,20 +233,17 @@ namespace feature FeaturesCollectorRef collector(datFilePath); - FeatureGeom::read_source_t buffer; + FeatureBuilder1::buffer_t buffer; for (size_t i = 0; i < midPoints.m_vec.size(); ++i) { ReaderSource src(reader); src.Skip(midPoints.m_vec[i].second); - FeatureGeom f; - feature::ReadFromSource(src, f, buffer); - - FeatureBuilderType fb; - f.InitFeatureBuilder(fb); + FeatureBuilder1 f; + feature::ReadFromSourceRowFormat(src, f); // emit the feature - collector(fb); + collector(static_cast(f)); } // at this point files should be closed diff --git a/indexer/indexer_tool/osm_element.hpp b/indexer/indexer_tool/osm_element.hpp index 817a044798..ba5ce325eb 100644 --- a/indexer/indexer_tool/osm_element.hpp +++ b/indexer/indexer_tool/osm_element.hpp @@ -16,11 +16,6 @@ #include "../../base/start_mem_debug.hpp" -namespace feature -{ - typedef list > holes_cont_t; - void TesselateInterior(FeatureBuilderGeom & featureBuilder, feature::holes_cont_t const & holes); -} /// @param TEmitter Feature accumulating policy /// @param THolder Nodes, ways, relations holder @@ -49,7 +44,7 @@ protected: public: /// @param[out] list of holes - feature::holes_cont_t m_holes; + list > m_holes; multipolygon_processor(uint64_t id, THolder & holder) : m_id(id), m_holder(holder) {} @@ -185,7 +180,7 @@ protected: } } m_typeProcessor; - typedef FeatureBuilderGeom feature_builder_t; + typedef FeatureBuilder1 feature_builder_t; bool GetPoint(uint64_t id, m2::PointD & pt) { @@ -198,7 +193,7 @@ protected: { multipolygon_processor processor(id, m_holder); m_holder.ForEachRelationByWay(id, processor); - feature::TesselateInterior(ft, processor.m_holes); + ft.SetAreaAddHoles(processor.m_holes); } } @@ -370,7 +365,7 @@ protected: if (p->childs.empty() || !base_type::GetPoint(id, pt)) return; - ft.AddPoint(pt); + ft.SetCenter(pt); } else if (p->name == "way") { @@ -396,12 +391,13 @@ protected: } } - if (ft.GetPointsCount() <= 1) + size_t const count = ft.GetPointsCount(); + if (count < 2) return; // Get the tesselation for an area object (only if it has area drawing rules, // otherwise it will stay a linear object). - if (isArea) + if (isArea && count > 2) base_type::FinishAreaFeature(id, ft); } else diff --git a/indexer/indexer_tool/tesselator.cpp b/indexer/indexer_tool/tesselator.cpp index 659874307e..da344d0bd5 100644 --- a/indexer/indexer_tool/tesselator.cpp +++ b/indexer/indexer_tool/tesselator.cpp @@ -1,4 +1,4 @@ -#include "osm_element.hpp" +#include "../../base/SRC_FIRST.hpp" #include "../cell_id.hpp" @@ -11,16 +11,17 @@ namespace feature tess::Tesselator & m_tess; AddTessPointF(tess::Tesselator & tess) : m_tess(tess) {} - void operator()(CoordPointT const & p) + void operator()(m2::PointD const & p) { - m_tess.add(tess::Vertex(p.first, p.second)); + m_tess.add(tess::Vertex(p.x, p.y)); } }; - void TesselateInterior(FeatureBuilderGeom & fb, feature::holes_cont_t const & holes) - { - ASSERT(fb.IsGeometryClosed(), ()); + typedef vector points_t; + void TesselateInterior( points_t const & bound, list const & holes, + points_t & triangles) + { tess::VectorDispatcher disp; tess::Tesselator tess; tess.setDispatcher(&disp); @@ -29,19 +30,13 @@ namespace feature tess.beginPolygon(); tess.beginContour(); - { - FeatureGeom::read_source_t bytes; - fb.Serialize(bytes.m_data); - FeatureGeom f(bytes); - f.ForEachPoint(AddTessPointF(tess), FeatureGeom::m_defScale); - } + for_each(bound.begin(), bound.end(), AddTessPointF(tess)); tess.endContour(); - for (feature::holes_cont_t::const_iterator it = holes.begin(); it != holes.end(); ++it) + for (list::const_iterator it = holes.begin(); it != holes.end(); ++it) { tess.beginContour(); - for (size_t i = 0; i < (*it).size(); ++i) - tess.add(tess::Vertex((*it)[i].x, (*it)[i].y)); + for_each(it->begin(), it->end(), AddTessPointF(tess)); tess.endContour(); } @@ -49,7 +44,6 @@ namespace feature for (size_t i = 0; i < disp.indices().size(); ++i) { - vector vertices; switch (disp.indices()[i].first) { case tess::TrianglesFan: @@ -63,13 +57,10 @@ namespace feature { int const idx = disp.indices()[i].second[j]; tess::Vertex const & v = disp.vertices()[idx]; - vertices.push_back(m2::PointD(v.x, v.y)); + triangles.push_back(m2::PointD(v.x, v.y)); } - ASSERT_EQUAL(vertices.size() % 3, 0, ()); - size_t const triangleCount = vertices.size() / 3; - for (size_t i = 0; i < triangleCount; ++i) - fb.AddTriangle(vertices[3*i + 0], vertices[3*i + 1], vertices[3*i + 2]); + ASSERT_EQUAL(triangles.size() % 3, 0, ()); } } } diff --git a/indexer/scale_index_builder.hpp b/indexer/scale_index_builder.hpp index 8d755fc077..dbaefc4840 100644 --- a/indexer/scale_index_builder.hpp +++ b/indexer/scale_index_builder.hpp @@ -75,7 +75,8 @@ public: template bool FeatureShouldBeIndexed(TFeature const & f) const { - (void)f.GetLimitRect(); // dummy call to force TFeature::ParseGeometry + // Call this to force TFeature::ParseGeometry + f.GetLimitRect(m_ScaleRange.second); uint32_t const minScale = feature::MinDrawableScaleForFeature(f); return (m_ScaleRange.first <= minScale && minScale < m_ScaleRange.second); diff --git a/indexer/scales.cpp b/indexer/scales.cpp index eb56ff1a99..978698663e 100644 --- a/indexer/scales.cpp +++ b/indexer/scales.cpp @@ -41,9 +41,22 @@ namespace scales return GetScaleLevel((dx + dy) / 2.0); } + namespace + { + double GetEpsilonImpl(int level, int logEps) + { + return (MercatorBounds::maxX - MercatorBounds::minX) / pow(2.0, double(level + logEps - initial_level)); + } + } + double GetEpsilonForLevel(int level) { - return (MercatorBounds::maxX - MercatorBounds::minX) / pow(2.0, double(level + 6 - initial_level)); + return GetEpsilonImpl(level, 6); + } + + double GetEpsilonForSimplify(int level) + { + return GetEpsilonImpl(level, 9); } bool IsGoodForLevel(int level, m2::RectD const & r) diff --git a/indexer/scales.hpp b/indexer/scales.hpp index acf475e02c..0d862ef10c 100644 --- a/indexer/scales.hpp +++ b/indexer/scales.hpp @@ -10,5 +10,6 @@ namespace scales int GetScaleLevel(double ratio); int GetScaleLevel(m2::RectD const & r); double GetEpsilonForLevel(int level); + double GetEpsilonForSimplify(int level); bool IsGoodForLevel(int level, m2::RectD const & r); } diff --git a/map/feature_vec_model.hpp b/map/feature_vec_model.hpp index f82e76a6aa..3cdd2f601a 100644 --- a/map/feature_vec_model.hpp +++ b/map/feature_vec_model.hpp @@ -15,21 +15,6 @@ namespace model { - class FeatureVector - { - vector m_vec; - m2::RectD m_rect; - - public: - template - void ForEachFeature(TParam const &, ToDo & toDo) - { - std::for_each(m_vec.begin(), m_vec.end(), toDo); - } - - m2::RectD GetWorldRect() const { return m_rect; } - }; - //#define USE_BUFFER_READER class FeaturesFetcher diff --git a/map/map_tests/map_foreach_test.cpp b/map/map_tests/map_foreach_test.cpp index 454e82a287..6dfa8cf43e 100644 --- a/map/map_tests/map_foreach_test.cpp +++ b/map/map_tests/map_foreach_test.cpp @@ -211,8 +211,9 @@ namespace void operator() (FeatureType const & f, uint64_t offset) { - if (f.DebugString(m_level) == m_test.second) - LOG(LINFO, ("Offset = ", offset)); + string const s = f.DebugString(m_level); + if (s == m_test.second) + LOG(LINFO, (s, "Feature offset = ", offset)); } }; } @@ -247,9 +248,8 @@ UNIT_TEST(IndexForEachTest) size_t errInd; if (!compare_sequence(v2, v1, compare_strings(), errInd)) { - LOG(LINFO, ("Failed for rect: ", r, ". Etalon size = ", v2.size(), ". Index size = ", v1.size())); src2.ForEachFeature(r, FindOffset(level, v2[errInd])); - TEST(false, ("Error in ForEachFeature test")); + TEST(false, ("Failed for rect: ", r, ". Etalon size = ", v2.size(), ". Index size = ", v1.size())); } if (!v2.empty() && (level < scales::GetUpperScale())) diff --git a/qt/draw_widget.hpp b/qt/draw_widget.hpp index 23fa3b3b15..02dc1fe489 100644 --- a/qt/draw_widget.hpp +++ b/qt/draw_widget.hpp @@ -30,7 +30,6 @@ namespace qt shared_ptr m_handle; typedef model::FeaturesFetcher model_t; - //typedef model::FeatureVector model_t; FrameWork m_framework; diff --git a/qt/searchwindow.hpp b/qt/searchwindow.hpp index 794d9c973d..3d51df34e0 100644 --- a/qt/searchwindow.hpp +++ b/qt/searchwindow.hpp @@ -25,7 +25,6 @@ namespace qt Q_OBJECT - //typedef model::FeatureVector model_t; typedef model::FeaturesFetcher model_t; FindEditorWnd * m_pEditor;