View Issue Details

IDProjectCategoryView StatusLast Update
0003781SAS.ПланетаБаг / Bugpublic06-09-2021 09:48
ReporterVadimK Assigned Tozed  
PrioritynormalSeverityminorReproducibilityalways
Status resolvedResolutionfixed 
PlatformWindowsOS7OS VersionHome Basic
Product Version201212 
Target Version211230Fixed in Version211230 
Summary0003781: При выборе проекции "Geographic Lat/Lon" вылазит "Invalid Floatig Point Operation"
DescriptionПри выборе проекции "Geographic Lat/Lon" и дальнейшей работой с картой вылазит ошибка"Invalid Floatig Point Operation".

Происходит это при малом масштабе, когда весь мир на экране (например самый-самый первый запуск программы), либо когда ползаешь по карте в верхних широтах 85+

Ошибка не пропадает, выскакивает постоянно, пока двигаешь мышью. В момент появления диалогового окошка мышка "прилипает" к полю карты и уползти обратно в "нижние" широты получается с трудом. Если закрыть программу в этот момент, то при следующем запуске она "теряет" карту (серое поле), которая выбрана. В меню Вид-Проекция остаётся только один вариант - "Оригинальная (из ZMP)".

Вывести из ступора программу можно, удалив ini-файлы.
Steps To ReproduceПрилагаю архив с файлами (настройки), созданными программой. Этот набор файлов относится к состоянию ступора, в который программа впадает. Распаковать и закинуть в ночную сборку.
TagsNo tags attached.
Attached Files
Geographic_Lat-Lon.png (14,517 bytes)   
Geographic_Lat-Lon.png (14,517 bytes)   
3781.diff (7,283 bytes)   
diff --git a/Src/CoordConvert/u_ProjectionTypeBase.pas b/Src/CoordConvert/u_ProjectionTypeBase.pas
index 5d527a72c..6c5c34f70 100644
--- a/Src/CoordConvert/u_ProjectionTypeBase.pas
+++ b/Src/CoordConvert/u_ProjectionTypeBase.pas
@@ -47,7 +47,8 @@ type
   protected
     function Relative2LonLatInternal(const APoint: TDoublePoint): TDoublePoint; virtual; abstract;
     function LonLat2RelativeInternal(const APoint: TDoublePoint): TDoublePoint; virtual; abstract;
-  private
+  protected
+    { IProjectionType }
     function GetHash: THashValue;
     function GetDatum: IDatum;
     function GetProjectionEPSG: Integer;
@@ -64,8 +65,8 @@ type
     procedure ValidateRelativePos(var APoint: TDoublePoint);
     procedure ValidateRelativeRect(var ARect: TDoubleRect);
 
-    procedure ValidateLonLatPos(var APoint: TDoublePoint);
-    procedure ValidateLonLatRect(var ARect: TDoubleRect);
+    procedure ValidateLonLatPos(var APoint: TDoublePoint); virtual;
+    procedure ValidateLonLatRect(var ARect: TDoubleRect); virtual;
 
     function CheckRelativePos(const APoint: TDoublePoint): boolean;
     function CheckRelativeRect(const ARect: TDoubleRect): boolean;
diff --git a/Src/CoordConvert/u_ProjectionTypeGELonLat.pas b/Src/CoordConvert/u_ProjectionTypeGELonLat.pas
index 9f3f373ba..e3136726d 100644
--- a/Src/CoordConvert/u_ProjectionTypeGELonLat.pas
+++ b/Src/CoordConvert/u_ProjectionTypeGELonLat.pas
@@ -24,9 +24,7 @@ unit u_ProjectionTypeGELonLat;
 interface
 
 uses
-  t_Hash,
   t_GeoTypes,
-  i_Datum,
   u_ProjectionTypeBase;
 
 type
@@ -34,44 +32,60 @@ type
   protected
     function Relative2LonLatInternal(const APoint: TDoublePoint): TDoublePoint; override;
     function LonLat2RelativeInternal(const APoint: TDoublePoint): TDoublePoint; override;
