WIP: [indexer] Allow feature visibility override #2798
14 changed files with 206 additions and 53 deletions
|
@ -279,6 +279,7 @@ void RuleDrawer::ProcessAreaStyle(FeatureType & f, Stylist const & s,
|
|||
{
|
||||
double const heightInMeters = GetBuildingHeightInMeters(f);
|
||||
double const minHeightInMeters = GetBuildingMinHeightInMeters(f);
|
||||
// Loads geometry of the feature.
|
||||
featureCenter = feature::GetCenter(f, zoomLevel);
|
||||
double const lon = mercator::XToLon(featureCenter.x);
|
||||
double const lat = mercator::YToLat(featureCenter.y);
|
||||
|
@ -294,12 +295,18 @@ void RuleDrawer::ProcessAreaStyle(FeatureType & f, Stylist const & s,
|
|||
if (applyPointStyle)
|
||||
{
|
||||
if (!is3dBuilding)
|
||||
{
|
||||
// Loads geometry of the feature.
|
||||
featureCenter = feature::GetCenter(f, zoomLevel);
|
||||
}
|
||||
applyPointStyle = m_globalRect.IsPointInside(featureCenter);
|
||||
}
|
||||
|
||||
pastk
commented
Вызов feature::GetMinDrawableScale(f) загрузил бы BEST_GEOMETRY, а это ошибка. С lazy loading всё хорошо, кроме того, что разработчику, использующему FeatureType API, неочевидно, какой вызов загружает геометрию (и главное - какую!), а какой нет. Это приводит к трудно находимым performance багам вроде #2840. Вроде как простое открывание PP тоже приводит к загрузке BEST_GEOMETRY для выбранной фичи и её копированию, я пока не понял, зачем.. Эти комменты - попытка (да, не самая лучшая) более явно показать, где что происходит. Лучшим решением было бы, возможно, как-то хинтить в названиях методов, что вызов тянет загрузку геометрии, или убрать методы вроде GetCenter(f) и оставить только GetCenter(f, zoomLevel). Стиль комментов поправлю. Вызов feature::GetMinDrawableScale(f) загрузил бы BEST_GEOMETRY, а это ошибка.
С lazy loading всё хорошо, кроме того, что разработчику, использующему FeatureType API, неочевидно, какой вызов загружает геометрию (и главное - какую!), а какой нет. Это приводит к трудно находимым performance багам вроде #2840.
Вроде как простое открывание PP тоже приводит к загрузке BEST_GEOMETRY для выбранной фичи и её копированию, я пока не понял, зачем..
Эти комменты - попытка (да, не самая лучшая) более явно показать, где что происходит. Лучшим решением было бы, возможно, как-то хинтить в названиях методов, что вызов тянет загрузку геометрии, или убрать методы вроде GetCenter(f) и оставить только GetCenter(f, zoomLevel).
Стиль комментов поправлю.
|
||||
if (applyPointStyle || is3dBuilding)
|
||||
{
|
||||
// At this point a proper geometry is loaded already.
|
||||
minVisibleScale = feature::GetMinDrawableScale(f);
|
||||
}
|
||||
|
||||
ApplyAreaFeature apply(m_context->GetTileKey(), insertShape, f.GetID(),
|
||||
m_currentScaleGtoP, isBuilding,
|
||||
|
|
|
@ -58,15 +58,24 @@ public:
|
|||
|
||||
Points const & GetSourcePoints()
|
||||
{
|
||||
// For short lines keep simplifying the previous version to ensure points visibility is consistent.
|
||||
return !m_current.empty() ? m_current : m_fb.GetOuterGeometry();
|
||||
}
|
||||
|
||||
// Its important AddPoints is called sequentially from upper scales to lower.
|
||||
void AddPoints(Points const & points, int scaleIndex)
|
||||
{
|
||||
if (m_ptsInner && points.size() <= m_maxNumTriangles)
|
||||
{
|
||||
// Store small features inline and keep a mask for individual points scale visibility.
|
||||
if (m_buffer.m_innerPts.empty())
|
||||
{
|
||||
// If geometry is added for the most detailed scale 3 only then
|
||||
// the mask is never updated and left == 0, which is fine as the feature
|
||||
// will not be visible on lower scales. And for the style design case
|
||||
// all points will be used as a fallback geometry anyway.
|
||||
m_buffer.m_innerPts = points;
|
||||
}
|
||||
else
|
||||
FillInnerPointsMask(points, scaleIndex);
|
||||
m_current = points;
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
#include "indexer/data_source.hpp"
|
||||
|
||||
#include "indexer/map_style_reader.hpp"
|
||||
#include "indexer/scale_index.hpp"
|
||||
#include "indexer/unique_index.hpp"
|
||||
|
||||
|
@ -44,6 +46,10 @@ public:
|
|||
feature::DataHeader const & header = mwmValue->GetHeader();
|
||||
CheckUniqueIndexes checkUnique;
|
||||
|
||||
// Read 3 additional scale indices to allow visibility changes
|
||||
// for style designers and for custom style users.
|
||||
if (GetStyleReader().IsVisibilityOverrideEnabled())
|
||||
scale += 3;
|
||||
// In case of WorldCoasts we should pass correct scale in ForEachInIntervalAndScale.
|
||||
auto const lastScale = header.GetLastScale();
|
||||
if (scale > lastScale)
|
||||
|
|
|
@ -153,6 +153,8 @@ bool GetBoundingBoxArea(FeatureType & ft, double & sqM)
|
|||
if (feature::GeomType::Area != ft.GetGeomType())
|
||||
return false;
|
||||
|
||||
// FIXME: the best geometry is loaded here always, use the current zoom level
|
||||
// see https://github.com/organicmaps/organicmaps/issues/2840
|
||||
sqM = mercator::AreaOnEarth(ft.GetLimitRect(scales::GetUpperScale()));
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -2,9 +2,13 @@
|
|||
|
||||
#include "indexer/classificator.hpp"
|
||||
#include "indexer/feature_algo.hpp"
|
||||
#include "indexer/feature_data.hpp"
|
||||
#include "indexer/feature_impl.hpp"
|
||||
#include "indexer/feature_utils.hpp"
|
||||
#include "indexer/feature_visibility.hpp"
|
||||
#include "indexer/map_object.hpp"
|
||||
#include "indexer/map_style_reader.hpp"
|
||||
#include "indexer/scales.hpp"
|
||||
#include "indexer/shared_load_info.hpp"
|
||||
|
||||
#include "geometry/mercator.hpp"
|
||||
|
@ -36,21 +40,22 @@ int GetScaleIndex(SharedLoadInfo const & loadInfo, int scale)
|
|||
{
|
||||
int const count = loadInfo.GetScalesCount();
|
||||
|
||||
// In case of WorldCoasts we should get correct last geometry.
|
||||
int const lastScale = loadInfo.GetLastScale();
|
||||
if (scale > lastScale)
|
||||
scale = lastScale;
|
||||
|
||||
switch (scale)
|
||||
{
|
||||
case FeatureType::WORST_GEOMETRY: return 0;
|
||||
case FeatureType::BEST_GEOMETRY: return count - 1;
|
||||
default:
|
||||
// In case of WorldCoasts we should get correct last geometry.
|
||||
int const lastScale = loadInfo.GetLastScale();
|
||||
if (scale > lastScale)
|
||||
scale = lastScale;
|
||||
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
if (scale <= loadInfo.GetScale(i))
|
||||
return i;
|
||||
}
|
||||
ASSERT(false, ("No suitable scale range in the map file."));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
@ -58,14 +63,10 @@ int GetScaleIndex(SharedLoadInfo const & loadInfo, int scale)
|
|||
int GetScaleIndex(SharedLoadInfo const & loadInfo, int scale,
|
||||
FeatureType::GeometryOffsets const & offsets)
|
||||
{
|
||||
int ind = -1;
|
||||
int const count = static_cast<int>(offsets.size());
|
||||
|
||||
// In case of WorldCoasts we should get correct last geometry.
|
||||
int const lastScale = loadInfo.GetLastScale();
|
||||
if (scale > lastScale)
|
||||
scale = lastScale;
|
||||
ASSERT_EQUAL(loadInfo.GetScalesCount(), static_cast<int>(offsets.size()), ());
|
||||
int const count = loadInfo.GetScalesCount();
|
||||
|
||||
int ind = 0;
|
||||
switch (scale)
|
||||
{
|
||||
case FeatureType::BEST_GEOMETRY:
|
||||
|
@ -73,30 +74,48 @@ int GetScaleIndex(SharedLoadInfo const & loadInfo, int scale,
|
|||
ind = count - 1;
|
||||
while (ind >= 0 && offsets[ind] == kInvalidOffset)
|
||||
--ind;
|
||||
if (ind >= 0)
|
||||
return ind;
|
||||
break;
|
||||
|
||||
case FeatureType::WORST_GEOMETRY:
|
||||
// Choose the worst existing geometry for the first visible scale.
|
||||
ind = 0;
|
||||
while (ind < count && offsets[ind] == kInvalidOffset)
|
||||
++ind;
|
||||
if (ind < count)
|
||||
return ind;
|
||||
break;
|
||||
|
||||
default:
|
||||
{
|
||||
int const n = loadInfo.GetScalesCount();
|
||||
for (int i = 0; i < n; ++i)
|
||||
// In case of WorldCoasts we should get correct last geometry.
|
||||
int const lastScale = loadInfo.GetLastScale();
|
||||
if (scale > lastScale)
|
||||
scale = lastScale;
|
||||
|
||||
if (!GetStyleReader().IsVisibilityOverrideEnabled())
|
||||
{
|
||||
if (scale <= loadInfo.GetScale(i))
|
||||
return (offsets[i] != kInvalidOffset ? i : -1);
|
||||
while (ind < count && scale > loadInfo.GetScale(ind))
|
||||
++ind;
|
||||
ASSERT_LESS(ind, count, ("No suitable scale range in the map file."));
|
||||
return (offsets[ind] != kInvalidOffset ? ind : -1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If there is no geometry for the requested scale
|
||||
// fallback to the next more detailed one.
|
||||
// TODO: if there is need we can remove excess line points by a primitive filter
|
||||
// similar to the one used in apply_feature_functors.cpp::ApplyLineFeatureGeometry::operator()
|
||||
while (ind < count && (scale > loadInfo.GetScale(ind) || offsets[ind] == kInvalidOffset))
|
||||
++ind;
|
||||
// Some WorldCoasts features have idx == 0 geometry only,
|
||||
// so its possible there is no fallback geometry.
|
||||
return (ind < count ? ind : -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ind >= 0 && ind < count)
|
||||
return ind;
|
||||
|
||||
ASSERT(false, ("Feature should have any geometry ..."));
|
||||
ASSERT(false, ("A feature doesn't have any geometry."));
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -339,6 +358,7 @@ void FeatureType::ParseHeader2()
|
|||
if (headerGeomType == HeaderGeomType::Line)
|
||||
{
|
||||
ptsCount = bitSource.Read(4);
|
||||
// If a mask of outer geometry is present.
|
||||
if (ptsCount == 0)
|
||||
ptsMask = bitSource.Read(4);
|
||||
else
|
||||
|
@ -358,6 +378,7 @@ void FeatureType::ParseHeader2()
|
|||
{
|
||||
if (ptsCount > 0)
|
||||
{
|
||||
// Inner geometry.
|
||||
int const count = ((ptsCount - 2) + 4 - 1) / 4;
|
||||
ASSERT_LESS(count, 4, ());
|
||||
|
||||
|
@ -374,6 +395,7 @@ void FeatureType::ParseHeader2()
|
|||
}
|
||||
else
|
||||
{
|
||||
// Outer geometry: first point is stored in the header (coding params).
|
||||
m_points.emplace_back(serial::LoadPoint(src, cp));
|
||||
ReadOffsets(*m_loadInfo, src, ptsMask, m_offsets.m_pts);
|
||||
}
|
||||
|
@ -382,6 +404,7 @@ void FeatureType::ParseHeader2()
|
|||
{
|
||||
if (trgCount > 0)
|
||||
{
|
||||
// Inner geometry (strips).
|
||||
trgCount += 2;
|
||||
|
||||
auto const * start = src.PtrUint8();
|
||||
|
@ -390,6 +413,7 @@ void FeatureType::ParseHeader2()
|
|||
}
|
||||
else
|
||||
{
|
||||
// Outer geometry.
|
||||
ReadOffsets(*m_loadInfo, src, trgMask, m_offsets.m_trg);
|
||||
}
|
||||
}
|
||||
|
@ -431,7 +455,7 @@ void FeatureType::ParseGeometry(int scale)
|
|||
{
|
||||
ASSERT_EQUAL(pointsCount, 1, ());
|
||||
|
||||
// outer geometry
|
||||
// Outer geometry.
|
||||
int const ind = GetScaleIndex(*m_loadInfo, scale, m_offsets.m_pts);
|
||||
if (ind != -1)
|
||||
{
|
||||
|
@ -445,20 +469,31 @@ void FeatureType::ParseGeometry(int scale)
|
|||
}
|
||||
else
|
||||
{
|
||||
// filter inner geometry
|
||||
|
||||
// Filter inner geometry.
|
||||
FeatureType::Points points;
|
||||
points.reserve(pointsCount);
|
||||
|
||||
int const scaleIndex = GetScaleIndex(*m_loadInfo, scale);
|
||||
ASSERT_LESS(scaleIndex, m_loadInfo->GetScalesCount(), ());
|
||||
|
||||
int const ind = GetScaleIndex(*m_loadInfo, scale);
|
||||
points.emplace_back(m_points.front());
|
||||
int fallbackInd = m_loadInfo->GetScalesCount() - 1;
|
||||
int pointInd = 0;
|
||||
for (size_t i = 1; i + 1 < pointsCount; ++i)
|
||||
{
|
||||
// check for point visibility in needed scaleIndex
|
||||
if (static_cast<int>((m_ptsSimpMask >> (2 * (i - 1))) & 0x3) <= scaleIndex)
|
||||
// Check for point visibility in needed scale index.
|
||||
pointInd = static_cast<int>((m_ptsSimpMask >> (2 * (i - 1))) & 0x3);
|
||||
if (pointInd <= ind)
|
||||
points.emplace_back(m_points[i]);
|
||||
else if (points.size() == 1 && fallbackInd > pointInd)
|
||||
fallbackInd = pointInd;
|
||||
}
|
||||
// Fallback to a closest more detailed geometry.
|
||||
if (GetStyleReader().IsVisibilityOverrideEnabled() && points.size() == 1)
|
||||
{
|
||||
for (size_t i = 1; i + 1 < pointsCount; ++i)
|
||||
{
|
||||
if (static_cast<int>((m_ptsSimpMask >> (2 * (i - 1))) & 0x3) == fallbackInd)
|
||||
points.emplace_back(m_points[i]);
|
||||
}
|
||||
}
|
||||
points.emplace_back(m_points.back());
|
||||
|
||||
|
@ -533,7 +568,7 @@ void FeatureType::ParseTriangles(int scale)
|
|||
{
|
||||
if (m_triangles.empty())
|
||||
{
|
||||
auto const ind = GetScaleIndex(*m_loadInfo, scale, m_offsets.m_trg);
|
||||
int const ind = GetScaleIndex(*m_loadInfo, scale, m_offsets.m_trg);
|
||||
if (ind != -1)
|
||||
{
|
||||
ReaderSource<FilesContainerR::TReader> src(m_loadInfo->GetTrianglesReader(ind));
|
||||
|
@ -545,6 +580,17 @@ void FeatureType::ParseTriangles(int scale)
|
|||
CalcRect(m_triangles, m_limitRect);
|
||||
}
|
||||
m_parsed.m_triangles = true;
|
||||
// After scanning extra visibility/scale indices some too small
|
||||
// area features (that were initially excluded from the index
|
||||
// because of their small size) appear again - filter them out.
|
||||
if (GetStyleReader().IsVisibilityOverrideEnabled()
|
||||
&& headerGeomType == HeaderGeomType::Area
|
||||
&& scale >= 0 && scale < m_loadInfo->GetLastScale()
|
||||
&& !IsDrawableForIndexGeometryOnly(*this, scale))
|
||||
{
|
||||
m_triangles.clear();
|
||||
m_limitRect = m2::RectD();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -11,8 +11,8 @@
|
|||
namespace feature
|
||||
{
|
||||
/// @returns point on a feature that is the closest to f.GetLimitRect().Center().
|
||||
/// It is used by many ednities in the core of mapsme. Do not modify it's
|
||||
/// logic if you really-really know what you are doing.
|
||||
/// It is used by many entities in the core. Do not modify it's
|
||||
/// logic unless you really-really know what you are doing.
|
||||
m2::PointD GetCenter(FeatureType & f, int scale)
|
||||
{
|
||||
GeomType const type = f.GetGeomType();
|
||||
|
|
|
@ -223,6 +223,7 @@ bool IsDrawableForIndexGeometryOnly(TypesHolder const & types, m2::RectD const &
|
|||
|
||||
static uint32_t const buildingPartType = c.GetTypeByPath({"building:part"});
|
||||
|
||||
// Exclude too small area features unless it's a part of a coast or a building.
|
||||
if (types.GetGeomType() == GeomType::Area && !types.Has(c.GetCoastType()) &&
|
||||
!types.Has(buildingPartType) && !scales::IsGoodForLevel(level, limitRect))
|
||||
![]()
```suggestion
// Exclude too small area features unless it's a part of a coast or a building.
```
|
||||
return false;
|
||||
|
|
|
@ -61,6 +61,9 @@ void MapObject::SetFromFeatureType(FeatureType & ft)
|
|||
m_roadNumber = ft.GetRoadNumber();
|
||||
m_featureID = ft.GetID();
|
||||
m_geomType = ft.GetGeomType();
|
||||
// TODO: BEST_GEOMETRY is likely needed for some special cases only,
|
||||
// i.e. matching an edited OSM feature, in other cases like opening
|
||||
// a place page WORST_GEOMETRY is going to be enough?
|
||||
if (m_geomType == feature::GeomType::Area)
|
||||
{
|
||||
m_triangles = ft.GetTrianglesAsPoints(FeatureType::BEST_GEOMETRY);
|
||||
|
|
|
@ -91,21 +91,28 @@ bool StyleReader::IsCarNavigationStyle() const
|
|||
m_mapStyle == MapStyle::MapStyleVehicleDark;
|
||||
}
|
||||
|
||||
ReaderPtr<Reader> StyleReader::GetDrawingRulesReader() const
|
||||
ReaderPtr<Reader> StyleReader::GetDrawingRulesReader()
|
||||
{
|
||||
std::string rulesFile =
|
||||
std::string("drules_proto") + GetStyleRulesSuffix(GetCurrentStyle()) + ".bin";
|
||||
|
||||
auto overriddenRulesFile =
|
||||
base::JoinPath(GetPlatform().WritableDir(), kStylesOverrideDir, rulesFile);
|
||||
if (Platform::IsFileExistsByFullPath(overriddenRulesFile))
|
||||
rulesFile = overriddenRulesFile;
|
||||
Platform const & pl = GetPlatform();
|
||||
if (m_isStylesOverrideEnabled)
|
||||
{
|
||||
auto overriddenRulesFile =
|
||||
base::JoinPath(pl.WritableDir(), kStylesOverrideDir, rulesFile);
|
||||
if (pl.IsFileExistsByFullPath(overriddenRulesFile))
|
||||
{
|
||||
rulesFile = overriddenRulesFile;
|
||||
m_isVisibilityOverrideEnabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef BUILD_DESIGNER
|
||||
// For Designer tool we have to look first into the resource folder.
|
||||
return GetPlatform().GetReader(rulesFile, "rwf");
|
||||
return pl.GetReader(rulesFile, "rwf");
|
||||
#else
|
||||
return GetPlatform().GetReader(rulesFile);
|
||||
return pl.GetReader(rulesFile);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -116,15 +123,19 @@ ReaderPtr<Reader> StyleReader::GetResourceReader(std::string const & file,
|
|||
std::string("resources-") + density + GetStyleResourcesSuffix(GetCurrentStyle());
|
||||
std::string resFile = base::JoinPath(resourceDir, file);
|
||||
|
||||
auto overriddenResFile = base::JoinPath(GetPlatform().WritableDir(), kStylesOverrideDir, resFile);
|
||||
if (GetPlatform().IsFileExistsByFullPath(overriddenResFile))
|
||||
resFile = overriddenResFile;
|
||||
Platform const & pl = GetPlatform();
|
||||
if (m_isStylesOverrideEnabled)
|
||||
{
|
||||
auto overriddenResFile = base::JoinPath(pl.WritableDir(), kStylesOverrideDir, resFile);
|
||||
if (pl.IsFileExistsByFullPath(overriddenResFile))
|
||||
resFile = overriddenResFile;
|
||||
}
|
||||
|
||||
#ifdef BUILD_DESIGNER
|
||||
// For Designer tool we have to look first into the resource folder.
|
||||
return GetPlatform().GetReader(resFile, "rwf");
|
||||
return pl.GetReader(resFile, "rwf");
|
||||
#else
|
||||
return GetPlatform().GetReader(resFile);
|
||||
return pl.GetReader(resFile);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -16,12 +16,43 @@ public:
|
|||
MapStyle GetCurrentStyle() const;
|
||||
bool IsCarNavigationStyle() const;
|
||||
|
||||
ReaderPtr<Reader> GetDrawingRulesReader() const;
|
||||
ReaderPtr<Reader> GetDrawingRulesReader();
|
||||
|
||||
ReaderPtr<Reader> GetResourceReader(std::string const & file, std::string const & density) const;
|
||||
ReaderPtr<Reader> GetDefaultResourceReader(std::string const & file) const;
|
||||
|
||||
// If enabled allows to load custom style files
|
||||
// from the "styles/" subdir of the writable dir.
|
||||
inline void SetStylesOverride(bool enabled)
|
||||
{
|
||||
m_isStylesOverrideEnabled = enabled;
|
||||
}
|
||||
|
||||
inline bool IsStylesOverrideEnabled() const
|
||||
{
|
||||
return m_isStylesOverrideEnabled;
|
||||
}
|
||||
|
||||
// If enabled allows rendering of features at up to 3 lower zoom levels
|
||||
// by reading extra scale/visibility indices and using a geometry fallback.
|
||||
inline void SetVisibilityOverride(bool enabled)
|
||||
{
|
||||
m_isVisibilityOverrideEnabled = enabled;
|
||||
}
|
||||
|
||||
inline bool IsVisibilityOverrideEnabled() const
|
||||
{
|
||||
return m_isVisibilityOverrideEnabled;
|
||||
}
|
||||
|
||||
private:
|
||||
bool m_isStylesOverrideEnabled = true;
|
||||
#ifdef BUILD_DESIGNER
|
||||
bool m_isVisibilityOverrideEnabled = true;
|
||||
#else
|
||||
bool m_isVisibilityOverrideEnabled = false;
|
||||
#endif
|
||||
|
||||
std::atomic<MapStyle> m_mapStyle;
|
||||
};
|
||||
|
||||
|
|
|
@ -47,6 +47,11 @@ public:
|
|||
m_cellsInBucket.resize(m_bucketsCount);
|
||||
}
|
||||
|
||||
// Every feature should be indexed at most once, namely for the smallest possible scale where
|
||||
// -- its geometry is non-empty;
|
||||
// -- it is visible;
|
||||
// -- it is allowed by the classificator.
|
||||
// If the feature is invisible at all scales, do not index it.
|
||||
template <class Feature>
|
||||
void operator()(Feature & ft, uint32_t index) const
|
||||
{
|
||||
|
@ -56,12 +61,15 @@ public:
|
|||
// The classificator won't allow this feature to be drawable for smaller
|
||||
// scales so the first buckets can be safely skipped.
|
||||
// todo(@pimenov) Parallelizing this loop may be helpful.
|
||||
// TODO: skip index building for scales [0,9] for country files and scales 10+ for the world file.
|
||||
for (uint32_t bucket = minScaleClassif; bucket < m_bucketsCount; ++bucket)
|
||||
{
|
||||
// There is a one-to-one correspondence between buckets and scales.
|
||||
// This is not immediately obvious and in fact there was an idea to map
|
||||
// a bucket to a contiguous range of scales.
|
||||
// todo(@pimenov): We probably should remove scale_index.hpp altogether.
|
||||
|
||||
// Check feature's geometry and visibility.
|
||||
if (!FeatureShouldBeIndexed(ft, static_cast<int>(bucket), bucket == minScaleClassif /* needReset */))
|
||||
{
|
||||
continue;
|
||||
|
@ -78,11 +86,6 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
// Every feature should be indexed at most once, namely for the smallest possible scale where
|
||||
// -- its geometry is non-empty;
|
||||
// -- it is visible;
|
||||
// -- it is allowed by the classificator.
|
||||
// If the feature is invisible at all scales, do not index it.
|
||||
template <class Feature>
|
||||
bool FeatureShouldBeIndexed(Feature & ft, int scale, bool needReset) const
|
||||
{
|
||||
|
@ -136,9 +139,10 @@ void IndexScales(feature::DataHeader const & header, FeaturesVector const & feat
|
|||
};
|
||||
using TDisplacementManager = DisplacementManager<decltype(PushCFT)>;
|
||||
|
||||
// Heuristically rearrange and filter single-point features to simplify
|
||||
// Single-point features are heuristically rearranged and filtered to simplify
|
||||
// the runtime decision of whether we should draw a feature
|
||||
// or sacrifice it for the sake of more important ones.
|
||||
// or sacrifice it for the sake of more important ones ("displacement").
|
||||
// Lines and areas are not displaceable and are just passed on to the index.
|
||||
TDisplacementManager manager(PushCFT);
|
||||
std::vector<uint32_t> featuresInBucket(bucketsCount);
|
||||
std::vector<uint32_t> cellsInBucket(bucketsCount);
|
||||
|
|
|
@ -65,6 +65,7 @@ namespace scales
|
|||
|
||||
bool IsGoodForLevel(int level, m2::RectD const & r)
|
||||
{
|
||||
ASSERT(level >= 0 && level <= GetUpperScale(), (level));
|
||||
// assume that feature is always visible in upper scale
|
||||
return (level == GetUpperScale() || std::max(r.SizeX(), r.SizeY()) > GetEpsilonForLevel(level));
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
|
||||
#include "indexer/categories_holder.hpp"
|
||||
#include "indexer/classificator.hpp"
|
||||
#include "indexer/classificator_loader.hpp"
|
||||
#include "indexer/drawing_rules.hpp"
|
||||
#include "indexer/editable_map_object.hpp"
|
||||
#include "indexer/feature.hpp"
|
||||
|
@ -2586,6 +2587,8 @@ bool Framework::ParseDrapeDebugCommand(string const & query)
|
|||
if (desiredStyle != MapStyleCount)
|
||||
{
|
||||
#if defined(OMIM_OS_ANDROID)
|
||||
// TODO: might not always work, sometimes SetMapStyle() is needed,
|
||||
// see android/jni/com/mapswithme/maps/Framework.cpp::MarkMapStyle()
|
||||
MarkMapStyle(desiredStyle);
|
||||
#else
|
||||
SetMapStyle(desiredStyle);
|
||||
|
@ -2625,6 +2628,35 @@ bool Framework::ParseDrapeDebugCommand(string const & query)
|
|||
m_isolinesManager.SetEnabled(false /* enable */);
|
||||
return true;
|
||||
}
|
||||
if (query == "?styles-override")
|
||||
{
|
||||
GetStyleReader().SetStylesOverride(true);
|
||||
// The visibility override will be enabled automatically
|
||||
// if there are any custom style files present.
|
||||
GetStyleReader().SetVisibilityOverride(false);
|
||||
// Reload in case style files were changed.
|
||||
classificator::Load();
|
||||
SetMapStyle(GetStyleReader().GetCurrentStyle());
|
||||
return true;
|
||||
}
|
||||
if (query == "?no-styles-override")
|
||||
{
|
||||
GetStyleReader().SetStylesOverride(false);
|
||||
GetStyleReader().SetVisibilityOverride(false);
|
||||
classificator::Load();
|
||||
SetMapStyle(GetStyleReader().GetCurrentStyle());
|
||||
return true;
|
||||
}
|
||||
if (query == "?visibility-override")
|
||||
{
|
||||
GetStyleReader().SetVisibilityOverride(true);
|
||||
return true;
|
||||
}
|
||||
if (query == "?no-visibility-override")
|
||||
{
|
||||
GetStyleReader().SetVisibilityOverride(false);
|
||||
return true;
|
||||
}
|
||||
if (query == "?debug-info")
|
||||
{
|
||||
m_drapeEngine->ShowDebugInfo(true /* shown */);
|
||||
|
|
|
@ -353,7 +353,7 @@ public:
|
|||
/// \brief Fills |nodes| with CountryIds of topmost nodes for this |countryId|.
|
||||
/// \param level is distance from top level except root.
|
||||
/// For disputed territories all possible owners will be added.
|
||||
/// Puts |countryId| to |nodes| when |level| is greater than the level of |countyId|.
|
||||
/// Puts |countryId| to |nodes| when |level| is greater than the level of |countryId|.
|
||||
void GetTopmostNodesFor(CountryId const & countryId, CountriesVec & nodes,
|
||||
size_t level = 0) const;
|
||||
|
||||
|
|
Reference in a new issue
Тут все комменты в этом файле избыточны, имхо. FeatureType работает как lazy loading.
Даже если бы выше не было совсем вызовов feature::GetCenter, вызов feature::GetMinDrawableScale все равно загрузил бы геометрию и посчитал нормальный scale.
А такой стиль коммента (аля python) только усложняет чтение кода: