mirror of
https://codeberg.org/comaps/comaps
synced 2026-01-06 12:34:24 +00:00
Compare commits
1 Commits
60b1ad232a
...
zy-panoram
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5aaf5aa378 |
@@ -147,6 +147,8 @@ set(SRC
|
|||||||
osm_o5m_source.hpp
|
osm_o5m_source.hpp
|
||||||
osm_source.cpp
|
osm_source.cpp
|
||||||
osm_xml_source.hpp
|
osm_xml_source.hpp
|
||||||
|
panoramax_collector.cpp
|
||||||
|
panoramax_collector.hpp
|
||||||
place_processor.cpp
|
place_processor.cpp
|
||||||
place_processor.hpp
|
place_processor.hpp
|
||||||
platform_helpers.cpp
|
platform_helpers.cpp
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#include "generator/mini_roundabout_transformer.hpp"
|
#include "generator/mini_roundabout_transformer.hpp"
|
||||||
#include "generator/node_mixer.hpp"
|
#include "generator/node_mixer.hpp"
|
||||||
#include "generator/osm2type.hpp"
|
#include "generator/osm2type.hpp"
|
||||||
|
#include "generator/panoramax_collector.hpp"
|
||||||
#include "generator/region_meta.hpp"
|
#include "generator/region_meta.hpp"
|
||||||
|
|
||||||
#include "routing/speed_camera_prohibition.hpp"
|
#include "routing/speed_camera_prohibition.hpp"
|
||||||
@@ -68,6 +69,9 @@ void CountryFinalProcessor::Process()
|
|||||||
if (!m_isolinesPath.empty())
|
if (!m_isolinesPath.empty())
|
||||||
AddIsolines();
|
AddIsolines();
|
||||||
|
|
||||||
|
LOG(LINFO, ("Enriching with Panoramax data..."));
|
||||||
|
EnrichPanoramax();
|
||||||
|
|
||||||
// DropProhibitedSpeedCameras();
|
// DropProhibitedSpeedCameras();
|
||||||
LOG(LINFO, ("Processing building parts..."));
|
LOG(LINFO, ("Processing building parts..."));
|
||||||
ProcessBuildingParts();
|
ProcessBuildingParts();
|
||||||
@@ -293,6 +297,50 @@ void CountryFinalProcessor::AddAddresses()
|
|||||||
LOG(LINFO, ("Total addresses:", totalStats));
|
LOG(LINFO, ("Total addresses:", totalStats));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CountryFinalProcessor::EnrichPanoramax()
|
||||||
|
{
|
||||||
|
if (m_panoramaxFilename.empty() || !Platform::IsFileExistsByFullPath(m_panoramaxFilename))
|
||||||
|
{
|
||||||
|
LOG(LINFO, ("Panoramax data not available, skipping enrichment"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG(LINFO, ("Enriching roads with Panoramax imagery data from:", m_panoramaxFilename));
|
||||||
|
|
||||||
|
// Load Panoramax imagery data
|
||||||
|
PanoramaxCollector collector;
|
||||||
|
if (!collector.LoadImageryData(m_panoramaxFilename, ""))
|
||||||
|
{
|
||||||
|
LOG(LWARNING, ("Failed to load Panoramax data"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enrich roads in each MWM
|
||||||
|
ForEachMwmTmp(m_temporaryMwmPath, [&](auto const & name, auto const & path)
|
||||||
|
{
|
||||||
|
if (!IsCountry(name))
|
||||||
|
return;
|
||||||
|
|
||||||
|
LOG(LINFO, ("Enriching Panoramax for:", name));
|
||||||
|
|
||||||
|
std::vector<FeatureBuilder> features;
|
||||||
|
ForEachFeatureRawFormat<serialization_policy::MaxAccuracy>(
|
||||||
|
path, [&](FeatureBuilder && fb, uint64_t) {
|
||||||
|
collector.EnrichRoad(fb);
|
||||||
|
features.emplace_back(std::move(fb));
|
||||||
|
});
|
||||||
|
|
||||||
|
// Rewrite the file with enriched features
|
||||||
|
FeatureBuilderWriter<serialization_policy::MaxAccuracy> writer(path, FileWriter::Op::OP_WRITE_TRUNCATE);
|
||||||
|
for (auto & fb : features)
|
||||||
|
writer.Write(fb);
|
||||||
|
|
||||||
|
LOG(LINFO, (name, "done"));
|
||||||
|
});
|
||||||
|
|
||||||
|
LOG(LINFO, ("Panoramax enrichment complete:", DebugPrint(collector.GetStats())));
|
||||||
|
}
|
||||||
|
|
||||||
void CountryFinalProcessor::ProcessCoastline()
|
void CountryFinalProcessor::ProcessCoastline()
|
||||||
{
|
{
|
||||||
/// @todo We can remove MinSize at all.
|
/// @todo We can remove MinSize at all.
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ public:
|
|||||||
void SetAddressesDir(std::string const & dir) { m_addressPath = dir; }
|
void SetAddressesDir(std::string const & dir) { m_addressPath = dir; }
|
||||||
|
|
||||||
void SetCityBoundariesFiles(std::string const & collectorFile) { m_boundariesCollectorFile = collectorFile; }
|
void SetCityBoundariesFiles(std::string const & collectorFile) { m_boundariesCollectorFile = collectorFile; }
|
||||||
|
void SetPanoramaxFile(std::string const & filename) { m_panoramaxFilename = filename; }
|
||||||
|
|
||||||
// FinalProcessorIntermediateMwmInterface overrides:
|
// FinalProcessorIntermediateMwmInterface overrides:
|
||||||
void Process() override;
|
void Process() override;
|
||||||
@@ -39,6 +40,7 @@ private:
|
|||||||
void AddFakeNodes();
|
void AddFakeNodes();
|
||||||
void AddIsolines();
|
void AddIsolines();
|
||||||
void AddAddresses();
|
void AddAddresses();
|
||||||
|
void EnrichPanoramax();
|
||||||
void DropProhibitedSpeedCameras();
|
void DropProhibitedSpeedCameras();
|
||||||
// void Finish();
|
// void Finish();
|
||||||
|
|
||||||
@@ -54,6 +56,7 @@ private:
|
|||||||
std::string m_fakeNodesFilename;
|
std::string m_fakeNodesFilename;
|
||||||
std::string m_miniRoundaboutsFilename;
|
std::string m_miniRoundaboutsFilename;
|
||||||
std::string m_addrInterpolFilename;
|
std::string m_addrInterpolFilename;
|
||||||
|
std::string m_panoramaxFilename;
|
||||||
|
|
||||||
std::string m_hierarchySrcFilename;
|
std::string m_hierarchySrcFilename;
|
||||||
|
|
||||||
|
|||||||
@@ -42,6 +42,9 @@ struct GenerateInfo
|
|||||||
// External folders with additional preprocessed data (isolines, addresses).
|
// External folders with additional preprocessed data (isolines, addresses).
|
||||||
std::string m_isolinesDir, m_addressesDir;
|
std::string m_isolinesDir, m_addressesDir;
|
||||||
|
|
||||||
|
// Panoramax imagery data file
|
||||||
|
std::string m_panoramaxFilename;
|
||||||
|
|
||||||
// Current generated file name if --output option is defined.
|
// Current generated file name if --output option is defined.
|
||||||
std::string m_fileName;
|
std::string m_fileName;
|
||||||
|
|
||||||
|
|||||||
@@ -107,6 +107,7 @@ DEFINE_string(nodes_list_path, "",
|
|||||||
DEFINE_bool(generate_isolines_info, false, "Generate the isolines info section");
|
DEFINE_bool(generate_isolines_info, false, "Generate the isolines info section");
|
||||||
DEFINE_string(isolines_path, "", "Path to isolines directory. If set, adds isolines linear features.");
|
DEFINE_string(isolines_path, "", "Path to isolines directory. If set, adds isolines linear features.");
|
||||||
DEFINE_string(addresses_path, "", "Path to addresses directory. If set, adds addr:interpolation features.");
|
DEFINE_string(addresses_path, "", "Path to addresses directory. If set, adds addr:interpolation features.");
|
||||||
|
DEFINE_string(panoramax_file, "", "Path to Panoramax imagery coords file. If set, enriches roads with street-level imagery availability.");
|
||||||
|
|
||||||
// Routing.
|
// Routing.
|
||||||
DEFINE_bool(make_routing_index, false, "Make sections with the routing information.");
|
DEFINE_bool(make_routing_index, false, "Make sections with the routing information.");
|
||||||
@@ -243,6 +244,7 @@ MAIN_WITH_ERROR_HANDLING([](int argc, char ** argv)
|
|||||||
genInfo.m_complexHierarchyFilename = FLAGS_complex_hierarchy_data;
|
genInfo.m_complexHierarchyFilename = FLAGS_complex_hierarchy_data;
|
||||||
genInfo.m_isolinesDir = FLAGS_isolines_path;
|
genInfo.m_isolinesDir = FLAGS_isolines_path;
|
||||||
genInfo.m_addressesDir = FLAGS_addresses_path;
|
genInfo.m_addressesDir = FLAGS_addresses_path;
|
||||||
|
genInfo.m_panoramaxFilename = FLAGS_panoramax_file;
|
||||||
|
|
||||||
// Use merged style.
|
// Use merged style.
|
||||||
GetStyleReader().SetCurrentStyle(MapStyleMerged);
|
GetStyleReader().SetCurrentStyle(MapStyleMerged);
|
||||||
|
|||||||
157
generator/panoramax_collector.cpp
Normal file
157
generator/panoramax_collector.cpp
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
#include "generator/panoramax_collector.hpp"
|
||||||
|
|
||||||
|
#include "indexer/feature_meta.hpp"
|
||||||
|
#include "indexer/ftypes_matcher.hpp"
|
||||||
|
|
||||||
|
#include "geometry/mercator.hpp"
|
||||||
|
|
||||||
|
#include "base/assert.hpp"
|
||||||
|
#include "base/logging.hpp"
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <fstream>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
namespace generator
|
||||||
|
{
|
||||||
|
|
||||||
|
PanoramaxCollector::PanoramaxCollector() = default;
|
||||||
|
|
||||||
|
bool PanoramaxCollector::LoadImageryData(std::string const & coordsFilePath, std::string const & /* tilesDir */)
|
||||||
|
{
|
||||||
|
LOG(LINFO, ("Loading Panoramax imagery from:", coordsFilePath));
|
||||||
|
|
||||||
|
std::ifstream file(coordsFilePath, std::ios::binary);
|
||||||
|
if (!file.is_open())
|
||||||
|
{
|
||||||
|
LOG(LWARNING, ("Failed to open Panoramax coords file:", coordsFilePath));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read header
|
||||||
|
uint32_t version;
|
||||||
|
uint64_t count;
|
||||||
|
|
||||||
|
file.read(reinterpret_cast<char*>(&version), sizeof(version));
|
||||||
|
file.read(reinterpret_cast<char*>(&count), sizeof(count));
|
||||||
|
|
||||||
|
if (version != 1)
|
||||||
|
{
|
||||||
|
LOG(LWARNING, ("Unsupported Panoramax coords file version:", version));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG(LINFO, ("Loading", count, "Panoramax imagery points..."));
|
||||||
|
|
||||||
|
// Read coordinates and build spatial index
|
||||||
|
std::vector<ImageryPoint> points;
|
||||||
|
points.reserve(count);
|
||||||
|
|
||||||
|
for (uint64_t i = 0; i < count; ++i)
|
||||||
|
{
|
||||||
|
double lat, lon;
|
||||||
|
file.read(reinterpret_cast<char*>(&lat), sizeof(lat));
|
||||||
|
file.read(reinterpret_cast<char*>(&lon), sizeof(lon));
|
||||||
|
|
||||||
|
// Convert to Mercator coordinates
|
||||||
|
m2::PointD mercPt = mercator::FromLatLon(lat, lon);
|
||||||
|
points.emplace_back(mercPt);
|
||||||
|
|
||||||
|
if ((i + 1) % 500000 == 0)
|
||||||
|
LOG(LINFO, ("Loaded", i + 1, "/", count, "points"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!file)
|
||||||
|
{
|
||||||
|
LOG(LWARNING, ("Error reading Panoramax coords file"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
LOG(LINFO, ("Building spatial index for", points.size(), "points..."));
|
||||||
|
|
||||||
|
// Build spatial index
|
||||||
|
for (auto const & pt : points)
|
||||||
|
m_imageryTree.Add(pt);
|
||||||
|
|
||||||
|
m_stats.m_totalImageryPoints = static_cast<uint32_t>(points.size());
|
||||||
|
|
||||||
|
LOG(LINFO, ("Panoramax data loaded:", m_stats.m_totalImageryPoints, "imagery points"));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PanoramaxCollector::EnrichRoad(feature::FeatureBuilder & fb)
|
||||||
|
{
|
||||||
|
// Only process roads
|
||||||
|
static auto const & isRoad = ftypes::IsWayChecker::Instance();
|
||||||
|
if (!isRoad(fb.GetTypes()))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
m_stats.m_totalRoads++;
|
||||||
|
|
||||||
|
// Check if road already has Panoramax flag
|
||||||
|
if (fb.GetMetadata().Has(feature::Metadata::FMD_PANORAMAX))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Get road geometry
|
||||||
|
std::vector<m2::PointD> roadPoints;
|
||||||
|
fb.ForEachPoint([&roadPoints](m2::PointD const & pt) {
|
||||||
|
roadPoints.push_back(pt);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (roadPoints.empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Calculate road bounding box
|
||||||
|
m2::RectD roadBBox;
|
||||||
|
for (auto const & pt : roadPoints)
|
||||||
|
roadBBox.Add(pt);
|
||||||
|
|
||||||
|
// Expand bounding box by threshold distance in Mercator units
|
||||||
|
auto const center = roadBBox.Center();
|
||||||
|
auto const inflateRect = mercator::RectByCenterXYAndSizeInMeters(center, kDistanceThresholdM);
|
||||||
|
roadBBox.Inflate(inflateRect.SizeX() / 2.0, inflateRect.SizeY() / 2.0);
|
||||||
|
|
||||||
|
// Query imagery tree for nearby points
|
||||||
|
bool hasImagery = false;
|
||||||
|
|
||||||
|
m_imageryTree.ForEachInRect(roadBBox, [&](ImageryPoint const & imgPt) {
|
||||||
|
if (hasImagery)
|
||||||
|
return; // Already found, early exit
|
||||||
|
|
||||||
|
// Check distance to each road point
|
||||||
|
for (auto const & roadPt : roadPoints)
|
||||||
|
{
|
||||||
|
double const distM = mercator::DistanceOnEarth(roadPt, imgPt.m_point);
|
||||||
|
if (distM <= kDistanceThresholdM)
|
||||||
|
{
|
||||||
|
hasImagery = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (hasImagery)
|
||||||
|
{
|
||||||
|
fb.GetMetadata().Set(feature::Metadata::FMD_PANORAMAX, "yes");
|
||||||
|
m_stats.m_roadsWithImagery++;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string DebugPrint(PanoramaxCollector::Stats const & s)
|
||||||
|
{
|
||||||
|
std::ostringstream out;
|
||||||
|
out << "PanoramaxCollector::Stats {"
|
||||||
|
<< " totalRoads: " << s.m_totalRoads
|
||||||
|
<< ", roadsWithImagery: " << s.m_roadsWithImagery
|
||||||
|
<< " (" << (s.m_totalRoads > 0 ? (s.m_roadsWithImagery * 100.0 / s.m_totalRoads) : 0) << "%)"
|
||||||
|
<< ", totalImageryPoints: " << s.m_totalImageryPoints
|
||||||
|
<< " }";
|
||||||
|
return out.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace generator
|
||||||
72
generator/panoramax_collector.hpp
Normal file
72
generator/panoramax_collector.hpp
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "generator/feature_builder.hpp"
|
||||||
|
|
||||||
|
#include "geometry/point2d.hpp"
|
||||||
|
#include "geometry/tree4d.hpp"
|
||||||
|
|
||||||
|
#include "base/geo_object_id.hpp"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace generator
|
||||||
|
{
|
||||||
|
/// Enriches road features with Panoramax street-level imagery availability flag.
|
||||||
|
/// Loads Panoramax imagery coordinates from tile index and marks roads within threshold distance.
|
||||||
|
class PanoramaxCollector
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// Distance threshold for matching roads to imagery (meters)
|
||||||
|
static double constexpr kDistanceThresholdM = 20.0;
|
||||||
|
|
||||||
|
/// ImageryPoint represents a single point where street-level imagery exists
|
||||||
|
struct ImageryPoint
|
||||||
|
{
|
||||||
|
m2::PointD m_point; // Mercator coordinates
|
||||||
|
|
||||||
|
ImageryPoint() = default;
|
||||||
|
explicit ImageryPoint(m2::PointD const & pt) : m_point(pt) {}
|
||||||
|
|
||||||
|
m2::RectD GetLimitRect() const { return m2::RectD(m_point, m_point); }
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Statistics for debugging/logging
|
||||||
|
struct Stats
|
||||||
|
{
|
||||||
|
uint32_t m_totalRoads = 0;
|
||||||
|
uint32_t m_roadsWithImagery = 0;
|
||||||
|
uint32_t m_totalImageryPoints = 0;
|
||||||
|
uint32_t m_tilesProcessed = 0;
|
||||||
|
|
||||||
|
friend std::string DebugPrint(Stats const & s);
|
||||||
|
};
|
||||||
|
|
||||||
|
PanoramaxCollector();
|
||||||
|
|
||||||
|
/// Load Panoramax imagery data from tile directory
|
||||||
|
/// @param tileIndexPath Path to tile_index.json
|
||||||
|
/// @param tilesDir Directory containing .mvt tiles
|
||||||
|
/// @return true if data loaded successfully
|
||||||
|
bool LoadImageryData(std::string const & tileIndexPath, std::string const & tilesDir);
|
||||||
|
|
||||||
|
/// Check if a road has nearby imagery and enrich it
|
||||||
|
/// @param fb FeatureBuilder for a road segment
|
||||||
|
/// @return true if imagery was found within threshold
|
||||||
|
bool EnrichRoad(feature::FeatureBuilder & fb);
|
||||||
|
|
||||||
|
Stats const & GetStats() const { return m_stats; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// Spatial index of imagery points
|
||||||
|
m4::Tree<ImageryPoint> m_imageryTree;
|
||||||
|
|
||||||
|
/// Statistics
|
||||||
|
Stats m_stats;
|
||||||
|
|
||||||
|
/// Decode an MVT tile and extract imagery coordinates
|
||||||
|
std::vector<m2::PointD> DecodeMVTTile(std::string const & tilePath,
|
||||||
|
int tileX, int tileY, int zoom);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace generator
|
||||||
@@ -182,6 +182,7 @@ RawGenerator::FinalProcessorPtr RawGenerator::CreateCountryFinalProcessor(Affili
|
|||||||
auto finalProcessor = std::make_shared<CountryFinalProcessor>(affiliations, m_genInfo.m_tmpDir, m_threadsCount);
|
auto finalProcessor = std::make_shared<CountryFinalProcessor>(affiliations, m_genInfo.m_tmpDir, m_threadsCount);
|
||||||
finalProcessor->SetIsolinesDir(m_genInfo.m_isolinesDir);
|
finalProcessor->SetIsolinesDir(m_genInfo.m_isolinesDir);
|
||||||
finalProcessor->SetAddressesDir(m_genInfo.m_addressesDir);
|
finalProcessor->SetAddressesDir(m_genInfo.m_addressesDir);
|
||||||
|
finalProcessor->SetPanoramaxFile(m_genInfo.m_panoramaxFilename);
|
||||||
finalProcessor->SetMiniRoundabouts(m_genInfo.GetIntermediateFileName(MINI_ROUNDABOUTS_FILENAME));
|
finalProcessor->SetMiniRoundabouts(m_genInfo.GetIntermediateFileName(MINI_ROUNDABOUTS_FILENAME));
|
||||||
finalProcessor->SetAddrInterpolation(m_genInfo.GetIntermediateFileName(ADDR_INTERPOL_FILENAME));
|
finalProcessor->SetAddrInterpolation(m_genInfo.GetIntermediateFileName(ADDR_INTERPOL_FILENAME));
|
||||||
if (addAds)
|
if (addAds)
|
||||||
|
|||||||
BIN
scripts/panoramax_tiles_switzerland/panoramax_coords.bin
Normal file
BIN
scripts/panoramax_tiles_switzerland/panoramax_coords.bin
Normal file
Binary file not shown.
Reference in New Issue
Block a user