-  public
-    constructor Create(
-      const AHash: THashValue;
-      const ADatum: IDatum;
-      const AProjEPSG: integer
-    );
+  protected
+    procedure ValidateLonLatPos(var APoint: TDoublePoint); override;
+    procedure ValidateLonLatRect(var ARect: TDoubleRect); override;
   end;
 
 implementation
 
-uses
-  Math;
+const
+  CMaxLatitude = 85.0511287798066;
 
 { TProjectionTypeGELonLat }
 
-constructor TProjectionTypeGELonLat.Create(
-  const AHash: THashValue;
-  const ADatum: IDatum;
-  const AProjEPSG: integer
-);
-begin
-  inherited Create(AHash, ADatum, AProjEPSG);
-end;
-
 function TProjectionTypeGELonLat.LonLat2RelativeInternal(
   const APoint: TDoublePoint
 ): TDoublePoint;
 begin
-  Result.x := (0.5 + APoint.x / 360);
-  Result.y := (0.5 - APoint.y / 360);
+  Result.X := (0.5 + APoint.X / 360);
+  Result.Y := (0.5 - APoint.Y / 360);
 end;
 
 function TProjectionTypeGELonLat.Relative2LonLatInternal(
   const APoint: TDoublePoint
 ): TDoublePoint;
 begin
-  Result.X := (APoint.x - 0.5) * 360;
-  Result.y := -(APoint.y - 0.5) * 360;
+  Result.X :=  (APoint.X - 0.5) * 360;
+  Result.Y := -(APoint.Y - 0.5) * 360;
+end;
+
+procedure _ValidateLonLatPos(var APoint: TDoublePoint); inline;
+begin
+  if APoint.X < -180 then begin
+    APoint.X := -180;
+  end else
+  if APoint.X > 180 then begin
+    APoint.X := 180;
+  end;
+
+  if APoint.Y < -CMaxLatitude then begin
+    APoint.Y := -CMaxLatitude;
+  end else
+  if APoint.Y > CMaxLatitude then begin
+    APoint.Y := CMaxLatitude;
+  end;
+end;
+
+procedure TProjectionTypeGELonLat.ValidateLonLatPos(var APoint: TDoublePoint);
+begin
+  _ValidateLonLatPos(APoint);
+end;
+
+procedure TProjectionTypeGELonLat.ValidateLonLatRect(var ARect: TDoubleRect);
+begin
+  _ValidateLonLatPos(ARect.TopLeft);
+  _ValidateLonLatPos(ARect.BottomRight);
 end;
 
 end.
diff --git a/Src/MapLayers/WindowLayers/ScaleLine/u_WindowLayerScaleLineHorizontal.pas b/Src/MapLayers/WindowLayers/ScaleLine/u_WindowLayerScaleLineHorizontal.pas
index 3ba43ded4..50b9d5749 100644
--- a/Src/MapLayers/WindowLayers/ScaleLine/u_WindowLayerScaleLineHorizontal.pas
+++ b/Src/MapLayers/WindowLayers/ScaleLine/u_WindowLayerScaleLineHorizontal.pas
@@ -104,6 +104,18 @@ begin
   VValidLegendWidth := (Config.Width div 4) * 4;
 
   num := GetMetersPerLine(AVisualCoordConverter, VValidLegendWidth);
+  if num <= 0 then begin
+    DrawScaleLegend(
+      VColor,
+      VOutLineColor,
+      VColor,
+      VValidLegendWidth,
+      ' ',
+      ' ',
+      Layer.Bitmap
+    );
+    Exit;
+  end;
 
   if Config.NumbersFormat = slnfNice then begin
     ModifyLenAndWidth(Num, VValidLegendWidth);
@@ -244,7 +256,9 @@ begin
   VFinishPixel := DoublePoint(VStartPixel.X + 1, VStartPixel.Y);
   VProjection.ValidatePixelPosFloat(VFinishPixel, True);
   VStartLonLat := VProjection.PixelPosFloat2LonLat(VStartPixel);
+  VProjection.ProjectionType.ValidateLonLatPos(VStartLonLat);
   VFinishLonLat := VProjection.PixelPosFloat2LonLat(VFinishPixel);
