mirror of
https://codeberg.org/comaps/comaps
synced 2025-12-19 13:03:36 +00:00
Compare commits
1 Commits
60b1ad232a
...
zy-panoram
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5aaf5aa378 |
@@ -147,6 +147,8 @@ set(SRC
|
||||
osm_o5m_source.hpp
|
||||
osm_source.cpp
|
||||
osm_xml_source.hpp
|
||||
panoramax_collector.cpp
|
||||
panoramax_collector.hpp
|
||||
place_processor.cpp
|
||||
place_processor.hpp
|
||||
platform_helpers.cpp
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "generator/mini_roundabout_transformer.hpp"
|
||||
#include "generator/node_mixer.hpp"
|
||||
#include "generator/osm2type.hpp"
|
||||
#include "generator/panoramax_collector.hpp"
|
||||
#include "generator/region_meta.hpp"
|
||||
|
||||
#include "routing/speed_camera_prohibition.hpp"
|
||||
@@ -68,6 +69,9 @@ void CountryFinalProcessor::Process()
|
||||
if (!m_isolinesPath.empty())
|
||||
AddIsolines();
|
||||
|
||||
LOG(LINFO, ("Enriching with Panoramax data..."));
|
||||
EnrichPanoramax();
|
||||
|
||||
// DropProhibitedSpeedCameras();
|
||||
LOG(LINFO, ("Processing building parts..."));
|
||||
ProcessBuildingParts();
|
||||
@@ -293,6 +297,50 @@ void CountryFinalProcessor::AddAddresses()
|
||||
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()
|
||||
{
|
||||
/// @todo We can remove MinSize at all.
|
||||
|
||||
@@ -26,6 +26,7 @@ public:
|
||||
void SetAddressesDir(std::string const & dir) { m_addressPath = dir; }
|
||||
|
||||
void SetCityBoundariesFiles(std::string const & collectorFile) { m_boundariesCollectorFile = collectorFile; }
|
||||
void SetPanoramaxFile(std::string const & filename) { m_panoramaxFilename = filename; }
|
||||
|
||||
// FinalProcessorIntermediateMwmInterface overrides:
|
||||
void Process() override;
|
||||
@@ -39,6 +40,7 @@ private:
|
||||
void AddFakeNodes();
|
||||
void AddIsolines();
|
||||
void AddAddresses();
|
||||
void EnrichPanoramax();
|
||||
void DropProhibitedSpeedCameras();
|
||||
// void Finish();
|
||||
|
||||
@@ -54,6 +56,7 @@ private:
|
||||
std::string m_fakeNodesFilename;
|
||||
std::string m_miniRoundaboutsFilename;
|
||||
std::string m_addrInterpolFilename;
|
||||
std::string m_panoramaxFilename;
|
||||
|
||||
std::string m_hierarchySrcFilename;
|
||||
|
||||
|
||||
@@ -42,6 +42,9 @@ struct GenerateInfo
|
||||
// External folders with additional preprocessed data (isolines, addresses).
|
||||
std::string m_isolinesDir, m_addressesDir;
|
||||
|
||||
// Panoramax imagery data file
|
||||
std::string m_panoramaxFilename;
|
||||
|
||||
// Current generated file name if --output option is defined.
|
||||
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_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(panoramax_file, "", "Path to Panoramax imagery coords file. If set, enriches roads with street-level imagery availability.");
|
||||
|
||||
// Routing.
|
||||
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_isolinesDir = FLAGS_isolines_path;
|
||||
genInfo.m_addressesDir = FLAGS_addresses_path;
|
||||
genInfo.m_panoramaxFilename = FLAGS_panoramax_file;
|
||||
|
||||
// Use merged style.
|
||||
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);
|
||||
finalProcessor->SetIsolinesDir(m_genInfo.m_isolinesDir);
|
||||
finalProcessor->SetAddressesDir(m_genInfo.m_addressesDir);
|
||||
finalProcessor->SetPanoramaxFile(m_genInfo.m_panoramaxFilename);
|
||||
finalProcessor->SetMiniRoundabouts(m_genInfo.GetIntermediateFileName(MINI_ROUNDABOUTS_FILENAME));
|
||||
finalProcessor->SetAddrInterpolation(m_genInfo.GetIntermediateFileName(ADDR_INTERPOL_FILENAME));
|
||||
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