diff options
| author | Ariel Costas Guerrero <ariel@costas.dev> | 2025-11-14 13:37:05 +0100 |
|---|---|---|
| committer | Ariel Costas Guerrero <ariel@costas.dev> | 2025-11-14 13:37:26 +0100 |
| commit | e030d6feff065b0f412d7e51684a0b6e6eca7fec (patch) | |
| tree | 18827cbfbaa5e6faf30fdb0aaec21eba84c39db9 /src | |
| parent | 08eaea3264f2e4628c40c8e79e3952f630b55221 (diff) | |
Implement new stop schedule generation format using protobuf
Diffstat (limited to 'src')
| -rw-r--r-- | src/Costasdev.Busurbano.Backend/Controllers/VigoController.cs | 57 | ||||
| -rw-r--r-- | src/Costasdev.Busurbano.Backend/Costasdev.Busurbano.Backend.csproj | 6 | ||||
| -rw-r--r-- | src/Costasdev.Busurbano.Backend/Types/StopSchedule.cs | 1311 | ||||
| -rw-r--r-- | src/common/stop_schedule.proto | 41 | ||||
| -rw-r--r-- | src/gtfs_vigo_stops/pyproject.toml | 2 | ||||
| -rw-r--r-- | src/gtfs_vigo_stops/src/__init__.py | 0 | ||||
| -rw-r--r-- | src/gtfs_vigo_stops/src/common.py | 35 | ||||
| -rw-r--r-- | src/gtfs_vigo_stops/src/proto/__init__.py | 1 | ||||
| -rw-r--r-- | src/gtfs_vigo_stops/src/proto/stop_schedule_pb2.py | 40 | ||||
| -rw-r--r-- | src/gtfs_vigo_stops/src/proto/stop_schedule_pb2.pyi | 60 | ||||
| -rw-r--r-- | src/gtfs_vigo_stops/src/report_writer.py | 99 | ||||
| -rw-r--r-- | src/gtfs_vigo_stops/src/stop_schedule_pb2.py | 41 | ||||
| -rw-r--r-- | src/gtfs_vigo_stops/src/stop_schedule_pb2.pyi | 60 | ||||
| -rw-r--r-- | src/gtfs_vigo_stops/src/stops.py | 46 | ||||
| -rw-r--r-- | src/gtfs_vigo_stops/stop_report.py | 158 | ||||
| -rw-r--r-- | src/gtfs_vigo_stops/uv.lock | 183 |
16 files changed, 1957 insertions, 183 deletions
diff --git a/src/Costasdev.Busurbano.Backend/Controllers/VigoController.cs b/src/Costasdev.Busurbano.Backend/Controllers/VigoController.cs index 362c73e..84b0461 100644 --- a/src/Costasdev.Busurbano.Backend/Controllers/VigoController.cs +++ b/src/Costasdev.Busurbano.Backend/Controllers/VigoController.cs @@ -6,6 +6,7 @@ using Costasdev.Busurbano.Backend.Types; using Costasdev.VigoTransitApi; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Options; +using static Costasdev.Busurbano.Backend.Types.StopArrivals.Types; using SysFile = System.IO.File; namespace Costasdev.Busurbano.Backend.Controllers; @@ -72,20 +73,6 @@ public class VigoController : ControllerBase } } - /*private StopEstimate[] LoadDebugEstimates() - { - var file = @"C:\Users\ariel\Desktop\GetStopEstimates.json"; - var contents = System.IO.File.ReadAllText(file); - return JsonSerializer.Deserialize<StopEstimate[]>(contents, JsonSerializerOptions.Web)!; - } - - private ScheduledStop[] LoadDebugTimetable() - { - var file = @"C:\Users\ariel\Desktop\GetStopTimetable.json"; - var contents = System.IO.File.ReadAllText(file); - return JsonSerializer.Deserialize<ScheduledStop[]>(contents)!; - }*/ - [HttpGet("GetConsolidatedCirculations")] public async Task<IActionResult> GetConsolidatedCirculations( [FromQuery] int stopId @@ -96,13 +83,13 @@ public class VigoController : ControllerBase var nowLocal = TimeZoneInfo.ConvertTime(DateTime.UtcNow, tz); var realtimeTask = _api.GetStopEstimates(stopId); - var timetableTask = LoadTimetable(stopId.ToString(), nowLocal.Date.ToString("yyyy-MM-dd")); + var timetableTask = LoadStopArrivalsProto(stopId.ToString(), nowLocal.Date.ToString("yyyy-MM-dd")); await Task.WhenAll(realtimeTask, timetableTask); var realTimeEstimates = realtimeTask.Result.Estimates; // Filter out records with unparseable times (e.g., hours >= 24) - var timetable = timetableTask.Result + var timetable = timetableTask.Result.Arrivals .Where(c => c.StartingDateTime() != null && c.CallingDateTime() != null) .ToList(); @@ -138,7 +125,7 @@ public class VigoController : ControllerBase .OrderBy(c => c.CallingDateTime()!.Value) .ToArray(); - ScheduledStop? closestCirculation = null; + ScheduledArrival? closestCirculation = null; // Matching strategy: // 1) Prefer a started trip whose scheduled calling time is close to the estimated arrival. @@ -280,6 +267,19 @@ public class VigoController : ControllerBase return Ok(sorted); } + private async Task<StopArrivals> LoadStopArrivalsProto(string stopId, string dateString) + { + var file = Path.Combine(_configuration.ScheduleBasePath, dateString, stopId + ".pb"); + if (!SysFile.Exists(file)) + { + throw new FileNotFoundException(); + } + + var contents = await SysFile.ReadAllBytesAsync(file); + var stopArrivals = StopArrivals.Parser.ParseFrom(contents); + return stopArrivals; + } + private async Task<List<ScheduledStop>> LoadTimetable(string stopId, string dateString) { var file = Path.Combine(_configuration.ScheduleBasePath, dateString, stopId + ".json"); @@ -317,3 +317,26 @@ public class VigoController : ControllerBase return stringBuilder.ToString().Normalize(NormalizationForm.FormC); } } + +public static class StopScheduleExtensions +{ + public static DateTime? StartingDateTime(this ScheduledArrival stop) + { + if (!TimeOnly.TryParse(stop.StartingTime, out var time)) + { + return null; + } + var dt = DateTime.Today + time.ToTimeSpan(); + return dt.AddSeconds(60 - dt.Second); + } + + public static DateTime? CallingDateTime(this ScheduledArrival stop) + { + if (!TimeOnly.TryParse(stop.CallingTime, out var time)) + { + return null; + } + var dt = DateTime.Today + time.ToTimeSpan(); + return dt.AddSeconds(60 - dt.Second); + } +} diff --git a/src/Costasdev.Busurbano.Backend/Costasdev.Busurbano.Backend.csproj b/src/Costasdev.Busurbano.Backend/Costasdev.Busurbano.Backend.csproj index abc9ad5..d25a59e 100644 --- a/src/Costasdev.Busurbano.Backend/Costasdev.Busurbano.Backend.csproj +++ b/src/Costasdev.Busurbano.Backend/Costasdev.Busurbano.Backend.csproj @@ -13,11 +13,7 @@ <ItemGroup> <PackageReference Include="Costasdev.VigoTransitApi" Version="0.1.0" /> - </ItemGroup> - - <ItemGroup> - <_ContentIncludedByDefault Remove="Views\Shared\_Layout.cshtml" /> - <_ContentIncludedByDefault Remove="Views\StopEstimates\GetEstimates.cshtml" /> + <PackageReference Include="Google.Protobuf" Version="3.33.1" /> </ItemGroup> <ItemGroup> diff --git a/src/Costasdev.Busurbano.Backend/Types/StopSchedule.cs b/src/Costasdev.Busurbano.Backend/Types/StopSchedule.cs new file mode 100644 index 0000000..7e24524 --- /dev/null +++ b/src/Costasdev.Busurbano.Backend/Types/StopSchedule.cs @@ -0,0 +1,1311 @@ +// <auto-generated> +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: stop_schedule.proto +// </auto-generated> +#pragma warning disable 1591, 0612, 3021, 8981 +#region Designer generated code + +using pb = global::Google.Protobuf; +using pbc = global::Google.Protobuf.Collections; +using pbr = global::Google.Protobuf.Reflection; +using scg = global::System.Collections.Generic; +namespace Costasdev.Busurbano.Backend.Types { + + /// <summary>Holder for reflection information generated from stop_schedule.proto</summary> + public static partial class StopScheduleReflection { + + #region Descriptor + /// <summary>File descriptor for stop_schedule.proto</summary> + public static pbr::FileDescriptor Descriptor { + get { return descriptor; } + } + private static pbr::FileDescriptor descriptor; + + static StopScheduleReflection() { + byte[] descriptorData = global::System.Convert.FromBase64String( + string.Concat( + "ChNzdG9wX3NjaGVkdWxlLnByb3RvEgVwcm90byIhCglFcHNnMjU4MjkSCQoB", + "eBgBIAEoARIJCgF5GAIgASgBIuMDCgxTdG9wQXJyaXZhbHMSDwoHc3RvcF9p", + "ZBgBIAEoCRIiCghsb2NhdGlvbhgDIAEoCzIQLnByb3RvLkVwc2cyNTgyORI2", + "CghhcnJpdmFscxgFIAMoCzIkLnByb3RvLlN0b3BBcnJpdmFscy5TY2hlZHVs", + "ZWRBcnJpdmFsGuUCChBTY2hlZHVsZWRBcnJpdmFsEhIKCnNlcnZpY2VfaWQY", + "ASABKAkSDwoHdHJpcF9pZBgCIAEoCRIMCgRsaW5lGAMgASgJEg0KBXJvdXRl", + "GAQgASgJEhAKCHNoYXBlX2lkGAUgASgJEhsKE3NoYXBlX2Rpc3RfdHJhdmVs", + "ZWQYBiABKAESFQoNc3RvcF9zZXF1ZW5jZRgLIAEoDRIUCgxuZXh0X3N0cmVl", + "dHMYDCADKAkSFQoNc3RhcnRpbmdfY29kZRgVIAEoCRIVCg1zdGFydGluZ19u", + "YW1lGBYgASgJEhUKDXN0YXJ0aW5nX3RpbWUYFyABKAkSFAoMY2FsbGluZ190", + "aW1lGCEgASgJEhMKC2NhbGxpbmdfc3NtGCIgASgNEhUKDXRlcm1pbnVzX2Nv", + "ZGUYKSABKAkSFQoNdGVybWludXNfbmFtZRgqIAEoCRIVCg10ZXJtaW51c190", + "aW1lGCsgASgJQiSqAiFDb3N0YXNkZXYuQnVzdXJiYW5vLkJhY2tlbmQuVHlw", + "ZXNiBnByb3RvMw==")); + descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData, + new pbr::FileDescriptor[] { }, + new pbr::GeneratedClrTypeInfo(null, null, new pbr::GeneratedClrTypeInfo[] { + new pbr::GeneratedClrTypeInfo(typeof(global::Costasdev.Busurbano.Backend.Types.Epsg25829), global::Costasdev.Busurbano.Backend.Types.Epsg25829.Parser, new[]{ "X", "Y" }, null, null, null, null), + new pbr::GeneratedClrTypeInfo(typeof(global::Costasdev.Busurbano.Backend.Types.StopArrivals), global::Costasdev.Busurbano.Backend.Types.StopArrivals.Parser, new[]{ "StopId", "Location", "Arrivals" }, null, null, null, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::Costasdev.Busurbano.Backend.Types.StopArrivals.Types.ScheduledArrival), global::Costasdev.Busurbano.Backend.Types.StopArrivals.Types.ScheduledArrival.Parser, new[]{ "ServiceId", "TripId", "Line", "Route", "ShapeId", "ShapeDistTraveled", "StopSequence", "NextStreets", "StartingCode", "StartingName", "StartingTime", "CallingTime", "CallingSsm", "TerminusCode", "TerminusName", "TerminusTime" }, null, null, null, null)}) + })); + } + #endregion + + } + #region Messages + [global::System.Diagnostics.DebuggerDisplayAttribute("{ToString(),nq}")] + public sealed partial class Epsg25829 : pb::IMessage<Epsg25829> + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + , pb::IBufferMessage + #endif + { + private static readonly pb::MessageParser<Epsg25829> _parser = new pb::MessageParser<Epsg25829>(() => new Epsg25829()); + private pb::UnknownFieldSet _unknownFields; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public static pb::MessageParser<Epsg25829> Parser { get { return _parser; } } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public static pbr::MessageDescriptor Descriptor { + get { return global::Costasdev.Busurbano.Backend.Types.StopScheduleReflection.Descriptor.MessageTypes[0]; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public Epsg25829() { + OnConstruction(); + } + + partial void OnConstruction(); + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public Epsg25829(Epsg25829 other) : this() { + x_ = other.x_; + y_ = other.y_; + _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public Epsg25829 Clone() { + return new Epsg25829(this); + } + + /// <summary>Field number for the "x" field.</summary> + public const int XFieldNumber = 1; + private double x_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public double X { + get { return x_; } + set { + x_ = value; + } + } + + /// <summary>Field number for the "y" field.</summary> + public const int YFieldNumber = 2; + private double y_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public double Y { + get { return y_; } + set { + y_ = value; + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override bool Equals(object other) { + return Equals(other as Epsg25829); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public bool Equals(Epsg25829 other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (!pbc::ProtobufEqualityComparers.BitwiseDoubleEqualityComparer.Equals(X, other.X)) return false; + if (!pbc::ProtobufEqualityComparers.BitwiseDoubleEqualityComparer.Equals(Y, other.Y)) return false; + return Equals(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override int GetHashCode() { + int hash = 1; + if (X != 0D) hash ^= pbc::ProtobufEqualityComparers.BitwiseDoubleEqualityComparer.GetHashCode(X); + if (Y != 0D) hash ^= pbc::ProtobufEqualityComparers.BitwiseDoubleEqualityComparer.GetHashCode(Y); + if (_unknownFields != null) { + hash ^= _unknownFields.GetHashCode(); + } + return hash; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override string ToString() { + return pb::JsonFormatter.ToDiagnosticString(this); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void WriteTo(pb::CodedOutputStream output) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + output.WriteRawMessage(this); + #else + if (X != 0D) { + output.WriteRawTag(9); + output.WriteDouble(X); + } + if (Y != 0D) { + output.WriteRawTag(17); + output.WriteDouble(Y); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(output); + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) { + if (X != 0D) { + output.WriteRawTag(9); + output.WriteDouble(X); + } + if (Y != 0D) { + output.WriteRawTag(17); + output.WriteDouble(Y); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(ref output); + } + } + #endif + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public int CalculateSize() { + int size = 0; + if (X != 0D) { + size += 1 + 8; + } + if (Y != 0D) { + size += 1 + 8; + } + if (_unknownFields != null) { + size += _unknownFields.CalculateSize(); + } + return size; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void MergeFrom(Epsg25829 other) { + if (other == null) { + return; + } + if (other.X != 0D) { + X = other.X; + } + if (other.Y != 0D) { + Y = other.Y; + } + _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void MergeFrom(pb::CodedInputStream input) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + input.ReadRawMessage(this); + #else + uint tag; + while ((tag = input.ReadTag()) != 0) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); + break; + case 9: { + X = input.ReadDouble(); + break; + } + case 17: { + Y = input.ReadDouble(); + break; + } + } + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input); + break; + case 9: { + X = input.ReadDouble(); + break; + } + case 17: { + Y = input.ReadDouble(); + break; + } + } + } + } + #endif + + } + + [global::System.Diagnostics.DebuggerDisplayAttribute("{ToString(),nq}")] + public sealed partial class StopArrivals : pb::IMessage<StopArrivals> + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + , pb::IBufferMessage + #endif + { + private static readonly pb::MessageParser<StopArrivals> _parser = new pb::MessageParser<StopArrivals>(() => new StopArrivals()); + private pb::UnknownFieldSet _unknownFields; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public static pb::MessageParser<StopArrivals> Parser { get { return _parser; } } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public static pbr::MessageDescriptor Descriptor { + get { return global::Costasdev.Busurbano.Backend.Types.StopScheduleReflection.Descriptor.MessageTypes[1]; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public StopArrivals() { + OnConstruction(); + } + + partial void OnConstruction(); + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public StopArrivals(StopArrivals other) : this() { + stopId_ = other.stopId_; + location_ = other.location_ != null ? other.location_.Clone() : null; + arrivals_ = other.arrivals_.Clone(); + _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public StopArrivals Clone() { + return new StopArrivals(this); + } + + /// <summary>Field number for the "stop_id" field.</summary> + public const int StopIdFieldNumber = 1; + private string stopId_ = ""; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public string StopId { + get { return stopId_; } + set { + stopId_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); + } + } + + /// <summary>Field number for the "location" field.</summary> + public const int LocationFieldNumber = 3; + private global::Costasdev.Busurbano.Backend.Types.Epsg25829 location_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public global::Costasdev.Busurbano.Backend.Types.Epsg25829 Location { + get { return location_; } + set { + location_ = value; + } + } + + /// <summary>Field number for the "arrivals" field.</summary> + public const int ArrivalsFieldNumber = 5; + private static readonly pb::FieldCodec<global::Costasdev.Busurbano.Backend.Types.StopArrivals.Types.ScheduledArrival> _repeated_arrivals_codec + = pb::FieldCodec.ForMessage(42, global::Costasdev.Busurbano.Backend.Types.StopArrivals.Types.ScheduledArrival.Parser); + private readonly pbc::RepeatedField<global::Costasdev.Busurbano.Backend.Types.StopArrivals.Types.ScheduledArrival> arrivals_ = new pbc::RepeatedField<global::Costasdev.Busurbano.Backend.Types.StopArrivals.Types.ScheduledArrival>(); + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public pbc::RepeatedField<global::Costasdev.Busurbano.Backend.Types.StopArrivals.Types.ScheduledArrival> Arrivals { + get { return arrivals_; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override bool Equals(object other) { + return Equals(other as StopArrivals); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public bool Equals(StopArrivals other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (StopId != other.StopId) return false; + if (!object.Equals(Location, other.Location)) return false; + if(!arrivals_.Equals(other.arrivals_)) return false; + return Equals(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override int GetHashCode() { + int hash = 1; + if (StopId.Length != 0) hash ^= StopId.GetHashCode(); + if (location_ != null) hash ^= Location.GetHashCode(); + hash ^= arrivals_.GetHashCode(); + if (_unknownFields != null) { + hash ^= _unknownFields.GetHashCode(); + } + return hash; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override string ToString() { + return pb::JsonFormatter.ToDiagnosticString(this); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void WriteTo(pb::CodedOutputStream output) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + output.WriteRawMessage(this); + #else + if (StopId.Length != 0) { + output.WriteRawTag(10); + output.WriteString(StopId); + } + if (location_ != null) { + output.WriteRawTag(26); + output.WriteMessage(Location); + } + arrivals_.WriteTo(output, _repeated_arrivals_codec); + if (_unknownFields != null) { + _unknownFields.WriteTo(output); + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) { + if (StopId.Length != 0) { + output.WriteRawTag(10); + output.WriteString(StopId); + } + if (location_ != null) { + output.WriteRawTag(26); + output.WriteMessage(Location); + } + arrivals_.WriteTo(ref output, _repeated_arrivals_codec); + if (_unknownFields != null) { + _unknownFields.WriteTo(ref output); + } + } + #endif + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public int CalculateSize() { + int size = 0; + if (StopId.Length != 0) { + size += 1 + pb::CodedOutputStream.ComputeStringSize(StopId); + } + if (location_ != null) { + size += 1 + pb::CodedOutputStream.ComputeMessageSize(Location); + } + size += arrivals_.CalculateSize(_repeated_arrivals_codec); + if (_unknownFields != null) { + size += _unknownFields.CalculateSize(); + } + return size; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void MergeFrom(StopArrivals other) { + if (other == null) { + return; + } + if (other.StopId.Length != 0) { + StopId = other.StopId; + } + if (other.location_ != null) { + if (location_ == null) { + Location = new global::Costasdev.Busurbano.Backend.Types.Epsg25829(); + } + Location.MergeFrom(other.Location); + } + arrivals_.Add(other.arrivals_); + _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void MergeFrom(pb::CodedInputStream input) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + input.ReadRawMessage(this); + #else + uint tag; + while ((tag = input.ReadTag()) != 0) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); + break; + case 10: { + StopId = input.ReadString(); + break; + } + case 26: { + if (location_ == null) { + Location = new global::Costasdev.Busurbano.Backend.Types.Epsg25829(); + } + input.ReadMessage(Location); + break; + } + case 42: { + arrivals_.AddEntriesFrom(input, _repeated_arrivals_codec); + break; + } + } + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input); + break; + case 10: { + StopId = input.ReadString(); + break; + } + case 26: { + if (location_ == null) { + Location = new global::Costasdev.Busurbano.Backend.Types.Epsg25829(); + } + input.ReadMessage(Location); + break; + } + case 42: { + arrivals_.AddEntriesFrom(ref input, _repeated_arrivals_codec); + break; + } + } + } + } + #endif + + #region Nested types + /// <summary>Container for nested types declared in the StopArrivals message type.</summary> + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public static partial class Types { + [global::System.Diagnostics.DebuggerDisplayAttribute("{ToString(),nq}")] + public sealed partial class ScheduledArrival : pb::IMessage<ScheduledArrival> + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + , pb::IBufferMessage + #endif + { + private static readonly pb::MessageParser<ScheduledArrival> _parser = new pb::MessageParser<ScheduledArrival>(() => new ScheduledArrival()); + private pb::UnknownFieldSet _unknownFields; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public static pb::MessageParser<ScheduledArrival> Parser { get { return _parser; } } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public static pbr::MessageDescriptor Descriptor { + get { return global::Costasdev.Busurbano.Backend.Types.StopArrivals.Descriptor.NestedTypes[0]; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public ScheduledArrival() { + OnConstruction(); + } + + partial void OnConstruction(); + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public ScheduledArrival(ScheduledArrival other) : this() { + serviceId_ = other.serviceId_; + tripId_ = other.tripId_; + line_ = other.line_; + route_ = other.route_; + shapeId_ = other.shapeId_; + shapeDistTraveled_ = other.shapeDistTraveled_; + stopSequence_ = other.stopSequence_; + nextStreets_ = other.nextStreets_.Clone(); + startingCode_ = other.startingCode_; + startingName_ = other.startingName_; + startingTime_ = other.startingTime_; + callingTime_ = other.callingTime_; + callingSsm_ = other.callingSsm_; + terminusCode_ = other.terminusCode_; + terminusName_ = other.terminusName_; + terminusTime_ = other.terminusTime_; + _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public ScheduledArrival Clone() { + return new ScheduledArrival(this); + } + + /// <summary>Field number for the "service_id" field.</summary> + public const int ServiceIdFieldNumber = 1; + private string serviceId_ = ""; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public string ServiceId { + get { return serviceId_; } + set { + serviceId_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); + } + } + + /// <summary>Field number for the "trip_id" field.</summary> + public const int TripIdFieldNumber = 2; + private string tripId_ = ""; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public string TripId { + get { return tripId_; } + set { + tripId_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); + } + } + + /// <summary>Field number for the "line" field.</summary> + public const int LineFieldNumber = 3; + private string line_ = ""; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public string Line { + get { return line_; } + set { + line_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); + } + } + + /// <summary>Field number for the "route" field.</summary> + public const int RouteFieldNumber = 4; + private string route_ = ""; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public string Route { + get { return route_; } + set { + route_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); + } + } + + /// <summary>Field number for the "shape_id" field.</summary> + public const int ShapeIdFieldNumber = 5; + private string shapeId_ = ""; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public string ShapeId { + get { return shapeId_; } + set { + shapeId_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); + } + } + + /// <summary>Field number for the "shape_dist_traveled" field.</summary> + public const int ShapeDistTraveledFieldNumber = 6; + private double shapeDistTraveled_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public double ShapeDistTraveled { + get { return shapeDistTraveled_; } + set { + shapeDistTraveled_ = value; + } + } + + /// <summary>Field number for the "stop_sequence" field.</summary> + public const int StopSequenceFieldNumber = 11; + private uint stopSequence_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public uint StopSequence { + get { return stopSequence_; } + set { + stopSequence_ = value; + } + } + + /// <summary>Field number for the "next_streets" field.</summary> + public const int NextStreetsFieldNumber = 12; + private static readonly pb::FieldCodec<string> _repeated_nextStreets_codec + = pb::FieldCodec.ForString(98); + private readonly pbc::RepeatedField<string> nextStreets_ = new pbc::RepeatedField<string>(); + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public pbc::RepeatedField<string> NextStreets { + get { return nextStreets_; } + } + + /// <summary>Field number for the "starting_code" field.</summary> + public const int StartingCodeFieldNumber = 21; + private string startingCode_ = ""; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public string StartingCode { + get { return startingCode_; } + set { + startingCode_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); + } + } + + /// <summary>Field number for the "starting_name" field.</summary> + public const int StartingNameFieldNumber = 22; + private string startingName_ = ""; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public string StartingName { + get { return startingName_; } + set { + startingName_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); + } + } + + /// <summary>Field number for the "starting_time" field.</summary> + public const int StartingTimeFieldNumber = 23; + private string startingTime_ = ""; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public string StartingTime { + get { return startingTime_; } + set { + startingTime_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); + } + } + + /// <summary>Field number for the "calling_time" field.</summary> + public const int CallingTimeFieldNumber = 33; + private string callingTime_ = ""; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public string CallingTime { + get { return callingTime_; } + set { + callingTime_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); + } + } + + /// <summary>Field number for the "calling_ssm" field.</summary> + public const int CallingSsmFieldNumber = 34; + private uint callingSsm_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public uint CallingSsm { + get { return callingSsm_; } + set { + callingSsm_ = value; + } + } + + /// <summary>Field number for the "terminus_code" field.</summary> + public const int TerminusCodeFieldNumber = 41; + private string terminusCode_ = ""; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public string TerminusCode { + get { return terminusCode_; } + set { + terminusCode_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); + } + } + + /// <summary>Field number for the "terminus_name" field.</summary> + public const int TerminusNameFieldNumber = 42; + private string terminusName_ = ""; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public string TerminusName { + get { return terminusName_; } + set { + terminusName_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); + } + } + + /// <summary>Field number for the "terminus_time" field.</summary> + public const int TerminusTimeFieldNumber = 43; + private string terminusTime_ = ""; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public string TerminusTime { + get { return terminusTime_; } + set { + terminusTime_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override bool Equals(object other) { + return Equals(other as ScheduledArrival); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public bool Equals(ScheduledArrival other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (ServiceId != other.ServiceId) return false; + if (TripId != other.TripId) return false; + if (Line != other.Line) return false; + if (Route != other.Route) return false; + if (ShapeId != other.ShapeId) return false; + if (!pbc::ProtobufEqualityComparers.BitwiseDoubleEqualityComparer.Equals(ShapeDistTraveled, other.ShapeDistTraveled)) return false; + if (StopSequence != other.StopSequence) return false; + if(!nextStreets_.Equals(other.nextStreets_)) return false; + if (StartingCode != other.StartingCode) return false; + if (StartingName != other.StartingName) return false; + if (StartingTime != other.StartingTime) return false; + if (CallingTime != other.CallingTime) return false; + if (CallingSsm != other.CallingSsm) return false; + if (TerminusCode != other.TerminusCode) return false; + if (TerminusName != other.TerminusName) return false; + if (TerminusTime != other.TerminusTime) return false; + return Equals(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override int GetHashCode() { + int hash = 1; + if (ServiceId.Length != 0) hash ^= ServiceId.GetHashCode(); + if (TripId.Length != 0) hash ^= TripId.GetHashCode(); + if (Line.Length != 0) hash ^= Line.GetHashCode(); + if (Route.Length != 0) hash ^= Route.GetHashCode(); + if (ShapeId.Length != 0) hash ^= ShapeId.GetHashCode(); + if (ShapeDistTraveled != 0D) hash ^= pbc::ProtobufEqualityComparers.BitwiseDoubleEqualityComparer.GetHashCode(ShapeDistTraveled); + if (StopSequence != 0) hash ^= StopSequence.GetHashCode(); + hash ^= nextStreets_.GetHashCode(); + if (StartingCode.Length != 0) hash ^= StartingCode.GetHashCode(); + if (StartingName.Length != 0) hash ^= StartingName.GetHashCode(); + if (StartingTime.Length != 0) hash ^= StartingTime.GetHashCode(); + if (CallingTime.Length != 0) hash ^= CallingTime.GetHashCode(); + if (CallingSsm != 0) hash ^= CallingSsm.GetHashCode(); + if (TerminusCode.Length != 0) hash ^= TerminusCode.GetHashCode(); + if (TerminusName.Length != 0) hash ^= TerminusName.GetHashCode(); + if (TerminusTime.Length != 0) hash ^= TerminusTime.GetHashCode(); + if (_unknownFields != null) { + hash ^= _unknownFields.GetHashCode(); + } + return hash; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override string ToString() { + return pb::JsonFormatter.ToDiagnosticString(this); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void WriteTo(pb::CodedOutputStream output) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + output.WriteRawMessage(this); + #else + if (ServiceId.Length != 0) { + output.WriteRawTag(10); + output.WriteString(ServiceId); + } + if (TripId.Length != 0) { + output.WriteRawTag(18); + output.WriteString(TripId); + } + if (Line.Length != 0) { + output.WriteRawTag(26); + output.WriteString(Line); + } + if (Route.Length != 0) { + output.WriteRawTag(34); + output.WriteString(Route); + } + if (ShapeId.Length != 0) { + output.WriteRawTag(42); + output.WriteString(ShapeId); + } + if (ShapeDistTraveled != 0D) { + output.WriteRawTag(49); + output.WriteDouble(ShapeDistTraveled); + } + if (StopSequence != 0) { + output.WriteRawTag(88); + output.WriteUInt32(StopSequence); + } + nextStreets_.WriteTo(output, _repeated_nextStreets_codec); + if (StartingCode.Length != 0) { + output.WriteRawTag(170, 1); + output.WriteString(StartingCode); + } + if (StartingName.Length != 0) { + output.WriteRawTag(178, 1); + output.WriteString(StartingName); + } + if (StartingTime.Length != 0) { + output.WriteRawTag(186, 1); + output.WriteString(StartingTime); + } + if (CallingTime.Length != 0) { + output.WriteRawTag(138, 2); + output.WriteString(CallingTime); + } + if (CallingSsm != 0) { + output.WriteRawTag(144, 2); + output.WriteUInt32(CallingSsm); + } + if (TerminusCode.Length != 0) { + output.WriteRawTag(202, 2); + output.WriteString(TerminusCode); + } + if (TerminusName.Length != 0) { + output.WriteRawTag(210, 2); + output.WriteString(TerminusName); + } + if (TerminusTime.Length != 0) { + output.WriteRawTag(218, 2); + output.WriteString(TerminusTime); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(output); + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) { + if (ServiceId.Length != 0) { + output.WriteRawTag(10); + output.WriteString(ServiceId); + } + if (TripId.Length != 0) { + output.WriteRawTag(18); + output.WriteString(TripId); + } + if (Line.Length != 0) { + output.WriteRawTag(26); + output.WriteString(Line); + } + if (Route.Length != 0) { + output.WriteRawTag(34); + output.WriteString(Route); + } + if (ShapeId.Length != 0) { + output.WriteRawTag(42); + output.WriteString(ShapeId); + } + if (ShapeDistTraveled != 0D) { + output.WriteRawTag(49); + output.WriteDouble(ShapeDistTraveled); + } + if (StopSequence != 0) { + output.WriteRawTag(88); + output.WriteUInt32(StopSequence); + } + nextStreets_.WriteTo(ref output, _repeated_nextStreets_codec); + if (StartingCode.Length != 0) { + output.WriteRawTag(170, 1); + output.WriteString(StartingCode); + } + if (StartingName.Length != 0) { + output.WriteRawTag(178, 1); + output.WriteString(StartingName); + } + if (StartingTime.Length != 0) { + output.WriteRawTag(186, 1); + output.WriteString(StartingTime); + } + if (CallingTime.Length != 0) { + output.WriteRawTag(138, 2); + output.WriteString(CallingTime); + } + if (CallingSsm != 0) { + output.WriteRawTag(144, 2); + output.WriteUInt32(CallingSsm); + } + if (TerminusCode.Length != 0) { + output.WriteRawTag(202, 2); + output.WriteString(TerminusCode); + } + if (TerminusName.Length != 0) { + output.WriteRawTag(210, 2); + output.WriteString(TerminusName); + } + if (TerminusTime.Length != 0) { + output.WriteRawTag(218, 2); + output.WriteString(TerminusTime); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(ref output); + } + } + #endif + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public int CalculateSize() { + int size = 0; + if (ServiceId.Length != 0) { + size += 1 + pb::CodedOutputStream.ComputeStringSize(ServiceId); + } + if (TripId.Length != 0) { + size += 1 + pb::CodedOutputStream.ComputeStringSize(TripId); + } + if (Line.Length != 0) { + size += 1 + pb::CodedOutputStream.ComputeStringSize(Line); + } + if (Route.Length != 0) { + size += 1 + pb::CodedOutputStream.ComputeStringSize(Route); + } + if (ShapeId.Length != 0) { + size += 1 + pb::CodedOutputStream.ComputeStringSize(ShapeId); + } + if (ShapeDistTraveled != 0D) { + size += 1 + 8; + } + if (StopSequence != 0) { + size += 1 + pb::CodedOutputStream.ComputeUInt32Size(StopSequence); + } + size += nextStreets_.CalculateSize(_repeated_nextStreets_codec); + if (StartingCode.Length != 0) { + size += 2 + pb::CodedOutputStream.ComputeStringSize(StartingCode); + } + if (StartingName.Length != 0) { + size += 2 + pb::CodedOutputStream.ComputeStringSize(StartingName); + } + if (StartingTime.Length != 0) { + size += 2 + pb::CodedOutputStream.ComputeStringSize(StartingTime); + } + if (CallingTime.Length != 0) { + size += 2 + pb::CodedOutputStream.ComputeStringSize(CallingTime); + } + if (CallingSsm != 0) { + size += 2 + pb::CodedOutputStream.ComputeUInt32Size(CallingSsm); + } + if (TerminusCode.Length != 0) { + size += 2 + pb::CodedOutputStream.ComputeStringSize(TerminusCode); + } + if (TerminusName.Length != 0) { + size += 2 + pb::CodedOutputStream.ComputeStringSize(TerminusName); + } + if (TerminusTime.Length != 0) { + size += 2 + pb::CodedOutputStream.ComputeStringSize(TerminusTime); + } + if (_unknownFields != null) { + size += _unknownFields.CalculateSize(); + } + return size; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void MergeFrom(ScheduledArrival other) { + if (other == null) { + return; + } + if (other.ServiceId.Length != 0) { + ServiceId = other.ServiceId; + } + if (other.TripId.Length != 0) { + TripId = other.TripId; + } + if (other.Line.Length != 0) { + Line = other.Line; + } + if (other.Route.Length != 0) { + Route = other.Route; + } + if (other.ShapeId.Length != 0) { + ShapeId = other.ShapeId; + } + if (other.ShapeDistTraveled != 0D) { + ShapeDistTraveled = other.ShapeDistTraveled; + } + if (other.StopSequence != 0) { + StopSequence = other.StopSequence; + } + nextStreets_.Add(other.nextStreets_); + if (other.StartingCode.Length != 0) { + StartingCode = other.StartingCode; + } + if (other.StartingName.Length != 0) { + StartingName = other.StartingName; + } + if (other.StartingTime.Length != 0) { + StartingTime = other.StartingTime; + } + if (other.CallingTime.Length != 0) { + CallingTime = other.CallingTime; + } + if (other.CallingSsm != 0) { + CallingSsm = other.CallingSsm; + } + if (other.TerminusCode.Length != 0) { + TerminusCode = other.TerminusCode; + } + if (other.TerminusName.Length != 0) { + TerminusName = other.TerminusName; + } + if (other.TerminusTime.Length != 0) { + TerminusTime = other.TerminusTime; + } + _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void MergeFrom(pb::CodedInputStream input) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + input.ReadRawMessage(this); + #else + uint tag; + while ((tag = input.ReadTag()) != 0) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); + break; + case 10: { + ServiceId = input.ReadString(); + break; + } + case 18: { + TripId = input.ReadString(); + break; + } + case 26: { + Line = input.ReadString(); + break; + } + case 34: { + Route = input.ReadString(); + break; + } + case 42: { + ShapeId = input.ReadString(); + break; + } + case 49: { + ShapeDistTraveled = input.ReadDouble(); + break; + } + case 88: { + StopSequence = input.ReadUInt32(); + break; + } + case 98: { + nextStreets_.AddEntriesFrom(input, _repeated_nextStreets_codec); + break; + } + case 170: { + StartingCode = input.ReadString(); + break; + } + case 178: { + StartingName = input.ReadString(); + break; + } + case 186: { + StartingTime = input.ReadString(); + break; + } + case 266: { + CallingTime = input.ReadString(); + break; + } + case 272: { + CallingSsm = input.ReadUInt32(); + break; + } + case 330: { + TerminusCode = input.ReadString(); + break; + } + case 338: { + TerminusName = input.ReadString(); + break; + } + case 346: { + TerminusTime = input.ReadString(); + break; + } + } + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input); + break; + case 10: { + ServiceId = input.ReadString(); + break; + } + case 18: { + TripId = input.ReadString(); + break; + } + case 26: { + Line = input.ReadString(); + break; + } + case 34: { + Route = input.ReadString(); + break; + } + case 42: { + ShapeId = input.ReadString(); + break; + } + case 49: { + ShapeDistTraveled = input.ReadDouble(); + break; + } + case 88: { + StopSequence = input.ReadUInt32(); + break; + } + case 98: { + nextStreets_.AddEntriesFrom(ref input, _repeated_nextStreets_codec); + break; + } + case 170: { + StartingCode = input.ReadString(); + break; + } + case 178: { + StartingName = input.ReadString(); + break; + } + case 186: { + StartingTime = input.ReadString(); + break; + } + case 266: { + CallingTime = input.ReadString(); + break; + } + case 272: { + CallingSsm = input.ReadUInt32(); + break; + } + case 330: { + TerminusCode = input.ReadString(); + break; + } + case 338: { + TerminusName = input.ReadString(); + break; + } + case 346: { + TerminusTime = input.ReadString(); + break; + } + } + } + } + #endif + + } + + } + #endregion + + } + + #endregion + +} + +#endregion Designer generated code diff --git a/src/common/stop_schedule.proto b/src/common/stop_schedule.proto new file mode 100644 index 0000000..e07e26c --- /dev/null +++ b/src/common/stop_schedule.proto @@ -0,0 +1,41 @@ +syntax = "proto3"; + +package proto; + +option csharp_namespace = "Costasdev.Busurbano.Backend.Types"; + +message Epsg25829 { + double x = 1; + double y = 2; +} + +message StopArrivals { + message ScheduledArrival { + string service_id = 1; + string trip_id = 2; + string line = 3; + string route = 4; + string shape_id = 5; + double shape_dist_traveled = 6; + + uint32 stop_sequence = 11; + repeated string next_streets = 12; + + string starting_code = 21; + string starting_name = 22; + string starting_time = 23; + + string calling_time = 33; + uint32 calling_ssm = 34; + + string terminus_code = 41; + string terminus_name = 42; + string terminus_time = 43; + } + + string stop_id = 1; + + Epsg25829 location = 3; + + repeated ScheduledArrival arrivals = 5; +} diff --git a/src/gtfs_vigo_stops/pyproject.toml b/src/gtfs_vigo_stops/pyproject.toml index 6b28831..97d24a3 100644 --- a/src/gtfs_vigo_stops/pyproject.toml +++ b/src/gtfs_vigo_stops/pyproject.toml @@ -7,6 +7,8 @@ requires-python = ">=3.13" dependencies = [ "colorama>=0.4.6", "jinja2>=3.1.6", + "protobuf>=5.29.1", + "pyproj>=3.7.2", "pytest>=8.4.1", "requests>=2.32.3", ] diff --git a/src/gtfs_vigo_stops/src/__init__.py b/src/gtfs_vigo_stops/src/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/src/gtfs_vigo_stops/src/__init__.py diff --git a/src/gtfs_vigo_stops/src/common.py b/src/gtfs_vigo_stops/src/common.py index fbb3a73..4b5cea5 100644 --- a/src/gtfs_vigo_stops/src/common.py +++ b/src/gtfs_vigo_stops/src/common.py @@ -1,8 +1,9 @@ """ Common utilities for GTFS report generation. """ -import os + import csv +import os from datetime import datetime, timedelta from typing import List @@ -11,49 +12,49 @@ def get_all_feed_dates(feed_dir: str) -> List[str]: """ Returns all dates the feed is valid for, using calendar.txt if present, else calendar_dates.txt. """ - calendar_path = os.path.join(feed_dir, 'calendar.txt') - calendar_dates_path = os.path.join(feed_dir, 'calendar_dates.txt') - + calendar_path = os.path.join(feed_dir, "calendar.txt") + calendar_dates_path = os.path.join(feed_dir, "calendar_dates.txt") + # Try calendar.txt first if os.path.exists(calendar_path): - with open(calendar_path, encoding='utf-8') as f: + with open(calendar_path, encoding="utf-8") as f: reader = csv.DictReader(f) start_dates: List[str] = [] end_dates: List[str] = [] for row in reader: - if row.get('start_date') and row.get('end_date'): - start_dates.append(row['start_date']) - end_dates.append(row['end_date']) + if row.get("start_date") and row.get("end_date"): + start_dates.append(row["start_date"]) + end_dates.append(row["end_date"]) if start_dates and end_dates: min_date = min(start_dates) max_date = max(end_dates) # Convert YYYYMMDD to YYYY-MM-DD - start = datetime.strptime(min_date, '%Y%m%d') - end = datetime.strptime(max_date, '%Y%m%d') + start = datetime.strptime(min_date, "%Y%m%d") + end = datetime.strptime(max_date, "%Y%m%d") result: List[str] = [] while start <= end: - result.append(start.strftime('%Y-%m-%d')) + result.append(start.strftime("%Y-%m-%d")) start += timedelta(days=1) return result - + # Fallback: use calendar_dates.txt if os.path.exists(calendar_dates_path): - with open(calendar_dates_path, encoding='utf-8') as f: + with open(calendar_dates_path, encoding="utf-8") as f: reader = csv.DictReader(f) dates: set[str] = set() for row in reader: - if row.get('exception_type') == '1' and row.get('date'): + if row.get("exception_type") == "1" and row.get("date"): # Convert YYYYMMDD to YYYY-MM-DD - d = row['date'] + d = row["date"] dates.add(f"{d[:4]}-{d[4:6]}-{d[6:]}") return sorted(dates) - + return [] def time_to_seconds(time_str: str) -> int: """Convert HH:MM:SS to seconds since midnight.""" - parts = time_str.split(':') + parts = time_str.split(":") if len(parts) != 3: return 0 hours, minutes, seconds = map(int, parts) diff --git a/src/gtfs_vigo_stops/src/proto/__init__.py b/src/gtfs_vigo_stops/src/proto/__init__.py new file mode 100644 index 0000000..b775c17 --- /dev/null +++ b/src/gtfs_vigo_stops/src/proto/__init__.py @@ -0,0 +1 @@ +# Protobuf generated files diff --git a/src/gtfs_vigo_stops/src/proto/stop_schedule_pb2.py b/src/gtfs_vigo_stops/src/proto/stop_schedule_pb2.py new file mode 100644 index 0000000..a4cabd8 --- /dev/null +++ b/src/gtfs_vigo_stops/src/proto/stop_schedule_pb2.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: src/common/stop_schedule.proto +# Protobuf Python Version: 6.33.0 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 33, + 0, + '', + 'src/common/stop_schedule.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1esrc/common/stop_schedule.proto\x12\nsrc.common\"!\n\tEpsg25829\x12\t\n\x01x\x18\x01 \x01(\x01\x12\t\n\x01y\x18\x02 \x01(\x01\"\xed\x03\n\x0cStopArrivals\x12\x0f\n\x07stop_id\x18\x01 \x01(\t\x12\'\n\x08location\x18\x03 \x01(\x0b\x32\x15.src.common.Epsg25829\x12;\n\x08\x61rrivals\x18\x05 \x03(\x0b\x32).src.common.StopArrivals.ScheduledArrival\x1a\xe5\x02\n\x10ScheduledArrival\x12\x12\n\nservice_id\x18\x01 \x01(\t\x12\x0f\n\x07trip_id\x18\x02 \x01(\t\x12\x0c\n\x04line\x18\x03 \x01(\t\x12\r\n\x05route\x18\x04 \x01(\t\x12\x10\n\x08shape_id\x18\x05 \x01(\t\x12\x1b\n\x13shape_dist_traveled\x18\x06 \x01(\x01\x12\x15\n\rstop_sequence\x18\x0b \x01(\r\x12\x14\n\x0cnext_streets\x18\x0c \x03(\t\x12\x15\n\rstarting_code\x18\x15 \x01(\t\x12\x15\n\rstarting_name\x18\x16 \x01(\t\x12\x15\n\rstarting_time\x18\x17 \x01(\t\x12\x14\n\x0c\x63\x61lling_time\x18! \x01(\t\x12\x13\n\x0b\x63\x61lling_ssm\x18\" \x01(\r\x12\x15\n\rterminus_code\x18) \x01(\t\x12\x15\n\rterminus_name\x18* \x01(\t\x12\x15\n\rterminus_time\x18+ \x01(\tb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'src.common.stop_schedule_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + DESCRIPTOR._loaded_options = None + _globals['_EPSG25829']._serialized_start=46 + _globals['_EPSG25829']._serialized_end=79 + _globals['_STOPARRIVALS']._serialized_start=82 + _globals['_STOPARRIVALS']._serialized_end=575 + _globals['_STOPARRIVALS_SCHEDULEDARRIVAL']._serialized_start=218 + _globals['_STOPARRIVALS_SCHEDULEDARRIVAL']._serialized_end=575 +# @@protoc_insertion_point(module_scope) diff --git a/src/gtfs_vigo_stops/src/proto/stop_schedule_pb2.pyi b/src/gtfs_vigo_stops/src/proto/stop_schedule_pb2.pyi new file mode 100644 index 0000000..aa42cdb --- /dev/null +++ b/src/gtfs_vigo_stops/src/proto/stop_schedule_pb2.pyi @@ -0,0 +1,60 @@ +from google.protobuf.internal import containers as _containers +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from collections.abc import Iterable as _Iterable, Mapping as _Mapping +from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class Epsg25829(_message.Message): + __slots__ = () + X_FIELD_NUMBER: _ClassVar[int] + Y_FIELD_NUMBER: _ClassVar[int] + x: float + y: float + def __init__(self, x: _Optional[float] = ..., y: _Optional[float] = ...) -> None: ... + +class StopArrivals(_message.Message): + __slots__ = () + class ScheduledArrival(_message.Message): + __slots__ = () + SERVICE_ID_FIELD_NUMBER: _ClassVar[int] + TRIP_ID_FIELD_NUMBER: _ClassVar[int] + LINE_FIELD_NUMBER: _ClassVar[int] + ROUTE_FIELD_NUMBER: _ClassVar[int] + SHAPE_ID_FIELD_NUMBER: _ClassVar[int] + SHAPE_DIST_TRAVELED_FIELD_NUMBER: _ClassVar[int] + STOP_SEQUENCE_FIELD_NUMBER: _ClassVar[int] + NEXT_STREETS_FIELD_NUMBER: _ClassVar[int] + STARTING_CODE_FIELD_NUMBER: _ClassVar[int] + STARTING_NAME_FIELD_NUMBER: _ClassVar[int] + STARTING_TIME_FIELD_NUMBER: _ClassVar[int] + CALLING_TIME_FIELD_NUMBER: _ClassVar[int] + CALLING_SSM_FIELD_NUMBER: _ClassVar[int] + TERMINUS_CODE_FIELD_NUMBER: _ClassVar[int] + TERMINUS_NAME_FIELD_NUMBER: _ClassVar[int] + TERMINUS_TIME_FIELD_NUMBER: _ClassVar[int] + service_id: str + trip_id: str + line: str + route: str + shape_id: str + shape_dist_traveled: float + stop_sequence: int + next_streets: _containers.RepeatedScalarFieldContainer[str] + starting_code: str + starting_name: str + starting_time: str + calling_time: str + calling_ssm: int + terminus_code: str + terminus_name: str + terminus_time: str + def __init__(self, service_id: _Optional[str] = ..., trip_id: _Optional[str] = ..., line: _Optional[str] = ..., route: _Optional[str] = ..., shape_id: _Optional[str] = ..., shape_dist_traveled: _Optional[float] = ..., stop_sequence: _Optional[int] = ..., next_streets: _Optional[_Iterable[str]] = ..., starting_code: _Optional[str] = ..., starting_name: _Optional[str] = ..., starting_time: _Optional[str] = ..., calling_time: _Optional[str] = ..., calling_ssm: _Optional[int] = ..., terminus_code: _Optional[str] = ..., terminus_name: _Optional[str] = ..., terminus_time: _Optional[str] = ...) -> None: ... + STOP_ID_FIELD_NUMBER: _ClassVar[int] + LOCATION_FIELD_NUMBER: _ClassVar[int] + ARRIVALS_FIELD_NUMBER: _ClassVar[int] + stop_id: str + location: Epsg25829 + arrivals: _containers.RepeatedCompositeFieldContainer[StopArrivals.ScheduledArrival] + def __init__(self, stop_id: _Optional[str] = ..., location: _Optional[_Union[Epsg25829, _Mapping]] = ..., arrivals: _Optional[_Iterable[_Union[StopArrivals.ScheduledArrival, _Mapping]]] = ...) -> None: ... diff --git a/src/gtfs_vigo_stops/src/report_writer.py b/src/gtfs_vigo_stops/src/report_writer.py index aa57834..9560895 100644 --- a/src/gtfs_vigo_stops/src/report_writer.py +++ b/src/gtfs_vigo_stops/src/report_writer.py @@ -2,13 +2,87 @@ Report writers for various output formats (HTML, JSON). Centralizes all write operations for different report types. """ -from typing import List, Dict, Any -from src.logger import get_logger -import os + import json +import os +from typing import Any, Dict, List + +from pyproj import Transformer + +from src.logger import get_logger +from src.proto.stop_schedule_pb2 import Epsg25829, StopArrivals + + +def write_stop_protobuf( + output_dir: str, + date: str, + stop_code: str, + arrivals: List[Dict[str, Any]], + stop_lat: float, + stop_lon: float, +) -> None: + """ + Write stop arrivals data to a Protobuf file. + + Args: + output_dir: Base output directory + date: Date string for the data + stop_code: Stop code identifier + stop_arrivals_proto: Serialized Protobuf data + """ + logger = get_logger("report_writer") + + transformer = Transformer.from_crs(4326, 25829, always_xy=True) + x, y = transformer.transform(stop_lon, stop_lat) + + item = StopArrivals( + stop_id=stop_code, + location=Epsg25829(x=x, y=y), + arrivals=[ + StopArrivals.ScheduledArrival( + service_id=arrival["service_id"], + trip_id=arrival["trip_id"], + line=arrival["line"], + route=arrival["route"], + shape_id=arrival["shape_id"], + shape_dist_traveled=arrival["shape_dist_traveled"], + stop_sequence=arrival["stop_sequence"], + next_streets=arrival["next_streets"], + starting_code=arrival["starting_code"], + starting_name=arrival["starting_name"], + starting_time=arrival["starting_time"], + calling_time=arrival["calling_time"], + calling_ssm=arrival["calling_ssm"], + terminus_code=arrival["terminus_code"], + terminus_name=arrival["terminus_name"], + terminus_time=arrival["terminus_time"], + ) + for arrival in arrivals + ], + ) + + try: + # Create the stops directory for this date + date_dir = os.path.join(output_dir, date) + os.makedirs(date_dir, exist_ok=True) + + # Create the Protobuf file + file_path = os.path.join(date_dir, f"{stop_code}.pb") + + with open(file_path, "wb") as f: + f.write(item.SerializeToString()) + + logger.debug(f"Stop Protobuf written to: {file_path}") + except Exception as e: + logger.error( + f"Error writing stop Protobuf to {output_dir}/stops/{date}/{stop_code}.pb: {e}" + ) + raise -def write_stop_json(output_dir: str, date: str, stop_code: str, arrivals: List[Dict[str, Any]]) -> None: +def write_stop_json( + output_dir: str, date: str, stop_code: str, arrivals: List[Dict[str, Any]] +) -> None: """ Write stop arrivals data to a JSON file. @@ -29,16 +103,23 @@ def write_stop_json(output_dir: str, date: str, stop_code: str, arrivals: List[D # Create the JSON file file_path = os.path.join(date_dir, f"{stop_code}.json") - with open(file_path, 'w', encoding='utf-8') as f: + with open(file_path, "w", encoding="utf-8") as f: json.dump(arrivals, f, ensure_ascii=False) logger.debug(f"Stop JSON written to: {file_path}") except Exception as e: - logger.error(f"Error writing stop JSON to {output_dir}/stops/{date}/{stop_code}.json: {e}") + logger.error( + f"Error writing stop JSON to {output_dir}/stops/{date}/{stop_code}.json: {e}" + ) raise -def write_index_json(output_dir: str, data: Dict[str, Any], filename: str = "index.json", pretty: bool = False) -> None: +def write_index_json( + output_dir: str, + data: Dict[str, Any], + filename: str = "index.json", + pretty: bool = False, +) -> None: """ Write index data to a JSON file. @@ -56,11 +137,11 @@ def write_index_json(output_dir: str, data: Dict[str, Any], filename: str = "ind # Write the index.json file index_filepath = os.path.join(output_dir, filename) - with open(index_filepath, 'w', encoding='utf-8') as f: + with open(index_filepath, "w", encoding="utf-8") as f: if pretty: json.dump(data, f, ensure_ascii=False, indent=2) else: - json.dump(data, f, ensure_ascii=False, separators=(',', ':')) + json.dump(data, f, ensure_ascii=False, separators=(",", ":")) logger.info(f"Index JSON written to: {index_filepath}") except Exception as e: diff --git a/src/gtfs_vigo_stops/src/stop_schedule_pb2.py b/src/gtfs_vigo_stops/src/stop_schedule_pb2.py new file mode 100644 index 0000000..285b057 --- /dev/null +++ b/src/gtfs_vigo_stops/src/stop_schedule_pb2.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: stop_schedule.proto +# Protobuf Python Version: 6.33.0 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 33, + 0, + '', + 'stop_schedule.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x13stop_schedule.proto\x12\x05proto\"!\n\tEpsg25829\x12\t\n\x01x\x18\x01 \x01(\x01\x12\t\n\x01y\x18\x02 \x01(\x01\"\xe3\x03\n\x0cStopArrivals\x12\x0f\n\x07stop_id\x18\x01 \x01(\t\x12\"\n\x08location\x18\x03 \x01(\x0b\x32\x10.proto.Epsg25829\x12\x36\n\x08\x61rrivals\x18\x05 \x03(\x0b\x32$.proto.StopArrivals.ScheduledArrival\x1a\xe5\x02\n\x10ScheduledArrival\x12\x12\n\nservice_id\x18\x01 \x01(\t\x12\x0f\n\x07trip_id\x18\x02 \x01(\t\x12\x0c\n\x04line\x18\x03 \x01(\t\x12\r\n\x05route\x18\x04 \x01(\t\x12\x10\n\x08shape_id\x18\x05 \x01(\t\x12\x1b\n\x13shape_dist_traveled\x18\x06 \x01(\x01\x12\x15\n\rstop_sequence\x18\x0b \x01(\r\x12\x14\n\x0cnext_streets\x18\x0c \x03(\t\x12\x15\n\rstarting_code\x18\x15 \x01(\t\x12\x15\n\rstarting_name\x18\x16 \x01(\t\x12\x15\n\rstarting_time\x18\x17 \x01(\t\x12\x14\n\x0c\x63\x61lling_time\x18! \x01(\t\x12\x13\n\x0b\x63\x61lling_ssm\x18\" \x01(\r\x12\x15\n\rterminus_code\x18) \x01(\t\x12\x15\n\rterminus_name\x18* \x01(\t\x12\x15\n\rterminus_time\x18+ \x01(\tB$\xaa\x02!Costasdev.Busurbano.Backend.Typesb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'stop_schedule_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\252\002!Costasdev.Busurbano.Backend.Types' + _globals['_EPSG25829']._serialized_start=30 + _globals['_EPSG25829']._serialized_end=63 + _globals['_STOPARRIVALS']._serialized_start=66 + _globals['_STOPARRIVALS']._serialized_end=549 + _globals['_STOPARRIVALS_SCHEDULEDARRIVAL']._serialized_start=192 + _globals['_STOPARRIVALS_SCHEDULEDARRIVAL']._serialized_end=549 +# @@protoc_insertion_point(module_scope) diff --git a/src/gtfs_vigo_stops/src/stop_schedule_pb2.pyi b/src/gtfs_vigo_stops/src/stop_schedule_pb2.pyi new file mode 100644 index 0000000..aa42cdb --- /dev/null +++ b/src/gtfs_vigo_stops/src/stop_schedule_pb2.pyi @@ -0,0 +1,60 @@ +from google.protobuf.internal import containers as _containers +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from collections.abc import Iterable as _Iterable, Mapping as _Mapping +from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class Epsg25829(_message.Message): + __slots__ = () + X_FIELD_NUMBER: _ClassVar[int] + Y_FIELD_NUMBER: _ClassVar[int] + x: float + y: float + def __init__(self, x: _Optional[float] = ..., y: _Optional[float] = ...) -> None: ... + +class StopArrivals(_message.Message): + __slots__ = () + class ScheduledArrival(_message.Message): + __slots__ = () + SERVICE_ID_FIELD_NUMBER: _ClassVar[int] + TRIP_ID_FIELD_NUMBER: _ClassVar[int] + LINE_FIELD_NUMBER: _ClassVar[int] + ROUTE_FIELD_NUMBER: _ClassVar[int] + SHAPE_ID_FIELD_NUMBER: _ClassVar[int] + SHAPE_DIST_TRAVELED_FIELD_NUMBER: _ClassVar[int] + STOP_SEQUENCE_FIELD_NUMBER: _ClassVar[int] + NEXT_STREETS_FIELD_NUMBER: _ClassVar[int] + STARTING_CODE_FIELD_NUMBER: _ClassVar[int] + STARTING_NAME_FIELD_NUMBER: _ClassVar[int] + STARTING_TIME_FIELD_NUMBER: _ClassVar[int] + CALLING_TIME_FIELD_NUMBER: _ClassVar[int] + CALLING_SSM_FIELD_NUMBER: _ClassVar[int] + TERMINUS_CODE_FIELD_NUMBER: _ClassVar[int] + TERMINUS_NAME_FIELD_NUMBER: _ClassVar[int] + TERMINUS_TIME_FIELD_NUMBER: _ClassVar[int] + service_id: str + trip_id: str + line: str + route: str + shape_id: str + shape_dist_traveled: float + stop_sequence: int + next_streets: _containers.RepeatedScalarFieldContainer[str] + starting_code: str + starting_name: str + starting_time: str + calling_time: str + calling_ssm: int + terminus_code: str + terminus_name: str + terminus_time: str + def __init__(self, service_id: _Optional[str] = ..., trip_id: _Optional[str] = ..., line: _Optional[str] = ..., route: _Optional[str] = ..., shape_id: _Optional[str] = ..., shape_dist_traveled: _Optional[float] = ..., stop_sequence: _Optional[int] = ..., next_streets: _Optional[_Iterable[str]] = ..., starting_code: _Optional[str] = ..., starting_name: _Optional[str] = ..., starting_time: _Optional[str] = ..., calling_time: _Optional[str] = ..., calling_ssm: _Optional[int] = ..., terminus_code: _Optional[str] = ..., terminus_name: _Optional[str] = ..., terminus_time: _Optional[str] = ...) -> None: ... + STOP_ID_FIELD_NUMBER: _ClassVar[int] + LOCATION_FIELD_NUMBER: _ClassVar[int] + ARRIVALS_FIELD_NUMBER: _ClassVar[int] + stop_id: str + location: Epsg25829 + arrivals: _containers.RepeatedCompositeFieldContainer[StopArrivals.ScheduledArrival] + def __init__(self, stop_id: _Optional[str] = ..., location: _Optional[_Union[Epsg25829, _Mapping]] = ..., arrivals: _Optional[_Iterable[_Union[StopArrivals.ScheduledArrival, _Mapping]]] = ...) -> None: ... diff --git a/src/gtfs_vigo_stops/src/stops.py b/src/gtfs_vigo_stops/src/stops.py index 4afe69c..2c58387 100644 --- a/src/gtfs_vigo_stops/src/stops.py +++ b/src/gtfs_vigo_stops/src/stops.py @@ -2,6 +2,7 @@ import csv import os from dataclasses import dataclass from typing import Dict, Optional + from src.logger import get_logger logger = get_logger("stops") @@ -18,28 +19,48 @@ class Stop: CACHED_STOPS: dict[str, dict[str, Stop]] = {} + +def get_all_stops_by_code(feed_dir: str) -> Dict[str, Stop]: + stops_by_code: Dict[str, Stop] = {} + all_stops = get_all_stops(feed_dir) + + for stop in all_stops.values(): + if stop.stop_code: + stops_by_code[stop.stop_code] = stop + + return stops_by_code + + def get_all_stops(feed_dir: str) -> Dict[str, Stop]: if feed_dir in CACHED_STOPS: return CACHED_STOPS[feed_dir] stops: Dict[str, Stop] = {} - file_path = os.path.join(feed_dir, 'stops.txt') + file_path = os.path.join(feed_dir, "stops.txt") try: - with open(file_path, 'r', encoding="utf-8", newline='') as f: - reader = csv.DictReader(f, quotechar='"', delimiter=',') + with open(file_path, "r", encoding="utf-8", newline="") as f: + reader = csv.DictReader(f, quotechar='"', delimiter=",") for row_num, row in enumerate(reader, start=2): try: stop = Stop( - stop_id=row['stop_id'], - stop_code=row.get('stop_code'), - stop_name=row['stop_name'].strip() if row.get('stop_name', '').strip() else row.get('stop_desc'), - stop_lat=float(row['stop_lat']) if row.get('stop_lat') else None, - stop_lon=float(row['stop_lon']) if row.get('stop_lon') else None, + stop_id=row["stop_id"], + stop_code=row.get("stop_code"), + stop_name=row["stop_name"].strip() + if row.get("stop_name", "").strip() + else row.get("stop_desc"), + stop_lat=float(row["stop_lat"]) + if row.get("stop_lat") + else None, + stop_lon=float(row["stop_lon"]) + if row.get("stop_lon") + else None, ) stops[stop.stop_id] = stop except Exception as e: - logger.warning(f"Error parsing stops.txt line {row_num}: {e} - line data: {row}") + logger.warning( + f"Error parsing stops.txt line {row_num}: {e} - line data: {row}" + ) except FileNotFoundError: logger.error(f"File not found: {file_path}") @@ -49,3 +70,10 @@ def get_all_stops(feed_dir: str) -> Dict[str, Stop]: CACHED_STOPS[feed_dir] = stops return stops + + +def get_numeric_code(stop_code: str | None) -> str: + if not stop_code: + return "" + numeric_code = "".join(c for c in stop_code if c.isdigit()) + return str(int(numeric_code)) if numeric_code else "" diff --git a/src/gtfs_vigo_stops/stop_report.py b/src/gtfs_vigo_stops/stop_report.py index 880eaf7..7db751c 100644 --- a/src/gtfs_vigo_stops/stop_report.py +++ b/src/gtfs_vigo_stops/stop_report.py @@ -1,54 +1,66 @@ +import argparse import os import shutil import sys import traceback -import argparse -from typing import List, Dict, Any -from multiprocessing import Pool, cpu_count +from typing import Any, Dict, List +from src.common import get_all_feed_dates from src.download import download_feed_from_url from src.logger import get_logger -from src.common import get_all_feed_dates, time_to_seconds -from src.stops import get_all_stops +from src.report_writer import write_stop_json, write_stop_protobuf +from src.routes import load_routes from src.services import get_active_services +from src.stop_times import get_stops_for_trips +from src.stops import get_all_stops, get_all_stops_by_code, get_numeric_code from src.street_name import get_street_name, normalise_stop_name from src.trips import get_trips_for_services -from src.stop_times import get_stops_for_trips -from src.routes import load_routes -from src.report_writer import write_stop_json logger = get_logger("stop_report") def parse_args(): parser = argparse.ArgumentParser( - description="Generate stop-based JSON reports for a date or date range.") - parser.add_argument('--output-dir', type=str, default="./output/", - help='Directory to write reports to (default: ./output/)') - parser.add_argument('--feed-dir', type=str, - help="Path to the feed directory") - parser.add_argument('--feed-url', type=str, - help="URL to download the GTFS feed from (if not using local feed directory)") - parser.add_argument('--force-download', action='store_true', - help="Force download even if the feed hasn't been modified (only applies when using --feed-url)") + description="Generate stop-based JSON reports for a date or date range." + ) + parser.add_argument( + "--output-dir", + type=str, + default="./output/", + help="Directory to write reports to (default: ./output/)", + ) + parser.add_argument("--feed-dir", type=str, help="Path to the feed directory") + parser.add_argument( + "--feed-url", + type=str, + help="URL to download the GTFS feed from (if not using local feed directory)", + ) + parser.add_argument( + "--force-download", + action="store_true", + help="Force download even if the feed hasn't been modified (only applies when using --feed-url)", + ) args = parser.parse_args() if args.feed_dir and args.feed_url: parser.error("Specify either --feed-dir or --feed-url, not both.") if not args.feed_dir and not args.feed_url: parser.error( - "You must specify either a path to the existing feed (unzipped) or a URL to download the GTFS feed from.") + "You must specify either a path to the existing feed (unzipped) or a URL to download the GTFS feed from." + ) if args.feed_dir and not os.path.exists(args.feed_dir): parser.error(f"Feed directory does not exist: {args.feed_dir}") return args def time_to_seconds(time_str: str) -> int: - """Convert HH:MM:SS to seconds since midnight.""" + """ + Convert HH:MM:SS to seconds since midnight. + """ if not time_str: return 0 - parts = time_str.split(':') + parts = time_str.split(":") if len(parts) != 3: return 0 @@ -59,17 +71,7 @@ def time_to_seconds(time_str: str) -> int: return 0 -def get_numeric_code(stop_code: str | None) -> str: - if not stop_code: - return "" - numeric_code = ''.join(c for c in stop_code if c.isdigit()) - return str(int(numeric_code)) if numeric_code else "" - - -def get_stop_arrivals( - feed_dir: str, - date: str -) -> Dict[str, List[Dict[str, Any]]]: +def get_stop_arrivals(feed_dir: str, date: str) -> Dict[str, List[Dict[str, Any]]]: """ Process trips for the given date and organize stop arrivals. @@ -89,16 +91,14 @@ def get_stop_arrivals( logger.info("No active services found for the given date.") return {} - logger.info( - f"Found {len(active_services)} active services for date {date}.") + logger.info(f"Found {len(active_services)} active services for date {date}.") trips = get_trips_for_services(feed_dir, active_services) total_trip_count = sum(len(trip_list) for trip_list in trips.values()) logger.info(f"Found {total_trip_count} trips for active services.") # Get all trip IDs - all_trip_ids = [trip.trip_id for trip_list in trips.values() - for trip in trip_list] + all_trip_ids = [trip.trip_id for trip_list in trips.values() for trip in trip_list] # Get stops for all trips stops_for_all_trips = get_stops_for_trips(feed_dir, all_trip_ids) @@ -121,8 +121,8 @@ def get_stop_arrivals( for trip in trip_list: # Get route information once per trip route_info = routes.get(trip.route_id, {}) - route_short_name = route_info.get('route_short_name', '') - trip_headsign = getattr(trip, 'headsign', '') or '' + route_short_name = route_info.get("route_short_name", "") + trip_headsign = getattr(trip, "headsign", "") or "" trip_id = trip.trip_id # Get stop times for this trip @@ -159,7 +159,11 @@ def get_stop_arrivals( for idx in range(len(segment_names) - 1, -1, -1): future_suffix_by_segment[idx] = future_tuple current_street = segment_names[idx] - future_tuple = (current_street,) + future_tuple if current_street is not None else future_tuple + future_tuple = ( + (current_street,) + future_tuple + if current_street is not None + else future_tuple + ) segment_future_lists: dict[int, list[str]] = {} @@ -189,48 +193,51 @@ def get_stop_arrivals( if segment_names: segment_idx = stop_to_segment_idx[i] if segment_idx not in segment_future_lists: - segment_future_lists[segment_idx] = list(future_suffix_by_segment[segment_idx]) + segment_future_lists[segment_idx] = list( + future_suffix_by_segment[segment_idx] + ) next_streets = segment_future_lists[segment_idx].copy() else: next_streets = [] trip_id_fmt = "_".join(trip_id.split("_")[1:3]) - stop_arrivals[stop_code].append({ - "trip_id": trip_id_fmt, - "service_id": service_id.split("_")[1], - "line": route_short_name, - "route": trip_headsign, - "stop_sequence": stop_time.stop_sequence, - 'shape_dist_traveled': getattr(stop_time, 'shape_dist_traveled', 0), - "next_streets": next_streets, - - "starting_code": starting_code, - "starting_name": starting_name, - "starting_time": starting_time, - - "calling_time": stop_time.departure_time, - "calling_ssm": time_to_seconds(stop_time.departure_time), - - "terminus_code": terminus_code, - "terminus_name": terminus_name, - "terminus_time": terminus_time, - }) + stop_arrivals[stop_code].append( + { + "service_id": service_id.split("_")[1], + "trip_id": trip_id_fmt, + "line": route_short_name, + "route": trip_headsign, + "shape_id": getattr(trip, "shape_id", ""), + "stop_sequence": stop_time.stop_sequence, + "shape_dist_traveled": getattr( + stop_time, "shape_dist_traveled", 0 + ), + "next_streets": next_streets, + "starting_code": starting_code, + "starting_name": starting_name, + "starting_time": starting_time, + "calling_time": stop_time.departure_time, + "calling_ssm": time_to_seconds(stop_time.departure_time), + "terminus_code": terminus_code, + "terminus_name": terminus_name, + "terminus_time": terminus_time, + } + ) # Sort each stop's arrivals by arrival time for stop_code in stop_arrivals: # Filter out entries with None arrival_seconds stop_arrivals[stop_code] = [ - item for item in stop_arrivals[stop_code] if item["calling_ssm"] is not None] + item for item in stop_arrivals[stop_code] if item["calling_ssm"] is not None + ] stop_arrivals[stop_code].sort(key=lambda x: x["calling_ssm"]) return stop_arrivals def process_date( - feed_dir: str, - date: str, - output_dir: str + feed_dir: str, date: str, output_dir: str ) -> tuple[str, Dict[str, int]]: """ Process a single date and write its stop JSON files. @@ -240,6 +247,8 @@ def process_date( try: logger.info(f"Starting stop report generation for date {date}") + stops_by_code = get_all_stops_by_code(feed_dir) + # Get all stop arrivals for the current date stop_arrivals = get_stop_arrivals(feed_dir, date) @@ -249,11 +258,25 @@ def process_date( # Write individual stop JSON files for stop_code, arrivals in stop_arrivals.items(): + # Get the stop from 'stops' by value to get the coords + stop_by_code = stops_by_code.get(stop_code) + + if stop_by_code is not None: + write_stop_protobuf( + output_dir, + date, + stop_code, + arrivals, + stop_by_code.stop_lat or 0.0, + stop_by_code.stop_lon or 0.0, + ) + write_stop_json(output_dir, date, stop_code, arrivals) # Create summary for index - stop_summary = {stop_code: len(arrivals) - for stop_code, arrivals in stop_arrivals.items()} + stop_summary = { + stop_code: len(arrivals) for stop_code, arrivals in stop_arrivals.items() + } logger.info(f"Processed {len(stop_arrivals)} stops for date {date}") return date, stop_summary @@ -271,15 +294,14 @@ def main(): feed_dir = args.feed_dir else: logger.info(f"Downloading GTFS feed from {feed_url}...") - feed_dir = download_feed_from_url( - feed_url, output_dir, args.force_download) + feed_dir = download_feed_from_url(feed_url, output_dir, args.force_download) if feed_dir is None: logger.info("Download was skipped (feed not modified). Exiting.") return all_dates = get_all_feed_dates(feed_dir) if not all_dates: - logger.error('No valid dates found in feed.') + logger.error("No valid dates found in feed.") return date_list = all_dates diff --git a/src/gtfs_vigo_stops/uv.lock b/src/gtfs_vigo_stops/uv.lock index 11158e4..bf7b7bd 100644 --- a/src/gtfs_vigo_stops/uv.lock +++ b/src/gtfs_vigo_stops/uv.lock @@ -1,44 +1,45 @@ version = 1 +revision = 3 requires-python = ">=3.13" [[package]] name = "certifi" version = "2025.4.26" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e8/9e/c05b3920a3b7d20d3d3310465f50348e5b3694f4f88c6daf736eef3024c4/certifi-2025.4.26.tar.gz", hash = "sha256:0a816057ea3cdefcef70270d2c515e4506bbc954f417fa5ade2021213bb8f0c6", size = 160705 } +sdist = { url = "https://files.pythonhosted.org/packages/e8/9e/c05b3920a3b7d20d3d3310465f50348e5b3694f4f88c6daf736eef3024c4/certifi-2025.4.26.tar.gz", hash = "sha256:0a816057ea3cdefcef70270d2c515e4506bbc954f417fa5ade2021213bb8f0c6", size = 160705, upload-time = "2025-04-26T02:12:29.51Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4a/7e/3db2bd1b1f9e95f7cddca6d6e75e2f2bd9f51b1246e546d88addca0106bd/certifi-2025.4.26-py3-none-any.whl", hash = "sha256:30350364dfe371162649852c63336a15c70c6510c2ad5015b21c2345311805f3", size = 159618 }, + { url = "https://files.pythonhosted.org/packages/4a/7e/3db2bd1b1f9e95f7cddca6d6e75e2f2bd9f51b1246e546d88addca0106bd/certifi-2025.4.26-py3-none-any.whl", hash = "sha256:30350364dfe371162649852c63336a15c70c6510c2ad5015b21c2345311805f3", size = 159618, upload-time = "2025-04-26T02:12:27.662Z" }, ] [[package]] name = "charset-normalizer" version = "3.4.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e4/33/89c2ced2b67d1c2a61c19c6751aa8902d46ce3dacb23600a283619f5a12d/charset_normalizer-3.4.2.tar.gz", hash = "sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63", size = 126367 } +sdist = { url = "https://files.pythonhosted.org/packages/e4/33/89c2ced2b67d1c2a61c19c6751aa8902d46ce3dacb23600a283619f5a12d/charset_normalizer-3.4.2.tar.gz", hash = "sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63", size = 126367, upload-time = "2025-05-02T08:34:42.01Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ea/12/a93df3366ed32db1d907d7593a94f1fe6293903e3e92967bebd6950ed12c/charset_normalizer-3.4.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:926ca93accd5d36ccdabd803392ddc3e03e6d4cd1cf17deff3b989ab8e9dbcf0", size = 199622 }, - { url = "https://files.pythonhosted.org/packages/04/93/bf204e6f344c39d9937d3c13c8cd5bbfc266472e51fc8c07cb7f64fcd2de/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eba9904b0f38a143592d9fc0e19e2df0fa2e41c3c3745554761c5f6447eedabf", size = 143435 }, - { url = "https://files.pythonhosted.org/packages/22/2a/ea8a2095b0bafa6c5b5a55ffdc2f924455233ee7b91c69b7edfcc9e02284/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3fddb7e2c84ac87ac3a947cb4e66d143ca5863ef48e4a5ecb83bd48619e4634e", size = 153653 }, - { url = "https://files.pythonhosted.org/packages/b6/57/1b090ff183d13cef485dfbe272e2fe57622a76694061353c59da52c9a659/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98f862da73774290f251b9df8d11161b6cf25b599a66baf087c1ffe340e9bfd1", size = 146231 }, - { url = "https://files.pythonhosted.org/packages/e2/28/ffc026b26f441fc67bd21ab7f03b313ab3fe46714a14b516f931abe1a2d8/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c9379d65defcab82d07b2a9dfbfc2e95bc8fe0ebb1b176a3190230a3ef0e07c", size = 148243 }, - { url = "https://files.pythonhosted.org/packages/c0/0f/9abe9bd191629c33e69e47c6ef45ef99773320e9ad8e9cb08b8ab4a8d4cb/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e635b87f01ebc977342e2697d05b56632f5f879a4f15955dfe8cef2448b51691", size = 150442 }, - { url = "https://files.pythonhosted.org/packages/67/7c/a123bbcedca91d5916c056407f89a7f5e8fdfce12ba825d7d6b9954a1a3c/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1c95a1e2902a8b722868587c0e1184ad5c55631de5afc0eb96bc4b0d738092c0", size = 145147 }, - { url = "https://files.pythonhosted.org/packages/ec/fe/1ac556fa4899d967b83e9893788e86b6af4d83e4726511eaaad035e36595/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ef8de666d6179b009dce7bcb2ad4c4a779f113f12caf8dc77f0162c29d20490b", size = 153057 }, - { url = "https://files.pythonhosted.org/packages/2b/ff/acfc0b0a70b19e3e54febdd5301a98b72fa07635e56f24f60502e954c461/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:32fc0341d72e0f73f80acb0a2c94216bd704f4f0bce10aedea38f30502b271ff", size = 156454 }, - { url = "https://files.pythonhosted.org/packages/92/08/95b458ce9c740d0645feb0e96cea1f5ec946ea9c580a94adfe0b617f3573/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:289200a18fa698949d2b39c671c2cc7a24d44096784e76614899a7ccf2574b7b", size = 154174 }, - { url = "https://files.pythonhosted.org/packages/78/be/8392efc43487ac051eee6c36d5fbd63032d78f7728cb37aebcc98191f1ff/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4a476b06fbcf359ad25d34a057b7219281286ae2477cc5ff5e3f70a246971148", size = 149166 }, - { url = "https://files.pythonhosted.org/packages/44/96/392abd49b094d30b91d9fbda6a69519e95802250b777841cf3bda8fe136c/charset_normalizer-3.4.2-cp313-cp313-win32.whl", hash = "sha256:aaeeb6a479c7667fbe1099af9617c83aaca22182d6cf8c53966491a0f1b7ffb7", size = 98064 }, - { url = "https://files.pythonhosted.org/packages/e9/b0/0200da600134e001d91851ddc797809e2fe0ea72de90e09bec5a2fbdaccb/charset_normalizer-3.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:aa6af9e7d59f9c12b33ae4e9450619cf2488e2bbe9b44030905877f0b2324980", size = 105641 }, - { url = "https://files.pythonhosted.org/packages/20/94/c5790835a017658cbfabd07f3bfb549140c3ac458cfc196323996b10095a/charset_normalizer-3.4.2-py3-none-any.whl", hash = "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0", size = 52626 }, + { url = "https://files.pythonhosted.org/packages/ea/12/a93df3366ed32db1d907d7593a94f1fe6293903e3e92967bebd6950ed12c/charset_normalizer-3.4.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:926ca93accd5d36ccdabd803392ddc3e03e6d4cd1cf17deff3b989ab8e9dbcf0", size = 199622, upload-time = "2025-05-02T08:32:56.363Z" }, + { url = "https://files.pythonhosted.org/packages/04/93/bf204e6f344c39d9937d3c13c8cd5bbfc266472e51fc8c07cb7f64fcd2de/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eba9904b0f38a143592d9fc0e19e2df0fa2e41c3c3745554761c5f6447eedabf", size = 143435, upload-time = "2025-05-02T08:32:58.551Z" }, + { url = "https://files.pythonhosted.org/packages/22/2a/ea8a2095b0bafa6c5b5a55ffdc2f924455233ee7b91c69b7edfcc9e02284/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3fddb7e2c84ac87ac3a947cb4e66d143ca5863ef48e4a5ecb83bd48619e4634e", size = 153653, upload-time = "2025-05-02T08:33:00.342Z" }, + { url = "https://files.pythonhosted.org/packages/b6/57/1b090ff183d13cef485dfbe272e2fe57622a76694061353c59da52c9a659/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98f862da73774290f251b9df8d11161b6cf25b599a66baf087c1ffe340e9bfd1", size = 146231, upload-time = "2025-05-02T08:33:02.081Z" }, + { url = "https://files.pythonhosted.org/packages/e2/28/ffc026b26f441fc67bd21ab7f03b313ab3fe46714a14b516f931abe1a2d8/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c9379d65defcab82d07b2a9dfbfc2e95bc8fe0ebb1b176a3190230a3ef0e07c", size = 148243, upload-time = "2025-05-02T08:33:04.063Z" }, + { url = "https://files.pythonhosted.org/packages/c0/0f/9abe9bd191629c33e69e47c6ef45ef99773320e9ad8e9cb08b8ab4a8d4cb/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e635b87f01ebc977342e2697d05b56632f5f879a4f15955dfe8cef2448b51691", size = 150442, upload-time = "2025-05-02T08:33:06.418Z" }, + { url = "https://files.pythonhosted.org/packages/67/7c/a123bbcedca91d5916c056407f89a7f5e8fdfce12ba825d7d6b9954a1a3c/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1c95a1e2902a8b722868587c0e1184ad5c55631de5afc0eb96bc4b0d738092c0", size = 145147, upload-time = "2025-05-02T08:33:08.183Z" }, + { url = "https://files.pythonhosted.org/packages/ec/fe/1ac556fa4899d967b83e9893788e86b6af4d83e4726511eaaad035e36595/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ef8de666d6179b009dce7bcb2ad4c4a779f113f12caf8dc77f0162c29d20490b", size = 153057, upload-time = "2025-05-02T08:33:09.986Z" }, + { url = "https://files.pythonhosted.org/packages/2b/ff/acfc0b0a70b19e3e54febdd5301a98b72fa07635e56f24f60502e954c461/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:32fc0341d72e0f73f80acb0a2c94216bd704f4f0bce10aedea38f30502b271ff", size = 156454, upload-time = "2025-05-02T08:33:11.814Z" }, + { url = "https://files.pythonhosted.org/packages/92/08/95b458ce9c740d0645feb0e96cea1f5ec946ea9c580a94adfe0b617f3573/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:289200a18fa698949d2b39c671c2cc7a24d44096784e76614899a7ccf2574b7b", size = 154174, upload-time = "2025-05-02T08:33:13.707Z" }, + { url = "https://files.pythonhosted.org/packages/78/be/8392efc43487ac051eee6c36d5fbd63032d78f7728cb37aebcc98191f1ff/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4a476b06fbcf359ad25d34a057b7219281286ae2477cc5ff5e3f70a246971148", size = 149166, upload-time = "2025-05-02T08:33:15.458Z" }, + { url = "https://files.pythonhosted.org/packages/44/96/392abd49b094d30b91d9fbda6a69519e95802250b777841cf3bda8fe136c/charset_normalizer-3.4.2-cp313-cp313-win32.whl", hash = "sha256:aaeeb6a479c7667fbe1099af9617c83aaca22182d6cf8c53966491a0f1b7ffb7", size = 98064, upload-time = "2025-05-02T08:33:17.06Z" }, + { url = "https://files.pythonhosted.org/packages/e9/b0/0200da600134e001d91851ddc797809e2fe0ea72de90e09bec5a2fbdaccb/charset_normalizer-3.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:aa6af9e7d59f9c12b33ae4e9450619cf2488e2bbe9b44030905877f0b2324980", size = 105641, upload-time = "2025-05-02T08:33:18.753Z" }, + { url = "https://files.pythonhosted.org/packages/20/94/c5790835a017658cbfabd07f3bfb549140c3ac458cfc196323996b10095a/charset_normalizer-3.4.2-py3-none-any.whl", hash = "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0", size = 52626, upload-time = "2025-05-02T08:34:40.053Z" }, ] [[package]] name = "colorama" version = "0.4.6" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, ] [[package]] @@ -48,6 +49,8 @@ source = { virtual = "." } dependencies = [ { name = "colorama" }, { name = "jinja2" }, + { name = "protobuf" }, + { name = "pyproj" }, { name = "pytest" }, { name = "requests" }, ] @@ -56,6 +59,8 @@ dependencies = [ requires-dist = [ { name = "colorama", specifier = ">=0.4.6" }, { name = "jinja2", specifier = ">=3.1.6" }, + { name = "protobuf", specifier = ">=5.29.1" }, + { name = "pyproj", specifier = ">=3.7.2" }, { name = "pytest", specifier = ">=8.4.1" }, { name = "requests", specifier = ">=2.32.3" }, ] @@ -64,18 +69,18 @@ requires-dist = [ name = "idna" version = "3.10" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 } +sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490, upload-time = "2024-09-15T18:07:39.745Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 }, + { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" }, ] [[package]] name = "iniconfig" version = "2.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793 } +sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793, upload-time = "2025-03-19T20:09:59.721Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050 }, + { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050, upload-time = "2025-03-19T20:10:01.071Z" }, ] [[package]] @@ -85,64 +90,126 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markupsafe" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115 } +sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899 }, + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, ] [[package]] name = "markupsafe" version = "3.0.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537 } +sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537, upload-time = "2024-10-18T15:21:54.129Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274 }, - { url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352 }, - { url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122 }, - { url = "https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085 }, - { url = "https://files.pythonhosted.org/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978 }, - { url = "https://files.pythonhosted.org/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208 }, - { url = "https://files.pythonhosted.org/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357 }, - { url = "https://files.pythonhosted.org/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344 }, - { url = "https://files.pythonhosted.org/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101 }, - { url = "https://files.pythonhosted.org/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603 }, - { url = "https://files.pythonhosted.org/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510 }, - { url = "https://files.pythonhosted.org/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486 }, - { url = "https://files.pythonhosted.org/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480 }, - { url = "https://files.pythonhosted.org/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914 }, - { url = "https://files.pythonhosted.org/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796 }, - { url = "https://files.pythonhosted.org/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473 }, - { url = "https://files.pythonhosted.org/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114 }, - { url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098 }, - { url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208 }, - { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739 }, + { url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274, upload-time = "2024-10-18T15:21:24.577Z" }, + { url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352, upload-time = "2024-10-18T15:21:25.382Z" }, + { url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122, upload-time = "2024-10-18T15:21:26.199Z" }, + { url = "https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085, upload-time = "2024-10-18T15:21:27.029Z" }, + { url = "https://files.pythonhosted.org/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978, upload-time = "2024-10-18T15:21:27.846Z" }, + { url = "https://files.pythonhosted.org/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208, upload-time = "2024-10-18T15:21:28.744Z" }, + { url = "https://files.pythonhosted.org/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357, upload-time = "2024-10-18T15:21:29.545Z" }, + { url = "https://files.pythonhosted.org/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344, upload-time = "2024-10-18T15:21:30.366Z" }, + { url = "https://files.pythonhosted.org/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101, upload-time = "2024-10-18T15:21:31.207Z" }, + { url = "https://files.pythonhosted.org/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603, upload-time = "2024-10-18T15:21:32.032Z" }, + { url = "https://files.pythonhosted.org/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510, upload-time = "2024-10-18T15:21:33.625Z" }, + { url = "https://files.pythonhosted.org/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486, upload-time = "2024-10-18T15:21:34.611Z" }, + { url = "https://files.pythonhosted.org/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480, upload-time = "2024-10-18T15:21:35.398Z" }, + { url = "https://files.pythonhosted.org/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914, upload-time = "2024-10-18T15:21:36.231Z" }, + { url = "https://files.pythonhosted.org/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796, upload-time = "2024-10-18T15:21:37.073Z" }, + { url = "https://files.pythonhosted.org/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473, upload-time = "2024-10-18T15:21:37.932Z" }, + { url = "https://files.pythonhosted.org/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114, upload-time = "2024-10-18T15:21:39.799Z" }, + { url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098, upload-time = "2024-10-18T15:21:40.813Z" }, + { url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208, upload-time = "2024-10-18T15:21:41.814Z" }, + { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739, upload-time = "2024-10-18T15:21:42.784Z" }, ] [[package]] name = "packaging" version = "25.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727 } +sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469 }, + { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, ] [[package]] name = "pluggy" version = "1.6.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412 } +sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538 }, + { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, +] + +[[package]] +name = "protobuf" +version = "6.33.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0a/03/a1440979a3f74f16cab3b75b0da1a1a7f922d56a8ddea96092391998edc0/protobuf-6.33.1.tar.gz", hash = "sha256:97f65757e8d09870de6fd973aeddb92f85435607235d20b2dfed93405d00c85b", size = 443432, upload-time = "2025-11-13T16:44:18.895Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/06/f1/446a9bbd2c60772ca36556bac8bfde40eceb28d9cc7838755bc41e001d8f/protobuf-6.33.1-cp310-abi3-win32.whl", hash = "sha256:f8d3fdbc966aaab1d05046d0240dd94d40f2a8c62856d41eaa141ff64a79de6b", size = 425593, upload-time = "2025-11-13T16:44:06.275Z" }, + { url = "https://files.pythonhosted.org/packages/a6/79/8780a378c650e3df849b73de8b13cf5412f521ca2ff9b78a45c247029440/protobuf-6.33.1-cp310-abi3-win_amd64.whl", hash = "sha256:923aa6d27a92bf44394f6abf7ea0500f38769d4b07f4be41cb52bd8b1123b9ed", size = 436883, upload-time = "2025-11-13T16:44:09.222Z" }, + { url = "https://files.pythonhosted.org/packages/cd/93/26213ff72b103ae55bb0d73e7fb91ea570ef407c3ab4fd2f1f27cac16044/protobuf-6.33.1-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:fe34575f2bdde76ac429ec7b570235bf0c788883e70aee90068e9981806f2490", size = 427522, upload-time = "2025-11-13T16:44:10.475Z" }, + { url = "https://files.pythonhosted.org/packages/c2/32/df4a35247923393aa6b887c3b3244a8c941c32a25681775f96e2b418f90e/protobuf-6.33.1-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:f8adba2e44cde2d7618996b3fc02341f03f5bc3f2748be72dc7b063319276178", size = 324445, upload-time = "2025-11-13T16:44:11.869Z" }, + { url = "https://files.pythonhosted.org/packages/8e/d0/d796e419e2ec93d2f3fa44888861c3f88f722cde02b7c3488fcc6a166820/protobuf-6.33.1-cp39-abi3-manylinux2014_s390x.whl", hash = "sha256:0f4cf01222c0d959c2b399142deb526de420be8236f22c71356e2a544e153c53", size = 339161, upload-time = "2025-11-13T16:44:12.778Z" }, + { url = "https://files.pythonhosted.org/packages/1d/2a/3c5f05a4af06649547027d288747f68525755de692a26a7720dced3652c0/protobuf-6.33.1-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:8fd7d5e0eb08cd5b87fd3df49bc193f5cfd778701f47e11d127d0afc6c39f1d1", size = 323171, upload-time = "2025-11-13T16:44:14.035Z" }, + { url = "https://files.pythonhosted.org/packages/08/b4/46310463b4f6ceef310f8348786f3cff181cea671578e3d9743ba61a459e/protobuf-6.33.1-py3-none-any.whl", hash = "sha256:d595a9fd694fdeb061a62fbe10eb039cc1e444df81ec9bb70c7fc59ebcb1eafa", size = 170477, upload-time = "2025-11-13T16:44:17.633Z" }, ] [[package]] name = "pygments" version = "2.19.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631 } +sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, +] + +[[package]] +name = "pyproj" +version = "3.7.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/04/90/67bd7260b4ea9b8b20b4f58afef6c223ecb3abf368eb4ec5bc2cdef81b49/pyproj-3.7.2.tar.gz", hash = "sha256:39a0cf1ecc7e282d1d30f36594ebd55c9fae1fda8a2622cee5d100430628f88c", size = 226279, upload-time = "2025-08-14T12:05:42.18Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217 }, + { url = "https://files.pythonhosted.org/packages/be/14/faf1b90d267cea68d7e70662e7f88cefdb1bc890bd596c74b959e0517a72/pyproj-3.7.2-cp313-cp313-macosx_13_0_x86_64.whl", hash = "sha256:19466e529b1b15eeefdf8ff26b06fa745856c044f2f77bf0edbae94078c1dfa1", size = 6214580, upload-time = "2025-08-14T12:04:28.804Z" }, + { url = "https://files.pythonhosted.org/packages/35/48/da9a45b184d375f62667f62eba0ca68569b0bd980a0bb7ffcc1d50440520/pyproj-3.7.2-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:c79b9b84c4a626c5dc324c0d666be0bfcebd99f7538d66e8898c2444221b3da7", size = 4615388, upload-time = "2025-08-14T12:04:30.553Z" }, + { url = "https://files.pythonhosted.org/packages/5e/e7/d2b459a4a64bca328b712c1b544e109df88e5c800f7c143cfbc404d39bfb/pyproj-3.7.2-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:ceecf374cacca317bc09e165db38ac548ee3cad07c3609442bd70311c59c21aa", size = 9628455, upload-time = "2025-08-14T12:04:32.435Z" }, + { url = "https://files.pythonhosted.org/packages/f8/85/c2b1706e51942de19076eff082f8495e57d5151364e78b5bef4af4a1d94a/pyproj-3.7.2-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:5141a538ffdbe4bfd157421828bb2e07123a90a7a2d6f30fa1462abcfb5ce681", size = 9514269, upload-time = "2025-08-14T12:04:34.599Z" }, + { url = "https://files.pythonhosted.org/packages/34/38/07a9b89ae7467872f9a476883a5bad9e4f4d1219d31060f0f2b282276cbe/pyproj-3.7.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f000841e98ea99acbb7b8ca168d67773b0191de95187228a16110245c5d954d5", size = 10808437, upload-time = "2025-08-14T12:04:36.485Z" }, + { url = "https://files.pythonhosted.org/packages/12/56/fda1daeabbd39dec5b07f67233d09f31facb762587b498e6fc4572be9837/pyproj-3.7.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8115faf2597f281a42ab608ceac346b4eb1383d3b45ab474fd37341c4bf82a67", size = 10745540, upload-time = "2025-08-14T12:04:38.568Z" }, + { url = "https://files.pythonhosted.org/packages/0d/90/c793182cbba65a39a11db2ac6b479fe76c59e6509ae75e5744c344a0da9d/pyproj-3.7.2-cp313-cp313-win32.whl", hash = "sha256:f18c0579dd6be00b970cb1a6719197fceecc407515bab37da0066f0184aafdf3", size = 5896506, upload-time = "2025-08-14T12:04:41.059Z" }, + { url = "https://files.pythonhosted.org/packages/be/0f/747974129cf0d800906f81cd25efd098c96509026e454d4b66868779ab04/pyproj-3.7.2-cp313-cp313-win_amd64.whl", hash = "sha256:bb41c29d5f60854b1075853fe80c58950b398d4ebb404eb532536ac8d2834ed7", size = 6310195, upload-time = "2025-08-14T12:04:42.974Z" }, + { url = "https://files.pythonhosted.org/packages/82/64/fc7598a53172c4931ec6edf5228280663063150625d3f6423b4c20f9daff/pyproj-3.7.2-cp313-cp313-win_arm64.whl", hash = "sha256:2b617d573be4118c11cd96b8891a0b7f65778fa7733ed8ecdb297a447d439100", size = 6230748, upload-time = "2025-08-14T12:04:44.491Z" }, + { url = "https://files.pythonhosted.org/packages/aa/f0/611dd5cddb0d277f94b7af12981f56e1441bf8d22695065d4f0df5218498/pyproj-3.7.2-cp313-cp313t-macosx_13_0_x86_64.whl", hash = "sha256:d27b48f0e81beeaa2b4d60c516c3a1cfbb0c7ff6ef71256d8e9c07792f735279", size = 6241729, upload-time = "2025-08-14T12:04:46.274Z" }, + { url = "https://files.pythonhosted.org/packages/15/93/40bd4a6c523ff9965e480870611aed7eda5aa2c6128c6537345a2b77b542/pyproj-3.7.2-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:55a3610d75023c7b1c6e583e48ef8f62918e85a2ae81300569d9f104d6684bb6", size = 4652497, upload-time = "2025-08-14T12:04:48.203Z" }, + { url = "https://files.pythonhosted.org/packages/1b/ae/7150ead53c117880b35e0d37960d3138fe640a235feb9605cb9386f50bb0/pyproj-3.7.2-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:8d7349182fa622696787cc9e195508d2a41a64765da9b8a6bee846702b9e6220", size = 9942610, upload-time = "2025-08-14T12:04:49.652Z" }, + { url = "https://files.pythonhosted.org/packages/d8/17/7a4a7eafecf2b46ab64e5c08176c20ceb5844b503eaa551bf12ccac77322/pyproj-3.7.2-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:d230b186eb876ed4f29a7c5ee310144c3a0e44e89e55f65fb3607e13f6db337c", size = 9692390, upload-time = "2025-08-14T12:04:51.731Z" }, + { url = "https://files.pythonhosted.org/packages/c3/55/ae18f040f6410f0ea547a21ada7ef3e26e6c82befa125b303b02759c0e9d/pyproj-3.7.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:237499c7862c578d0369e2b8ac56eec550e391a025ff70e2af8417139dabb41c", size = 11047596, upload-time = "2025-08-14T12:04:53.748Z" }, + { url = "https://files.pythonhosted.org/packages/e6/2e/d3fff4d2909473f26ae799f9dda04caa322c417a51ff3b25763f7d03b233/pyproj-3.7.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8c225f5978abd506fd9a78eaaf794435e823c9156091cabaab5374efb29d7f69", size = 10896975, upload-time = "2025-08-14T12:04:55.875Z" }, + { url = "https://files.pythonhosted.org/packages/f2/bc/8fc7d3963d87057b7b51ebe68c1e7c51c23129eee5072ba6b86558544a46/pyproj-3.7.2-cp313-cp313t-win32.whl", hash = "sha256:2da731876d27639ff9d2d81c151f6ab90a1546455fabd93368e753047be344a2", size = 5953057, upload-time = "2025-08-14T12:04:58.466Z" }, + { url = "https://files.pythonhosted.org/packages/cc/27/ea9809966cc47d2d51e6d5ae631ea895f7c7c7b9b3c29718f900a8f7d197/pyproj-3.7.2-cp313-cp313t-win_amd64.whl", hash = "sha256:f54d91ae18dd23b6c0ab48126d446820e725419da10617d86a1b69ada6d881d3", size = 6375414, upload-time = "2025-08-14T12:04:59.861Z" }, + { url = "https://files.pythonhosted.org/packages/5b/f8/1ef0129fba9a555c658e22af68989f35e7ba7b9136f25758809efec0cd6e/pyproj-3.7.2-cp313-cp313t-win_arm64.whl", hash = "sha256:fc52ba896cfc3214dc9f9ca3c0677a623e8fdd096b257c14a31e719d21ff3fdd", size = 6262501, upload-time = "2025-08-14T12:05:01.39Z" }, + { url = "https://files.pythonhosted.org/packages/42/17/c2b050d3f5b71b6edd0d96ae16c990fdc42a5f1366464a5c2772146de33a/pyproj-3.7.2-cp314-cp314-macosx_13_0_x86_64.whl", hash = "sha256:2aaa328605ace41db050d06bac1adc11f01b71fe95c18661497763116c3a0f02", size = 6214541, upload-time = "2025-08-14T12:05:03.166Z" }, + { url = "https://files.pythonhosted.org/packages/03/68/68ada9c8aea96ded09a66cfd9bf87aa6db8c2edebe93f5bf9b66b0143fbc/pyproj-3.7.2-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:35dccbce8201313c596a970fde90e33605248b66272595c061b511c8100ccc08", size = 4617456, upload-time = "2025-08-14T12:05:04.563Z" }, + { url = "https://files.pythonhosted.org/packages/81/e4/4c50ceca7d0e937977866b02cb64e6ccf4df979a5871e521f9e255df6073/pyproj-3.7.2-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:25b0b7cb0042444c29a164b993c45c1b8013d6c48baa61dc1160d834a277e83b", size = 9615590, upload-time = "2025-08-14T12:05:06.094Z" }, + { url = "https://files.pythonhosted.org/packages/05/1e/ada6fb15a1d75b5bd9b554355a69a798c55a7dcc93b8d41596265c1772e3/pyproj-3.7.2-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:85def3a6388e9ba51f964619aa002a9d2098e77c6454ff47773bb68871024281", size = 9474960, upload-time = "2025-08-14T12:05:07.973Z" }, + { url = "https://files.pythonhosted.org/packages/51/07/9d48ad0a8db36e16f842f2c8a694c1d9d7dcf9137264846bef77585a71f3/pyproj-3.7.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b1bccefec3875ab81eabf49059e2b2ea77362c178b66fd3528c3e4df242f1516", size = 10799478, upload-time = "2025-08-14T12:05:14.102Z" }, + { url = "https://files.pythonhosted.org/packages/85/cf/2f812b529079f72f51ff2d6456b7fef06c01735e5cfd62d54ffb2b548028/pyproj-3.7.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d5371ca114d6990b675247355a801925814eca53e6c4b2f1b5c0a956336ee36e", size = 10710030, upload-time = "2025-08-14T12:05:16.317Z" }, + { url = "https://files.pythonhosted.org/packages/99/9b/4626a19e1f03eba4c0e77b91a6cf0f73aa9cb5d51a22ee385c22812bcc2c/pyproj-3.7.2-cp314-cp314-win32.whl", hash = "sha256:77f066626030f41be543274f5ac79f2a511fe89860ecd0914f22131b40a0ec25", size = 5991181, upload-time = "2025-08-14T12:05:19.492Z" }, + { url = "https://files.pythonhosted.org/packages/04/b2/5a6610554306a83a563080c2cf2c57565563eadd280e15388efa00fb5b33/pyproj-3.7.2-cp314-cp314-win_amd64.whl", hash = "sha256:5a964da1696b8522806f4276ab04ccfff8f9eb95133a92a25900697609d40112", size = 6434721, upload-time = "2025-08-14T12:05:21.022Z" }, + { url = "https://files.pythonhosted.org/packages/ae/ce/6c910ea2e1c74ef673c5d48c482564b8a7824a44c4e35cca2e765b68cfcc/pyproj-3.7.2-cp314-cp314-win_arm64.whl", hash = "sha256:e258ab4dbd3cf627809067c0ba8f9884ea76c8e5999d039fb37a1619c6c3e1f6", size = 6363821, upload-time = "2025-08-14T12:05:22.627Z" }, + { url = "https://files.pythonhosted.org/packages/e4/e4/5532f6f7491812ba782a2177fe9de73fd8e2912b59f46a1d056b84b9b8f2/pyproj-3.7.2-cp314-cp314t-macosx_13_0_x86_64.whl", hash = "sha256:bbbac2f930c6d266f70ec75df35ef851d96fdb3701c674f42fd23a9314573b37", size = 6241773, upload-time = "2025-08-14T12:05:24.577Z" }, + { url = "https://files.pythonhosted.org/packages/20/1f/0938c3f2bbbef1789132d1726d9b0e662f10cfc22522743937f421ad664e/pyproj-3.7.2-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:b7544e0a3d6339dc9151e9c8f3ea62a936ab7cc446a806ec448bbe86aebb979b", size = 4652537, upload-time = "2025-08-14T12:05:26.391Z" }, + { url = "https://files.pythonhosted.org/packages/c7/a8/488b1ed47d25972f33874f91f09ca8f2227902f05f63a2b80dc73e7b1c97/pyproj-3.7.2-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:f7f5133dca4c703e8acadf6f30bc567d39a42c6af321e7f81975c2518f3ed357", size = 9940864, upload-time = "2025-08-14T12:05:27.985Z" }, + { url = "https://files.pythonhosted.org/packages/c7/cc/7f4c895d0cb98e47b6a85a6d79eaca03eb266129eed2f845125c09cf31ff/pyproj-3.7.2-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:5aff3343038d7426aa5076f07feb88065f50e0502d1b0d7c22ddfdd2c75a3f81", size = 9688868, upload-time = "2025-08-14T12:05:30.425Z" }, + { url = "https://files.pythonhosted.org/packages/b2/b7/c7e306b8bb0f071d9825b753ee4920f066c40fbfcce9372c4f3cfb2fc4ed/pyproj-3.7.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:b0552178c61f2ac1c820d087e8ba6e62b29442debddbb09d51c4bf8acc84d888", size = 11045910, upload-time = "2025-08-14T12:05:32.507Z" }, + { url = "https://files.pythonhosted.org/packages/42/fb/538a4d2df695980e2dde5c04d965fbdd1fe8c20a3194dc4aaa3952a4d1be/pyproj-3.7.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:47d87db2d2c436c5fd0409b34d70bb6cdb875cca2ebe7a9d1c442367b0ab8d59", size = 10895724, upload-time = "2025-08-14T12:05:35.465Z" }, + { url = "https://files.pythonhosted.org/packages/e8/8b/a3f0618b03957de9db5489a04558a8826f43906628bb0b766033aa3b5548/pyproj-3.7.2-cp314-cp314t-win32.whl", hash = "sha256:c9b6f1d8ad3e80a0ee0903a778b6ece7dca1d1d40f6d114ae01bc8ddbad971aa", size = 6056848, upload-time = "2025-08-14T12:05:37.553Z" }, + { url = "https://files.pythonhosted.org/packages/bc/56/413240dd5149dd3291eda55aa55a659da4431244a2fd1319d0ae89407cfb/pyproj-3.7.2-cp314-cp314t-win_amd64.whl", hash = "sha256:1914e29e27933ba6f9822663ee0600f169014a2859f851c054c88cf5ea8a333c", size = 6517676, upload-time = "2025-08-14T12:05:39.126Z" }, + { url = "https://files.pythonhosted.org/packages/15/73/a7141a1a0559bf1a7aa42a11c879ceb19f02f5c6c371c6d57fd86cefd4d1/pyproj-3.7.2-cp314-cp314t-win_arm64.whl", hash = "sha256:d9d25bae416a24397e0d85739f84d323b55f6511e45a522dd7d7eae70d10c7e4", size = 6391844, upload-time = "2025-08-14T12:05:40.745Z" }, ] [[package]] @@ -156,9 +223,9 @@ dependencies = [ { name = "pluggy" }, { name = "pygments" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/08/ba/45911d754e8eba3d5a841a5ce61a65a685ff1798421ac054f85aa8747dfb/pytest-8.4.1.tar.gz", hash = "sha256:7c67fd69174877359ed9371ec3af8a3d2b04741818c51e5e99cc1742251fa93c", size = 1517714 } +sdist = { url = "https://files.pythonhosted.org/packages/08/ba/45911d754e8eba3d5a841a5ce61a65a685ff1798421ac054f85aa8747dfb/pytest-8.4.1.tar.gz", hash = "sha256:7c67fd69174877359ed9371ec3af8a3d2b04741818c51e5e99cc1742251fa93c", size = 1517714, upload-time = "2025-06-18T05:48:06.109Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/29/16/c8a903f4c4dffe7a12843191437d7cd8e32751d5de349d45d3fe69544e87/pytest-8.4.1-py3-none-any.whl", hash = "sha256:539c70ba6fcead8e78eebbf1115e8b589e7565830d7d006a8723f19ac8a0afb7", size = 365474 }, + { url = "https://files.pythonhosted.org/packages/29/16/c8a903f4c4dffe7a12843191437d7cd8e32751d5de349d45d3fe69544e87/pytest-8.4.1-py3-none-any.whl", hash = "sha256:539c70ba6fcead8e78eebbf1115e8b589e7565830d7d006a8723f19ac8a0afb7", size = 365474, upload-time = "2025-06-18T05:48:03.955Z" }, ] [[package]] @@ -171,16 +238,16 @@ dependencies = [ { name = "idna" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218 } +sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218, upload-time = "2024-05-29T15:37:49.536Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928 }, + { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928, upload-time = "2024-05-29T15:37:47.027Z" }, ] [[package]] name = "urllib3" version = "2.4.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8a/78/16493d9c386d8e60e442a35feac5e00f0913c0f4b7c217c11e8ec2ff53e0/urllib3-2.4.0.tar.gz", hash = "sha256:414bc6535b787febd7567804cc015fee39daab8ad86268f1310a9250697de466", size = 390672 } +sdist = { url = "https://files.pythonhosted.org/packages/8a/78/16493d9c386d8e60e442a35feac5e00f0913c0f4b7c217c11e8ec2ff53e0/urllib3-2.4.0.tar.gz", hash = "sha256:414bc6535b787febd7567804cc015fee39daab8ad86268f1310a9250697de466", size = 390672, upload-time = "2025-04-10T15:23:39.232Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6b/11/cc635220681e93a0183390e26485430ca2c7b5f9d33b15c74c2861cb8091/urllib3-2.4.0-py3-none-any.whl", hash = "sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813", size = 128680 }, + { url = "https://files.pythonhosted.org/packages/6b/11/cc635220681e93a0183390e26485430ca2c7b5f9d33b15c74c2861cb8091/urllib3-2.4.0-py3-none-any.whl", hash = "sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813", size = 128680, upload-time = "2025-04-10T15:23:37.377Z" }, ] |