+  VProjection.ProjectionType.ValidateLonLatPos(VFinishLonLat);
   Result := VProjection.ProjectionType.Datum.CalcDist(VStartLonLat, VFinishLonLat) * ALineWidth;
 end;
 
diff --git a/Src/MapLayers/WindowLayers/ScaleLine/u_WindowLayerScaleLineVertical.pas b/Src/MapLayers/WindowLayers/ScaleLine/u_WindowLayerScaleLineVertical.pas
index a2fbe0551..9167a8763 100644
--- a/Src/MapLayers/WindowLayers/ScaleLine/u_WindowLayerScaleLineVertical.pas
+++ b/Src/MapLayers/WindowLayers/ScaleLine/u_WindowLayerScaleLineVertical.pas
@@ -249,10 +249,12 @@ begin
   VCenterPixelXY := AVisualCoordConverter.GetCenterMapPixelFloat;
   VProjection.ValidatePixelPosFloatStrict(VCenterPixelXY, False);
   VStartLonLat := VProjection.PixelPosFloat2LonLat(VCenterPixelXY);
+  VProjection.ProjectionType.ValidateLonLatPos(VStartLonLat);
 
   VFinishPixelXY := DoublePoint(VCenterPixelXY.X, VCenterPixelXY.Y - ALineHeight / 2);
   if VProjection.CheckPixelPosFloatStrict(VFinishPixelXY) then begin
     VFinishLonLat := VProjection.PixelPosFloat2LonLat(VFinishPixelXY);
+    VProjection.ProjectionType.ValidateLonLatPos(VFinishLonLat);
     AHalfLen := VProjection.ProjectionType.Datum.CalcDist(VStartLonLat, VFinishLonLat);
   end else begin
     AHalfLen := -1;
@@ -261,6 +263,7 @@ begin
   VFinishPixelXY := DoublePoint(VCenterPixelXY.X, VCenterPixelXY.Y - ALineHeight);
   if VProjection.CheckPixelPosFloatStrict(VFinishPixelXY) then begin
     VFinishLonLat := VProjection.PixelPosFloat2LonLat(VFinishPixelXY);
+    VProjection.ProjectionType.ValidateLonLatPos(VFinishLonLat);
     AFullLen := VProjection.ProjectionType.Datum.CalcDist(VStartLonLat, VFinishLonLat);
   end else begin
     AFullLen := -1;
diff --git a/Src/MapLayers/WindowLayers/StatusBar/u_WindowLayerStatusBar.pas b/Src/MapLayers/WindowLayers/StatusBar/u_WindowLayerStatusBar.pas
index ec9862f7c..0062bf674 100644
--- a/Src/MapLayers/WindowLayers/StatusBar/u_WindowLayerStatusBar.pas
+++ b/Src/MapLayers/WindowLayers/StatusBar/u_WindowLayerStatusBar.pas
@@ -489,6 +489,7 @@ begin
   VMapPoint := VVisualCoordConverter.LocalPixel2MapPixelFloat(VMousePos);
   VProjection.ValidatePixelPosFloatStrict(VMapPoint, True);
   VLonLat := VProjection.PixelPosFloat2LonLat(VMapPoint);
+  VProjection.ProjectionType.ValidateLonLatPos(VLonLat);
 
   I := Low(TStatusBarItemID);
   AItems[I].Visible := FConfig.ViewZoomInfo;
3781.diff (7,283 bytes)   

Relationships

related to 0003065 confirmed Создать IProjectionConverter 

Activities

zed

04-09-2021 09:47

manager   ~0020186

Да, есть такая проблема. Эта проекция отличается тем, что карта в ней не квадратная (как для Меркатора на сфере или эллипсоиде), а прямоугольная.

Возможно, достаточно вот тут подправить расчёты для Y, чтобы значения не выходили за +-85 градусов, но не уверен, что это не поломает что-нибудь в другом месте...

vdemidov

04-09-2021 21:19

manager   ~0020188

Last edited: 04-09-2021 21:28

