Add BookmarkCategory.

This commit is contained in:
vng 2012-05-10 18:59:48 +03:00 committed by Alex Zolotarev
parent 0218b4973e
commit 18b6f31366
11 changed files with 359 additions and 269 deletions

View file

@ -1,9 +1,14 @@
#import <UIKit/UIKit.h>
class BookmarkCategory;
@interface BookmarksVC : UIViewController<UITableViewDelegate, UITableViewDataSource>
{
UITableView * m_table;
// Needed to change Edit/Cancel buttons
UINavigationItem * m_navItem;
// Current category to show.
BookmarkCategory * m_category;
}
@end
@end

View file

@ -21,16 +21,10 @@
m_navItem.rightBarButtonItem = [[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:@selector(onCancelEdit)] autorelease];
}
- (id)initWithFramework:(Framework *)f
{
if ((self = [super initWithNibName:nil bundle:nil]))
{
}
return self;
}
- (void)loadView
{
// TODO Initialize and change m_category.
UIView * parentView = [[[CustomNavigationView alloc] init] autorelease];
parentView.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight;
@ -39,7 +33,7 @@
m_navItem.leftBarButtonItem = [[[UIBarButtonItem alloc] initWithTitle:NSLocalizedString(@"Maps", @"Bookmarks - Close bookmarks button") style: UIBarButtonItemStyleDone
target:self action:@selector(onCloseButton:)] autorelease];
// Display Edit button only if table is not empty
if (GetFramework().BookmarksCount())
if (m_category->GetBookmarksCount() > 0)
{
[self.editButtonItem setTarget:self];
[self.editButtonItem setAction:@selector(onEdit)];
@ -78,7 +72,7 @@
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return GetFramework().BookmarksCount();
return m_category->GetBookmarksCount();
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
@ -87,10 +81,9 @@
if (!cell)
cell = [[[SearchCell alloc] initWithReuseIdentifier:@"FeatureCell"] autorelease];
Bookmark bm;
GetFramework().GetBookmark(indexPath.row, bm);
Bookmark const * bm = m_category->GetBookmark(indexPath.row);
cell.featureName.text = [NSString stringWithUTF8String:bm.GetName().c_str()];
cell.featureName.text = [NSString stringWithUTF8String:bm->GetName().c_str()];
cell.featureCountry.text = [NSString stringWithUTF8String:"Region"];
cell.featureType.text = [NSString stringWithUTF8String:"Type"];
cell.featureDistance.text = [NSString stringWithFormat:@"%f", 0.0];
@ -100,12 +93,10 @@
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
Framework & f = GetFramework();
if (indexPath.row < (NSInteger)f.BookmarksCount())
if (indexPath.row < (NSInteger)m_category->GetBookmarksCount())
{
Bookmark bm;
f.GetBookmark(indexPath.row, bm);
f.ShowRect(bm.GetViewport());
Bookmark const * bm = m_category->GetBookmark(indexPath.row);
GetFramework().ShowRect(bm->GetViewport());
// Same as "Close".
[self dismissModalViewControllerAnimated:YES];
@ -125,7 +116,7 @@
{
if (editingStyle == UITableViewCellEditingStyleDelete)
{
GetFramework().RemoveBookmark(indexPath.row);
m_category->RemoveBookmark(indexPath.row);
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
}

View file

@ -492,7 +492,6 @@ static void OnSearchResultCallback(search::Results const & res, int queryId)
{
// Zoom to the feature
case search::Result::RESULT_FEATURE:
m_framework->AddBookmark(res.GetFeatureCenter(), res.GetString());
m_framework->ShowSearchResult(res);
// Same as "Close" button but do not disable placemark

201
map/bookmark.cpp Normal file
View file

@ -0,0 +1,201 @@
#include "bookmark.hpp"
#include "../indexer/mercator.hpp"
#include "../coding/parse_xml.hpp" // LoadFromKML
#include "../base/stl_add.hpp"
#include "../base/string_utils.hpp"
#include "../std/algorithm.hpp"
#include <boost/algorithm/string.hpp> // boost::trim
void BookmarkCategory::AddBookmark(Bookmark const & bm)
{
m_bookmarks.push_back(new Bookmark(bm));
}
BookmarkCategory::~BookmarkCategory()
{
ClearBookmarks();
}
void BookmarkCategory::ClearBookmarks()
{
for_each(m_bookmarks.begin(), m_bookmarks.end(), DeleteFunctor());
m_bookmarks.clear();
}
void BookmarkCategory::RemoveBookmark(size_t index)
{
if (index < m_bookmarks.size())
m_bookmarks.erase(m_bookmarks.begin() + index);
}
Bookmark const * BookmarkCategory::GetBookmark(size_t index) const
{
return (index < m_bookmarks.size() ? m_bookmarks[index] : 0);
}
namespace
{
class KMLParser
{
BookmarkCategory & m_category;
int m_level;
string m_name;
m2::PointD m_org;
void Reset()
{
m_name.clear();
m_org = m2::PointD(-1000, -1000);
}
void SetOrigin(string const & s)
{
// order in string is: lon, lat, z
strings::SimpleTokenizer iter(s, ", ");
if (iter)
{
double lon;
if (strings::to_double(*iter, lon) && MercatorBounds::ValidLon(lon) && ++iter)
{
double lat;
if (strings::to_double(*iter, lat) && MercatorBounds::ValidLat(lat))
m_org = m2::PointD(MercatorBounds::LonToX(lon), MercatorBounds::LatToY(lat));
}
}
}
bool IsValid() const
{
return (!m_name.empty() &&
MercatorBounds::ValidX(m_org.x) && MercatorBounds::ValidY(m_org.y));
}
public:
KMLParser(BookmarkCategory & cat) : m_category(cat), m_level(0)
{
Reset();
}
bool Push(string const & name)
{
switch (m_level)
{
case 0:
if (name != "kml") return false;
break;
case 1:
if (name != "Document") return false;
break;
case 2:
if (name != "Placemark") return false;
break;
case 3:
if (name != "Point" && name != "name") return false;
break;
case 4:
if (name != "coordinates") return false;
}
++m_level;
return true;
}
void AddAttr(string const &, string const &) {}
void Pop(string const &)
{
--m_level;
if (m_level == 2 && IsValid())
{
m_category.AddBookmark(Bookmark(m_org, m_name));
Reset();
}
}
class IsSpace
{
public:
bool operator() (char c) const
{
return ::isspace(c);
}
};
void CharData(string value)
{
boost::trim(value);
if (!value.empty())
switch (m_level)
{
case 4: m_name = value; break;
case 5: SetOrigin(value); break;
}
}
};
}
void BookmarkCategory::LoadFromKML(ReaderPtr<Reader> const & reader)
{
ReaderSource<ReaderPtr<Reader> > src(reader);
KMLParser parser(*this);
ParseXML(src, parser, true);
}
namespace
{
char const * kmlHeader =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
"<kml xmlns=\"http://earth.google.com/kml/2.2\">\n"
"<Document>\n"
" <name>MapsWithMe</name>\n";
char const * kmlFooter =
"</Document>\n"
"</kml>\n";
string PointToString(m2::PointD const & org)
{
double const lon = MercatorBounds::XToLon(org.x);
double const lat = MercatorBounds::YToLat(org.y);
ostringstream ss;
ss.precision(8);
ss << lon << "," << lat;
return ss.str();
}
}
void BookmarkCategory::SaveToKML(ostream & s)
{
s << kmlHeader;
for (size_t i = 0; i < m_bookmarks.size(); ++i)
{
Bookmark const * bm = m_bookmarks[i];
s << " <Placemark>\n"
<< " <name>" << bm->GetName() << "</name>\n"
<< " <Point>\n"
<< " <coordinates>" << PointToString(bm->GetOrg()) << "</coordinates>\n"
<< " </Point>\n"
<< " </Placemark>\n";
}
s << kmlFooter;
}

View file

@ -1,8 +1,13 @@
#pragma once
#include "../coding/reader.hpp"
#include "../geometry/point2d.hpp"
#include "../geometry/rect2d.hpp"
#include "../std/string.hpp"
#include "../std/noncopyable.hpp"
#include "../std/iostream.hpp"
class Bookmark
@ -21,3 +26,28 @@ public:
string const & GetName() const { return m_name; }
m2::RectD GetViewport() const { return m2::RectD(m_org, m_org); }
};
class BookmarkCategory : private noncopyable
{
string m_name;
vector<Bookmark *> m_bookmarks;
bool m_visible;
public:
BookmarkCategory(string const & name) : m_name(name), m_visible(true) {}
~BookmarkCategory();
void ClearBookmarks();
void AddBookmark(Bookmark const & bm);
bool IsVisible() const { return m_visible; }
string GetName() const { return m_name; }
inline size_t GetBookmarksCount() const { return m_bookmarks.size(); }
Bookmark const * GetBookmark(size_t index) const;
void RemoveBookmark(size_t index);
void LoadFromKML(ReaderPtr<Reader> const & reader);
void SaveToKML(ostream & s);
};

View file

@ -43,8 +43,13 @@ int FeaturesFetcher::AddMap(string const & file)
}
catch (Reader::Exception const & e)
{
LOG(LERROR, ("Data file adding error: ", e.what()));
LOG(LERROR, ("IO error while adding ", file, " map. ", e.what()));
}
catch (RootException const & e)
{
LOG(LERROR, ("Can't find map ", file, ". ", e.what()));
}
return version;
}

View file

@ -19,17 +19,12 @@
#include "../indexer/feature.hpp"
#include "../indexer/scales.hpp"
#include "../coding/parse_xml.hpp" // LoadFromKML
#include "../base/math.hpp"
#include "../base/string_utils.hpp"
#include "../std/algorithm.hpp"
#include "../std/target_os.hpp"
#include "../std/vector.hpp"
#include <boost/algorithm/string.hpp>
void Framework::AddMap(string const & file)
{
@ -147,18 +142,12 @@ Framework::Framework()
// is connected/disconnected
GetLocalMaps(maps);
#endif
// Remove duplicate maps if they're both present in resources and in WritableDir
sort(maps.begin(), maps.end());
maps.erase(unique(maps.begin(), maps.end()), maps.end());
try
{
for_each(maps.begin(), maps.end(), bind(&Framework::AddMap, this, _1));
}
catch (RootException const & e)
{
LOG(LERROR, ("Can't add map: ", e.what()));
}
for_each(maps.begin(), maps.end(), bind(&Framework::AddMap, this, _1));
m_storage.Init(bind(&Framework::AddMap, this, _1),
bind(&Framework::RemoveMap, this, _1),
@ -167,7 +156,9 @@ Framework::Framework()
}
Framework::~Framework()
{}
{
ClearBookmarks();
}
void Framework::AddLocalMaps()
{
@ -191,224 +182,83 @@ void Framework::RemoveLocalMaps()
m_model.RemoveAllCountries();
}
void Framework::AddBookmark(m2::PointD const & pt, string const & name)
void Framework::AddBookmark(string const & category, Bookmark const & bm)
{
m_bookmarks.push_back(Bookmark(pt, name));
GetBmCategory(category)->AddBookmark(bm);
}
void Framework::GetBookmark(size_t index, Bookmark & bm) const
namespace
{
ASSERT_LESS(index, BookmarksCount(), ());
list<Bookmark>::const_iterator it = m_bookmarks.begin();
advance(it, index); // not so fast ...
bm = *it;
class EqualCategoryName
{
string const & m_name;
public:
EqualCategoryName(string const & name) : m_name(name) {}
bool operator() (BookmarkCategory const * cat) const
{
return (cat->GetName() == m_name);
}
};
}
size_t Framework::GetBookmark(m2::PointD pt, Bookmark & bm) const
BookmarkCategory * Framework::GetBmCategory(size_t index) const
{
return (index < m_bookmarks.size() ? m_bookmarks[index] : 0);
}
BookmarkCategory * Framework::GetBmCategory(string const & name)
{
vector<BookmarkCategory *>::iterator i =
find_if(m_bookmarks.begin(), m_bookmarks.end(), EqualCategoryName(name));
if (i != m_bookmarks.end())
return (*i);
else
{
BookmarkCategory * cat = new BookmarkCategory(name);
m_bookmarks.push_back(cat);
return cat;
}
}
Bookmark const * Framework::GetBookmark(m2::PointD pt) const
{
// Get the global rect of touching area.
int const sm = 20 * GetPlatform().VisualScale();
m2::RectD rect(PtoG(m2::PointD(pt.x - sm, pt.y - sm)), PtoG(m2::PointD(pt.x + sm, pt.y + sm)));
size_t bestInd = static_cast<size_t>(-1);
Bookmark const * ret = 0;
double minD = numeric_limits<double>::max();
size_t ind = 0;
for (list<Bookmark>::const_iterator it = m_bookmarks.begin(); it != m_bookmarks.end(); ++it, ++ind)
for (size_t i = 0; i < m_bookmarks.size(); ++i)
{
if (rect.IsPointInside(it->GetOrg()))
size_t const count = m_bookmarks[i]->GetBookmarksCount();
for (size_t j = 0; j < count; ++j)
{
double const d = rect.Center().SquareLength(it->GetOrg());
if (d < minD)
Bookmark const * bm = m_bookmarks[i]->GetBookmark(j);
m2::PointD const pt = bm->GetOrg();
if (rect.IsPointInside(pt))
{
bm = *it;
bestInd = ind;
double const d = rect.Center().SquareLength(pt);
if (d < minD)
{
ret = bm;
minD = d;
}
}
}
}
return bestInd;
}
void Framework::RemoveBookmark(size_t index)
{
if (index >= m_bookmarks.size())
{
LOG(LWARNING, ("Trying to delete invalid bookmark with index", index));
return;
}
list<Bookmark>::iterator it = m_bookmarks.begin();
advance(it, index); // not so fast ...
m_bookmarks.erase(it);
return ret;
}
void Framework::ClearBookmarks()
{
for_each(m_bookmarks.begin(), m_bookmarks.end(), DeleteFunctor());
m_bookmarks.clear();
}
namespace
{
class KMLParser
{
Framework & m_framework;
int m_level;
string m_name;
m2::PointD m_org;
void Reset()
{
m_name.clear();
m_org = m2::PointD(-1000, -1000);
}
void SetOrigin(string const & s)
{
// order in string is: lon, lat, z
strings::SimpleTokenizer iter(s, ", ");
if (iter)
{
double lon;
if (strings::to_double(*iter, lon) && MercatorBounds::ValidLon(lon) && ++iter)
{
double lat;
if (strings::to_double(*iter, lat) && MercatorBounds::ValidLat(lat))
m_org = m2::PointD(MercatorBounds::LonToX(lon), MercatorBounds::LatToY(lat));
}
}
}
bool IsValid() const
{
return (!m_name.empty() &&
MercatorBounds::ValidX(m_org.x) && MercatorBounds::ValidY(m_org.y));
}
public:
KMLParser(Framework & f) : m_framework(f), m_level(0)
{
Reset();
}
bool Push(string const & name)
{
switch (m_level)
{
case 0:
if (name != "kml") return false;
break;
case 1:
if (name != "Document") return false;
break;
case 2:
if (name != "Placemark") return false;
break;
case 3:
if (name != "Point" && name != "name") return false;
break;
case 4:
if (name != "coordinates") return false;
}
++m_level;
return true;
}
void AddAttr(string const &, string const &) {}
void Pop(string const &)
{
--m_level;
if (m_level == 2 && IsValid())
{
m_framework.AddBookmark(m_org, m_name);
Reset();
}
}
class IsSpace
{
public:
bool operator() (char c) const
{
return ::isspace(c);
}
};
void CharData(string value)
{
boost::trim(value);
if (!value.empty())
switch (m_level)
{
case 4: m_name = value; break;
case 5: SetOrigin(value); break;
}
}
};
}
void Framework::LoadFromKML(ReaderPtr<Reader> const & reader)
{
ReaderSource<ReaderPtr<Reader> > src(reader);
KMLParser parser(*this);
ParseXML(src, parser, true);
}
namespace
{
char const * kmlHeader =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
"<kml xmlns=\"http://earth.google.com/kml/2.2\">\n"
"<Document>\n"
" <name>MapsWithMe</name>\n";
char const * kmlFooter =
"</Document>\n"
"</kml>\n";
string PointToString(m2::PointD const & org)
{
double const lon = MercatorBounds::XToLon(org.x);
double const lat = MercatorBounds::YToLat(org.y);
ostringstream ss;
ss.precision(8);
ss << lon << "," << lat;
return ss.str();
}
}
void Framework::SaveToKML(std::ostream & s)
{
s << kmlHeader;
for (list<Bookmark>::const_iterator i = m_bookmarks.begin(); i != m_bookmarks.end(); ++i)
{
s << " <Placemark>\n"
<< " <name>" << i->GetName() << "</name>\n"
<< " <Point>\n"
<< " <coordinates>" << PointToString(i->GetOrg()) << "</coordinates>\n"
<< " </Point>\n"
<< " </Placemark>\n";
}
s << kmlFooter;
}
void Framework::GetLocalMaps(vector<string> & outMaps)
{
Platform & pl = GetPlatform();
@ -620,11 +470,17 @@ void Framework::DrawAdditionalInfo(shared_ptr<PaintEvent> const & e)
if (m_drawPlacemark)
m_informationDisplay.drawPlacemark(pDrawer, "placemark", m_navigator.GtoP(m_placemark));
for (list<Bookmark>::const_iterator i = m_bookmarks.begin(); i != m_bookmarks.end(); ++i)
{
/// @todo Pass different symbol.
m_informationDisplay.drawPlacemark(pDrawer, "placemark", m_navigator.GtoP(i->GetOrg()));
}
for (size_t i = 0; i < m_bookmarks.size(); ++i)
if (m_bookmarks[i]->IsVisible())
{
size_t const count = m_bookmarks[i]->GetBookmarksCount();
for (size_t j = 0; j < count; ++j)
{
Bookmark const * bm = m_bookmarks[i]->GetBookmark(j);
/// @todo Pass different symbol.
m_informationDisplay.drawPlacemark(pDrawer, "placemark", m_navigator.GtoP(bm->GetOrg()));
}
}
pDrawer->screen()->endFrame();
}

View file

@ -60,7 +60,7 @@ protected:
model::FeaturesFetcher m_model;
Navigator m_navigator;
list<Bookmark> m_bookmarks;
vector<BookmarkCategory *> m_bookmarks;
scoped_ptr<RenderPolicy> m_renderPolicy;
bool m_hasPendingInvalidate, m_doForceUpdate, m_queryMaxScaleMode, m_drawPlacemark;
@ -124,24 +124,23 @@ public:
void AddLocalMaps();
void RemoveLocalMaps();
void AddBookmark(m2::PointD const & pt, string const & name);
inline size_t BookmarksCount() const { return m_bookmarks.size(); }
void GetBookmark(size_t index, Bookmark & bm) const;
void AddBookmark(string const & category, Bookmark const & bm);
inline size_t GetBmCategoriesCount() const { return m_bookmarks.size(); }
BookmarkCategory * GetBmCategory(size_t index) const;
/// Find or create new category by name.
BookmarkCategory * GetBmCategory(string const & name);
/// Get bookmark by touch.
/// @param[in] pixPt Coordinates of touch point in pixels.
/// @return Index of bookmark (-1, if bookmark wasn't found).
size_t GetBookmark(m2::PointD pixPt, Bookmark & bm) const;
/// @return NULL If not biikmark near the point.
Bookmark const * GetBookmark(m2::PointD pixPt) const;
void RemoveBookmark(size_t index);
void ClearBookmarks();
inline m2::PointD PtoG(m2::PointD const & p) const { return m_navigator.PtoG(p); }
inline m2::PointD GtoP(m2::PointD const & p) const { return m_navigator.GtoP(p); }
void LoadFromKML(ReaderPtr<Reader> const & reader);
void SaveToKML(std::ostream & s);
storage::Storage & Storage() { return m_storage; }
void OnLocationStatusChanged(location::TLocationStatus newStatus);

View file

@ -90,6 +90,7 @@ SOURCES += \
address_finder.cpp \
tile_set.cpp \
geourl_process.cpp \
bookmark.cpp \
!iphone*:!bada*:!android* {
HEADERS += qgl_render_context.hpp

View file

@ -54,18 +54,17 @@ char const * kmlString =
"</Document>"
"</kml>";
void CheckBookmarks(Framework const & fm)
void CheckBookmarks(BookmarkCategory const & cat)
{
TEST_EQUAL(fm.BookmarksCount(), 3, ());
TEST_EQUAL(cat.GetBookmarksCount(), 3, ());
Bookmark bm;
fm.GetBookmark(0, bm);
TEST_EQUAL(bm.GetName(), "Nebraska", ());
fm.GetBookmark(1, bm);
TEST_EQUAL(bm.GetName(), "Monongahela National Forest", ());
Bookmark const * bm = cat.GetBookmark(0);
TEST_EQUAL(bm->GetName(), "Nebraska", ());
bm = cat.GetBookmark(1);
TEST_EQUAL(bm->GetName(), "Monongahela National Forest", ());
fm.GetBookmark(2, bm);
m2::PointD const org = bm.GetOrg();
bm = cat.GetBookmark(2);
m2::PointD const org = bm->GetOrg();
TEST_ALMOST_EQUAL(MercatorBounds::XToLon(org.x), 27.566765, ());
TEST_ALMOST_EQUAL(MercatorBounds::YToLat(org.y), 53.900047, ());
}
@ -73,31 +72,31 @@ char const * kmlString =
UNIT_TEST(Bookmarks_ImportKML)
{
Framework fm;
fm.LoadFromKML(new MemReader(kmlString, strlen(kmlString)));
BookmarkCategory cat("Default");
cat.LoadFromKML(new MemReader(kmlString, strlen(kmlString)));
CheckBookmarks(fm);
CheckBookmarks(cat);
}
UNIT_TEST(Bookmarks_ExportKML)
{
Framework fm;
fm.LoadFromKML(new MemReader(kmlString, strlen(kmlString)));
BookmarkCategory cat("Default");
cat.LoadFromKML(new MemReader(kmlString, strlen(kmlString)));
CheckBookmarks(fm);
CheckBookmarks(cat);
{
ofstream of("Bookmarks.kml");
fm.SaveToKML(of);
cat.SaveToKML(of);
}
fm.ClearBookmarks();
cat.ClearBookmarks();
TEST_EQUAL(fm.BookmarksCount(), 0, ());
TEST_EQUAL(cat.GetBookmarksCount(), 0, ());
fm.LoadFromKML(new FileReader("Bookmarks.kml"));
cat.LoadFromKML(new FileReader("Bookmarks.kml"));
CheckBookmarks(fm);
CheckBookmarks(cat);
}
UNIT_TEST(Bookmarks_Getting)
@ -111,11 +110,14 @@ UNIT_TEST(Bookmarks_Getting)
// This is not correct because Framework::OnSize doesn't work until SetRenderPolicy is called.
//TEST(m2::AlmostEqual(m2::PointD(400, 200), pixC), (pixC));
fm.AddBookmark(m2::PointD(38, 20), "1");
fm.AddBookmark(m2::PointD(41, 20), "2");
fm.AddBookmark(m2::PointD(41, 40), "3");
fm.AddBookmark("cat1", Bookmark(m2::PointD(38, 20), "1"));
fm.AddBookmark("cat2", Bookmark(m2::PointD(41, 20), "2"));
fm.AddBookmark("cat3", Bookmark(m2::PointD(41, 40), "3"));
Bookmark bm;
TEST_EQUAL(fm.GetBookmark(pixC, bm), 1, ());
TEST_EQUAL(bm.GetName(), "2", ());
Bookmark const * bm = fm.GetBookmark(pixC);
TEST(bm != 0, ());
TEST_EQUAL(bm->GetName(), "2", ());
TEST(fm.GetBookmark(m2::PointD(0, 0)) == 0, ());
TEST(fm.GetBookmark(m2::PointD(800, 400)) == 0, ());
}

View file

@ -138,7 +138,8 @@ void SearchPanel::OnSearchResult(ResultsT * res, int queryId)
if (e.GetResultType() == ResultT::RESULT_FEATURE)
{
frm.AddBookmark(e.GetFeatureCenter(), e.GetString());
// For debug purposes: add bookmarks for search results
frm.AddBookmark("Search", Bookmark(e.GetFeatureCenter(), e.GetString()));
m_pTable->setItem(rowCount, 0,
create_item(QString::fromUtf8(e.GetFeatureType())));