>Возможно, достаточно вот тут подправить расчёты для Y, чтобы значения не выходили за +-85 градусов, но не уверен, что это не поломает что-нибудь в другом месте...
Нет. Там надо разбираться почему именно вылазит ошибка. У всех типов проекций покрытие чуток отличается, так что там есть механизм подгоняющий координты к допустимым при переходах от одной проекциии к другой. Нужно понять почему он не сработал и в чем конкретно. Просто этот тип проекции сильно отличается вот на нем и вылезло, но может вылезти на любой паре разных типов и даже на разных проекциях одного типа с разными параметрами.

PS: Скорее всего где-то забыил вызвать Validate для координат или прямоугольника при переходе между проекциями.

PPS: Собственно IProjectionConverter и планироваля для унификации таких операций, но увы там непочатый край работы. Общая идея: по двум IProjection получаем объект, который умеет правильно конвертировать все используемые в САС примитивы туда и обратно правильно со всеми проверками (ну или вообще ничего не делая, если это одинаковые проекции)

zed

05-09-2021 07:56

manager   ~0020189

Ошибка возникает из-за того, что допустимыми координатами для Lat в географической проекции в SAS считаются фантастические 180..-180. Все валидации вызываются и проходят без ошибок, но потом вызывается IDatum.CalcDist (при перерисовке Масштабной шкалы), который, естественно, падает с такими входными данными.

Попробовал пофиксить конвертацию Relative2LonLat и обратно, чтобы он ограничил Lat диапазоном 90..-90 - ошибка уходит, но тайлы начинают проецироваться неверно. Т.е. страдает уже отображение тайлов.

Сейчас думаю попробовать поиграться с ограничением относительных координат. Дело в том, что сейчас считается, что для всех проекций относительные координаты валидны в диапазоне 0..1. Может, если сделать кастомную валидацию для географической проекции с допустимыми значениями для Y в диапазоне 0..0.5 (что в итоге ограничит и Lat допустимыми значениями), получится ошибку исправить.

zed

05-09-2021 09:08

manager   ~0020190

Вроде получилось исправить. Тестовая сборка: https://disk.yandex.ru/d/cAaF1nBPVee_EQ

vdemidov
Патч приложил в аттаче. Так пойдёт?

vdemidov

06-09-2021 08:21

manager   ~0020191

> Патч приложил в аттаче. Так пойдёт?
Ну как минимум константа
  CMaxLatitude = 85.0511287798066;
фигня. Если уже ограничиваться то 90. А в других типах проекций будут свои ограничения.
А еще изменение форматирования вместе с изменением логики это капец. Невозможно найти где логика менялась.

Issue History

Date Modified Username Field Change
01-09-2021 19:44 VadimK New Issue
01-09-2021 19:44 VadimK File Added: BUG_STATE_SAS.Planet.Nightly.210824.10169.zip
01-09-2021 19:44 VadimK File Added: Geographic_Lat-Lon.png
01-09-2021 19:45 VadimK File Added: InvalidFloatigPointOperation.png
04-09-2021 09:47 zed Note Added: 0020186
04-09-2021 09:47 zed Status new => confirmed
04-09-2021 09:47 zed Product Version .Nightly => 201212
04-09-2021 09:47 zed Target Version => 50xxxx
04-09-2021 21:19 vdemidov Note Added: 0020188
04-09-2021 21:20 vdemidov Note Edited: 0020188
04-09-2021 21:25 vdemidov Relationship added related to 0003065
04-09-2021 21:28 vdemidov Note Edited: 0020188
05-09-2021 07:56 zed Note Added: 0020189
05-09-2021 09:08 zed Note Added: 0020190
05-09-2021 09:08 zed File Added: 3781.diff
06-09-2021 08:21 vdemidov Note Added: 0020191
06-09-2021 09:47 zed Status confirmed => resolved
06-09-2021 09:47 zed Fixed in Version => 211230
06-09-2021 09:47 zed Resolution open => fixed
06-09-2021 09:47 zed Assigned To => zed
06-09-2021 09:48 zed Target Version 50xxxx => 211230
06-09-2021 09:48 zed Summary при выборе проекции "Geographic Lat/Lon" вылазит "Invalid Floatig Point Operation" => При выборе проекции "Geographic Lat/Lon" вылазит "Invalid Floatig Point Operation"
08-08-2025 13:22 zed Category Баг => Баг / Bug